浏览代码

run php cs fix

Sebijk 3 年之前
父节点
当前提交
cb7b5bdadc
共有 4 个文件被更改,包括 1676 次插入1653 次删除
  1. 416 410
      src/serverlib/sms.class.php
  2. 815 801
      src/serverlib/template.class.php
  3. 234 231
      src/serverlib/unzip.class.php
  4. 211 211
      src/serverlib/zip.class.php

+ 416 - 410
src/serverlib/sms.class.php

@@ -19,420 +19,426 @@
  *
  *
  */
  */
 
 
-if(!defined('B1GMAIL_INIT'))
-	die('Directly calling this file is not supported');
+if (!defined('B1GMAIL_INIT')) {
+    die('Directly calling this file is not supported');
+}
 
 
-if(!class_exists('BMHTTP'))
-	include(B1GMAIL_DIR . 'serverlib/http.class.php');
+if (!class_exists('BMHTTP')) {
+    include B1GMAIL_DIR.'serverlib/http.class.php';
+}
 
 
 /**
 /**
- * sms class
+ * sms class.
  */
  */
 class BMSMS
 class BMSMS
 {
 {
-	var $_userID;
-	var $_userObject;
-
-	/**
-	 * constructor
-	 *
-	 * @param int $userID User ID
-	 * @param BMUser $userObject User object
-	 * @return BMSMS
-	 */
-	function __construct($userID, &$userObject)
-	{
-		$this->_userID = (int)$userID;
-		$this->_userObject = &$userObject;
-	}
-
-	/**
-	 * get outbox
-	 *
-	 * @return array
-	 */
-	function GetOutbox($sortColumn = 'id', $sortOrder = 'DESC')
-	{
-		global $db;
-
-		$result = array();
-		$res = $db->Query('SELECT id,`from`,`to`,`text`,price,`date` FROM {pre}smsend WHERE isSMS=1 AND user=? AND deleted=0 ORDER BY `' . $sortColumn . '` ' . $sortOrder . ' LIMIT 15',
-			$this->_userID);
-		while($row = $res->FetchArray(MYSQLI_ASSOC))
-			$result[$row['id']] = $row;
-		$res->Free();
-
-		return($result);
-	}
-
-	/**
-	 * delete sms outbox entry
-	 *
-	 * @param int $id ID
-	 */
-	function DeleteOutboxEntry($id)
-	{
-		global $db;
-
-		$db->Query('UPDATE {pre}smsend SET `deleted`=1,`from`=?,`to`=?,`text`=? WHERE id=? AND user=?',
-			'', '', '',
-			$id, $this->_userID);
-		return($db->AffectedRows() == 1);
-	}
-
-	/**
-	 * check if $no conforms $pre-list
-	 *
-	 * @param string $no
-	 * @param string $pre
-	 * @return bool
-	 */
-	function PreOK($no, $pre)
-	{
-		if(trim($pre) != '')
-		{
-			$ok = false;
-			$entries = explode(':', $pre);
-			foreach($entries as $entry)
-			{
-				$entry = str_replace('+', '00', preg_replace('/[^0-9]/', '', $entry));
-				if(substr($no, 0, strlen($entry)) == $entry)
-				{
-					$ok = true;
-					break;
-				}
-			}
-		}
-		else
-			$ok = true;
-
-		return($ok);
-	}
-
-	/**
-	 * get available SMS types
-	 *
-	 * @return array
-	 */
-	function GetTypes()
-	{
-		global $db;
-
-		if(is_object($this->_userObject))
-		{
-			$group = $this->_userObject->GetGroup();
-			$groupRow = $group->Fetch();
-		}
-		else
-			$groupRow = array('sms_sig' => '');
-
-		$result = array();
-		$res = $db->Query('SELECT id,titel,typ,std,price,gateway,flags,maxlength FROM {pre}smstypen ORDER BY titel ASC');
-		while($row = $res->FetchArray(MYSQLI_ASSOC))
-			$result[$row['id']] = array(
-				'id'		=> $row['id'],
-				'title'		=> $row['titel'],
-				'type'		=> $row['typ'],
-				'default'	=> $row['std'] == 1,
-				'price'		=> $row['price'],
-				'gateway'	=> $row['gateway'],
-				'flags'		=> $row['flags'],
-				'maxlength'	=> $row['maxlength'] - strlen($groupRow['sms_sig'])
-			);
-		$res->Free();
-
-		return($result);
-	}
-
-	/**
-	 * get max SMS chars for user
-	 *
-	 * @return int
-	 */
-	function GetMaxChars($typeID = 0)
-	{
-		global $db;
-
-		if(is_object($this->_userObject))
-		{
-			$group = $this->_userObject->GetGroup();
-			$groupRow = $group->Fetch();
-		}
-		else
-			$groupRow = array('sms_sig' => '');
-
-		$maxChars = 160;
-
-		if($typeID > 0)
-		{
-			$res = $db->Query('SELECT maxlength FROM {pre}smstypen WHERE id=?', $typeID);
-			while($row = $res->FetchArray(MYSQLI_ASSOC))
-				$maxChars = $row['maxlength'];
-			$res->Free();
-		}
-
-		return($maxChars - strlen($groupRow['sms_sig']));
-	}
-
-	/**
-	 * get default gateway or gateway specified by ID
-	 *
-	 * @param int $id ID (0 = default gateway)
-	 * @return array
-	 */
-	function GetGateway($id = 0)
-	{
-		global $bm_prefs, $db;
-
-		if($id == 0)
-			$id = $bm_prefs['sms_gateway'];
-
-		$res = $db->Query('SELECT id,titel,getstring,success,`user`,`pass` FROM {pre}smsgateways WHERE id=?',
-			$id);
-		if($res->RowCount() == 1)
-		{
-			$row = $res->FetchArray(MYSQLI_ASSOC);
-			$res->Free();
-
-			return(array(
-				'id'		=> $row['id'],
-				'title'		=> $row['titel'],
-				'getstring'	=> $row['getstring'],
-				'success'	=> $row['success'],
-				'user'		=> $row['user'],
-				'pass'		=> $row['pass']
-			));
-		}
-		return(false);
-	}
-
-	/**
-	 * send SMS
-	 *
-	 * @param string $from From
-	 * @param string $to To
-	 * @param string $text Text
-	 * @param int $type Type ID (0 = default type)
-	 * @param bool $charge Charge user account?
-	 * @param bool $putToOutbox Put SMS to outbox?
-	 * @param int $outboxID ID of outbox entry, if already available
-	 * @return bool
-	 */
-	function Send($from, $to, $text, $type = 0, $charge = true, $putToOutbox = true, $outboxID = 0)
-	{
-		global $bm_prefs, $db, $lang_user;
-
-		// module handler
-		ModuleFunction('OnSendSMS', array(&$text, &$type, &$from, &$to, &$this->_userObject));
-
-		// get type and type list
-		$types = $this->GetTypes();
-		if($type == 0)
-		{
-			foreach($types as $typeID=>$typeInfo)
-				if($typeInfo['default'])
-					$type = $typeID;
-			if($type == 0)
-			{
-				PutLog(sprintf('Default SMS type <%d> not found while trying to send SMS from <%s> to <%s> (userID: %d)',
-					$type,
-					$from,
-					$to,
-					$this->_userID),
-					PRIO_WARNING,
-					__FILE__,
-					__LINE__);
-				return(false);
-			}
-		}
-		if(isset($types[$type]))
-			$type = $types[$type];
-		else
-		{
-			PutLog(sprintf('SMS type <%d> not found while trying to send SMS from <%s> to <%s> (userID: %d)',
-				$type,
-				$from,
-				$to,
-				$this->_userID),
-				PRIO_WARNING,
-				__FILE__,
-				__LINE__);
-			return(false);
-		}
-
-		// crop text
-		if(_strlen($text) > $this->GetMaxChars($type['id']))
-			$text = _substr($text, 0, $this->GetMaxChars($type['id']));
-
-		// check account balance
-		if($charge)
-		{
-			$balance = $this->_userObject->GetBalance();
-			if($balance < $type['price'])
-			{
-				PutLog(sprintf('Failed to send SMS from <%s> to <%s>: Not enough credits (userID: %d)',
-					$from,
-					$to,
-					$this->_userID),
-					PRIO_NOTE,
-					__FILE__,
-					__LINE__);
-				return(false);
-			}
-		}
-
-		// get gateway info
-		$gateway = $this->GetGateway($type['gateway']);
-		if(!$gateway)
-		{
-			PutLog(sprintf('Gateway <%d> not found while trying to send SMS with type <%d> from <%s> to <%s> (userID: %d)',
-				$type['gateway'],
-				$type['id'],
-				$from,
-				$to,
-				$this->_userID),
-				PRIO_WARNING,
-				__FILE__,
-				__LINE__);
-			return(false);
-		}
-
-		// prepare formatted numbers
-		$fromPlus = preg_replace('/^00/', '+', $from);
-		$toPlus = preg_replace('/^00/', '+', $to);
-
-		// build GET string
-		$getString = $gateway['getstring'];
-		$getString = str_replace('%%user%%',
-								 _urlencode($gateway['user']),
-								 $getString);
-		$getString = str_replace('%%passwort%%',
-								 _urlencode($gateway['pass']),
-								 $getString);
-		$getString = str_replace('%%from%%',
-								 urlencode(CharsetDecode($from, false, 'ISO-8859-1')),
-								 $getString);
-		$getString = str_replace('%%fromPlus%%',
-								 urlencode(CharsetDecode($fromPlus, false, 'ISO-8859-1')),
-								 $getString);
-		$getString = str_replace('%%from_utf8%%',
-								 urlencode(CharsetDecode($from, false, 'UTF-8')),
-								 $getString);
-		$getString = str_replace('%%to%%',
-								 _urlencode($to),
-								 $getString);
-		$getString = str_replace('%%toPlus%%',
-								 _urlencode($toPlus),
-								 $getString);
-		$getString = str_replace('%%msg%%',
-								 urlencode(CharsetDecode($text, false, 'ISO-8859-1')),
-								 $getString);
-		$getString = str_replace('%%msg_utf8%%',
-								 urlencode(CharsetDecode($text, false, 'UTF-8')),
-								 $getString);
-		if($this->_userObject)
-		{
-			$getString = str_replace('%%usermail%%',
-									 _urlencode($this->_userObject->_row['email']),
-									 $getString);
-			$getString = str_replace('%%userid%%',
-									 _urlencode($this->_userObject->_id),
-									 $getString);
-		}
-		$getString = str_replace('%%typ%%',
-								 _urlencode($type['type']),
-								 $getString);
-
-		// request!
-		$http = _new('BMHTTP', array($getString));
-		$result = $http->DownloadToString();
-		$success = (trim($gateway['success']) == '' && trim($result) == '')
-			|| (strpos(strtolower($result), strtolower($gateway['success'])) !== false);
-
-		// log
-		PutLog(sprintf('SMS success: %d; expected result: <%s>; gateway result: <%s> (userID: %d)',
-			$success,
-			$gateway['success'],
-			$result,
-			$this->_userID),
-			PRIO_DEBUG,
-			__FILE__,
-			__LINE__);
-
-		// ok?
-		if($success)
-		{
-			// status ID?
-			if(preg_match('/Status\:([0-9]*)/', $result, $reg) && is_array($reg) && isset($reg[1]))
-				$statusID = $reg[1];
-			else
-				$statusID = -1;
-
-			// stats
-			Add2Stat('sms');
-
-			// charge, if requested
-			if($charge)
-				$outboxID = $this->_userObject->Debit($type['price']*-1, $lang_user['tx_sms']);
-
-			// put to outbox?
-			if($putToOutbox)
-				if($outboxID == 0)
-				{
-					$db->Query('INSERT INTO {pre}smsend(user,monat,price,isSMS,`from`,`to`,`text`,statusid,`date`) VALUES(?,?,?,?,?,?,?,?,?)',
-						$this->_userID,
-						(int)date('mY'),
-						0,
-						1,
-						$from,
-						$to,
-						$text,
-						$statusID,
-						time());
-					$outboxID = $db->InsertId();
-				}
-				else
-					$db->Query('UPDATE {pre}smsend SET isSMS=?,`from`=?,`to`=?,`text`=?,statusid=?,`date`=? WHERE id=? AND user=?',
-						1,
-						$from,
-						$to,
-						$text,
-						$statusID,
-						time(),
-						$outboxID,
-						$this->_userID);
-
-			// module handler
-			ModuleFunction('AfterSendSMS', array(true, $result, $outboxID));
-
-			// log
-			PutLog(sprintf('Sent SMS from <%s> to <%s> (type: %d; statusID: %d; charged: %d; userID: %d)',
-				$from,
-				$to,
-				$type['id'],
-				$statusID,
-				$charge ? $type['price'] : 0,
-				$this->_userID),
-				PRIO_NOTE,
-				__FILE__,
-				__LINE__);
-			return(true);
-		}
-		else
-		{
-			// module handler
-			ModuleFunction('AfterSendSMS', array(false, $result, $outboxID));
-
-			// log
-			PutLog(sprintf('Failed to send SMS from <%s> to <%s> (type: %d; charged: 0; userID: %d)',
-				$from,
-				$to,
-				$type['id'],
-				$this->_userID),
-				PRIO_NOTE,
-				__FILE__,
-				__LINE__);
-			return(false);
-		}
-	}
+    private $_userID;
+    private $_userObject;
+
+    /**
+     * constructor.
+     *
+     * @param int    $userID     User ID
+     * @param BMUser $userObject User object
+     *
+     * @return BMSMS
+     */
+    public function __construct($userID, &$userObject)
+    {
+        $this->_userID = (int) $userID;
+        $this->_userObject = &$userObject;
+    }
+
+    /**
+     * get outbox.
+     *
+     * @return array
+     */
+    public function GetOutbox($sortColumn = 'id', $sortOrder = 'DESC')
+    {
+        global $db;
+
+        $result = [];
+        $res = $db->Query('SELECT id,`from`,`to`,`text`,price,`date` FROM {pre}smsend WHERE isSMS=1 AND user=? AND deleted=0 ORDER BY `'.$sortColumn.'` '.$sortOrder.' LIMIT 15',
+            $this->_userID);
+        while ($row = $res->FetchArray(MYSQLI_ASSOC)) {
+            $result[$row['id']] = $row;
+        }
+        $res->Free();
+
+        return $result;
+    }
+
+    /**
+     * delete sms outbox entry.
+     *
+     * @param int $id ID
+     */
+    public function DeleteOutboxEntry($id)
+    {
+        global $db;
+
+        $db->Query('UPDATE {pre}smsend SET `deleted`=1,`from`=?,`to`=?,`text`=? WHERE id=? AND user=?',
+            '', '', '',
+            $id, $this->_userID);
+
+        return $db->AffectedRows() == 1;
+    }
+
+    /**
+     * check if $no conforms $pre-list.
+     *
+     * @param string $no
+     * @param string $pre
+     *
+     * @return bool
+     */
+    public function PreOK($no, $pre)
+    {
+        if (trim($pre) != '') {
+            $ok = false;
+            $entries = explode(':', $pre);
+            foreach ($entries as $entry) {
+                $entry = str_replace('+', '00', preg_replace('/[^0-9]/', '', $entry));
+                if (substr($no, 0, strlen($entry)) == $entry) {
+                    $ok = true;
+                    break;
+                }
+            }
+        } else {
+            $ok = true;
+        }
+
+        return $ok;
+    }
+
+    /**
+     * get available SMS types.
+     *
+     * @return array
+     */
+    public function GetTypes()
+    {
+        global $db;
+
+        if (is_object($this->_userObject)) {
+            $group = $this->_userObject->GetGroup();
+            $groupRow = $group->Fetch();
+        } else {
+            $groupRow = ['sms_sig' => ''];
+        }
+
+        $result = [];
+        $res = $db->Query('SELECT id,titel,typ,std,price,gateway,flags,maxlength FROM {pre}smstypen ORDER BY titel ASC');
+        while ($row = $res->FetchArray(MYSQLI_ASSOC)) {
+            $result[$row['id']] = [
+                'id' => $row['id'],
+                'title' => $row['titel'],
+                'type' => $row['typ'],
+                'default' => $row['std'] == 1,
+                'price' => $row['price'],
+                'gateway' => $row['gateway'],
+                'flags' => $row['flags'],
+                'maxlength' => $row['maxlength'] - strlen($groupRow['sms_sig']),
+            ];
+        }
+        $res->Free();
+
+        return $result;
+    }
+
+    /**
+     * get max SMS chars for user.
+     *
+     * @return int
+     */
+    public function GetMaxChars($typeID = 0)
+    {
+        global $db;
+
+        if (is_object($this->_userObject)) {
+            $group = $this->_userObject->GetGroup();
+            $groupRow = $group->Fetch();
+        } else {
+            $groupRow = ['sms_sig' => ''];
+        }
+
+        $maxChars = 160;
+
+        if ($typeID > 0) {
+            $res = $db->Query('SELECT maxlength FROM {pre}smstypen WHERE id=?', $typeID);
+            while ($row = $res->FetchArray(MYSQLI_ASSOC)) {
+                $maxChars = $row['maxlength'];
+            }
+            $res->Free();
+        }
+
+        return $maxChars - strlen($groupRow['sms_sig']);
+    }
+
+    /**
+     * get default gateway or gateway specified by ID.
+     *
+     * @param int $id ID (0 = default gateway)
+     *
+     * @return array
+     */
+    public function GetGateway($id = 0)
+    {
+        global $bm_prefs, $db;
+
+        if ($id == 0) {
+            $id = $bm_prefs['sms_gateway'];
+        }
+
+        $res = $db->Query('SELECT id,titel,getstring,success,`user`,`pass` FROM {pre}smsgateways WHERE id=?',
+            $id);
+        if ($res->RowCount() == 1) {
+            $row = $res->FetchArray(MYSQLI_ASSOC);
+            $res->Free();
+
+            return [
+                'id' => $row['id'],
+                'title' => $row['titel'],
+                'getstring' => $row['getstring'],
+                'success' => $row['success'],
+                'user' => $row['user'],
+                'pass' => $row['pass'],
+            ];
+        }
+
+        return false;
+    }
+
+    /**
+     * send SMS.
+     *
+     * @param string $from        From
+     * @param string $to          To
+     * @param string $text        Text
+     * @param int    $type        Type ID (0 = default type)
+     * @param bool   $charge      Charge user account?
+     * @param bool   $putToOutbox Put SMS to outbox?
+     * @param int    $outboxID    ID of outbox entry, if already available
+     *
+     * @return bool
+     */
+    public function Send($from, $to, $text, $type = 0, $charge = true, $putToOutbox = true, $outboxID = 0)
+    {
+        global $bm_prefs, $db, $lang_user;
+
+        // module handler
+        ModuleFunction('OnSendSMS', [&$text, &$type, &$from, &$to, &$this->_userObject]);
+
+        // get type and type list
+        $types = $this->GetTypes();
+        if ($type == 0) {
+            foreach ($types as $typeID => $typeInfo) {
+                if ($typeInfo['default']) {
+                    $type = $typeID;
+                }
+            }
+            if ($type == 0) {
+                PutLog(sprintf('Default SMS type <%d> not found while trying to send SMS from <%s> to <%s> (userID: %d)',
+                    $type,
+                    $from,
+                    $to,
+                    $this->_userID),
+                    PRIO_WARNING,
+                    __FILE__,
+                    __LINE__);
+
+                return false;
+            }
+        }
+        if (isset($types[$type])) {
+            $type = $types[$type];
+        } else {
+            PutLog(sprintf('SMS type <%d> not found while trying to send SMS from <%s> to <%s> (userID: %d)',
+                $type,
+                $from,
+                $to,
+                $this->_userID),
+                PRIO_WARNING,
+                __FILE__,
+                __LINE__);
+
+            return false;
+        }
+
+        // crop text
+        if (_strlen($text) > $this->GetMaxChars($type['id'])) {
+            $text = _substr($text, 0, $this->GetMaxChars($type['id']));
+        }
+
+        // check account balance
+        if ($charge) {
+            $balance = $this->_userObject->GetBalance();
+            if ($balance < $type['price']) {
+                PutLog(sprintf('Failed to send SMS from <%s> to <%s>: Not enough credits (userID: %d)',
+                    $from,
+                    $to,
+                    $this->_userID),
+                    PRIO_NOTE,
+                    __FILE__,
+                    __LINE__);
+
+                return false;
+            }
+        }
+
+        // get gateway info
+        $gateway = $this->GetGateway($type['gateway']);
+        if (!$gateway) {
+            PutLog(sprintf('Gateway <%d> not found while trying to send SMS with type <%d> from <%s> to <%s> (userID: %d)',
+                $type['gateway'],
+                $type['id'],
+                $from,
+                $to,
+                $this->_userID),
+                PRIO_WARNING,
+                __FILE__,
+                __LINE__);
+
+            return false;
+        }
+
+        // prepare formatted numbers
+        $fromPlus = preg_replace('/^00/', '+', $from);
+        $toPlus = preg_replace('/^00/', '+', $to);
+
+        // build GET string
+        $getString = $gateway['getstring'];
+        $getString = str_replace('%%user%%',
+                                 _urlencode($gateway['user']),
+                                 $getString);
+        $getString = str_replace('%%passwort%%',
+                                 _urlencode($gateway['pass']),
+                                 $getString);
+        $getString = str_replace('%%from%%',
+                                 urlencode(CharsetDecode($from, false, 'ISO-8859-1')),
+                                 $getString);
+        $getString = str_replace('%%fromPlus%%',
+                                 urlencode(CharsetDecode($fromPlus, false, 'ISO-8859-1')),
+                                 $getString);
+        $getString = str_replace('%%from_utf8%%',
+                                 urlencode(CharsetDecode($from, false, 'UTF-8')),
+                                 $getString);
+        $getString = str_replace('%%to%%',
+                                 _urlencode($to),
+                                 $getString);
+        $getString = str_replace('%%toPlus%%',
+                                 _urlencode($toPlus),
+                                 $getString);
+        $getString = str_replace('%%msg%%',
+                                 urlencode(CharsetDecode($text, false, 'ISO-8859-1')),
+                                 $getString);
+        $getString = str_replace('%%msg_utf8%%',
+                                 urlencode(CharsetDecode($text, false, 'UTF-8')),
+                                 $getString);
+        if ($this->_userObject) {
+            $getString = str_replace('%%usermail%%',
+                                     _urlencode($this->_userObject->_row['email']),
+                                     $getString);
+            $getString = str_replace('%%userid%%',
+                                     _urlencode($this->_userObject->_id),
+                                     $getString);
+        }
+        $getString = str_replace('%%typ%%',
+                                 _urlencode($type['type']),
+                                 $getString);
+
+        // request!
+        $http = _new('BMHTTP', [$getString]);
+        $result = $http->DownloadToString();
+        $success = (trim($gateway['success']) == '' && trim($result) == '')
+            || (strpos(strtolower($result), strtolower($gateway['success'])) !== false);
+
+        // log
+        PutLog(sprintf('SMS success: %d; expected result: <%s>; gateway result: <%s> (userID: %d)',
+            $success,
+            $gateway['success'],
+            $result,
+            $this->_userID),
+            PRIO_DEBUG,
+            __FILE__,
+            __LINE__);
+
+        // ok?
+        if ($success) {
+            // status ID?
+            if (preg_match('/Status\:([0-9]*)/', $result, $reg) && is_array($reg) && isset($reg[1])) {
+                $statusID = $reg[1];
+            } else {
+                $statusID = -1;
+            }
+
+            // stats
+            Add2Stat('sms');
+
+            // charge, if requested
+            if ($charge) {
+                $outboxID = $this->_userObject->Debit($type['price'] * -1, $lang_user['tx_sms']);
+            }
+
+            // put to outbox?
+            if ($putToOutbox) {
+                if ($outboxID == 0) {
+                    $db->Query('INSERT INTO {pre}smsend(user,monat,price,isSMS,`from`,`to`,`text`,statusid,`date`) VALUES(?,?,?,?,?,?,?,?,?)',
+                        $this->_userID,
+                        (int) date('mY'),
+                        0,
+                        1,
+                        $from,
+                        $to,
+                        $text,
+                        $statusID,
+                        time());
+                    $outboxID = $db->InsertId();
+                } else {
+                    $db->Query('UPDATE {pre}smsend SET isSMS=?,`from`=?,`to`=?,`text`=?,statusid=?,`date`=? WHERE id=? AND user=?',
+                        1,
+                        $from,
+                        $to,
+                        $text,
+                        $statusID,
+                        time(),
+                        $outboxID,
+                        $this->_userID);
+                }
+            }
+
+            // module handler
+            ModuleFunction('AfterSendSMS', [true, $result, $outboxID]);
+
+            // log
+            PutLog(sprintf('Sent SMS from <%s> to <%s> (type: %d; statusID: %d; charged: %d; userID: %d)',
+                $from,
+                $to,
+                $type['id'],
+                $statusID,
+                $charge ? $type['price'] : 0,
+                $this->_userID),
+                PRIO_NOTE,
+                __FILE__,
+                __LINE__);
+
+            return true;
+        } else {
+            // module handler
+            ModuleFunction('AfterSendSMS', [false, $result, $outboxID]);
+
+            // log
+            PutLog(sprintf('Failed to send SMS from <%s> to <%s> (type: %d; charged: 0; userID: %d)',
+                $from,
+                $to,
+                $type['id'],
+                $this->_userID),
+                PRIO_NOTE,
+                __FILE__,
+                __LINE__);
+
+            return false;
+        }
+    }
 }
 }

+ 815 - 801
src/serverlib/template.class.php

@@ -19,901 +19,915 @@
  *
  *
  */
  */
 
 
-if(!defined('B1GMAIL_INIT'))
-	die('Directly calling this file is not supported');
+if (!defined('B1GMAIL_INIT')) {
+    die('Directly calling this file is not supported');
+}
 
 
 /**
 /**
- * smarty
+ * smarty.
  */
  */
-include(B1GMAIL_DIR . 'serverlib/3rdparty/smarty/Smarty.class.php');
+include B1GMAIL_DIR.'serverlib/3rdparty/smarty/Smarty.class.php';
 
 
 /**
 /**
- * template class (extends smarty)
+ * template class (extends smarty).
  */
  */
 class Template extends Smarty
 class Template extends Smarty
 {
 {
-	var $_cssFiles, $_jsFiles;
-	var $tplDir;
-	var $reassignFolderList = false;
-	var $hookTable = array();
-
-	/**
-	 * constructor
-	 *
-	 * @return Template
-	 */
-	function __construct()
-	{
-		global $bm_prefs, $lang_user, $lang_info;
-
-		$this->_cssFiles = array('nli' => array(), 'li' => array(), 'admin' => array());
-		$this->_jsFiles = array('nli' => array(), 'li' => array(), 'admin' => array());
-
-		// template & cache directories
-		if(ADMIN_MODE)
-		{
-			$this->template_dir	= B1GMAIL_DIR . 'admin/templates/';
-			$this->compile_dir	= B1GMAIL_DIR . 'admin/templates/cache/';
-			$this->assign('tpldir', $this->tplDir = './templates/');
-		}
-		else
-		{
-			$this->template_dir = B1GMAIL_DIR . 'templates/' . $bm_prefs['template'] . '/';
-			$this->compile_dir 	= B1GMAIL_DIR . 'templates/' . $bm_prefs['template'] . '/cache/';
-			$this->assign('tpldir', $this->tplDir = B1GMAIL_REL . 'templates/' . $bm_prefs['template'] . '/');
-		}
-
-		// variables
-		$this->assign('service_title', 			HTMLFormat($bm_prefs['titel']));
-		$this->assign('charset', 				$lang_info['charset']);
-		if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
-			$this->assign('selfurl', 			str_replace('http://', 'https://', $bm_prefs['selfurl']));
-		else
-			$this->assign('selfurl', 			$bm_prefs['selfurl']);
-		$this->assign('_tpldir', 				'templates/' . $bm_prefs['template'] . '/');
-		$this->assign('_tplname', 				$bm_prefs['template']);
-		$this->assign('_regEnabled', 			$bm_prefs['regenabled'] == 'yes');
-		$this->assign('serverTZ',				date('Z'));
-
-		// post vars?
-		if(isset($_POST['transPostVars']))
-		{
-			$_safePost = array();
-			foreach($_POST as $key=>$val)
-				$_safePost[$key] = HTMLFormat($val);
-			$this->assign('_safePost', $_safePost);
-		}
-
-		// functions
-		$this->register_function('banner', 			'TemplateBanner');
-		$this->register_function('lng', 			'TemplateLang');
-		$this->register_function('comment', 		'TemplateComment');
-		$this->register_function('date', 			'TemplateDate');
-		$this->register_function('size', 			'TemplateSize');
-		$this->register_function('text', 			'TemplateText');
-		$this->register_function('domain', 			'TemplateDomain');
-		$this->register_function('email', 			'TemplateEMail');
-		$this->register_function('progressBar',		'TemplateProgressBar');
-		$this->register_function('miniCalendar',	'TemplateMiniCalendar');
-		$this->register_function('fileSelector',	'TemplateFileSelector');
-		$this->register_function('pageNav',			'TemplatePageNav');
-		$this->register_function('addressList',		'TemplateAddressList');
-		$this->register_function('storeTime',		'TemplateStoreTime');
-		$this->register_function('halfHourToTime',	'TemplateHalfHourToTime');
-		$this->register_function('implode',			'TemplateImplode');
-		$this->register_function('mobileNr',		'TemplateMobileNr');
-		$this->register_function('hook',			'TemplateHook');
-		$this->register_function('fileDateSig',		'TemplateFileDateSig');
-		$this->register_function('number',			'TemplateNumber');
-		$this->register_function('fieldDate',		'TemplateFieldDate');
-
-		// module handler
-		ModuleFunction('OnCreateTemplate', array(&$this));
-	}
-
-	/**
-	 * register with a template hook
-	 *
-	 * @param string $id Hook ID
-	 * @param string $tpl File name of template to be included
-	 */
-	function registerHook($id, $tpl)
-	{
-		if(!isset($this->hookTable[$id]))
-			$this->hookTable[$id] = array($tpl);
-		else
-			$this->hookTable[$id][] = $tpl;
-	}
-
-	/**
-	 * adds a JS file to be included in the page
-	 *
-	 * @param string $area Area (nli/li/admin)
-	 * @param string $file Filename
-	 */
-	function addJSFile($area, $file)
-	{
-		if(isset($this->_jsFiles[$area]))
-			if(!in_array($file, $this->_jsFiles[$area]))
-			{
-				if(file_Exists($file))
-					$file .= '?' . substr(md5(filemtime($file)), 0, 6);
-				$this->_jsFiles[$area][] = $file;
-				return(true);
-			}
-
-		return(false);
-	}
-
-	/**
-	 * adds a CSS file to be included in the page
-	 *
-	 * @param string $area Area (nli/li/admin)
-	 * @param string $file Filename
-	 */
-	function addCSSFile($area, $file)
-	{
-		if(isset($this->_cssFiles[$area]))
-			if(!in_array($file, $this->_cssFiles[$area]))
-			{
-				$this->_cssFiles[$area][] = $file;
-				return(true);
-			}
-
-		return(false);
-	}
-
-	function fetch($resource_name, $cache_id = null, $compile_id = null, $display = false)
-	{
-		global $thisUser, $userRow, $groupRow, $lang_user, $plugins, $bm_prefs, $adminRow, $currentLanguage;
-
-		$this->assign('templatePrefs', GetTemplatePrefs($bm_prefs['template']));
-
-		// admin mode?
-		if(ADMIN_MODE && isset($adminRow))
-		{
-			$this->assign('adminRow', $adminRow);
-
-			$bmVer = B1GMAIL_VERSION;
-
-			$this->assign('bmver', $bmVer);
-
-			$pluginMenuItems = array();
-			foreach($plugins->_plugins as $className=>$pluginInfo)
-				if($plugins->getParam('admin_pages', $className))
-					$pluginMenuItems[$className] = array('title' => $plugins->getParam('admin_page_title', $className),
-															'icon' => $plugins->getParam('admin_page_icon', $className),);
-
-			asort($pluginMenuItems);
-			$this->assign('pluginMenuItems', $pluginMenuItems);
-
-			$this->assign('isGerman', strpos(strtolower($currentLanguage), 'deutsch') !== false);
-		}
-
-		// tabs
-		if(isset($userRow) && isset($groupRow))
-		{
-			$newMenu = array(
-				array(
-					'icon'		=> 'send_mail',
-					'faIcon'	=> 'fa-envelope-o',
-					'link'		=> 'email.compose.php?sid=',
-					'text'		=> $lang_user['email'],
-					'order'		=> 100
-				),
-				array(
-					'sep'		=> true,
-					'order'		=> 200
-				),
-				array(
-					'icon'		=> 'ico_calendar',
-					'faIcon'	=> 'fa-calendar',
-					'link'		=> 'organizer.calendar.php?action=addDate&sid=',
-					'text'		=> $lang_user['date2'],
-					'order'		=> 300
-				),
-				array(
-					'icon'		=> 'ico_todo',
-					'faIcon'	=> 'fa-tasks',
-					'link'		=> 'organizer.todo.php?action=addTask&sid=',
-					'text'		=> $lang_user['task'],
-					'order'		=> 400
-				),
-				array(
-					'icon'		=> 'ico_addressbook',
-					'faIcon'	=> 'fa-address-book-o',
-					'link'		=> 'organizer.addressbook.php?action=addContact&sid=',
-					'text'		=> $lang_user['contact'],
-					'order'		=> 500
-				),
-				array(
-					'icon'		=> 'ico_notes',
-					'faIcon'	=> 'fa-sticky-note-o',
-					'link'		=> 'organizer.notes.php?action=addNote&sid=',
-					'text'		=> $lang_user['note'],
-					'order'		=> 600
-				)
-			);
-
-			$pageTabs = array(
-				'start' => array(
-					'icon'		=> 'start',
-					'faIcon'	=> 'fa-home',
-					'link'		=> 'start.php?sid=',
-					'text'		=> $lang_user['start'],
-					'order'		=> 100
-				),
-				'email' => array(
-					'icon'		=> 'email',
-					'faIcon'	=> 'fa-envelope-o',
-					'link'		=> 'email.php?sid=',
-					'text'		=> $lang_user['email'],
-					'order'		=> 200
-				)
-			);
-
-			if($thisUser->SMSEnabled())
-			{
-				$pageTabs['sms'] = array(
-					'icon'		=> 'sms',
-					'faIcon'	=> 'fa-comments',
-					'link'		=> 'sms.php?sid=',
-					'text'		=> $lang_user['sms'],
-					'order'		=> 300
-				);
-
-				$newMenu[] = array(
-					'sep'		=> true,
-					'order'		=> 800
-				);
-				$newMenu[] = array(
-					'icon'		=> 'ico_composesms',
-					'faIcon'	=> 'fa-comments',
-					'link'		=> 'sms.php?sid=',
-					'text'		=> $lang_user['sms'],
-					'order'		=> 801
-				);
-			}
-
-			$pageTabs = array_merge($pageTabs, array(
-				'organizer' => array(
-					'icon'		=> 'organizer',
-					'faIcon'	=> 'fa-calendar',
-					'link'		=> 'organizer.php?sid=',
-					'text'		=> $lang_user['organizer'],
-					'order'		=> 400
-				)));
-
-			if($groupRow['webdisk'] + $userRow['diskspace_add'] > 0)
-			{
-				$pageTabs = array_merge($pageTabs, array(
-					'webdisk' => array(
-						'icon'		=> 'webdisk',
-						'faIcon'	=> 'fa-cloud',
-						'link'		=> 'webdisk.php?sid=',
-						'text'		=> $lang_user['webdisk'],
-						'order'		=> 500
-					)));
-
-				$newMenu[] = array(
-					'sep'		=> true,
-					'order'		=> 700
-				);
-				$newMenu[] = array(
-					'icon'		=> 'webdisk_file',
-					'faIcon'	=> 'fa-file-o',
-					'link'		=> 'webdisk.php?do=uploadFilesForm&sid=',
-					'text'		=> $lang_user['file'],
-					'order'		=> 701
-				);
-			}
-
-			$moduleResult = $plugins->callFunction('getUserPages', false, true, array(true));
-			foreach($moduleResult as $userPages)
-				$pageTabs = array_merge($pageTabs, $userPages);
-
-			$moduleResult = $plugins->callFunction('getNewMenu', false, true, array(true));
-			foreach($moduleResult as $newEntries)
-				$newMenu = array_merge($newMenu, $newEntries);
-
-			$pageTabs = array_merge($pageTabs, array(
-				'prefs' => array(
-					'icon'		=> 'prefs',
-					'faIcon'	=> 'fa-cog',
-					'link'		=> 'prefs.php?sid=',
-					'text'		=> $lang_user['prefs'],
-					'order'		=> 600
-				)));
-
-			// sort by order
-			if(is_array($tabOrder = @unserialize($bm_prefs['taborder'])))
-				foreach($tabOrder as $orderKey=>$orderVal)
-					if(isset($pageTabs[$orderKey]))
-						if($orderVal == -1)
-							unset($pageTabs[$orderKey]);
-						else
-							$pageTabs[$orderKey]['order'] = $orderVal;
-
-			ModuleFunction('BeforePageTabsAssign', array(&$pageTabs));
-			uasort($pageTabs, 'TemplateTabSort');
-			uasort($newMenu, 'TemplateTabSort');
-
-			$this->assign('pageTabs', $pageTabs);
-			$this->assign('pageTabsCount', count($pageTabs));
-			$this->assign('newMenu', $newMenu);
-			$this->assign('_userEmail', $userRow['email']);
-			$this->assign('searchDetailsDefault', $userRow['search_details_default']=='yes');
-			$this->assign('ftsBGIndexing', $bm_prefs['fts_bg_indexing']=='yes' && $groupRow['ftsearch']=='yes' && FTS_SUPPORT);
-
-			if($groupRow['notifications'] == 'yes')
-			{
-				$this->assign('bmUnreadNotifications', $thisUser->GetUnreadNotifications());
-				$this->assign('bmNotifyInterval', $bm_prefs['notify_interval']);
-				$this->assign('bmNotifySound', $userRow['notify_sound'] == 'yes');
-			}
-		}
-
-		// pugin pages (not logged in)
-		else
-		{
-			$menu = array();
-			$moduleResult = $plugins->callFunction('getUserPages', false, true, array(false));
-			foreach($moduleResult as $userPages)
-				$menu = array_merge($menu, $userPages);
-			$this->assign('pluginUserPages', $menu);
-		}
-
-		// folder list
-		if($this->reassignFolderList)
-		{
-			global $mailbox;
-
-			if(isset($mailbox) && is_object($mailbox))
-			{
-				list(, $pageMenu) = $mailbox->GetPageFolderList();
-				$this->assign('folderList', $pageMenu);
-			}
-		}
-
-		ModuleFunction('BeforeDisplayTemplate', array($resource_name, &$this));
-
-		$this->assign('_cssFiles', $this->_cssFiles);
-		$this->assign('_jsFiles', $this->_jsFiles);
-
-		StartPageOutput();
-		return Smarty::fetch($resource_name, $cache_id, $compile_id, $display);
-	}
+    public $_cssFiles;
+    public $_jsFiles;
+    public $tplDir;
+    public $reassignFolderList = false;
+    public $hookTable = [];
+
+    /**
+     * constructor.
+     *
+     * @return Template
+     */
+    public function __construct()
+    {
+        global $bm_prefs, $lang_user, $lang_info;
+
+        $this->_cssFiles = ['nli' => [], 'li' => [], 'admin' => []];
+        $this->_jsFiles = ['nli' => [], 'li' => [], 'admin' => []];
+
+        // template & cache directories
+        if (ADMIN_MODE) {
+            $this->template_dir = B1GMAIL_DIR.'admin/templates/';
+            $this->compile_dir = B1GMAIL_DIR.'admin/templates/cache/';
+            $this->assign('tpldir', $this->tplDir = './templates/');
+        } else {
+            $this->template_dir = B1GMAIL_DIR.'templates/'.$bm_prefs['template'].'/';
+            $this->compile_dir = B1GMAIL_DIR.'templates/'.$bm_prefs['template'].'/cache/';
+            $this->assign('tpldir', $this->tplDir = B1GMAIL_REL.'templates/'.$bm_prefs['template'].'/');
+        }
+
+        // variables
+        $this->assign('service_title', HTMLFormat($bm_prefs['titel']));
+        $this->assign('charset', $lang_info['charset']);
+        if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
+            $this->assign('selfurl', str_replace('http://', 'https://', $bm_prefs['selfurl']));
+        } else {
+            $this->assign('selfurl', $bm_prefs['selfurl']);
+        }
+        $this->assign('_tpldir', 'templates/'.$bm_prefs['template'].'/');
+        $this->assign('_tplname', $bm_prefs['template']);
+        $this->assign('_regEnabled', $bm_prefs['regenabled'] == 'yes');
+        $this->assign('serverTZ', date('Z'));
+
+        // post vars?
+        if (isset($_POST['transPostVars'])) {
+            $_safePost = [];
+            foreach ($_POST as $key => $val) {
+                $_safePost[$key] = HTMLFormat($val);
+            }
+            $this->assign('_safePost', $_safePost);
+        }
+
+        // functions
+        $this->register_function('banner', 'TemplateBanner');
+        $this->register_function('lng', 'TemplateLang');
+        $this->register_function('comment', 'TemplateComment');
+        $this->register_function('date', 'TemplateDate');
+        $this->register_function('size', 'TemplateSize');
+        $this->register_function('text', 'TemplateText');
+        $this->register_function('domain', 'TemplateDomain');
+        $this->register_function('email', 'TemplateEMail');
+        $this->register_function('progressBar', 'TemplateProgressBar');
+        $this->register_function('miniCalendar', 'TemplateMiniCalendar');
+        $this->register_function('fileSelector', 'TemplateFileSelector');
+        $this->register_function('pageNav', 'TemplatePageNav');
+        $this->register_function('addressList', 'TemplateAddressList');
+        $this->register_function('storeTime', 'TemplateStoreTime');
+        $this->register_function('halfHourToTime', 'TemplateHalfHourToTime');
+        $this->register_function('implode', 'TemplateImplode');
+        $this->register_function('mobileNr', 'TemplateMobileNr');
+        $this->register_function('hook', 'TemplateHook');
+        $this->register_function('fileDateSig', 'TemplateFileDateSig');
+        $this->register_function('number', 'TemplateNumber');
+        $this->register_function('fieldDate', 'TemplateFieldDate');
+
+        // module handler
+        ModuleFunction('OnCreateTemplate', [&$this]);
+    }
+
+    /**
+     * register with a template hook.
+     *
+     * @param string $id  Hook ID
+     * @param string $tpl File name of template to be included
+     */
+    public function registerHook($id, $tpl)
+    {
+        if (!isset($this->hookTable[$id])) {
+            $this->hookTable[$id] = [$tpl];
+        } else {
+            $this->hookTable[$id][] = $tpl;
+        }
+    }
+
+    /**
+     * adds a JS file to be included in the page.
+     *
+     * @param string $area Area (nli/li/admin)
+     * @param string $file Filename
+     */
+    public function addJSFile($area, $file)
+    {
+        if (isset($this->_jsFiles[$area])) {
+            if (!in_array($file, $this->_jsFiles[$area])) {
+                if (file_exists($file)) {
+                    $file .= '?'.substr(md5(filemtime($file)), 0, 6);
+                }
+                $this->_jsFiles[$area][] = $file;
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * adds a CSS file to be included in the page.
+     *
+     * @param string $area Area (nli/li/admin)
+     * @param string $file Filename
+     */
+    public function addCSSFile($area, $file)
+    {
+        if (isset($this->_cssFiles[$area])) {
+            if (!in_array($file, $this->_cssFiles[$area])) {
+                $this->_cssFiles[$area][] = $file;
+
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public function fetch($resource_name, $cache_id = null, $compile_id = null, $display = false)
+    {
+        global $thisUser, $userRow, $groupRow, $lang_user, $plugins, $bm_prefs, $adminRow, $currentLanguage;
+
+        $this->assign('templatePrefs', GetTemplatePrefs($bm_prefs['template']));
+
+        // admin mode?
+        if (ADMIN_MODE && isset($adminRow)) {
+            $this->assign('adminRow', $adminRow);
+
+            $bmVer = B1GMAIL_VERSION;
+
+            $this->assign('bmver', $bmVer);
+
+            $pluginMenuItems = [];
+            foreach ($plugins->_plugins as $className => $pluginInfo) {
+                if ($plugins->getParam('admin_pages', $className)) {
+                    $pluginMenuItems[$className] = ['title' => $plugins->getParam('admin_page_title', $className),
+                                                            'icon' => $plugins->getParam('admin_page_icon', $className), ];
+                }
+            }
+
+            asort($pluginMenuItems);
+            $this->assign('pluginMenuItems', $pluginMenuItems);
+
+            $this->assign('isGerman', strpos(strtolower($currentLanguage), 'deutsch') !== false);
+        }
+
+        // tabs
+        if (isset($userRow) && isset($groupRow)) {
+            $newMenu = [
+                [
+                    'icon' => 'send_mail',
+                    'faIcon' => 'fa-envelope-o',
+                    'link' => 'email.compose.php?sid=',
+                    'text' => $lang_user['email'],
+                    'order' => 100,
+                ],
+                [
+                    'sep' => true,
+                    'order' => 200,
+                ],
+                [
+                    'icon' => 'ico_calendar',
+                    'faIcon' => 'fa-calendar',
+                    'link' => 'organizer.calendar.php?action=addDate&sid=',
+                    'text' => $lang_user['date2'],
+                    'order' => 300,
+                ],
+                [
+                    'icon' => 'ico_todo',
+                    'faIcon' => 'fa-tasks',
+                    'link' => 'organizer.todo.php?action=addTask&sid=',
+                    'text' => $lang_user['task'],
+                    'order' => 400,
+                ],
+                [
+                    'icon' => 'ico_addressbook',
+                    'faIcon' => 'fa-address-book-o',
+                    'link' => 'organizer.addressbook.php?action=addContact&sid=',
+                    'text' => $lang_user['contact'],
+                    'order' => 500,
+                ],
+                [
+                    'icon' => 'ico_notes',
+                    'faIcon' => 'fa-sticky-note-o',
+                    'link' => 'organizer.notes.php?action=addNote&sid=',
+                    'text' => $lang_user['note'],
+                    'order' => 600,
+                ],
+            ];
+
+            $pageTabs = [
+                'start' => [
+                    'icon' => 'start',
+                    'faIcon' => 'fa-home',
+                    'link' => 'start.php?sid=',
+                    'text' => $lang_user['start'],
+                    'order' => 100,
+                ],
+                'email' => [
+                    'icon' => 'email',
+                    'faIcon' => 'fa-envelope-o',
+                    'link' => 'email.php?sid=',
+                    'text' => $lang_user['email'],
+                    'order' => 200,
+                ],
+            ];
+
+            if ($thisUser->SMSEnabled()) {
+                $pageTabs['sms'] = [
+                    'icon' => 'sms',
+                    'faIcon' => 'fa-comments',
+                    'link' => 'sms.php?sid=',
+                    'text' => $lang_user['sms'],
+                    'order' => 300,
+                ];
+
+                $newMenu[] = [
+                    'sep' => true,
+                    'order' => 800,
+                ];
+                $newMenu[] = [
+                    'icon' => 'ico_composesms',
+                    'faIcon' => 'fa-comments',
+                    'link' => 'sms.php?sid=',
+                    'text' => $lang_user['sms'],
+                    'order' => 801,
+                ];
+            }
+
+            $pageTabs = array_merge($pageTabs, [
+                'organizer' => [
+                    'icon' => 'organizer',
+                    'faIcon' => 'fa-calendar',
+                    'link' => 'organizer.php?sid=',
+                    'text' => $lang_user['organizer'],
+                    'order' => 400,
+                ], ]);
+
+            if ($groupRow['webdisk'] + $userRow['diskspace_add'] > 0) {
+                $pageTabs = array_merge($pageTabs, [
+                    'webdisk' => [
+                        'icon' => 'webdisk',
+                        'faIcon' => 'fa-cloud',
+                        'link' => 'webdisk.php?sid=',
+                        'text' => $lang_user['webdisk'],
+                        'order' => 500,
+                    ], ]);
+
+                $newMenu[] = [
+                    'sep' => true,
+                    'order' => 700,
+                ];
+                $newMenu[] = [
+                    'icon' => 'webdisk_file',
+                    'faIcon' => 'fa-file-o',
+                    'link' => 'webdisk.php?do=uploadFilesForm&sid=',
+                    'text' => $lang_user['file'],
+                    'order' => 701,
+                ];
+            }
+
+            $moduleResult = $plugins->callFunction('getUserPages', false, true, [true]);
+            foreach ($moduleResult as $userPages) {
+                $pageTabs = array_merge($pageTabs, $userPages);
+            }
+
+            $moduleResult = $plugins->callFunction('getNewMenu', false, true, [true]);
+            foreach ($moduleResult as $newEntries) {
+                $newMenu = array_merge($newMenu, $newEntries);
+            }
+
+            $pageTabs = array_merge($pageTabs, [
+                'prefs' => [
+                    'icon' => 'prefs',
+                    'faIcon' => 'fa-cog',
+                    'link' => 'prefs.php?sid=',
+                    'text' => $lang_user['prefs'],
+                    'order' => 600,
+                ], ]);
+
+            // sort by order
+            if (is_array($tabOrder = @unserialize($bm_prefs['taborder']))) {
+                foreach ($tabOrder as $orderKey => $orderVal) {
+                    if (isset($pageTabs[$orderKey])) {
+                        if ($orderVal == -1) {
+                            unset($pageTabs[$orderKey]);
+                        } else {
+                            $pageTabs[$orderKey]['order'] = $orderVal;
+                        }
+                    }
+                }
+            }
+
+            ModuleFunction('BeforePageTabsAssign', [&$pageTabs]);
+            uasort($pageTabs, 'TemplateTabSort');
+            uasort($newMenu, 'TemplateTabSort');
+
+            $this->assign('pageTabs', $pageTabs);
+            $this->assign('pageTabsCount', count($pageTabs));
+            $this->assign('newMenu', $newMenu);
+            $this->assign('_userEmail', $userRow['email']);
+            $this->assign('searchDetailsDefault', $userRow['search_details_default'] == 'yes');
+            $this->assign('ftsBGIndexing', $bm_prefs['fts_bg_indexing'] == 'yes' && $groupRow['ftsearch'] == 'yes' && FTS_SUPPORT);
+
+            if ($groupRow['notifications'] == 'yes') {
+                $this->assign('bmUnreadNotifications', $thisUser->GetUnreadNotifications());
+                $this->assign('bmNotifyInterval', $bm_prefs['notify_interval']);
+                $this->assign('bmNotifySound', $userRow['notify_sound'] == 'yes');
+            }
+        }
+
+        // pugin pages (not logged in)
+        else {
+            $menu = [];
+            $moduleResult = $plugins->callFunction('getUserPages', false, true, [false]);
+            foreach ($moduleResult as $userPages) {
+                $menu = array_merge($menu, $userPages);
+            }
+            $this->assign('pluginUserPages', $menu);
+        }
+
+        // folder list
+        if ($this->reassignFolderList) {
+            global $mailbox;
+
+            if (isset($mailbox) && is_object($mailbox)) {
+                list(, $pageMenu) = $mailbox->GetPageFolderList();
+                $this->assign('folderList', $pageMenu);
+            }
+        }
+
+        ModuleFunction('BeforeDisplayTemplate', [$resource_name, &$this]);
+
+        $this->assign('_cssFiles', $this->_cssFiles);
+        $this->assign('_jsFiles', $this->_jsFiles);
+
+        StartPageOutput();
+
+        return Smarty::fetch($resource_name, $cache_id, $compile_id, $display);
+    }
 }
 }
 
 
 /**
 /**
- * helper functions
+ * helper functions.
  */
  */
 function TemplateTabSort($a, $b)
 function TemplateTabSort($a, $b)
 {
 {
-	$aOrder = isset($a['order']) ? $a['order'] : 599;
-	$bOrder = isset($b['order']) ? $b['order'] : 599;
-
-	if($aOrder == $bOrder)
-		return(0);
-	else if($aOrder < $bOrder)
-		return(-1);
-	else
-		return(1);
+    $aOrder = isset($a['order']) ? $a['order'] : 599;
+    $bOrder = isset($b['order']) ? $b['order'] : 599;
+
+    if ($aOrder == $bOrder) {
+        return 0;
+    } elseif ($aOrder < $bOrder) {
+        return -1;
+    } else {
+        return 1;
+    }
 }
 }
 
 
 /**
 /**
- * functions registered with smarty
+ * functions registered with smarty.
  */
  */
 function TemplateFileDateSig($params, &$smarty)
 function TemplateFileDateSig($params, &$smarty)
 {
 {
-	$fileName = $smarty->template_dir . $params['file'];
-	if(!file_exists($fileName))
-		return('');
-	$time = filemtime($fileName);
-	return(substr(md5($time), 0, 6));
+    $fileName = $smarty->template_dir.$params['file'];
+    if (!file_exists($fileName)) {
+        return '';
+    }
+    $time = filemtime($fileName);
+
+    return substr(md5($time), 0, 6);
 }
 }
 function TemplateBanner($params, &$smarty)
 function TemplateBanner($params, &$smarty)
 {
 {
-	global $db, $groupRow;
-
-	if(isset($groupRow) && is_array($groupRow) && $groupRow['ads'] == 'no')
-		return('');
-
-	if(isset($params['category']) && (($category = trim($params['category'])) != ''))
-		$res = $db->Query('SELECT id,code FROM {pre}ads WHERE paused=? AND category=? ORDER BY (views/weight) ASC LIMIT 1',
-			'no',
-			$category);
-	else
-		$res = $db->Query('SELECT id,code FROM {pre}ads WHERE paused=? ORDER BY (views/weight) ASC LIMIT 1',
-			'no');
-	if($res->RowCount() == 1)
-	{
-		list($bannerID, $bannerCode) = $res->FetchArray(MYSQLI_NUM);
-		$res->Free();
-
-		$db->Query('UPDATE {pre}ads SET views=views+1 WHERE id=?',
-			$bannerID);
-
-		return($bannerCode);
-	}
-
-	return('');
+    global $db, $groupRow;
+
+    if (isset($groupRow) && is_array($groupRow) && $groupRow['ads'] == 'no') {
+        return '';
+    }
+
+    if (isset($params['category']) && (($category = trim($params['category'])) != '')) {
+        $res = $db->Query('SELECT id,code FROM {pre}ads WHERE paused=? AND category=? ORDER BY (views/weight) ASC LIMIT 1',
+            'no',
+            $category);
+    } else {
+        $res = $db->Query('SELECT id,code FROM {pre}ads WHERE paused=? ORDER BY (views/weight) ASC LIMIT 1',
+            'no');
+    }
+    if ($res->RowCount() == 1) {
+        list($bannerID, $bannerCode) = $res->FetchArray(MYSQLI_NUM);
+        $res->Free();
+
+        $db->Query('UPDATE {pre}ads SET views=views+1 WHERE id=?',
+            $bannerID);
+
+        return $bannerCode;
+    }
+
+    return '';
 }
 }
 function TemplateImplode($params, &$smarty)
 function TemplateImplode($params, &$smarty)
 {
 {
-	return(implode($params['glue'], $params['pieces']));
+    return implode($params['glue'], $params['pieces']);
 }
 }
 function TemplateLang($params, &$smarty)
 function TemplateLang($params, &$smarty)
 {
 {
-	global $lang_user, $lang_client, $lang_admin;
+    global $lang_user, $lang_client, $lang_admin;
 
 
-	$phrase = $params['p'];
+    $phrase = $params['p'];
 
 
-	if(ADMIN_MODE && isset($lang_admin[$phrase]))
-		return($lang_admin[$phrase]);
+    if (ADMIN_MODE && isset($lang_admin[$phrase])) {
+        return $lang_admin[$phrase];
+    }
 
 
-	if(!ADMIN_MODE && isset($lang_user[$phrase]))
-		return($lang_user[$phrase]);
+    if (!ADMIN_MODE && isset($lang_user[$phrase])) {
+        return $lang_user[$phrase];
+    }
 
 
-	return('#UNKNOWN_PHRASE(' . $phrase . ')#');
+    return '#UNKNOWN_PHRASE('.$phrase.')#';
 }
 }
 function TemplateHalfHourToTime($params, &$smarty)
 function TemplateHalfHourToTime($params, &$smarty)
 {
 {
-	$value = $params['value'];
-
-	if(isset($params['dateStart']))
-	{
-		return(mktime(
-			$value%2==0 ? $value/2 : ($value-1)/2,
-			$value%2==0 ? 0 : 30,
-			0,
-			date('m', $params['dateStart']),
-			date('d', $parmas['dateStart']),
-			date('Y', $params['dateStart'])
-		));
-	}
-
-	if($value%2==0)
-		return(sprintf('%d:%02d', $value/2, 0));
-	else
-		return(sprintf('%d:%02d', ($value-1)/2, 30));
+    $value = $params['value'];
+
+    if (isset($params['dateStart'])) {
+        return mktime(
+            $value % 2 == 0 ? $value / 2 : ($value - 1) / 2,
+            $value % 2 == 0 ? 0 : 30,
+            0,
+            date('m', $params['dateStart']),
+            date('d', $parmas['dateStart']),
+            date('Y', $params['dateStart'])
+        );
+    }
+
+    if ($value % 2 == 0) {
+        return sprintf('%d:%02d', $value / 2, 0);
+    } else {
+        return sprintf('%d:%02d', ($value - 1) / 2, 30);
+    }
 }
 }
 function TemplateComment($params, &$smarty)
 function TemplateComment($params, &$smarty)
 {
 {
-	if(!DEBUG)
-		return('');
-	return('<!-- ' . $params['text'] . ' -->');
+    if (!DEBUG) {
+        return '';
+    }
+
+    return '<!-- '.$params['text'].' -->';
 }
 }
 function TemplateDate($params, &$smarty)
 function TemplateDate($params, &$smarty)
 {
 {
-	global $userRow, $bm_prefs, $lang_user;
-
-	if(isset($params['nozero']) && $params['timestamp']==0)
-		return('-');
-
-	if(isset($userRow))
-		$format = $userRow['datumsformat'];
-	else
-		$format = $bm_prefs['datumsformat'];
-	$ts = $params['timestamp'];
-
-	if($ts == -1)
-		return($lang_user['unknown']);
-
-	$diff = time() - $ts;
-	if(isset($params['elapsed']))
-	{
-		if($diff >= 0 && $diff < TIME_ONE_MINUTE)
-			$elapsed = sprintf($diff == 1 ? $lang_user['elapsed_second'] : $lang_user['elapsed_seconds'], $diff);
-		else if($diff >= TIME_ONE_MINUTE && $diff < TIME_ONE_HOUR)
-			$elapsed = sprintf(round($diff/TIME_ONE_MINUTE, 0) == 1 ? $lang_user['elapsed_minute'] : $lang_user['elapsed_minutes'], round($diff/TIME_ONE_MINUTE, 0));
-		else if($diff >= TIME_ONE_HOUR && $diff < TIME_ONE_DAY)
-			$elapsed = sprintf(round($diff/TIME_ONE_HOUR, 0) == 1 ? $lang_user['elapsed_hour'] : $lang_user['elapsed_hours'], round($diff/TIME_ONE_HOUR, 0));
-		else if($diff >= TIME_ONE_DAY)
-			$elapsed = sprintf(round($diff/TIME_ONE_DAY, 0) == 1 ? $lang_user['elapsed_day'] : $lang_user['elapsed_days'], round($diff/TIME_ONE_DAY, 0));
-		else
-			$elapsed = '';
-	}
-	else
-		$elapsed = '';
-
-	if(isset($params['dayonly']))
-	{
-		return(date('d.m.Y', $ts) . $elapsed);
-	}
-	else if(isset($params['format']))
-	{
-		return(_strftime($params['format'], $ts));
-	}
-	else if(isset($params['short']))
-	{
-		if(date('d.m.Y', $ts) == date('d.m.Y'))
-			return(date('H:i', $ts));
-		else if($ts > time()-6*TIME_ONE_DAY)
-			return(_strftime('%A', $ts));
-		else
-			return(date('d.m.y', $ts));
-	}
-	else if(!isset($params['nice']))
-	{
-		return(date($format, $ts) . $elapsed);
-	}
-	else
-	{
-		$today = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
-
-		if($ts >= $today && $ts <= $today+TIME_ONE_DAY)
-			return(sprintf('%s, %s', $lang_user['today'], date('H:i:s', $ts)) . $elapsed);
-		else if($ts >= $today-86400 && $ts < $today)
-			return(sprintf('%s, %s', $lang_user['yesterday'], date('H:i:s', $ts)) . $elapsed);
-		else
-			return(date($format, $ts) . $elapsed);
-	}
+    global $userRow, $bm_prefs, $lang_user;
+
+    if (isset($params['nozero']) && $params['timestamp'] == 0) {
+        return '-';
+    }
+
+    if (isset($userRow)) {
+        $format = $userRow['datumsformat'];
+    } else {
+        $format = $bm_prefs['datumsformat'];
+    }
+    $ts = $params['timestamp'];
+
+    if ($ts == -1) {
+        return $lang_user['unknown'];
+    }
+
+    $diff = time() - $ts;
+    if (isset($params['elapsed'])) {
+        if ($diff >= 0 && $diff < TIME_ONE_MINUTE) {
+            $elapsed = sprintf($diff == 1 ? $lang_user['elapsed_second'] : $lang_user['elapsed_seconds'], $diff);
+        } elseif ($diff >= TIME_ONE_MINUTE && $diff < TIME_ONE_HOUR) {
+            $elapsed = sprintf(round($diff / TIME_ONE_MINUTE, 0) == 1 ? $lang_user['elapsed_minute'] : $lang_user['elapsed_minutes'], round($diff / TIME_ONE_MINUTE, 0));
+        } elseif ($diff >= TIME_ONE_HOUR && $diff < TIME_ONE_DAY) {
+            $elapsed = sprintf(round($diff / TIME_ONE_HOUR, 0) == 1 ? $lang_user['elapsed_hour'] : $lang_user['elapsed_hours'], round($diff / TIME_ONE_HOUR, 0));
+        } elseif ($diff >= TIME_ONE_DAY) {
+            $elapsed = sprintf(round($diff / TIME_ONE_DAY, 0) == 1 ? $lang_user['elapsed_day'] : $lang_user['elapsed_days'], round($diff / TIME_ONE_DAY, 0));
+        } else {
+            $elapsed = '';
+        }
+    } else {
+        $elapsed = '';
+    }
+
+    if (isset($params['dayonly'])) {
+        return date('d.m.Y', $ts).$elapsed;
+    } elseif (isset($params['format'])) {
+        return _strftime($params['format'], $ts);
+    } elseif (isset($params['short'])) {
+        if (date('d.m.Y', $ts) == date('d.m.Y')) {
+            return date('H:i', $ts);
+        } elseif ($ts > time() - 6 * TIME_ONE_DAY) {
+            return _strftime('%A', $ts);
+        } else {
+            return date('d.m.y', $ts);
+        }
+    } elseif (!isset($params['nice'])) {
+        return date($format, $ts).$elapsed;
+    } else {
+        $today = mktime(0, 0, 0, date('m'), date('d'), date('Y'));
+
+        if ($ts >= $today && $ts <= $today + TIME_ONE_DAY) {
+            return sprintf('%s, %s', $lang_user['today'], date('H:i:s', $ts)).$elapsed;
+        } elseif ($ts >= $today - 86400 && $ts < $today) {
+            return sprintf('%s, %s', $lang_user['yesterday'], date('H:i:s', $ts)).$elapsed;
+        } else {
+            return date($format, $ts).$elapsed;
+        }
+    }
 }
 }
 function TemplateSize($params, &$smarty)
 function TemplateSize($params, &$smarty)
 {
 {
-	global $lang_user;
-
-	$size = $params['bytes'];
-
-	if($size == -1)
-		return('<i>' . $lang_user['unlimited'] . '</i>');
-
-	if($size < 1024)
-		return((int)$size . ' B');
-	else if($size < 1024*1024)
-		return(sprintf('%.2f KB', round($size/1024, 2)));
-	else if($size < 1024*1024*1024)
-		return(sprintf('%.2f MB', round($size/1024/1024, 2)));
-	else
-		return(sprintf('%.2f GB', round($size/1024/1024/1024, 2)));
+    global $lang_user;
+
+    $size = $params['bytes'];
+
+    if ($size == -1) {
+        return '<i>'.$lang_user['unlimited'].'</i>';
+    }
+
+    if ($size < 1024) {
+        return (int) $size.' B';
+    } elseif ($size < 1024 * 1024) {
+        return sprintf('%.2f KB', round($size / 1024, 2));
+    } elseif ($size < 1024 * 1024 * 1024) {
+        return sprintf('%.2f MB', round($size / 1024 / 1024, 2));
+    } else {
+        return sprintf('%.2f GB', round($size / 1024 / 1024 / 1024, 2));
+    }
 }
 }
 function cutHTML($str, $length, $add = '')
 function cutHTML($str, $length, $add = '')
 {
 {
-	// no &#;-entities -> use substr
-	if(!preg_match('/\&#([x0-9]*);/', $str)
-		&& !preg_match('/\&([a-zA-Z]*);/', $str))
-		return(_strlen($str) > $length ? _substr($str, 0, $length-_strlen($add)) . $add : $str);
-
-	// otherwise use the complicated way
-	$tooLong = false;
-	$result = array();
-	for($i=0; $i<strlen($str); $i++)
-	{
-		$match = false;
-		if(strlen($str)-$i > 3
-			&& (preg_match('/^\&#([x0-9]*);/', substr($str, $i), $match)
-				|| preg_match('/^\&([a-zA-Z]*);/', substr($str, $i), $match)))
-		{
-			$result[] = $match[0];
-			$i += strlen($match[0])-1;
-		}
-		else
-			$result[] = $str[$i];
-
-		if(count($result) >= $length)
-		{
-			$tooLong = true;
-			break;
-		}
-	}
-
-	if($tooLong)
-		return(implode('', array_slice($result, 0, $length-strlen($add))) . $add);
-	else
-		return(implode('', $result));
+    // no &#;-entities -> use substr
+    if (!preg_match('/\&#([x0-9]*);/', $str)
+        && !preg_match('/\&([a-zA-Z]*);/', $str)) {
+        return _strlen($str) > $length ? _substr($str, 0, $length - _strlen($add)).$add : $str;
+    }
+
+    // otherwise use the complicated way
+    $tooLong = false;
+    $result = [];
+    for ($i = 0; $i < strlen($str); ++$i) {
+        $match = false;
+        if (strlen($str) - $i > 3
+            && (preg_match('/^\&#([x0-9]*);/', substr($str, $i), $match)
+                || preg_match('/^\&([a-zA-Z]*);/', substr($str, $i), $match))) {
+            $result[] = $match[0];
+            $i += strlen($match[0]) - 1;
+        } else {
+            $result[] = $str[$i];
+        }
+
+        if (count($result) >= $length) {
+            $tooLong = true;
+            break;
+        }
+    }
+
+    if ($tooLong) {
+        return implode('', array_slice($result, 0, $length - strlen($add))).$add;
+    } else {
+        return implode('', $result);
+    }
 }
 }
 function TemplateFieldDate($params, &$smarty)
 function TemplateFieldDate($params, &$smarty)
 {
 {
-	global $bm_prefs;
+    global $bm_prefs;
 
 
-	$val = $params['value'];
-	if(empty($val))
-		return '-';
+    $val = $params['value'];
+    if (empty($val)) {
+        return '-';
+    }
 
 
-	$parts = explode('-', $val);
-	if(count($parts) != 3)
-		return '-';
+    $parts = explode('-', $val);
+    if (count($parts) != 3) {
+        return '-';
+    }
 
 
-	list($y, $m, $d) = $parts;
-	if($y == 0 || $m == 0 || $d == 0)
-		return '-';
+    list($y, $m, $d) = $parts;
+    if ($y == 0 || $m == 0 || $d == 0) {
+        return '-';
+    }
 
 
-	return sprintf('%02d.%02d.%04d', $d, $m, $y);
+    return sprintf('%02d.%02d.%04d', $d, $m, $y);
 }
 }
 function TemplateNumber($params, &$smarty)
 function TemplateNumber($params, &$smarty)
 {
 {
-	$no = (int)$params['value'];
-	if(isset($params['min']))
-		$no = max($params['min'], $no);
-	if(isset($params['max']))
-		$no = min($params['max'], $no);
-	return($no);
+    $no = (int) $params['value'];
+    if (isset($params['min'])) {
+        $no = max($params['min'], $no);
+    }
+    if (isset($params['max'])) {
+        $no = min($params['max'], $no);
+    }
+
+    return $no;
 }
 }
 function TemplateDomain($params, &$smarty)
 function TemplateDomain($params, &$smarty)
 {
 {
-	$domain = $params['value'];
-	return(HTMLFormat(DecodeDomain($domain)));
+    $domain = $params['value'];
+
+    return HTMLFormat(DecodeDomain($domain));
 }
 }
 function TemplateEMail($params, &$smarty)
 function TemplateEMail($params, &$smarty)
 {
 {
-	$email = DecodeEMail($params['value']);
-	if(isset($params['cut']))
-		$email = cutHTML($email, $params['cut'], '...');
-	return(HTMLFormat($email));
+    $email = DecodeEMail($params['value']);
+    if (isset($params['cut'])) {
+        $email = cutHTML($email, $params['cut'], '...');
+    }
+
+    return HTMLFormat($email);
 }
 }
 function TemplateText($params, &$smarty)
 function TemplateText($params, &$smarty)
 {
 {
-	$text = $params['value'];
-
-	if(isset($params['ucFirst']))
-		$text = ucfirst($text);
-
-	if(isset($params['escape']))
-	{
-		$text = addslashes($text);
-		if(isset($params['noentities']))
-			$text = str_replace('/', '\/', $text);
-	}
-
-	if(isset($params['cut']))
-		$text = cutHTML($text, $params['cut'], '...');
-
-	if($text == '' && !isset($params['allowEmpty']))
-		return(' - ');
-
-	if(isset($params['stripTags']))
-		$text = strip_tags($text);
-
-	if(isset($params['noentities']))
-	{
-		return($text);
-	}
-	else
-	{
-		$text = HTMLFormat($text, isset($params['allowDoubleEnc']) && $params['allowDoubleEnc']);
-		return($text);
-	}
+    $text = $params['value'];
+
+    if (isset($params['ucFirst'])) {
+        $text = ucfirst($text);
+    }
+
+    if (isset($params['escape'])) {
+        $text = addslashes($text);
+        if (isset($params['noentities'])) {
+            $text = str_replace('/', '\/', $text);
+        }
+    }
+
+    if (isset($params['cut'])) {
+        $text = cutHTML($text, $params['cut'], '...');
+    }
+
+    if ($text == '' && !isset($params['allowEmpty'])) {
+        return ' - ';
+    }
+
+    if (isset($params['stripTags'])) {
+        $text = strip_tags($text);
+    }
+
+    if (isset($params['noentities'])) {
+        return $text;
+    } else {
+        $text = HTMLFormat($text, isset($params['allowDoubleEnc']) && $params['allowDoubleEnc']);
+
+        return $text;
+    }
 }
 }
 function TemplateAddressList($params, &$smarty)
 function TemplateAddressList($params, &$smarty)
 {
 {
-	$list = '';
-	$short = isset($params['short']);
-
-	foreach($params['list'] as $addressItem)
-	{
-		if($short)
-		{
-			if(isset($params['simple']))
-				$list .= '; ' . trim(HTMLFormat($addressItem['name']) != '' ? HTMLFormat($addressItem['name']) : HTMLFormat(DecodeEMail($addressItem['mail'])));
-			else
-				$list .= sprintf(' <a class="mailAddressLink" href="javascript:void(0);" onclick="currentEMail=\'%s\';showAddressMenu(event);">%s</a>',
-					addslashes(DecodeEMail($addressItem['mail'])),
-					trim(HTMLFormat($addressItem['name']) != '' ? HTMLFormat($addressItem['name']) : HTMLFormat(DecodeEMail($addressItem['mail']))));
-		}
-		else
-		{
-			if(isset($params['simple']))
-				$list .= '; ' . trim(HTMLFormat($addressItem['name']) . ' ' . (trim($addressItem['name']) != '' ? '&lt;' . HTMLFormat(DecodeEMail($addressItem['mail'])) . '&gt;'
-							: HTMLFormat(DecodeEMail($addressItem['mail']))));
-			else
-				$list .= sprintf(' <a class="mailAddressLink" href="javascript:void(0);" onclick="currentEMail=\'%s\';showAddressMenu(event);">%s</a>',
-					DecodeEMail(addslashes($addressItem['mail'])),
-					trim(HTMLFormat($addressItem['name']) . ' ' . (trim($addressItem['name']) != '' ? '&lt;' . HTMLFormat(DecodeEMail($addressItem['mail'])) . '&gt;'
-					: HTMLFormat(DecodeEMail($addressItem['mail'])))));
-		}
-	}
-
-	if(isset($params['simple']))
-		$list = substr($list, 2);
-
-	return(trim($list));
+    $list = '';
+    $short = isset($params['short']);
+
+    foreach ($params['list'] as $addressItem) {
+        if ($short) {
+            if (isset($params['simple'])) {
+                $list .= '; '.trim(HTMLFormat($addressItem['name']) != '' ? HTMLFormat($addressItem['name']) : HTMLFormat(DecodeEMail($addressItem['mail'])));
+            } else {
+                $list .= sprintf(' <a class="mailAddressLink" href="javascript:void(0);" onclick="currentEMail=\'%s\';showAddressMenu(event);">%s</a>',
+                    addslashes(DecodeEMail($addressItem['mail'])),
+                    trim(HTMLFormat($addressItem['name']) != '' ? HTMLFormat($addressItem['name']) : HTMLFormat(DecodeEMail($addressItem['mail']))));
+            }
+        } else {
+            if (isset($params['simple'])) {
+                $list .= '; '.trim(HTMLFormat($addressItem['name']).' '.(trim($addressItem['name']) != '' ? '&lt;'.HTMLFormat(DecodeEMail($addressItem['mail'])).'&gt;'
+                            : HTMLFormat(DecodeEMail($addressItem['mail']))));
+            } else {
+                $list .= sprintf(' <a class="mailAddressLink" href="javascript:void(0);" onclick="currentEMail=\'%s\';showAddressMenu(event);">%s</a>',
+                    DecodeEMail(addslashes($addressItem['mail'])),
+                    trim(HTMLFormat($addressItem['name']).' '.(trim($addressItem['name']) != '' ? '&lt;'.HTMLFormat(DecodeEMail($addressItem['mail'])).'&gt;'
+                    : HTMLFormat(DecodeEMail($addressItem['mail'])))));
+            }
+        }
+    }
+
+    if (isset($params['simple'])) {
+        $list = substr($list, 2);
+    }
+
+    return trim($list);
 }
 }
 function TemplateProgressBar($params, &$smarty)
 function TemplateProgressBar($params, &$smarty)
 {
 {
-	$value = $params['value'];
-	$max = $params['max'];
-	$width = $params['width'];
-	$name = isset($params['name']) ? $params['name'] : mt_rand(0, 1000);
-
-	if($max == 0)
-		$valueWidth = 0;
-	else
-		$valueWidth = $width/$max * $value;
-
-	return(sprintf('<div class="progressBar" id="pb_%s" style="width:%dpx;"><div class="progressBarValue" id="pb_%s_value" style="width:%dpx;"></div></div>',
-		$name,
-		$width,
-		$name,
-		min($width-2, $valueWidth)));
+    $value = $params['value'];
+    $max = $params['max'];
+    $width = $params['width'];
+    $name = isset($params['name']) ? $params['name'] : mt_rand(0, 1000);
+
+    if ($max == 0) {
+        $valueWidth = 0;
+    } else {
+        $valueWidth = $width / $max * $value;
+    }
+
+    return sprintf('<div class="progressBar" id="pb_%s" style="width:%dpx;"><div class="progressBarValue" id="pb_%s_value" style="width:%dpx;"></div></div>',
+        $name,
+        $width,
+        $name,
+        min($width - 2, $valueWidth));
 }
 }
 function TemplateMiniCalendar($params, &$smarty)
 function TemplateMiniCalendar($params, &$smarty)
 {
 {
-	global $userRow;
-	if(!isset($userRow))
-		return('Not logged in');
-	if(!class_exists('BMCalendar'))
-		include(B1GMAIL_DIR . 'serverlib/calendar.class.php');
-	$calendar = _new('BMCalendar', array($userRow['id']));
-	return($calendar->GenerateMiniCalendar(-1, -1));
+    global $userRow;
+    if (!isset($userRow)) {
+        return 'Not logged in';
+    }
+    if (!class_exists('BMCalendar')) {
+        include B1GMAIL_DIR.'serverlib/calendar.class.php';
+    }
+    $calendar = _new('BMCalendar', [$userRow['id']]);
+
+    return $calendar->GenerateMiniCalendar(-1, -1);
 }
 }
 function TemplateFileSelector($params, &$smarty)
 function TemplateFileSelector($params, &$smarty)
 {
 {
-	global $lang_user, $groupRow;
-
-	$name = $params['name'];
-	$size = isset($params['size']) ? (int)$params['size'] : 30;
-
-	return(sprintf('<table width="100%%" cellspacing="1" cellpadding="0">'
-				.		'<tr>'
-				.			'<td width="10"><select onchange="changeFileSelectorSource(this, \'%s\')">'
-				.								'<option value="local">%s</option>'
-				. ((isset($groupRow) && is_array($groupRow) && $groupRow['webdisk'] > 0) ?
-												'<option value="webdisk">%s</option>' : '<!-- %s -->')
-				.							'</select></td>'
-				.			'<td width="5">&nbsp;</td>'
-				.			'<td><div id="fileSelector_local_%s" style="display:;"><input type="file" id="localFile_%s" name="localFile_%s%s" size="%d" style="width: 100%%;"%s /></div>'
-				.				'<div id="fileSelector_webdisk_%s" style="display:none;"><input type="hidden" name="webdiskFile_%s_id" id="webdiskFile_%s_id" value="" /><input type="text" id="webdiskFile_%s" name="webdiskFile_%s" size="%d" readonly="readonly" /> <input onclick="webdiskDialog(\'%s\', \'open\', \'webdiskFile_%s\')" type="button" value="..." /></div></td>'
-				.		'</tr>'
-				.	'</table>',
-				$name,
-				$lang_user['localfile'],
-				$lang_user['webdiskfile'],
-				$name,
-				$name,
-				$name,
-				isset($params['multiple']) ? '[]' : '',
-				$size,
-				isset($params['multiple']) ? ' multiple="multiple"' : '',
-				$name,
-				$name,
-				$name,
-				$name,
-				$name,
-				$size,
-				session_id(),
-				$name));
+    global $lang_user, $groupRow;
+
+    $name = $params['name'];
+    $size = isset($params['size']) ? (int) $params['size'] : 30;
+
+    return sprintf('<table width="100%%" cellspacing="1" cellpadding="0">'
+                .'<tr>'
+                .'<td width="10"><select onchange="changeFileSelectorSource(this, \'%s\')">'
+                .'<option value="local">%s</option>'
+                .((isset($groupRow) && is_array($groupRow) && $groupRow['webdisk'] > 0) ?
+                                                '<option value="webdisk">%s</option>' : '<!-- %s -->')
+                .'</select></td>'
+                .'<td width="5">&nbsp;</td>'
+                .'<td><div id="fileSelector_local_%s" style="display:;"><input type="file" id="localFile_%s" name="localFile_%s%s" size="%d" style="width: 100%%;"%s /></div>'
+                .'<div id="fileSelector_webdisk_%s" style="display:none;"><input type="hidden" name="webdiskFile_%s_id" id="webdiskFile_%s_id" value="" /><input type="text" id="webdiskFile_%s" name="webdiskFile_%s" size="%d" readonly="readonly" /> <input onclick="webdiskDialog(\'%s\', \'open\', \'webdiskFile_%s\')" type="button" value="..." /></div></td>'
+                .'</tr>'
+                .'</table>',
+                $name,
+                $lang_user['localfile'],
+                $lang_user['webdiskfile'],
+                $name,
+                $name,
+                $name,
+                isset($params['multiple']) ? '[]' : '',
+                $size,
+                isset($params['multiple']) ? ' multiple="multiple"' : '',
+                $name,
+                $name,
+                $name,
+                $name,
+                $name,
+                $size,
+                session_id(),
+                $name);
 }
 }
 function TemplatePageNav($params, &$smarty)
 function TemplatePageNav($params, &$smarty)
 {
 {
-	$tpl_on = $params['on'];
-	$tpl_off = $params['off'];
-	$aktuelle_seite = $params['page'];
-	$anzahl_seiten = $params['pages'];
-	$ret = '';
-
-	$seiten = array($aktuelle_seite-3,
-		$aktuelle_seite-2,
-		$aktuelle_seite-1,
-		$aktuelle_seite,
-		$aktuelle_seite+1,
-		$aktuelle_seite+2,
-		$aktuelle_seite+3);
-
-	if($aktuelle_seite > 1)
-	{
-		$ret .= str_replace('.t', '&lt;&lt;', str_replace('.s', ($aktuelle_seite-1), $tpl_off));
-	}
-
-	foreach($seiten as $key=>$val)
-	{
-		if($val >= 1 && $val <= $anzahl_seiten)
-		{
-			if($aktuelle_seite == $val)
-			{
-				$ret .= str_replace(array('.s', '.t'), $val, $tpl_on);
-			}
-			else
-			{
-				$ret .= str_replace(array('.s', '.t'), $val, $tpl_off);
-			}
-		}
-	}
-
-	if($aktuelle_seite < $anzahl_seiten)
-	{
-		$ret .= str_replace('.t', '&gt;&gt;', str_replace('.s', ($aktuelle_seite+1), $tpl_off));
-	}
-
-	return($ret);
+    $tpl_on = $params['on'];
+    $tpl_off = $params['off'];
+    $aktuelle_seite = $params['page'];
+    $anzahl_seiten = $params['pages'];
+    $ret = '';
+
+    $seiten = [$aktuelle_seite - 3,
+        $aktuelle_seite - 2,
+        $aktuelle_seite - 1,
+        $aktuelle_seite,
+        $aktuelle_seite + 1,
+        $aktuelle_seite + 2,
+        $aktuelle_seite + 3, ];
+
+    if ($aktuelle_seite > 1) {
+        $ret .= str_replace('.t', '&lt;&lt;', str_replace('.s', ($aktuelle_seite - 1), $tpl_off));
+    }
+
+    foreach ($seiten as $key => $val) {
+        if ($val >= 1 && $val <= $anzahl_seiten) {
+            if ($aktuelle_seite == $val) {
+                $ret .= str_replace(['.s', '.t'], $val, $tpl_on);
+            } else {
+                $ret .= str_replace(['.s', '.t'], $val, $tpl_off);
+            }
+        }
+    }
+
+    if ($aktuelle_seite < $anzahl_seiten) {
+        $ret .= str_replace('.t', '&gt;&gt;', str_replace('.s', ($aktuelle_seite + 1), $tpl_off));
+    }
+
+    return $ret;
 }
 }
 function TemplateStoreTime($params, &$smarty)
 function TemplateStoreTime($params, &$smarty)
 {
 {
-	global $lang_user;
-
-	$time = $params['value'];
-
-	if($time == 86400)
-		return('1 ' . $lang_user['days']);
-	else if($time == 172800)
-		return('2 ' . $lang_user['days']);
-	else if($time == 432000)
-		return('5 ' . $lang_user['days']);
-	else if($time == 604800)
-		return('7 ' . $lang_user['days']);
-	else if($time == 1209600)
-		return('2 ' . $lang_user['weeks']);
-	else if($time == 2419200)
-		return('4 ' . $lang_user['weeks']);
-	else if($time == 4828400)
-		return('2 ' . $lang_user['months']);
-	else
-		return('-');
+    global $lang_user;
+
+    $time = $params['value'];
+
+    if ($time == 86400) {
+        return '1 '.$lang_user['days'];
+    } elseif ($time == 172800) {
+        return '2 '.$lang_user['days'];
+    } elseif ($time == 432000) {
+        return '5 '.$lang_user['days'];
+    } elseif ($time == 604800) {
+        return '7 '.$lang_user['days'];
+    } elseif ($time == 1209600) {
+        return '2 '.$lang_user['weeks'];
+    } elseif ($time == 2419200) {
+        return '4 '.$lang_user['weeks'];
+    } elseif ($time == 4828400) {
+        return '2 '.$lang_user['months'];
+    } else {
+        return '-';
+    }
 }
 }
 function TemplateHook($params, &$smarty)
 function TemplateHook($params, &$smarty)
 {
 {
-	$result = '';
+    $result = '';
 
 
-	if(DEBUG && isset($_REQUEST['_showHooks']))
-		$result .= '<div>#' . $params['id'] . '</div>';
+    if (DEBUG && isset($_REQUEST['_showHooks'])) {
+        $result .= '<div>#'.$params['id'].'</div>';
+    }
 
 
-	if(DEBUG)
-		$result .= '<!-- hook(' . $params['id'] . ') -->';
+    if (DEBUG) {
+        $result .= '<!-- hook('.$params['id'].') -->';
+    }
 
 
-	if(isset($smarty->hookTable) && is_array($smarty->hookTable)
-	   && isset($smarty->hookTable[$params['id']]))
-	{
-		foreach($smarty->hookTable[$params['id']] as $file)
-			$result .= $smarty->fetch($file);
-	}
+    if (isset($smarty->hookTable) && is_array($smarty->hookTable)
+       && isset($smarty->hookTable[$params['id']])) {
+        foreach ($smarty->hookTable[$params['id']] as $file) {
+            $result .= $smarty->fetch($file);
+        }
+    }
 
 
-	if(DEBUG)
-		$result .= '<!-- /hook(' . $params['id'] . ') -->';
+    if (DEBUG) {
+        $result .= '<!-- /hook('.$params['id'].') -->';
+    }
 
 
-	return($result);
+    return $result;
 }
 }
 function TemplateMobileNr($params, &$smarty)
 function TemplateMobileNr($params, &$smarty)
 {
 {
-	global $groupRow;
-
-	$value = isset($params['value']) ? $params['value'] : '';
-	$name = $params['name'];
-	$size = isset($params['size']) ? $params['size'] : '100%';
-
-	if(trim($groupRow['sms_pre']) != '')
-	{
-		$preOptions = '';
-		$haveValue = false;
-		$entries = explode(':', $groupRow['sms_pre']);
-		foreach($entries as $entry)
-		{
-			if(trim($entry) != '')
-			{
-				if(substr($value, 0, strlen($entry)) == $entry && !$haveValue)
-				{
-					$preOptions .= sprintf('<option value="%s" selected="selected">%s</option>',
-						$entry,
-						$entry);
-					$value = substr($value, strlen($entry));
-					$haveValue = true;
-				}
-				else
-				{
-					$preOptions .= sprintf('<option value="%s">%s</option>',
-						$entry,
-						$entry);
-				}
-			}
-		}
-
-		return(sprintf('<table width="%s" cellspacing="0" cellpadding="0">'
-					.		'<tr>'
-					.			'<td width="1" nowrap="nowrap"><nobr>(<select name="%s_pre" id="%s_pre">%s</select>)&nbsp;</nobr></td>'
-					.			'<td><input type="text" name="%s_no" id="%s_no" style="width:100%%;" value="%s" /></td>'
-					.		'</tr>'
-					.	'</table>',
-			$size,
-			$name, $name,
-			$preOptions,
-			$name, $name,
-			$value));
-	}
-	else
-	{
-		return(sprintf('<input type="text" name="%s" id="%s" style="width:%s;" value="%s" />',
-			$name,
-			$name,
-			$size,
-			HTMLFormat($value)));
-	}
+    global $groupRow;
+
+    $value = isset($params['value']) ? $params['value'] : '';
+    $name = $params['name'];
+    $size = isset($params['size']) ? $params['size'] : '100%';
+
+    if (trim($groupRow['sms_pre']) != '') {
+        $preOptions = '';
+        $haveValue = false;
+        $entries = explode(':', $groupRow['sms_pre']);
+        foreach ($entries as $entry) {
+            if (trim($entry) != '') {
+                if (substr($value, 0, strlen($entry)) == $entry && !$haveValue) {
+                    $preOptions .= sprintf('<option value="%s" selected="selected">%s</option>',
+                        $entry,
+                        $entry);
+                    $value = substr($value, strlen($entry));
+                    $haveValue = true;
+                } else {
+                    $preOptions .= sprintf('<option value="%s">%s</option>',
+                        $entry,
+                        $entry);
+                }
+            }
+        }
+
+        return sprintf('<table width="%s" cellspacing="0" cellpadding="0">'
+                    .'<tr>'
+                    .'<td width="1" nowrap="nowrap"><nobr>(<select name="%s_pre" id="%s_pre">%s</select>)&nbsp;</nobr></td>'
+                    .'<td><input type="text" name="%s_no" id="%s_no" style="width:100%%;" value="%s" /></td>'
+                    .'</tr>'
+                    .'</table>',
+            $size,
+            $name, $name,
+            $preOptions,
+            $name, $name,
+            $value);
+    } else {
+        return sprintf('<input type="text" name="%s" id="%s" style="width:%s;" value="%s" />',
+            $name,
+            $name,
+            $size,
+            HTMLFormat($value));
+    }
 }
 }

+ 234 - 231
src/serverlib/unzip.class.php

@@ -19,240 +19,243 @@
  *
  *
  */
  */
 
 
-if(!defined('B1GMAIL_INIT'))
-	die('Directly calling this file is not supported');
+if (!defined('B1GMAIL_INIT')) {
+    die('Directly calling this file is not supported');
+}
 
 
 /**
 /**
- * Basic UnZIP class
- *
+ * Basic UnZIP class.
  */
  */
 class BMUnZIP
 class BMUnZIP
 {
 {
-	var $_fp;
-	var $_centralDirStruct;
-
-	/**
-	 * constructor
-	 *
-	 * @param resource $fp Input file stream
-	 * @return BMUnZIP
-	 */
-	function __construct($fp)
-	{
-		$this->_fp = $fp;
-		$this->_readCentralDirStruct();
-	}
-
-	/**
-	 * get ZIP file directory listing
-	 *
-	 * @return array
-	 */
-	function GetFileList()
-	{
-		return($this->_centralDirStruct);
-	}
-
-	/**
-	 * get ZIP file directory listing prepared for tree display
-	 *
-	 * @return array
-	 */
-	function GetFileTree()
-	{
-		$tree = array();
-		$folderNoCounter = count($this->_centralDirStruct);
-
-		// add files
-		foreach($this->_centralDirStruct as $fileNo=>$file)
-		{
-			$file['parentID']		= -2;
-			$file['type']			= 'file';
-			$file['fileNo'] 		= $fileNo;
-			$file['baseName']		= basename($file['fileName']);
-
-			$tree[] 				= $file;
-		}
-
-		// add folders
-		$again = true;
-		while($again)
-		{
-			$again = false;
-
-			foreach($tree as $id=>$item)
-			{
-				if($item['parentID'] != -2)
-					continue;
-
-				$parentFolderName	= dirname($item['fileName']);
-
-				if($parentFolderName != '.' && $parentFolderName != '')
-				{
-					$parentID			= -2;
-
-					foreach($tree as $id2=>$item2)
-					{
-						if($item2['type'] == 'folder' && $item2['fileName'] == $parentFolderName)
-						{
-							$parentID = $item2['fileNo'];
-							break;
-						}
-					}
-
-					if($parentID == -2)
-					{
-						$folderNo = $folderNoCounter++;
-						$tree[] = array('type' 			=> 'folder',
-										'fileName'		=> $parentFolderName,
-										'baseName' 		=> basename($parentFolderName),
-										'fileNo' 		=> $folderNo,
-										'parentID'		=> -2);
-						$parentID = $folderNo;
-						$again = true;
-					}
-
-					$tree[$id]['parentID'] = $parentID;
-				}
-			}
-		}
-
-		return($tree);
-	}
-
-	/**
-	 * extract a file by file no
-	 *
-	 * @param int $fileNo File no (from GetFileList() array)
-	 * @param resource $fp Output file stream
-	 * @return bool
-	 */
-	function ExtractFile($fileNo, $fp = false, $sizeLimit = -1)
-	{
-		if(!isset($this->_centralDirStruct[$fileNo]))
-			return(false);
-
-		if(!in_array($this->_centralDirStruct[$fileNo]['compressionMethod'], array(8, 12)))
-			return(false);
-
-		fseek($this->_fp, $this->_centralDirStruct[$fileNo]['relativeOffset'], SEEK_SET);
-
-		$fileHeader = fread($this->_fp, 30);
-
-		$_fileHeader = @unpack('Vsignature/vversionNeeded/vflags/vcompressionMethod/vmTime/vmDate/Vcrc32/VcompressedSize/VuncompressedSize/vfileNameLength/vextraFieldLength',
-						 	   $fileHeader);
-
-		if(!$_fileHeader || $_fileHeader['signature'] != 0x04034b50)
-			return(false);
-
-		fseek($this->_fp, $_fileHeader['fileNameLength']+$_fileHeader['extraFieldLength'], SEEK_CUR);
-
-		$_fileHeader = $this->_centralDirStruct[$fileNo];
-
-		if($_fileHeader['compressedSize'] > 0)
-		{
-			$compressedData = fread($this->_fp, $_fileHeader['compressedSize']);
-			$uncompressedData = '';
-
-			if($_fileHeader['compressionMethod'] == 8)
-			{
-				$uncompressedData = @gzinflate($compressedData);
-			}
-			else if($_fileHeader['compressionMethod'] == 12)
-			{
-				$uncompressedData = @bzdecompress($compressedData);
-			}
-
-			unset($compressedData);
-
-			if(crc32($uncompressedData) != $_fileHeader['crc32'])
-				return(false);
-
-			if($fp !== false)
-				fwrite($fp, $uncompressedData, $sizeLimit != -1 ? $sizeLimit : strlen($uncompressedData));
-			else
-				echo($uncompressedData);
-		}
-		else if($_fileHeader['crc32'] != 0)
-		{
-			return(false);
-		}
-
-		return(true);
-	}
-
-	/**
-	 * read central dir struct from ZIP file
-	 *
-	 */
-	function _readCentralDirStruct()
-	{
-		$this->_centralDirStruct = array();
-
-		fseek($this->_fp, -22, SEEK_END);
-		$endOfCDS = fread($this->_fp, 22);
-
-		while(substr($endOfCDS, 0, 4) != pack('V', 0x06054b50))
-		{
-			fseek($this->_fp, -1, SEEK_CUR);
-			$endOfCDS = fgetc($this->_fp) . $endOfCDS;
-			fseek($this->_fp, -1, SEEK_CUR);
-
-			if(ftell($this->_fp) < 2)
-				break;
-		}
-
-		if(substr($endOfCDS, 0, 4) != pack('V', 0x06054b50))
-		{
-			if(DEBUG) trigger_error('File corrupt or not in ZIP format', E_USER_NOTICE);
-			return;
-		}
-
-		// parse endOfCDS record
-		$_endOfCDS = @unpack('Vsignature/vdiskNo/vcdsStartDiskNo/vcdsDiskEntryCount/vcdsTotalEntryCount/VcdsSize/VcdsOffset/vcommentLength', $endOfCDS);
-		if(!$_endOfCDS)
-		{
-			if(DEBUG) trigger_error('File corrupt or not in ZIP format (eoCDS broken)', E_USER_NOTICE);
-			return;
-		}
-
-		// seek to CDS offset
-		fseek($this->_fp, $_endOfCDS['cdsOffset'], SEEK_SET);
-
-		// read CDS entries
-		for($i=0; $i<$_endOfCDS['cdsDiskEntryCount'] && !feof($this->_fp); $i++)
-		{
-			$fileHeader = fread($this->_fp, 46);
-
-			$_fileHeader = @unpack('Vsignature/vversion/vversionNeeded/vflags/vcompressionMethod/vmTime/vmDate/Vcrc32/VcompressedSize/VuncompressedSize/vfileNameLength/vextraFieldLength/vfileCommentLength/vdiskNumberStart/vinternalAttrs/VexternalAttrs/VrelativeOffset',
-								   $fileHeader);
-			if(!$_fileHeader || $_fileHeader['signature'] != 0x02014b50)
-			{
-				if(DEBUG) trigger_error('File corrupt (CDS broken)');
-				return;
-			}
-
-			$_fileHeader['cdsOffset'] = ftell($this->_fp) - strlen($fileHeader);
-
-			if($_fileHeader['fileNameLength'] > 0)
-				$_fileHeader['fileName'] 	= fread($this->_fp, $_fileHeader['fileNameLength']);
-			if($_fileHeader['extraFieldLength'] > 0)
-				$_fileHeader['extraField']	= fread($this->_fp, $_fileHeader['extraFieldLength']);
-			if($_fileHeader['fileCommentLength'] > 0)
-				$_fileHeader['fileComment']	= fread($this->_fp, $_fileHeader['fileCommentLength']);
-
-			if(substr($_fileHeader['fileName'], -1) == '/' && $_fileHeader['uncompressedSize'] == 0)
-				continue;
-
-			$this->_centralDirStruct[] = $_fileHeader;
-		}
-
-		// sort by filename
-		uasort($this->_centralDirStruct, array(&$this, '_sortHandler'));
-	}
-
-	function _sortHandler($s1, $s2)
-	{
-		return(strcmp($s1['fileName'], $s2['fileName']));
-	}
+    private $_fp;
+    private $_centralDirStruct;
+
+    /**
+     * constructor.
+     *
+     * @param resource $fp Input file stream
+     *
+     * @return BMUnZIP
+     */
+    public function __construct($fp)
+    {
+        $this->_fp = $fp;
+        $this->_readCentralDirStruct();
+    }
+
+    /**
+     * get ZIP file directory listing.
+     *
+     * @return array
+     */
+    public function GetFileList()
+    {
+        return $this->_centralDirStruct;
+    }
+
+    /**
+     * get ZIP file directory listing prepared for tree display.
+     *
+     * @return array
+     */
+    public function GetFileTree()
+    {
+        $tree = [];
+        $folderNoCounter = count($this->_centralDirStruct);
+
+        // add files
+        foreach ($this->_centralDirStruct as $fileNo => $file) {
+            $file['parentID'] = -2;
+            $file['type'] = 'file';
+            $file['fileNo'] = $fileNo;
+            $file['baseName'] = basename($file['fileName']);
+
+            $tree[] = $file;
+        }
+
+        // add folders
+        $again = true;
+        while ($again) {
+            $again = false;
+
+            foreach ($tree as $id => $item) {
+                if ($item['parentID'] != -2) {
+                    continue;
+                }
+
+                $parentFolderName = dirname($item['fileName']);
+
+                if ($parentFolderName != '.' && $parentFolderName != '') {
+                    $parentID = -2;
+
+                    foreach ($tree as $id2 => $item2) {
+                        if ($item2['type'] == 'folder' && $item2['fileName'] == $parentFolderName) {
+                            $parentID = $item2['fileNo'];
+                            break;
+                        }
+                    }
+
+                    if ($parentID == -2) {
+                        $folderNo = $folderNoCounter++;
+                        $tree[] = ['type' => 'folder',
+                                        'fileName' => $parentFolderName,
+                                        'baseName' => basename($parentFolderName),
+                                        'fileNo' => $folderNo,
+                                        'parentID' => -2, ];
+                        $parentID = $folderNo;
+                        $again = true;
+                    }
+
+                    $tree[$id]['parentID'] = $parentID;
+                }
+            }
+        }
+
+        return $tree;
+    }
+
+    /**
+     * extract a file by file no.
+     *
+     * @param int      $fileNo File no (from GetFileList() array)
+     * @param resource $fp     Output file stream
+     *
+     * @return bool
+     */
+    public function ExtractFile($fileNo, $fp = false, $sizeLimit = -1)
+    {
+        if (!isset($this->_centralDirStruct[$fileNo])) {
+            return false;
+        }
+
+        if (!in_array($this->_centralDirStruct[$fileNo]['compressionMethod'], [8, 12])) {
+            return false;
+        }
+
+        fseek($this->_fp, $this->_centralDirStruct[$fileNo]['relativeOffset'], SEEK_SET);
+
+        $fileHeader = fread($this->_fp, 30);
+
+        $_fileHeader = @unpack('Vsignature/vversionNeeded/vflags/vcompressionMethod/vmTime/vmDate/Vcrc32/VcompressedSize/VuncompressedSize/vfileNameLength/vextraFieldLength',
+                               $fileHeader);
+
+        if (!$_fileHeader || $_fileHeader['signature'] != 0x04034b50) {
+            return false;
+        }
+
+        fseek($this->_fp, $_fileHeader['fileNameLength'] + $_fileHeader['extraFieldLength'], SEEK_CUR);
+
+        $_fileHeader = $this->_centralDirStruct[$fileNo];
+
+        if ($_fileHeader['compressedSize'] > 0) {
+            $compressedData = fread($this->_fp, $_fileHeader['compressedSize']);
+            $uncompressedData = '';
+
+            if ($_fileHeader['compressionMethod'] == 8) {
+                $uncompressedData = @gzinflate($compressedData);
+            } elseif ($_fileHeader['compressionMethod'] == 12) {
+                $uncompressedData = @bzdecompress($compressedData);
+            }
+
+            unset($compressedData);
+
+            if (crc32($uncompressedData) != $_fileHeader['crc32']) {
+                return false;
+            }
+
+            if ($fp !== false) {
+                fwrite($fp, $uncompressedData, $sizeLimit != -1 ? $sizeLimit : strlen($uncompressedData));
+            } else {
+                echo $uncompressedData;
+            }
+        } elseif ($_fileHeader['crc32'] != 0) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * read central dir struct from ZIP file.
+     */
+    private function _readCentralDirStruct()
+    {
+        $this->_centralDirStruct = [];
+
+        fseek($this->_fp, -22, SEEK_END);
+        $endOfCDS = fread($this->_fp, 22);
+
+        while (substr($endOfCDS, 0, 4) != pack('V', 0x06054b50)) {
+            fseek($this->_fp, -1, SEEK_CUR);
+            $endOfCDS = fgetc($this->_fp).$endOfCDS;
+            fseek($this->_fp, -1, SEEK_CUR);
+
+            if (ftell($this->_fp) < 2) {
+                break;
+            }
+        }
+
+        if (substr($endOfCDS, 0, 4) != pack('V', 0x06054b50)) {
+            if (DEBUG) {
+                trigger_error('File corrupt or not in ZIP format', E_USER_NOTICE);
+            }
+
+            return;
+        }
+
+        // parse endOfCDS record
+        $_endOfCDS = @unpack('Vsignature/vdiskNo/vcdsStartDiskNo/vcdsDiskEntryCount/vcdsTotalEntryCount/VcdsSize/VcdsOffset/vcommentLength', $endOfCDS);
+        if (!$_endOfCDS) {
+            if (DEBUG) {
+                trigger_error('File corrupt or not in ZIP format (eoCDS broken)', E_USER_NOTICE);
+            }
+
+            return;
+        }
+
+        // seek to CDS offset
+        fseek($this->_fp, $_endOfCDS['cdsOffset'], SEEK_SET);
+
+        // read CDS entries
+        for ($i = 0; $i < $_endOfCDS['cdsDiskEntryCount'] && !feof($this->_fp); ++$i) {
+            $fileHeader = fread($this->_fp, 46);
+
+            $_fileHeader = @unpack('Vsignature/vversion/vversionNeeded/vflags/vcompressionMethod/vmTime/vmDate/Vcrc32/VcompressedSize/VuncompressedSize/vfileNameLength/vextraFieldLength/vfileCommentLength/vdiskNumberStart/vinternalAttrs/VexternalAttrs/VrelativeOffset',
+                                   $fileHeader);
+            if (!$_fileHeader || $_fileHeader['signature'] != 0x02014b50) {
+                if (DEBUG) {
+                    trigger_error('File corrupt (CDS broken)');
+                }
+
+                return;
+            }
+
+            $_fileHeader['cdsOffset'] = ftell($this->_fp) - strlen($fileHeader);
+
+            if ($_fileHeader['fileNameLength'] > 0) {
+                $_fileHeader['fileName'] = fread($this->_fp, $_fileHeader['fileNameLength']);
+            }
+            if ($_fileHeader['extraFieldLength'] > 0) {
+                $_fileHeader['extraField'] = fread($this->_fp, $_fileHeader['extraFieldLength']);
+            }
+            if ($_fileHeader['fileCommentLength'] > 0) {
+                $_fileHeader['fileComment'] = fread($this->_fp, $_fileHeader['fileCommentLength']);
+            }
+
+            if (substr($_fileHeader['fileName'], -1) == '/' && $_fileHeader['uncompressedSize'] == 0) {
+                continue;
+            }
+
+            $this->_centralDirStruct[] = $_fileHeader;
+        }
+
+        // sort by filename
+        uasort($this->_centralDirStruct, [&$this, '_sortHandler']);
+    }
+
+    private function _sortHandler($s1, $s2)
+    {
+        return strcmp($s1['fileName'], $s2['fileName']);
+    }
 }
 }

+ 211 - 211
src/serverlib/zip.class.php

@@ -19,220 +19,220 @@
  *
  *
  */
  */
 
 
-if(!defined('B1GMAIL_INIT'))
-	die('Directly calling this file is not supported');
+if (!defined('B1GMAIL_INIT')) {
+    die('Directly calling this file is not supported');
+}
 
 
 /**
 /**
- * ZIP class
- *
+ * ZIP class.
  */
  */
 class BMZIP
 class BMZIP
 {
 {
-	/**
-	 * b1gZIP stream (used if b1gZIP is installed)
-	 *
-	 * @var resource
-	 */
-	var $_b1gzip_stream;
-
-	/**
-	 * output stream
-	 *
-	 * @var resource
-	 */
-	var $_fp;
-
-	/**
-	 * central directory structure
-	 *
-	 * @var array
-	 */
-	var $_centralDirStruct;
-
-	/**
-	 * constructor
-	 *
-	 * @param resource $fp Output stream
-	 * @return BMZIP
-	 */
-	function __construct($fp)
-	{
-		// output stream
-		$this->_fp = $fp;
-
-		// use b1gZIP?
-		if(function_exists('b1gzip_create')
-			&& function_exists('b1gzip_add')
-			&& function_exists('b1gzip_final'))
-		{
-			$this->_b1gzip_stream = b1gzip_create();
-		}
-		else
-		{
-			$this->_b1gzip_stream = false;
-			$this->_centralDirStruct = array();
-		}
-	}
-
-	/**
-	 * add a file to ZIP file
-	 *
-	 * @param string $fileName File name
-	 * @param string $zipFileName File name in ZIP file
-	 * @return bool
-	 */
-	function AddFile($fileName, $zipFileName = false)
-	{
-		$fileFP = @fopen($fileName, 'rb');
-		if($fileFP)
-		{
-			$result = $this->AddFileByFP($fileFP, $fileName, $zipFileName);
-			fclose($fileFP);
-			return($result);
-		}
-		else
-			return(false);
-	}
-
-	/**
-	 * add a file to ZIP file by file pointer
-	 *
-	 * @param resource $fileFP
-	 * @param string $fileName
-	 * @param string $zipFileName
-	 * @return bool
-	 */
-	function AddFileByFP($fileFP, $fileName, $zipFileName = false)
-	{
-		if(!$zipFileName)
-			$zipFileName = basename($fileName);
-
-		// read file
-		fseek($fileFP, 0, SEEK_SET);
-		$fileData = '';
-		while(is_resource($fileFP) && !feof($fileFP))
-			$fileData .= @fread($fileFP, 4096);
-		$uncompressedSize = strlen($fileData);
-
-		// use b1gZIP
-		if($this->_b1gzip_stream)
-		{
-			b1gzip_add($this->_b1gzip_stream, $fileData, $zipFileName);
-			return(true);
-		}
-
-		// or own implementation
-		else
-		{
-			// compute crc32
-			$crc32 = crc32($fileData);
-			$compressedData = gzcompress($fileData);
-			unset($fileData);
-			$compressedData = substr($compressedData, 2, -4);
-			$compressedSize = strlen($compressedData);
-
-			// write file header
-			$this->_beginFile($crc32, $compressedSize, $uncompressedSize, $zipFileName);
-			fwrite($this->_fp, $compressedData);
-		}
-
-		return(false);
-	}
-
-	/**
-	 * begin file
-	 *
-	 * @param int $crc32
-	 * @param int $compressedSize
-	 * @param int $uncompressedSize
-	 * @param string $fileName
-	 */
-	function _beginFile($crc32, $compressedSize, $uncompressedSize, $fileName)
-	{
-		// local header
-		$header = pack('VvvvvvVVVvv',
-			0x04034b50,
-			0x0014,
-			0x0,
-			0x0008,
-			(date('H') << 11) | (date('i') << 5) | round(date('s')/2, 0),
-			(date('Y')-1980 << 9) | (date('m') << 5) | date('d'),
-			$crc32,
-			$compressedSize,
-			$uncompressedSize,
-			strlen($fileName),
-			0x0);
-		$offset = ftell($this->_fp);
-		fwrite($this->_fp, $header);
-		fwrite($this->_fp, $fileName);
-
-		// central dir struct entry
-		$entry = pack('VvvvvvvVVVvvvvvVV',
-			0x02014b50,
-			0x0,
-			0x0014,
-			0x0,
-			0x0008,
-			(date('H') << 11) | (date('i') << 5) | round(date('s')/2, 0),
-			(date('Y')-1980 << 9) | (date('m') << 5) | date('d'),
-			$crc32,
-			$compressedSize,
-			$uncompressedSize,
-			strlen($fileName),
-			0x0,
-			0x0,
-			0x0,
-			0x0,
-			32,
-			$offset);
-		$entry .= $fileName;
-		$this->_centralDirStruct[] = $entry;
-	}
-
-	/**
-	 * finish zip file
-	 *
-	 * @return int Size
-	 */
-	function Finish()
-	{
-		// use b1gZIP?
-		if($this->_b1gzip_stream)
-		{
-			$zipData = b1gzip_final($this->_b1gzip_stream);
-			fwrite($this->_fp, $zipData);
-			fseek($this->_fp, 0, SEEK_SET);
-			return(strlen($zipData));
-		}
-
-		// or own implementation
-		else
-		{
-			// write central dir struct
-			$offset = ftell($this->_fp);
-			$dLength = 0;
-			foreach($this->_centralDirStruct as $item)
-			{
-				fwrite($this->_fp, $item);
-				$dLength += strlen($item);
-			}
-
-			// write footer
-			$footer = pack('VvvvvVVv',
-				0x06054b50,
-				0x0,
-				0x0,
-				count($this->_centralDirStruct),
-				count($this->_centralDirStruct),
-				$dLength,
-				$offset,
-				0x0);
-			fwrite($this->_fp, $footer);
-
-			// return
-			$len = ftell($this->_fp);
-			fseek($this->_fp, 0, SEEK_SET);
-			return($len);
-		}
-	}
+    /**
+     * b1gZIP stream (used if b1gZIP is installed).
+     *
+     * @var resource
+     */
+    private $_b1gzip_stream;
+
+    /**
+     * output stream.
+     *
+     * @var resource
+     */
+    private $_fp;
+
+    /**
+     * central directory structure.
+     *
+     * @var array
+     */
+    private $_centralDirStruct;
+
+    /**
+     * constructor.
+     *
+     * @param resource $fp Output stream
+     *
+     * @return BMZIP
+     */
+    public function __construct($fp)
+    {
+        // output stream
+        $this->_fp = $fp;
+
+        // use b1gZIP?
+        if (function_exists('b1gzip_create')
+            && function_exists('b1gzip_add')
+            && function_exists('b1gzip_final')) {
+            $this->_b1gzip_stream = b1gzip_create();
+        } else {
+            $this->_b1gzip_stream = false;
+            $this->_centralDirStruct = [];
+        }
+    }
+
+    /**
+     * add a file to ZIP file.
+     *
+     * @param string $fileName    File name
+     * @param string $zipFileName File name in ZIP file
+     *
+     * @return bool
+     */
+    public function AddFile($fileName, $zipFileName = false)
+    {
+        $fileFP = @fopen($fileName, 'rb');
+        if ($fileFP) {
+            $result = $this->AddFileByFP($fileFP, $fileName, $zipFileName);
+            fclose($fileFP);
+
+            return $result;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * add a file to ZIP file by file pointer.
+     *
+     * @param resource $fileFP
+     * @param string   $fileName
+     * @param string   $zipFileName
+     *
+     * @return bool
+     */
+    public function AddFileByFP($fileFP, $fileName, $zipFileName = false)
+    {
+        if (!$zipFileName) {
+            $zipFileName = basename($fileName);
+        }
+
+        // read file
+        fseek($fileFP, 0, SEEK_SET);
+        $fileData = '';
+        while (is_resource($fileFP) && !feof($fileFP)) {
+            $fileData .= @fread($fileFP, 4096);
+        }
+        $uncompressedSize = strlen($fileData);
+
+        // use b1gZIP
+        if ($this->_b1gzip_stream) {
+            b1gzip_add($this->_b1gzip_stream, $fileData, $zipFileName);
+
+            return true;
+        }
+
+        // or own implementation
+        else {
+            // compute crc32
+            $crc32 = crc32($fileData);
+            $compressedData = gzcompress($fileData);
+            unset($fileData);
+            $compressedData = substr($compressedData, 2, -4);
+            $compressedSize = strlen($compressedData);
+
+            // write file header
+            $this->_beginFile($crc32, $compressedSize, $uncompressedSize, $zipFileName);
+            fwrite($this->_fp, $compressedData);
+        }
+
+        return false;
+    }
+
+    /**
+     * begin file.
+     *
+     * @param int    $crc32
+     * @param int    $compressedSize
+     * @param int    $uncompressedSize
+     * @param string $fileName
+     */
+    private function _beginFile($crc32, $compressedSize, $uncompressedSize, $fileName)
+    {
+        // local header
+        $header = pack('VvvvvvVVVvv',
+            0x04034b50,
+            0x0014,
+            0x0,
+            0x0008,
+            (date('H') << 11) | (date('i') << 5) | round(date('s') / 2, 0),
+            (date('Y') - 1980 << 9) | (date('m') << 5) | date('d'),
+            $crc32,
+            $compressedSize,
+            $uncompressedSize,
+            strlen($fileName),
+            0x0);
+        $offset = ftell($this->_fp);
+        fwrite($this->_fp, $header);
+        fwrite($this->_fp, $fileName);
+
+        // central dir struct entry
+        $entry = pack('VvvvvvvVVVvvvvvVV',
+            0x02014b50,
+            0x0,
+            0x0014,
+            0x0,
+            0x0008,
+            (date('H') << 11) | (date('i') << 5) | round(date('s') / 2, 0),
+            (date('Y') - 1980 << 9) | (date('m') << 5) | date('d'),
+            $crc32,
+            $compressedSize,
+            $uncompressedSize,
+            strlen($fileName),
+            0x0,
+            0x0,
+            0x0,
+            0x0,
+            32,
+            $offset);
+        $entry .= $fileName;
+        $this->_centralDirStruct[] = $entry;
+    }
+
+    /**
+     * finish zip file.
+     *
+     * @return int Size
+     */
+    public function Finish()
+    {
+        // use b1gZIP?
+        if ($this->_b1gzip_stream) {
+            $zipData = b1gzip_final($this->_b1gzip_stream);
+            fwrite($this->_fp, $zipData);
+            fseek($this->_fp, 0, SEEK_SET);
+
+            return strlen($zipData);
+        }
+
+        // or own implementation
+        else {
+            // write central dir struct
+            $offset = ftell($this->_fp);
+            $dLength = 0;
+            foreach ($this->_centralDirStruct as $item) {
+                fwrite($this->_fp, $item);
+                $dLength += strlen($item);
+            }
+
+            // write footer
+            $footer = pack('VvvvvVVv',
+                0x06054b50,
+                0x0,
+                0x0,
+                count($this->_centralDirStruct),
+                count($this->_centralDirStruct),
+                $dLength,
+                $offset,
+                0x0);
+            fwrite($this->_fp, $footer);
+
+            // return
+            $len = ftell($this->_fp);
+            fseek($this->_fp, 0, SEEK_SET);
+
+            return $len;
+        }
+    }
 }
 }