git subrepo commit (merge) mailcow/src/mailcow-dockerized

subrepo: subdir:   "mailcow/src/mailcow-dockerized"
  merged:   "c7b1dc37"
upstream: origin:   "https://github.com/mailcow/mailcow-dockerized.git"
  branch:   "master"
  commit:   "a366494c"
git-subrepo: version:  "0.4.6"
  origin:   "???"
  commit:   "???"
Change-Id: Id574ecd4e02e3c4fbf8a1efd49be11c0b6d19a3f
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/functions.inc.php b/mailcow/src/mailcow-dockerized/data/web/inc/functions.inc.php
index 3bab56b..6418945 100644
--- a/mailcow/src/mailcow-dockerized/data/web/inc/functions.inc.php
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/functions.inc.php
@@ -526,8 +526,9 @@
           ':remote' => get_remote_ip()

         ));

       }

-      catch (Exception $e) {

-        // Do nothing

+      catch (PDOException $e) {

+        # handle the exception here, as the exception handler function results in a white page

+        error_log($e->getMessage(), 0);

       }

     }

   }

@@ -1015,20 +1016,58 @@
   }

   return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];

 }

-function update_sogo_static_view() {

+function update_sogo_static_view($mailbox = null) {

   if (getenv('SKIP_SOGO') == "y") {

     return true;

   }

   global $pdo;

   global $lang;

-  $stmt = $pdo->query("SELECT 'OK' FROM INFORMATION_SCHEMA.TABLES

-    WHERE TABLE_NAME = 'sogo_view'");

-  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));

-  if ($num_results != 0) {

-    $stmt = $pdo->query("REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`)

-      SELECT `c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings` from sogo_view");

-    $stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");

+

+  $mailbox_exists = false;

+  if ($mailbox !== null) {

+    // Check if the mailbox exists

+    $stmt = $pdo->prepare("SELECT username FROM mailbox WHERE username = :mailbox AND active = '1'");

+    $stmt->execute(array(':mailbox' => $mailbox));

+    $row = $stmt->fetch(PDO::FETCH_ASSOC);  

+    if ($row){

+      $mailbox_exists = true;

+    }

   }

+

+  $query = "REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`)

+            SELECT

+              mailbox.username,

+              mailbox.domain,

+              mailbox.username,

+              IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.force_pw_update')) = '0',

+                 IF(JSON_UNQUOTE(JSON_VALUE(attributes, '$.sogo_access')) = 1, password, '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'),

+                 '{SSHA256}A123A123A321A321A321B321B321B123B123B321B432F123E321123123321321'),

+              mailbox.name,

+              mailbox.username,

+              IFNULL(GROUP_CONCAT(ga.aliases ORDER BY ga.aliases SEPARATOR ' '), ''),

+              IFNULL(gda.ad_alias, ''),

+              IFNULL(external_acl.send_as_acl, ''),

+              mailbox.kind,

+              mailbox.multiple_bookings

+            FROM

+              mailbox

+              LEFT OUTER JOIN grouped_mail_aliases ga ON ga.username REGEXP CONCAT('(^|,)', mailbox.username, '($|,)')

+              LEFT OUTER JOIN grouped_domain_alias_address gda ON gda.username = mailbox.username

+              LEFT OUTER JOIN grouped_sender_acl_external external_acl ON external_acl.username = mailbox.username

+            WHERE

+              mailbox.active = '1'";

+  

+  if ($mailbox_exists) {

+    $query .= " AND mailbox.username = :mailbox";

+    $stmt = $pdo->prepare($query);

+    $stmt->execute(array(':mailbox' => $mailbox));

+  } else {

+    $query .= " GROUP BY mailbox.username";

+    $stmt = $pdo->query($query);

+  }

+  

+  $stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");

+  

   flush_memcached();

 }

 function edit_user_account($_data) {

@@ -1739,7 +1778,7 @@
               $_SESSION['return'][] =  array(

                   'type' => 'danger',

                   'log' => array(__FUNCTION__, $username, '*'),

-                  'msg' => array('webauthn_verification_failed', 'authenticator not found')

+                  'msg' => array('webauthn_authenticator_failed')

               );

               return false;

             } 

@@ -1748,11 +1787,20 @@
                 $_SESSION['return'][] =  array(

                     'type' => 'danger',

                     'log' => array(__FUNCTION__, $username, '*'),

-                    'msg' => array('webauthn_verification_failed', 'publicKey not found')

+                    'msg' => array('webauthn_publickey_failed')

                 );

                 return false;

             }

 

+            if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){

+              $_SESSION['return'][] =  array(

+                  'type' => 'danger',

+                  'log' => array(__FUNCTION__, $username, '*'),

+                  'msg' => array('webauthn_username_failed')

+              );

+              return false;

+            }

+

             try {

                 $WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_webauthn['publicKey'], $challenge, null, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN'], $GLOBALS['WEBAUTHN_USER_PRESENT_FLAG']);

             }

@@ -1784,21 +1832,12 @@
                 $_SESSION['return'][] =  array(

                   'type' => 'danger',

                   'log' => array(__FUNCTION__, $username, '*'),

-                  'msg' => array('webauthn_verification_failed', 'could not determine user role')

+                  'msg' => array('webauthn_role_failed')

                 );

                 return false;

               }

             }

 

-            if ($process_webauthn['username'] != $_SESSION['pending_mailcow_cc_username']){

-                $_SESSION['return'][] =  array(

-                    'type' => 'danger',

-                    'log' => array(__FUNCTION__, $username, '*'),

-                    'msg' => array('webauthn_verification_failed', 'user who requests does not match with sql entry')

-                );

-                return false;

-            }

-

             $_SESSION["mailcow_cc_username"] = $process_webauthn['username'];

             $_SESSION['tfa_id'] = $process_webauthn['id'];

             $_SESSION['authReq'] = null;

@@ -2093,6 +2132,120 @@
     break;

   }

 }

+function cors($action, $data = null) {

+  global $redis;

+

+  switch ($action) {

+    case "edit":

+      if ($_SESSION['mailcow_cc_role'] != "admin") {

+        $_SESSION['return'][] =  array(

+          'type' => 'danger',

+          'log' => array(__FUNCTION__, $action, $data),

+          'msg' => 'access_denied'

+        );

+        return false;

+      }    

+

+      $allowed_origins = isset($data['allowed_origins']) ? $data['allowed_origins'] : array($_SERVER['SERVER_NAME']);

+      $allowed_origins = !is_array($allowed_origins) ? array_filter(array_map('trim', explode("\n", $allowed_origins))) : $allowed_origins;

+      foreach ($allowed_origins as $origin) {

+        if (!filter_var($origin, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME) && $origin != '*') {

+          $_SESSION['return'][] = array(

+            'type' => 'danger',

+            'log' => array(__FUNCTION__, $action, $data),

+            'msg' => 'cors_invalid_origin'

+          );

+          return false;

+        }

+      }

+

+      $allowed_methods = isset($data['allowed_methods']) ? $data['allowed_methods'] : array('GET', 'POST', 'PUT', 'DELETE');

+      $allowed_methods  = !is_array($allowed_methods) ? array_map('trim', preg_split( "/( |,|;|\n)/", $allowed_methods)) : $allowed_methods;

+      $available_methods = array('GET', 'POST', 'PUT', 'DELETE');

+      foreach ($allowed_methods as $method) {

+        if (!in_array($method, $available_methods)) {

+          $_SESSION['return'][] = array(

+            'type' => 'danger',

+            'log' => array(__FUNCTION__, $action, $data),

+            'msg' => 'cors_invalid_method'

+          );

+          return false;

+        }

+      }

+

+      try {

+        $redis->hMSet('CORS_SETTINGS', array(

+          'allowed_origins' => implode(', ', $allowed_origins),

+          'allowed_methods' => implode(', ', $allowed_methods)

+        ));   

+      } catch (RedisException $e) {

+        $_SESSION['return'][] = array(

+          'type' => 'danger',

+          'log' => array(__FUNCTION__, $action, $data),

+          'msg' => array('redis_error', $e)

+        );

+        return false;

+      }

+

+      $_SESSION['return'][] = array(

+        'type' => 'success',

+        'log' => array(__FUNCTION__, $action, $data),

+        'msg' => 'cors_headers_edited'

+      );

+      return true;

+    break;

+    case "get":

+      try {

+        $cors_settings                  = $redis->hMGet('CORS_SETTINGS', array('allowed_origins', 'allowed_methods'));

+      } catch (RedisException $e) {

+        $_SESSION['return'][] = array(

+          'type' => 'danger',

+          'log' => array(__FUNCTION__, $action, $data),

+          'msg' => array('redis_error', $e)

+        );

+      }

+

+      $cors_settings                    = !$cors_settings ? array('allowed_origins' => $_SERVER['SERVER_NAME'], 'allowed_methods' => 'GET, POST, PUT, DELETE') : $cors_settings;

+      $cors_settings['allowed_origins'] = empty($cors_settings['allowed_origins']) ? $_SERVER['SERVER_NAME'] : $cors_settings['allowed_origins'];

+      $cors_settings['allowed_methods'] = empty($cors_settings['allowed_methods']) ? 'GET, POST, PUT, DELETE, OPTION' : $cors_settings['allowed_methods'];

+

+      return $cors_settings;

+    break;

+    case "set_headers":

+      $cors_settings = cors('get');

+      // check if requested origin is in allowed origins

+      $allowed_origins = explode(', ', $cors_settings['allowed_origins']);

+      $cors_settings['allowed_origins'] = $allowed_origins[0];

+      if (in_array('*', $allowed_origins)){

+        $cors_settings['allowed_origins'] = '*';

+      } else if (in_array($_SERVER['HTTP_ORIGIN'], $allowed_origins)) {

+        $cors_settings['allowed_origins'] = $_SERVER['HTTP_ORIGIN'];

+      }

+      // always allow OPTIONS for preflight request

+      $cors_settings["allowed_methods"] = empty($cors_settings["allowed_methods"]) ? 'OPTIONS' : $cors_settings["allowed_methods"] . ', ' . 'OPTIONS';

+

+      header('Access-Control-Allow-Origin: ' . $cors_settings['allowed_origins']);

+      header('Access-Control-Allow-Methods: '. $cors_settings['allowed_methods']);

+      header('Access-Control-Allow-Headers: Accept, Content-Type, X-Api-Key, Origin');

+

+      // Access-Control settings requested, this is just a preflight request

+      if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS' && 

+        isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) &&

+        isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {

+  

+        $allowed_methods = explode(', ', $cors_settings["allowed_methods"]);

+        if (in_array($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'], $allowed_methods, true))

+          // method allowed send 200 OK

+          http_response_code(200);

+        else

+          // method not allowed send 405 METHOD NOT ALLOWED

+          http_response_code(405);

+

+        exit;

+      }

+    break;

+  }

+}

 

 function get_logs($application, $lines = false) {

   if ($lines === false) {