changeset 18:b631c1c7a3ce

More cleaning up more php8 Warnings/deprecations, including _many_ ediff-based fixes on rcmail_output_html.php
author Charlie Root
date Tue, 07 Oct 2025 11:20:40 -0400
parents dd5ed6ef69c9
children b6a96bdd6b29
files program/include/rcmail_output_html.php program/lib/Roundcube/rcube_db_pgsql.php program/lib/Roundcube/rcube_imap_generic.php program/lib/Roundcube/rcube_mime.php program/steps/mail/getunread.inc
diffstat 5 files changed, 196 insertions(+), 135 deletions(-) [+]
line wrap: on
line diff
--- a/program/include/rcmail_output_html.php	Mon Oct 06 12:20:32 2025 -0400
+++ b/program/include/rcmail_output_html.php	Tue Oct 07 11:20:40 2025 -0400
@@ -70,7 +70,7 @@
         $this->set_env('task', $task);
         $this->set_env('x_frame_options', $this->config->get('x_frame_options', 'sameorigin'));
         $this->set_env('standard_windows', (bool) $this->config->get('standard_windows'));
-        $this->set_env('locale', $_SESSION['language']);
+        $this->set_env('locale', !empty($_SESSION['language']) ? $_SESSION['language'] : 'en_US');
         $this->set_env('devel_mode', $this->devel_mode);
 
         // add cookie info
@@ -85,10 +85,13 @@
 
         $this->set_assets_path($this->config->get('assets_path'), $this->config->get('assets_dir'));
 
-        if (!empty($_REQUEST['_extwin']))
+        if (!empty($_REQUEST['_extwin'])) {
             $this->set_env('extwin', 1);
-        if ($this->framed || $framed)
+        }
+
+        if ($this->framed || $framed) {
             $this->set_env('framed', 1);
+        }
 
         $lic = <<<EOF
 /*
@@ -210,14 +213,19 @@
         if (!empty($this->pagetitle)) {
             $title = $this->pagetitle;
         }
-        else if ($this->env['task'] == 'login') {
-            $title = $this->app->gettext(array(
-                'name' => 'welcome',
-                'vars' => array('product' => $this->config->get('product_name')
-            )));
+        else if (isset($this->env['task'])) {
+            if ($this->env['task'] == 'login') {
+                $title = $this->app->gettext([
+                        'name' => 'welcome',
+                        'vars' => ['product' => $this->config->get('product_name')]
+                ]);
+            }
+            else {
+                $title = ucfirst($this->env['task']);
+            }
         }
         else {
-            $title = ucfirst($this->env['task']);
+            $title = '';
         }
 
         return $title;
@@ -285,13 +293,13 @@
         $path_elements = explode('/', $skin_path);
         $skin_id       = end($path_elements);
 
-        if (!$meta['name']) {
+        if (empty($meta['name'])) {
             $meta['name'] = $skin_id;
         }
 
         $this->skins[$skin_id] = $meta;
 
-        if ($meta['extends']) {
+        if (!empty($meta['extends'])) {
             $path = RCUBE_INSTALL_PATH . 'skins/';
             if (is_dir($path . $meta['extends']) && is_readable($path . $meta['extends'])) {
                 $this->load_skin('skins/' . $meta['extends']);
@@ -310,8 +318,9 @@
     {
         foreach ($this->skin_paths as $skin_path) {
             $filename = RCUBE_INSTALL_PATH . $skin_path . '/templates/' . $name . '.html';
-            if ((is_file($filename) && is_readable($filename))
-                || ($this->deprecated_templates[$name] && $this->template_exists($this->deprecated_templates[$name]))
+            if (
+                (is_file($filename) && is_readable($filename))
+                || (!empty($this->deprecated_templates[$name]) && $this->template_exists($this->deprecated_templates[$name]))
             ) {
                 return true;
             }
@@ -416,8 +425,9 @@
                     $vars = array_map(array('rcube','Q'), $vars);
                 $msgtext = $this->app->gettext(array('name' => $message, 'vars' => $vars));
             }
-            else
+            else {
                 $msgtext = $message;
+            }
 
             $this->message = $message;
             $this->command('display_message', $msgtext, $type, $timeout * 1000);
@@ -462,7 +472,7 @@
      */
     public function redirect($p = array(), $delay = 1, $secure = false)
     {
-        if ($this->env['extwin'])
+      if (!empty($this->env['extwin']))
             $p['extwin'] = 1;
         $location = $this->app->url($p, false, false, $secure);
         header('Location: ' . $location);
@@ -525,10 +535,12 @@
         }
 
         // write all javascript commands
-        $this->add_script($commands, 'head_top');
+        if (!empty($commands)) {
+	  $this->add_script($commands, 'head_top');
+	}
 
         // allow (legal) iframe content to be loaded
-        $iframe = $this->framed || $this->env['framed'];
+        $iframe = $this->framed || !empty($this->env['framed']);
         if (!headers_sent() && $iframe && ($xopt = $this->app->config->get('x_frame_options', 'sameorigin'))) {
             if (strtolower($xopt) != 'sameorigin') {
                 header('X-Frame-Options: sameorigin', true);
@@ -583,8 +595,9 @@
             $path = RCUBE_INSTALL_PATH . "$skin_path/templates/$name.html";
 
             // fallback to deprecated template names
-            if (!is_readable($path) && ($dname = $this->deprecated_templates[$realname])) {
-                $path = RCUBE_INSTALL_PATH . "$skin_path/templates/$dname.html";
+            if (!is_readable($path) && !empty($this->deprecated_templates[$realname])) {
+                $dname = $this->deprecated_templates[$realname];
+                $path  = RCUBE_INSTALL_PATH . "$skin_path/templates/$dname.html";
 
                 if (is_readable($path)) {
                     rcube::raise_error(array(
@@ -631,7 +644,7 @@
             );
         }
 
-        // parse for specialtags
+        // parse for special tags
         $output = $this->parse_conditions($templ);
         $output = $this->parse_xml($output);
 	#rcube::write_log('mail',"px: ".strlen($output));
@@ -664,6 +677,7 @@
     {
         $out             = '';
         $parent_commands = 0;
+        $parent_prefix   = '';
         $top_commands    = array();
 
         // these should be always on top,
@@ -676,7 +690,7 @@
         }
 
         // unlock interface after iframe load
-        $unlock = preg_replace('/[^a-z0-9]/i', '', $_REQUEST['_unlock']);
+        $unlock = isset($_REQUEST['_unlock']) ? preg_replace('/[^a-z0-9]/i', '', $_REQUEST['_unlock']) : 0;
         if ($this->framed) {
             $top_commands[] = array('iframe_loaded', $unlock);
         }
@@ -729,7 +743,7 @@
      */
     public function abs_url($str, $search_path = false)
     {
-        if ($str[0] == '/') {
+        if (isset($str[0]) && $str[0] == '/') {
             if ($search_path && ($file_url = $this->get_skin_file($str, $skin_path))) {
                 return $file_url;
             }
@@ -877,7 +891,7 @@
     }
 
     /**
-     * Public wrapper to dipp into template parsing.
+     * Public wrapper to dip into template parsing.
      *
      * @param string $input Template content
      *
@@ -957,7 +971,7 @@
         $out    = $matches[0];
         $attrib = html::parse_attrib_string($matches[1]);
 
-        if (strtolower($attrib['method']) == 'post') {
+        if (!empty($attrib['method']) && strtolower($attrib['method']) == 'post') {
             $hidden = new html_hiddenfield(array('name' => '_token', 'value' => $this->app->get_request_token()));
             $out .= "\n" . $hidden->show();
         }
@@ -987,10 +1001,10 @@
             array(
                 "\$_SESSION['\\1']",
                 "\$this->app->config->get('\\1',rcube_utils::get_boolean('\\3'))",
-                "\$this->env['\\1']",
+                "(\$this->env['\\1'] ?? null)",
                 "rcube_utils::get_input_value('\\1', rcube_utils::INPUT_GPC)",
-                "\$_COOKIE['\\1']",
-                "\$this->browser->{'\\1'}",
+                "(\$_COOKIE['\\1'] ?? null)",
+                "(\$this->browser->{'\\1'} ?? null)",
                 "'{$this->template_name}'",
             ),
             $expression
@@ -1046,7 +1060,7 @@
         switch ($command) {
             // return a button
             case 'button':
-                if ($attrib['name'] || $attrib['command']) {
+                if (!empty($attrib['name']) || !empty($attrib['command'])) {
 		  #rcube::write_log('lab',"button in: ".print_r($attrib,true));
                     $but = $this->button($attrib);
 		    #rcube::write_log('lab',"button out:\n$but");
@@ -1062,18 +1076,26 @@
 
             // show a label
             case 'label':
-                if ($attrib['expression'])
+                if (!empty($attrib['expression'])) {
                     $attrib['name'] = $this->eval_expression($attrib['expression']);
+                }
 
-                if ($attrib['name'] || $attrib['command']) {
-                    $vars = $attrib + array('product' => $this->config->get('product_name'));
+                if (!empty($attrib['name']) || !empty($attrib['command'])) {
+                    $vars = $attrib + ['product' => $this->config->get('product_name')];
                     unset($vars['name'], $vars['command']);
 
-                    $label   = $this->app->gettext($attrib + array('vars' => $vars));
-                    $quoting = !empty($attrib['quoting']) ? strtolower($attrib['quoting']) : (rcube_utils::get_boolean((string)$attrib['html']) ? 'no' : '');
+                    $label   = $this->app->gettext($attrib + ['vars' => $vars]);
+                    $quoting = null;
+
+                    if (!empty($attrib['quoting'])) {
+                        $quoting = strtolower($attrib['quoting']);
+                    }
+                    else if (isset($attrib['html'])) {
+                        $quoting = rcube_utils::get_boolean((string) $attrib['html']) ? 'no' : '';
+                    }
 
                     // 'noshow' can be used in skins to define new labels
-                    if ($attrib['noshow']) {
+                    if (!empty($attrib['noshow'])) {
                         return '';
                     }
 
@@ -1101,8 +1123,13 @@
             // include a file
             case 'include':
                 $old_base_path = $this->base_path;
-                if (!empty($attrib['skin_path'])) $attrib['skinpath'] = $attrib['skin_path'];
-                if ($path = $this->get_skin_file($attrib['file'], $skin_path, $attrib['skinpath'])) {
+                $attr_skin_path = !empty($attrib['skinpath']) ? $attrib['skinpath'] : null;
+
+                if (!empty($attrib['skin_path'])) {
+                    $attr_skin_path = $attrib['skin_path'];
+                }
+
+                if ($path = $this->get_skin_file($attrib['file'], $skin_path, $attr_skin_path)) {
                     // set base_path to core skin directory (not plugin's skin)
                     $this->base_path = preg_replace('!plugins/\w+/!', '', $skin_path);
                     $path = realpath(RCUBE_INSTALL_PATH . $path);
@@ -1129,7 +1156,7 @@
 
             // define a container block
             case 'container':
-                if ($attrib['name'] && $attrib['id']) {
+                if (!empty($attrib['name']) && !empty($attrib['id'])) {
                     $this->command('gui_container', $attrib['name'], $attrib['id']);
                     // let plugins insert some content here
                     $hook = $this->app->plugins->exec_hook("template_container", $attrib);
@@ -1141,20 +1168,19 @@
             case 'object':
                 $object  = strtolower($attrib['name']);
                 $content = '';
+                $handler = null;
 
-                // we are calling a class/method
-                if (($handler = $this->object_handlers[$object]) && is_array($handler)) {
-                    if (is_callable($handler)) {
-                        // We assume that objects with src attribute are internal (in most
-                        // cases this is a watermark frame). We need this to make sure assets_path
-                        // is added to the internal assets paths
-                        $external = empty($attrib['src']);
-                        $content  = call_user_func($handler, $attrib);
-                    }
+                if (!empty($this->object_handlers[$object])) {
+                    $handler = $this->object_handlers[$object];
                 }
                 // execute object handler function
-                else if (function_exists($handler)) {
-                    $content = call_user_func($handler, $attrib);
+                if (is_callable($handler)) {
+
+                    // We assume that objects with src attribute are internal (in most
+                    // cases this is a watermark frame). We need this to make sure assets_path
+                    // is added to the internal assets paths
+                    $external = empty($attrib['src']);
+                    $content  = call_user_func($handler, $attrib);
                 }
                 else if ($object == 'doctype') {
                     $content = html::doctype($attrib['value']);
@@ -1185,11 +1211,11 @@
                 else if ($object == 'version') {
                     $ver = (string)RCMAIL_VERSION;
                     if (is_file(RCUBE_INSTALL_PATH . '.svn/entries')) {
-                        if (preg_match('/Revision:\s(\d+)/', @shell_exec('svn info'), $regs))
+                        if (preg_match('/Revision:\s(\d+)/', (string) @shell_exec('svn info'), $regs))
                           $ver .= ' [SVN r'.$regs[1].']';
                     }
                     else if (is_file(RCUBE_INSTALL_PATH . '.git/index')) {
-                        if (preg_match('/Date:\s+([^\n]+)/', @shell_exec('git log -1'), $regs)) {
+                        if (preg_match('/Date:\s+([^\n]+)/', (string) @shell_exec('git log -1'), $regs)) {
                             if ($date = date('Ymd.Hi', strtotime($regs[1]))) {
                                 $ver .= ' [GIT '.$date.']';
                             }
@@ -1212,7 +1238,7 @@
                 }
 
                 // exec plugin hooks for this template object
-                $hook = $this->app->plugins->exec_hook("template_object_$object", $attrib + array('content' => $content));
+                $hook = $this->app->plugins->exec_hook("template_object_$object", $attrib + array('content' => (string) $content));
 
                 if (strlen($hook['content']) && !empty($external)) {
                     $object_id                 = uniqid('TEMPLOBJECT:', true);
@@ -1234,11 +1260,11 @@
 
                 switch ($var[0]) {
                     case 'env':
-                        $value = $this->env[$name];
+                        $value = $this->env[$name] ?? null;
                         break;
                     case 'config':
                         $value = $this->config->get($name);
-                        if (is_array($value) && $value[$_SESSION['storage_host']]) {
+                        if (is_array($value) && !empty($value[$_SESSION['storage_host']])) {
                             $value = $value[$_SESSION['storage_host']];
                         }
                         break;
@@ -1246,13 +1272,13 @@
                         $value = rcube_utils::get_input_value($name, rcube_utils::INPUT_GPC);
                         break;
                     case 'session':
-                        $value = $_SESSION[$name];
+                        $value = $_SESSION[$name] ?? '';
                         break;
                     case 'cookie':
                         $value = htmlspecialchars($_COOKIE[$name]);
                         break;
                     case 'browser':
-                        $value = $this->browser->{$name};
+                        $value = $this->browser->{$name} ?? '';
                         break;
                 }
 
@@ -1292,7 +1318,7 @@
     {
         // insert objects' contents
         foreach ($this->objects as $key => $val) {
-            $output = str_replace($key, $val, $output, $count);
+	  $output = str_replace($key, (string) $val, $output, $count);
             if ($count) {
                 $this->objects[$key] = null;
             }
@@ -1321,12 +1347,12 @@
         // these commands can be called directly via url
         $a_static_commands = array('compose', 'list', 'preferences', 'folders', 'identities');
 
-        if (!($attrib['command'] || $attrib['name'] || $attrib['href'])) {
+        if (empty($attrib['command']) && empty($attrib['name']) && empty($attrib['href'])) {
             return '';
         }
 
         // try to find out the button type
-        if ($attrib['type']) {
+        if (!empty($attrib['type'])) {
             $attrib['type'] = strtolower($attrib['type']);
             if ($pos = strpos($attrib['type'], '-menuitem')) {
                 $attrib['type'] = substr($attrib['type'], 0, -9);
@@ -1334,16 +1360,16 @@
             }
         }
         else {
-            $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link';
+	  $attrib['type'] = (!empty($attrib['image']) || !empty($attrib['imagepas']) || !empty($attrib['imageact'])) ? 'image' : 'link';
         }
 
-        $command = $attrib['command'];
+        $command = $attrib['command']??'';
 
-        if ($attrib['task']) {
+        if (!empty($attrib['task'])) {
             $element = $command = $attrib['task'] . '.' . $command;
         }
         else {
-            $element = ($this->env['task'] ? $this->env['task'] . '.' : '') . $command;
+	  $element = (!empty($this->env['task']) ? $this->env['task'] . '.' : '') . $command;
         }
 
         if ($disabled_actions === null) {
@@ -1355,37 +1381,44 @@
             return '';
         }
 
-        if (!$attrib['image']) {
-            $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact'];
+        if (empty($attrib['image'])) {
+            if (!empty($attrib['imagepas'])) {
+                $attrib['image'] = $attrib['imagepas'];
+            }
+            else if (!empty($attrib['imageact'])) {
+                $attrib['image'] = $attrib['imageact'];
+            }
         }
 
-        if (!$attrib['id']) {
+        if (empty($attrib['id'])) {
             $attrib['id'] =  sprintf('rcmbtn%d', $s_button_count++);
         }
         // get localized text for labels and titles
-        if ($attrib['title']) {
-            $attrib['title'] = html::quote($this->app->gettext($attrib['title'], $attrib['domain']));
+        $domain = !empty($attrib['domain']) ? $attrib['domain'] : null;
+        if (!empty($attrib['title'])) {
+            $attrib['title'] = html::quote($this->app->gettext($attrib['title'], $domain));
         }
-        if ($attrib['label']) {
-            $attrib['label'] = html::quote($this->app->gettext($attrib['label'], $attrib['domain']));
+        if (!empty($attrib['label'])) {
+            $attrib['label'] = html::quote($this->app->gettext($attrib['label'], $domain));
         }
-        if ($attrib['alt']) {
-            $attrib['alt'] = html::quote($this->app->gettext($attrib['alt'], $attrib['domain']));
+        if (!empty($attrib['alt'])) {
+            $attrib['alt'] = html::quote($this->app->gettext($attrib['alt'], $domain));
         }
 
         // set accessibility attributes
-        if (!$attrib['role']) {
+        if (empty($attrib['role'])) {
             $attrib['role'] = 'button';
         }
         if (!empty($attrib['class']) && !empty($attrib['classact']) || !empty($attrib['imagepas']) && !empty($attrib['imageact'])) {
-            if (array_key_exists('tabindex', $attrib))
+            if (array_key_exists('tabindex', $attrib)) {
                 $attrib['data-tabindex'] = $attrib['tabindex'];
-            $attrib['tabindex'] = '-1';  // disable button by default
+            }
+            $attrib['tabindex']      = '-1';  // disable button by default
             $attrib['aria-disabled'] = 'true';
         }
 
         // set title to alt attribute for IE browsers
-        if ($this->browser->ie && !$attrib['title'] && $attrib['alt']) {
+        if ($this->browser->ie && empty($attrib['title']) && !empty($attrib['alt'])) {
             $attrib['title'] = $attrib['alt'];
         }
 
@@ -1395,16 +1428,16 @@
         }
 
         // register button in the system
-        if ($attrib['command']) {
+        if (!empty($attrib['command'])) {
             $this->add_script(sprintf(
                 "%s.register_button('%s', '%s', '%s', '%s', '%s', '%s');",
                 self::JS_OBJECT_NAME,
                 $command,
                 $attrib['id'],
                 $attrib['type'],
-                $attrib['imageact'] ? $this->abs_url($attrib['imageact']) : $attrib['classact'],
-                $attrib['imagesel'] ? $this->abs_url($attrib['imagesel']) : $attrib['classsel'],
-                $attrib['imageover'] ? $this->abs_url($attrib['imageover']) : ''
+                !empty($attrib['imageact']) ? $this->abs_url($attrib['imageact']) : (!empty($attrib['classact']) ? $attrib['classact'] : ''),
+                !empty($attrib['imagesel']) ? $this->abs_url($attrib['imagesel']) : (!empty($attrib['classsel']) ? $attrib['classsel'] : ''),
+                !empty($attrib['imageover']) ? $this->abs_url($attrib['imageover']) : ''
             ));
 
             // make valid href to specific buttons
@@ -1412,8 +1445,8 @@
                 $attrib['href']    = $this->app->url(array('task' => $attrib['command']));
                 $attrib['onclick'] = sprintf("return %s.command('switch-task','%s',this,event)", self::JS_OBJECT_NAME, $attrib['command']);
             }
-            else if ($attrib['task'] && in_array($attrib['task'], rcmail::$main_tasks)) {
-                $attrib['href'] = $this->app->url(array('action' => $attrib['command'], 'task' => $attrib['task']));
+            else if (!empty($attrib['task']) && in_array($attrib['task'], rcmail::$main_tasks)) {
+                $attrib['href'] = $this->app->url(['action' => $attrib['command'], 'task' => $attrib['task']]);
             }
             else if (in_array($attrib['command'], $a_static_commands)) {
                 $attrib['href'] = $this->app->url(array('action' => $attrib['command']));
@@ -1424,23 +1457,27 @@
         }
 
         // overwrite attributes
-        if (!$attrib['href']) {
+        if (empty($attrib['href'])) {
             $attrib['href'] = '#';
         }
-        if ($attrib['task']) {
-            if ($attrib['classact'])
+
+        if (!empty($attrib['task'])) {
+            if (!empty($attrib['classact'])) {
                 $attrib['class'] = $attrib['classact'];
+            }
         }
-        else if ($command && !$attrib['onclick']) {
+        else if ($command && empty($attrib['onclick'])) {
             $attrib['onclick'] = sprintf(
                 "return %s.command('%s','%s',this,event)",
                 self::JS_OBJECT_NAME,
                 $command,
-                $attrib['prop']
+                !empty($attrib['prop']) ? $attrib['prop'] : ''
             );
         }
 
-        $out = '';
+        $out         = '';
+        $btn_content = null;
+        $link_attrib = [];
 
         // generate image tag
         if ($attrib['type'] == 'image') {
@@ -1452,28 +1489,40 @@
                 )
             );
             $btn_content = sprintf('<img src="%s"%s />', $this->abs_url($attrib['image']), $attrib_str);
-            if ($attrib['label']) {
+            if (!empty($attrib['label'])) {
                 $btn_content .= ' '.$attrib['label'];
             }
             $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'target');
         }
         else if ($attrib['type'] == 'link') {
-            $btn_content = isset($attrib['content']) ? $attrib['content'] : ($attrib['label'] ? $attrib['label'] : $attrib['command']);
-            $link_attrib = array_merge(html::$common_attrib, array('href', 'onclick', 'tabindex', 'target'));
-            if ($attrib['innerclass'])
+            $btn_content = $attrib['content'] ?? (!empty($attrib['label']) ? $attrib['label'] : $attrib['command']);
+            $link_attrib = array_merge(html::$common_attrib, ['href', 'onclick', 'tabindex', 'target', 'rel']);
+            if (!empty($attrib['innerclass'])) {
                 $btn_content = html::span($attrib['innerclass'], $btn_content);
+            }
         }
         else if ($attrib['type'] == 'input') {
             $attrib['type'] = 'button';
 
-            if ($attrib['label']) {
+            if (!empty($attrib['label'])) {
                 $attrib['value'] = $attrib['label'];
             }
-            if ($attrib['command']) {
-              $attrib['disabled'] = 'disabled';
+            if (!empty($attrib['command'])) {
+                $attrib['disabled'] = 'disabled';
             }
 
-            $out = html::tag('input', $attrib, null, array('type', 'value', 'onclick', 'id', 'class', 'style', 'tabindex', 'disabled'));
+            $out = html::tag('input', $attrib, null, ['type', 'value', 'onclick', 'id', 'class', 'style', 'tabindex', 'disabled']);
+        }
+        else {
+            if (!empty($attrib['label'])) {
+                $attrib['value'] = $attrib['label'];
+            }
+            if (!empty($attrib['command'])) {
+                $attrib['disabled'] = 'disabled';
+            }
+
+            $content = $attrib['content'] ?? $attrib['label'];
+            $out = html::tag('button', $attrib, $content, ['type', 'value', 'onclick', 'id', 'class', 'style', 'tabindex', 'disabled']);
         }
 
         // generate html code for button
@@ -1482,12 +1531,12 @@
             $out = sprintf('<a%s>%s</a>', $attrib_str, $btn_content);
         }
 
-        if ($attrib['wrapper']) {
+        if (!empty($attrib['wrapper'])) {
             $out = html::tag($attrib['wrapper'], null, $out);
         }
 
-        if ($menuitem) {
-            $class = $attrib['menuitem-class'] ? ' class="' . $attrib['menuitem-class'] . '"' : '';
+        if (!empty($menuitem)) {
+            $class = !empty($attrib['menuitem-class']) ? ' class="' . $attrib['menuitem-class'] . '"' : '';
             $out   = '<li role="menuitem"' . $class . '>' . $out . '</li>';
         }
 
@@ -1506,7 +1555,7 @@
             $file = $this->file_mod($this->scripts_path . $file);
         }
 
-        if (!is_array($this->script_files[$position])) {
+        if (!isset($this->script_files[$position]) || !is_array($this->script_files[$position])) {
             $this->script_files[$position] = array();
         }
 
@@ -1553,7 +1602,7 @@
 
     /**
      * Add HTML code to the page footer
-     * To be added right befor </body>
+     * To be added right before </body>
      *
      * @param string $str HTML code
      */
@@ -1627,7 +1676,7 @@
             $this->add_script('$(document).ready(function(){ ' . $this->scripts['docready'] . "\n});", 'foot');
         }
 
-        if (is_array($this->script_files['foot'])) {
+        if (!empty($this->script_files['foot'])) {
             foreach ($this->script_files['foot'] as $file) {
                 $page_footer .= html::script($file);
             }
@@ -1658,7 +1707,7 @@
             $page_header = "<head>\n<title>$page_title</title>\n$page_header\n</head>\n";
         }
 
-        // add page hader
+        // add page header
         if ($hpos) {
             $output = substr_replace($output, $page_header, $hpos, 0);
         }
@@ -1675,7 +1724,7 @@
         }
 
         // add css files in head, before scripts, for speed up with parallel downloads
-        if (!empty($this->css_files) && !$is_empty
+        if (!empty($this->css_files) && empty($is_empty)
             && (($pos = stripos($output, '<script ')) || ($pos = stripos($output, '</head>')))
         ) {
             $css = '';
@@ -1718,16 +1767,16 @@
     {
         static $idcount = 0;
 
-        if (!$attrib['id']) {
+        if (empty($attrib['id'])) {
             $attrib['id'] = 'rcmframe' . ++$idcount;
         }
 
         $attrib['name'] = $attrib['id'];
-        $attrib['src']  = $attrib['src'] ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif';
+        $attrib['src']  = !empty($attrib['src']) ? $this->abs_url($attrib['src'], true) : 'program/resources/blank.gif';
 
         // register as 'contentframe' object
-        if ($is_contentframe || $attrib['contentframe']) {
-            $this->set_env('contentframe', $attrib['contentframe'] ? $attrib['contentframe'] : $attrib['name']);
+        if ($is_contentframe || !empty($attrib['contentframe'])) {
+	  $this->set_env('contentframe', !empty($attrib['contentframe']) ? $attrib['contentframe'] : $attrib['name']);
             $this->set_env('blankpage', $this->asset_url($attrib['src']));
         }
 
@@ -1747,12 +1796,14 @@
      */
     public function form_tag($attrib, $content = null)
     {
-        if ($this->env['extwin']) {
-            $hiddenfield = new html_hiddenfield(array('name' => '_extwin', 'value' => '1'));
+        $hidden = '';
+
+        if (!empty($this->env['extwin'])) {
+            $hiddenfield = new html_hiddenfield(['name' => '_extwin', 'value' => '1']);
             $hidden = $hiddenfield->show();
         }
-        else if ($this->framed || $this->env['framed']) {
-            $hiddenfield = new html_hiddenfield(array('name' => '_framed', 'value' => '1'));
+        else if ($this->framed || !empty($this->env['framed'])) {
+            $hiddenfield = new html_hiddenfield(['name' => '_framed', 'value' => '1']);
             $hidden = $hiddenfield->show();
         }
 
@@ -1779,17 +1830,19 @@
     public function request_form($attrib, $content = '')
     {
         $hidden = new html_hiddenfield();
-        if ($attrib['task']) {
-            $hidden->add(array('name' => '_task', 'value' => $attrib['task']));
+
+        if (!empty($attrib['task'])) {
+            $hidden->add(['name' => '_task', 'value' => $attrib['task']]);
         }
-        if ($attrib['action']) {
-            $hidden->add(array('name' => '_action', 'value' => $attrib['action']));
+
+        if (!empty($attrib['action'])) {
+            $hidden->add(['name' => '_action', 'value' => $attrib['action']]);
         }
 
         // we already have a <form> tag
-        if ($attrib['form']) {
-            if ($this->framed || $this->env['framed']) {
-                $hidden->add(array('name' => '_framed', 'value' => '1'));
+        if (!empty($attrib['form'])) {
+            if ($this->framed || !empty($this->env['framed'])) {
+                $hidden->add(['name' => '_framed', 'value' => '1']);
             }
 
             return $hidden->show() . $content;
@@ -1813,13 +1866,13 @@
     {
         static $username;
 
-        // alread fetched
+        // already fetched
         if (!empty($username)) {
             return $username;
         }
 
         // Current username is an e-mail address
-        if (strpos($_SESSION['username'], '@')) {
+        if (isset($_SESSION['username']) && strpos($_SESSION['username'], '@')) {
             $username = $_SESSION['username'];
         }
         // get e-mail address from default identity
@@ -1830,7 +1883,9 @@
             $username = $this->app->user->get_username();
         }
 
-        return rcube_utils::idn_to_utf8($username);
+        $username = rcube_utils::idn_to_utf8($username);
+
+        return html::quote($username);
     }
 
     /**
@@ -1850,8 +1905,12 @@
 
         // save original url
         $url = rcube_utils::get_input_value('_url', rcube_utils::INPUT_POST);
-        if (empty($url) && !preg_match('/_(task|action)=logout/', $_SERVER['QUERY_STRING']))
-            $url = $_SERVER['QUERY_STRING'];
+        if (empty($url)
+	    && !empty($_SERVER['QUERY_STRING'])
+            && !preg_match('/_(task|action)=logout/', $_SERVER['QUERY_STRING'])
+	    ) {
+	  $url = $_SERVER['QUERY_STRING'];
+	}
 
         // Disable autocapitalization on iPad/iPhone (#1488609)
         $attrib['autocapitalize'] = 'off';
@@ -1953,7 +2012,7 @@
         $images = array_map(array($this, 'abs_url'), $images);
         $images = array_map(array($this, 'asset_url'), $images);
 
-        if (empty($images) || $_REQUEST['_task'] == 'logout') {
+        if (empty($images) || (isset($_REQUEST['_task']) && $_REQUEST['_task'] == 'logout')) {
             return;
         }
 
@@ -1982,7 +2041,7 @@
         if (empty($attrib['id'])) {
             $attrib['id'] = 'rcmqsearchbox';
         }
-        if ($attrib['type'] == 'search' && !$this->browser->khtml) {
+        if (isset($attrib['type']) && $attrib['type'] == 'search' && !$this->browser->khtml) {
             unset($attrib['type'], $attrib['results']);
         }
 
@@ -2006,7 +2065,7 @@
     /**
      * Builder for GUI object 'message'
      *
-     * @param array Named tag parameters
+     * @param array $attrib Named tag parameters
      * @return string HTML code for the gui object
      */
     protected function message_container($attrib)
--- a/program/lib/Roundcube/rcube_db_pgsql.php	Mon Oct 06 12:20:32 2025 -0400
+++ b/program/lib/Roundcube/rcube_db_pgsql.php	Tue Oct 07 11:20:40 2025 -0400
@@ -120,13 +120,15 @@
      */
     public function now($interval = 0)
     {
-        if ($interval) {
-            $add = ' ' . ($interval > 0 ? '+' : '-') . " interval '";
-            $add .= $interval > 0 ? intval($interval) : intval($interval) * -1;
-            $add .= " seconds'";
-        }
+      $result = 'now()';
 
-        return "now()" . $add;
+       if ($interval) {
+	 $result .= ' ' . ($interval > 0 ? '+' : '-') . " interval '";
+	 $result .= $interval > 0 ? intval($interval) : intval($interval) * -1;
+	 $result .= " seconds'";
+       }
+
+       return $result;
     }
 
     /**
--- a/program/lib/Roundcube/rcube_imap_generic.php	Mon Oct 06 12:20:32 2025 -0400
+++ b/program/lib/Roundcube/rcube_imap_generic.php	Tue Oct 07 11:20:40 2025 -0400
@@ -2563,7 +2563,7 @@
                             if (strlen($field) < 3) {
                                 break;
                             }
-                            if ($result[$id]->others[$field]) {
+                            if (($result[$id]->others[$field])??null) {
                                 $string = array_merge((array)$result[$id]->others[$field], (array)$string);
                             }
                             $result[$id]->others[$field] = $string;
--- a/program/lib/Roundcube/rcube_mime.php	Mon Oct 06 12:20:32 2025 -0400
+++ b/program/lib/Roundcube/rcube_mime.php	Tue Oct 07 11:20:40 2025 -0400
@@ -198,7 +198,7 @@
                 // aggregation as a whole.
 
                 $tmp[] = $text;
-                if ($next_match = $matches[$idx+1]) {
+                if ($next_match = ($matches[$idx+1]??null)) {
                     if ($next_match[0][1] == $start
                         && $next_match[1][0] == $charset
                         && $next_match[2][0] == $encoding
--- a/program/steps/mail/getunread.inc	Mon Oct 06 12:20:32 2025 -0400
+++ b/program/steps/mail/getunread.inc	Tue Oct 07 11:20:40 2025 -0400
@@ -41,7 +41,7 @@
         // after possible message status change when opening a message
         // not in preview frame
         if ($unseen || $unseen_old === null || $mbox == $current) {
-            $OUTPUT->command('set_unread_count', $mbox, $unseen, $inbox && $mbox_row == 'INBOX');
+            $OUTPUT->command('set_unread_count', $mbox, $unseen, $inbox && $mbox == 'INBOX');
         }
 
         rcmail_set_unseen_count($mbox, $unseen);