2
0
andryyy 8 жил өмнө
parent
commit
9c37cd76e5

+ 19 - 0
data/web/admin.php

@@ -24,6 +24,7 @@ $tfa_data = get_tfa();
     <li role="presentation"><a href="#tab-sogo-logs" aria-controls="tab-sogo-logs" role="tab" data-toggle="tab">SOGo</a></li>
     <li role="presentation"><a href="#tab-fail2ban-logs" aria-controls="tab-fail2ban-logs" role="tab" data-toggle="tab">Fail2ban</a></li>
     <li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
+    <li role="presentation"><a href="#tab-autodiscover-logs" aria-controls="tab-autodiscover-logs" role="tab" data-toggle="tab">Autodiscover</a></li>
     </ul>
     </li>
   </ul>
@@ -469,6 +470,24 @@ XYZ
     </div>
   </div>
 
+  <div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
+    <div class="panel panel-default">
+      <div class="panel-heading">Autodiscover
+        <div class="btn-group pull-right">
+          <a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
+          <ul class="dropdown-menu">
+            <li><a href="#" id="refresh_autodiscover_log"><?=$lang['admin']['refresh'];?></a></li>
+          </ul>
+        </div>
+      </div>
+      <div class="panel-body">
+        <div class="table-responsive">
+          <table class="table table-striped table-condensed" id="autodiscover_log"></table>
+        </div>
+      </div>
+    </div>
+  </div>
+
   </div>
 </div> <!-- /container -->
 <?php

+ 58 - 2
data/web/autodiscover.php

@@ -16,7 +16,7 @@ error_reporting(0);
 $data = trim(file_get_contents("php://input"));
 
 if ($autodiscover_config['autodiscoverType'] == 'activesync') {
-  if (preg_match("/Outlook/i", $_SERVER['HTTP_USER_AGENT'])) {
+  if (preg_match("/(Outlook|Office)/i", $_SERVER['HTTP_USER_AGENT'])) {
     if ($autodiscover_config['useEASforOutlook'] == 'yes') {
       preg_match("/^((?!.*Mac).)*(Outlook|Office).+1[5-9].*/i", $_SERVER['HTTP_USER_AGENT'], $supported_outlook);
       if (empty($supported_outlook)) {
@@ -43,6 +43,25 @@ $login_user = strtolower(trim($_SERVER['PHP_AUTH_USER']));
 $login_role = check_login($login_user, $_SERVER['PHP_AUTH_PW']);
 
 if (!isset($_SERVER['PHP_AUTH_USER']) OR $login_role !== "user") {
+  try {
+    $json = json_encode(
+      array(
+        "time" => time(),
+        "ua" => $_SERVER['HTTP_USER_AGENT'],
+        "user" => "none",
+        "service" => "Error: must be authenticated"
+      )
+    );
+    $redis->lPush('AUTODISCOVER_LOG', $json);
+    $redis->lTrim('AUTODISCOVER_LOG', 0, 100);
+  }
+  catch (RedisException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'Redis: '.$e
+    );
+    return false;
+  }
   header('WWW-Authenticate: Basic realm=""');
   header('HTTP/1.0 401 Unauthorized');
   exit(0);
@@ -56,6 +75,25 @@ else {
 <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
 <?php
       if(!$data) {
+        try {
+          $json = json_encode(
+            array(
+              "time" => time(),
+              "ua" => $_SERVER['HTTP_USER_AGENT'],
+              "user" => $_SERVER['PHP_AUTH_USER'],
+              "service" => "Error: invalid or missing request data"
+            )
+          );
+          $redis->lPush('AUTODISCOVER_LOG', $json);
+          $redis->lTrim('AUTODISCOVER_LOG', 0, 100);
+        }
+        catch (RedisException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'Redis: '.$e
+          );
+          return false;
+        }
         list($usec, $sec) = explode(' ', microtime());
 ?>
   <Response>
@@ -91,7 +129,25 @@ else {
       else {
         $displayname = $email;
       }
-
+      try {
+        $json = json_encode(
+          array(
+            "time" => time(),
+            "ua" => $_SERVER['HTTP_USER_AGENT'],
+            "user" => $_SERVER['PHP_AUTH_USER'],
+            "service" => $autodiscover_config['autodiscoverType']
+          )
+        );
+        $redis->lPush('AUTODISCOVER_LOG', $json);
+        $redis->lTrim('AUTODISCOVER_LOG', 0, 100);
+      }
+      catch (RedisException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'Redis: '.$e
+        );
+        return false;
+      }
       if ($autodiscover_config['autodiscoverType'] == 'imap') {
 ?>
   <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a">

+ 119 - 0
data/web/inc/functions.autoconfiguration.inc.php

@@ -0,0 +1,119 @@
+<?php
+function autoconfiguration($_action, $_type, $_data = null) {
+	global $pdo;
+	global $lang;
+  switch ($_action) {
+    case 'edit':
+      if (!isset($_SESSION['acl']['eas_autoconfig']) || $_SESSION['acl']['eas_autoconfig'] != "1" ) {
+        $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+      switch ($_type) {
+        case 'autodiscover':
+          $objects = (array)$_data['object'];
+          foreach ($objects as $object) {
+            if (is_valid_domain_name($object) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+              $exclude_regex = (isset($_data['exclude_regex'])) ? $_data['exclude_regex'] : null;
+              $exclude_regex = (isset($_data['exclude_regex'])) ? $_data['exclude_regex'] : null;
+              try {
+                $stmt = $pdo->prepare("SELECT COUNT(`domain`) AS `domain_c` FROM `autodiscover`
+                  WHERE `domain` = :domain");
+                $stmt->execute(array(':domain' => $object));
+                $num_results = $stmt->fetchColumn();
+                if ($num_results > 0) {
+                  $stmt = $pdo->prepare("SELECT COUNT(`domain`) AS `domain_c` FROM `autodiscover`
+                    WHERE `domain` = :domain");
+                }
+              }
+              catch(PDOException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'MySQL: '.$e
+                );
+                return false;
+              }
+            }
+            elseif (filter_var($object, FILTER_VALIDATE_EMAIL) === true && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['domain_modified'], htmlspecialchars(implode(', ', $objects)))
+          );
+        break;
+      }
+    break;
+    case 'get':
+      switch ($_type) {
+        case 'autodiscover':
+          $autodiscover = array();
+            if (is_valid_domain_name($_data) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              try {
+                $stmt = $pdo->prepare("SELECT * FROM `autodiscover`
+                  WHERE `domain` = :domain");
+                $stmt->execute(array(':domain' => $_data));
+                $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+                while($row = array_shift($rows)) {
+                  $autodiscover['mailbox'] = $row['mailbox'];
+                  $autodiscover['domain'] = $row['domain'];
+                  $autodiscover['service'] = $row['service'];
+                  $autodiscover['exclude_regex'] = $row['exclude_regex'];
+                  $autodiscover['created'] = $row['created'];
+                  $autodiscover['modified'] = $row['modified'];
+                }
+              }
+              catch(PDOException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'MySQL: '.$e
+                );
+                return false;
+              }
+            }
+            elseif (filter_var($_data, FILTER_VALIDATE_EMAIL) === true && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              try {
+                $stmt = $pdo->prepare("SELECT * FROM `autodiscover`
+                  WHERE `mailbox` = :mailbox");
+                $stmt->execute(array(':mailbox' => $_data));
+                $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+                while($row = array_shift($rows)) {
+                  $autodiscover['mailbox'] = $row['mailbox'];
+                  $autodiscover['domain'] = $row['domain'];
+                  $autodiscover['service'] = $row['service'];
+                  $autodiscover['exclude_regex'] = $row['exclude_regex'];
+                  $autodiscover['created'] = $row['created'];
+                  $autodiscover['modified'] = $row['modified'];
+                }
+              }
+              catch(PDOException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'MySQL: '.$e
+                );
+                return false;
+              }
+            }
+          return $autodiscover;
+        break;
+      }
+    break;
+    case 'reset':
+      switch ($_type) {
+        case 'autodiscover':
+          if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+            return false;
+          }
+        break;
+      }
+    break;
+  }
+}
+$miau = "Microsoft Office/15.0 (Windows NT 5.1; macOS Outlook 16.0.4734; Pro)";
+preg_match("/^((?!.*Mac|.*emClient).)*(Outlook|Office).+1[5-9].*/i", $miau, $output_array);
+if (empty($output_array)) {
+  echo "imap";
+}

+ 8 - 0
data/web/inc/functions.inc.php

@@ -900,6 +900,14 @@ function get_logs($container, $lines = 100) {
       return $data_array;
     }
   }
+  if ($container == "autodiscover-mailcow") {
+    if ($data = $redis->lRange('AUTODISCOVER_LOG', 0, $lines)) {
+      foreach ($data as $json_line) {
+        $data_array[] = json_decode($json_line, true);
+      }
+      return $data_array;
+    }
+  }
   if ($container == "rspamd-history") {
     $curl = curl_init();
     curl_setopt($curl, CURLOPT_URL,"http://rspamd-mailcow:11334/history");

+ 51 - 0
data/web/js/admin.js

@@ -124,6 +124,10 @@ jQuery(function($){
     e.preventDefault();
     draw_postfix_logs();
   });
+  $("#refresh_autodiscover_log").on('click', function(e) {
+    e.preventDefault();
+    draw_autodiscover_logs();
+  });
   $("#refresh_dovecot_log").on('click', function(e) {
     e.preventDefault();
     draw_dovecot_logs();
@@ -192,6 +196,52 @@ jQuery(function($){
       }
     });
   }
+  function draw_autodiscover_logs() {
+    ft_autodiscover_logs = FooTable.init('#autodiscover_log', {
+      "columns": [
+        {"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
+        {"name":"ua","title":"User-Agent","style":{"min-width":"200px"}},
+        {"name":"user","title":"Username","style":{"min-width":"200px"}},
+        {"name":"service","title":"Service"},
+      ],
+      "rows": $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/logs/autodiscover/100',
+        jsonp: false,
+        error: function () {
+          console.log('Cannot draw autodiscover log table');
+        },
+        success: function (data) {
+          $.each(data, function (i, item) {
+            item.ua = '<span style="font-size:small">' + item.ua + '</span>';
+            if (item.service == "activesync") {
+              item.service = '<span class="label label-info">ActiveSync</span>';
+            }
+            else if (item.service == "imap") {
+              item.service = '<span class="label label-success">IMAP, SMTP, Cal-/CardDAV</span>';
+            }
+            else {
+              item.service = '<span class="label label-danger">' + item.service + '</span>';
+            }
+          });
+        }
+      }),
+      "empty": lang.empty,
+      "paging": {
+        "enabled": true,
+        "limit": 5,
+        "size": log_pagination_size
+      },
+      "filtering": {
+        "enabled": true,
+        "position": "left",
+        "placeholder": lang.filter_table
+      },
+      "sorting": {
+        "enabled": true
+      }
+    });
+  }
   function draw_fail2ban_logs() {
     ft_fail2ban_logs = FooTable.init('#fail2ban_log', {
       "columns": [
@@ -637,6 +687,7 @@ jQuery(function($){
     });
   }
   draw_postfix_logs();
+  draw_autodiscover_logs();
   draw_dovecot_logs();
   draw_sogo_logs();
   draw_fail2ban_logs();

+ 14 - 0
data/web/json_api.php

@@ -768,6 +768,20 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
                 else {
                   echo '{}';
                 }
+              case "autodiscover":
+                if (isset($extra) && !empty($extra)) {
+                  $extra = intval($extra);
+                  $logs = get_logs('autodiscover-mailcow', $extra);
+                }
+                else {
+                  $logs = get_logs('autodiscover-mailcow', -1);
+                }
+                if (isset($logs) && !empty($logs)) {
+                  echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+                }
+                else {
+                  echo '{}';
+                }
               break;
               case "sogo":
                 if (isset($extra) && !empty($extra)) {