mirror of
https://github.com/YunoHost-Apps/z-push_ynh.git
synced 2024-09-03 18:05:58 +02:00
1069 lines
No EOL
39 KiB
PHP
1069 lines
No EOL
39 KiB
PHP
<?php
|
|
/***********************************************
|
|
* File : utils.php
|
|
* Project : Z-Push
|
|
* Descr : Several utility functions
|
|
*
|
|
* Created : 03.04.2008
|
|
*
|
|
* Copyright 2007 - 2013 Zarafa Deutschland GmbH
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License, version 3,
|
|
* as published by the Free Software Foundation with the following additional
|
|
* term according to sec. 7:
|
|
*
|
|
* According to sec. 7 of the GNU Affero General Public License, version 3,
|
|
* the terms of the AGPL are supplemented with the following terms:
|
|
*
|
|
* "Zarafa" is a registered trademark of Zarafa B.V.
|
|
* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
|
|
* The licensing of the Program under the AGPL does not imply a trademark license.
|
|
* Therefore any rights, title and interest in our trademarks remain entirely with us.
|
|
*
|
|
* However, if you propagate an unmodified version of the Program you are
|
|
* allowed to use the term "Z-Push" to indicate that you distribute the Program.
|
|
* Furthermore you may use our trademarks where it is necessary to indicate
|
|
* the intended purpose of a product or service provided you use it in accordance
|
|
* with honest practices in industrial or commercial matters.
|
|
* If you want to propagate modified versions of the Program under the name "Z-Push",
|
|
* you may only do so if you have a written permission by Zarafa Deutschland GmbH
|
|
* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Consult LICENSE file for details
|
|
************************************************/
|
|
|
|
class Utils {
|
|
/**
|
|
* Prints a variable as string
|
|
* If a boolean is sent, 'true' or 'false' is displayed
|
|
*
|
|
* @param string $var
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
static public function PrintAsString($var) {
|
|
return ($var)?(($var===true)?'true':$var):(($var===false)?'false':(($var==='')?'empty':$var));
|
|
//return ($var)?(($var===true)?'true':$var):'false';
|
|
}
|
|
|
|
/**
|
|
* Splits a "domain\user" string into two values
|
|
* If the string cotains only the user, domain is returned empty
|
|
*
|
|
* @param string $domainuser
|
|
*
|
|
* @access public
|
|
* @return array index 0: user 1: domain
|
|
*/
|
|
static public function SplitDomainUser($domainuser) {
|
|
$pos = strrpos($domainuser, '\\');
|
|
if($pos === false){
|
|
$user = $domainuser;
|
|
$domain = '';
|
|
}
|
|
else{
|
|
$domain = substr($domainuser,0,$pos);
|
|
$user = substr($domainuser,$pos+1);
|
|
}
|
|
return array($user, $domain);
|
|
}
|
|
|
|
/**
|
|
* Build an address string from the components
|
|
*
|
|
* @param string $street the street
|
|
* @param string $zip the zip code
|
|
* @param string $city the city
|
|
* @param string $state the state
|
|
* @param string $country the country
|
|
*
|
|
* @access public
|
|
* @return string the address string or null
|
|
*/
|
|
static public function BuildAddressString($street, $zip, $city, $state, $country) {
|
|
$out = "";
|
|
|
|
if (isset($country) && $street != "") $out = $country;
|
|
|
|
$zcs = "";
|
|
if (isset($zip) && $zip != "") $zcs = $zip;
|
|
if (isset($city) && $city != "") $zcs .= (($zcs)?" ":"") . $city;
|
|
if (isset($state) && $state != "") $zcs .= (($zcs)?" ":"") . $state;
|
|
if ($zcs) $out = $zcs . "\r\n" . $out;
|
|
|
|
if (isset($street) && $street != "") $out = $street . (($out)?"\r\n\r\n". $out: "") ;
|
|
|
|
return ($out)?$out:null;
|
|
}
|
|
|
|
/**
|
|
* Build the fileas string from the components according to the configuration.
|
|
*
|
|
* @param string $lastname
|
|
* @param string $firstname
|
|
* @param string $middlename
|
|
* @param string $company
|
|
*
|
|
* @access public
|
|
* @return string fileas
|
|
*/
|
|
static public function BuildFileAs($lastname = "", $firstname = "", $middlename = "", $company = "") {
|
|
if (defined('FILEAS_ORDER')) {
|
|
$fileas = $lastfirst = $firstlast = "";
|
|
$names = trim ($firstname . " " . $middlename);
|
|
$lastname = trim($lastname);
|
|
$company = trim($company);
|
|
|
|
// lastfirst is "lastname, firstname middlename"
|
|
// firstlast is "firstname middlename lastname"
|
|
if (strlen($lastname) > 0) {
|
|
$lastfirst = $lastname;
|
|
if (strlen($names) > 0){
|
|
$lastfirst .= ", $names";
|
|
$firstlast = "$names $lastname";
|
|
}
|
|
else {
|
|
$firstlast = $lastname;
|
|
}
|
|
}
|
|
elseif (strlen($names) > 0) {
|
|
$lastfirst = $firstlast = $names;
|
|
}
|
|
|
|
// if fileas with a company is selected
|
|
// but company is emtpy then it will
|
|
// fallback to firstlast or lastfirst
|
|
// (depending on which is selected for company)
|
|
switch (FILEAS_ORDER) {
|
|
case SYNC_FILEAS_COMPANYONLY:
|
|
if (strlen($company) > 0) {
|
|
$fileas = $company;
|
|
}
|
|
elseif (strlen($firstlast) > 0)
|
|
$fileas = $lastfirst;
|
|
break;
|
|
case SYNC_FILEAS_COMPANYLAST:
|
|
if (strlen($company) > 0) {
|
|
$fileas = $company;
|
|
if (strlen($lastfirst) > 0)
|
|
$fileas .= "($lastfirst)";
|
|
}
|
|
elseif (strlen($lastfirst) > 0)
|
|
$fileas = $lastfirst;
|
|
break;
|
|
case SYNC_FILEAS_COMPANYFIRST:
|
|
if (strlen($company) > 0) {
|
|
$fileas = $company;
|
|
if (strlen($firstlast) > 0) {
|
|
$fileas .= " ($firstlast)";
|
|
}
|
|
}
|
|
elseif (strlen($firstlast) > 0) {
|
|
$fileas = $firstlast;
|
|
}
|
|
break;
|
|
case SYNC_FILEAS_FIRSTCOMPANY:
|
|
if (strlen($firstlast) > 0) {
|
|
$fileas = $firstlast;
|
|
if (strlen($company) > 0) {
|
|
$fileas .= " ($company)";
|
|
}
|
|
}
|
|
elseif (strlen($company) > 0) {
|
|
$fileas = $company;
|
|
}
|
|
break;
|
|
case SYNC_FILEAS_LASTCOMPANY:
|
|
if (strlen($lastfirst) > 0) {
|
|
$fileas = $lastfirst;
|
|
if (strlen($company) > 0) {
|
|
$fileas .= " ($company)";
|
|
}
|
|
}
|
|
elseif (strlen($company) > 0) {
|
|
$fileas = $company;
|
|
}
|
|
break;
|
|
case SYNC_FILEAS_LASTFIRST:
|
|
if (strlen($lastfirst) > 0) {
|
|
$fileas = $lastfirst;
|
|
}
|
|
break;
|
|
default:
|
|
$fileas = $firstlast;
|
|
break;
|
|
}
|
|
if (strlen($fileas) == 0)
|
|
ZLog::Write(LOGLEVEL_DEBUG, "Fileas is empty.");
|
|
return $fileas;
|
|
}
|
|
ZLog::Write(LOGLEVEL_DEBUG, "FILEAS_ORDER not defined. Add it to your config.php.");
|
|
return null;
|
|
}
|
|
/**
|
|
* Checks if the PHP-MAPI extension is available and in a requested version
|
|
*
|
|
* @param string $version the version to be checked ("6.30.10-18495", parts or build number)
|
|
*
|
|
* @access public
|
|
* @return boolean installed version is superior to the checked strin
|
|
*/
|
|
static public function CheckMapiExtVersion($version = "") {
|
|
// compare build number if requested
|
|
if (preg_match('/^\d+$/', $version) && strlen($version) > 3) {
|
|
$vs = preg_split('/-/', phpversion("mapi"));
|
|
return ($version <= $vs[1]);
|
|
}
|
|
|
|
if (extension_loaded("mapi")){
|
|
if (version_compare(phpversion("mapi"), $version) == -1){
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Parses and returns an ecoded vCal-Uid from an
|
|
* OL compatible GlobalObjectID
|
|
*
|
|
* @param string $olUid an OL compatible GlobalObjectID
|
|
*
|
|
* @access public
|
|
* @return string the vCal-Uid if available in the olUid, else the original olUid as HEX
|
|
*/
|
|
static public function GetICalUidFromOLUid($olUid){
|
|
//check if "vCal-Uid" is somewhere in outlookid case-insensitive
|
|
$icalUid = stristr($olUid, "vCal-Uid");
|
|
if ($icalUid !== false) {
|
|
//get the length of the ical id - go back 4 position from where "vCal-Uid" was found
|
|
$begin = unpack("V", substr($olUid, strlen($icalUid) * (-1) - 4, 4));
|
|
//remove "vCal-Uid" and packed "1" and use the ical id length
|
|
return substr($icalUid, 12, ($begin[1] - 13));
|
|
}
|
|
return strtoupper(bin2hex($olUid));
|
|
}
|
|
|
|
/**
|
|
* Checks the given UID if it is an OL compatible GlobalObjectID
|
|
* If not, the given UID is encoded inside the GlobalObjectID
|
|
*
|
|
* @param string $icalUid an appointment uid as HEX
|
|
*
|
|
* @access public
|
|
* @return string an OL compatible GlobalObjectID
|
|
*
|
|
*/
|
|
static public function GetOLUidFromICalUid($icalUid) {
|
|
if (strlen($icalUid) <= 64) {
|
|
$len = 13 + strlen($icalUid);
|
|
$OLUid = pack("V", $len);
|
|
$OLUid .= "vCal-Uid";
|
|
$OLUid .= pack("V", 1);
|
|
$OLUid .= $icalUid;
|
|
return hex2bin("040000008200E00074C5B7101A82E0080000000000000000000000000000000000000000". bin2hex($OLUid). "00");
|
|
}
|
|
else
|
|
return hex2bin($icalUid);
|
|
}
|
|
|
|
/**
|
|
* Extracts the basedate of the GlobalObjectID and the RecurStartTime
|
|
*
|
|
* @param string $goid OL compatible GlobalObjectID
|
|
* @param long $recurStartTime
|
|
*
|
|
* @access public
|
|
* @return long basedate
|
|
*/
|
|
static public function ExtractBaseDate($goid, $recurStartTime) {
|
|
$hexbase = substr(bin2hex($goid), 32, 8);
|
|
$day = hexdec(substr($hexbase, 6, 2));
|
|
$month = hexdec(substr($hexbase, 4, 2));
|
|
$year = hexdec(substr($hexbase, 0, 4));
|
|
|
|
if ($day && $month && $year) {
|
|
$h = $recurStartTime >> 12;
|
|
$m = ($recurStartTime - $h * 4096) >> 6;
|
|
$s = $recurStartTime - $h * 4096 - $m * 64;
|
|
|
|
return gmmktime($h, $m, $s, $month, $day, $year);
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Converts SYNC_FILTERTYPE into a timestamp
|
|
*
|
|
* @param int Filtertype
|
|
*
|
|
* @access public
|
|
* @return long
|
|
*/
|
|
static public function GetCutOffDate($restrict) {
|
|
switch($restrict) {
|
|
case SYNC_FILTERTYPE_1DAY:
|
|
$back = 60 * 60 * 24;
|
|
break;
|
|
case SYNC_FILTERTYPE_3DAYS:
|
|
$back = 60 * 60 * 24 * 3;
|
|
break;
|
|
case SYNC_FILTERTYPE_1WEEK:
|
|
$back = 60 * 60 * 24 * 7;
|
|
break;
|
|
case SYNC_FILTERTYPE_2WEEKS:
|
|
$back = 60 * 60 * 24 * 14;
|
|
break;
|
|
case SYNC_FILTERTYPE_1MONTH:
|
|
$back = 60 * 60 * 24 * 31;
|
|
break;
|
|
case SYNC_FILTERTYPE_3MONTHS:
|
|
$back = 60 * 60 * 24 * 31 * 3;
|
|
break;
|
|
case SYNC_FILTERTYPE_6MONTHS:
|
|
$back = 60 * 60 * 24 * 31 * 6;
|
|
break;
|
|
default:
|
|
return 0; // unlimited
|
|
}
|
|
|
|
return time() - $back;
|
|
}
|
|
|
|
/**
|
|
* Converts SYNC_TRUNCATION into bytes
|
|
*
|
|
* @param int SYNC_TRUNCATION
|
|
*
|
|
* @return long
|
|
*/
|
|
static public function GetTruncSize($truncation) {
|
|
switch($truncation) {
|
|
case SYNC_TRUNCATION_HEADERS:
|
|
return 0;
|
|
case SYNC_TRUNCATION_512B:
|
|
return 512;
|
|
case SYNC_TRUNCATION_1K:
|
|
return 1024;
|
|
case SYNC_TRUNCATION_2K:
|
|
return 2*1024;
|
|
case SYNC_TRUNCATION_5K:
|
|
return 5*1024;
|
|
case SYNC_TRUNCATION_10K:
|
|
return 10*1024;
|
|
case SYNC_TRUNCATION_20K:
|
|
return 20*1024;
|
|
case SYNC_TRUNCATION_50K:
|
|
return 50*1024;
|
|
case SYNC_TRUNCATION_100K:
|
|
return 100*1024;
|
|
case SYNC_TRUNCATION_ALL:
|
|
return 1024*1024; // We'll limit to 1MB anyway
|
|
default:
|
|
return 1024; // Default to 1Kb
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Truncate an UTF-8 encoded sting correctly
|
|
*
|
|
* If it's not possible to truncate properly, an empty string is returned
|
|
*
|
|
* @param string $string - the string
|
|
* @param string $length - position where string should be cut
|
|
* @return string truncated string
|
|
*/
|
|
static public function Utf8_truncate($string, $length) {
|
|
// make sure length is always an interger
|
|
$length = (int)$length;
|
|
|
|
if (strlen($string) <= $length)
|
|
return $string;
|
|
|
|
while($length >= 0) {
|
|
if ((ord($string[$length]) < 0x80) || (ord($string[$length]) >= 0xC0))
|
|
return substr($string, 0, $length);
|
|
|
|
$length--;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/**
|
|
* Indicates if the specified folder type is a system folder
|
|
*
|
|
* @param int $foldertype
|
|
*
|
|
* @access public
|
|
* @return boolean
|
|
*/
|
|
static public function IsSystemFolder($foldertype) {
|
|
return ($foldertype == SYNC_FOLDER_TYPE_INBOX || $foldertype == SYNC_FOLDER_TYPE_DRAFTS || $foldertype == SYNC_FOLDER_TYPE_WASTEBASKET || $foldertype == SYNC_FOLDER_TYPE_SENTMAIL ||
|
|
$foldertype == SYNC_FOLDER_TYPE_OUTBOX || $foldertype == SYNC_FOLDER_TYPE_TASK || $foldertype == SYNC_FOLDER_TYPE_APPOINTMENT || $foldertype == SYNC_FOLDER_TYPE_CONTACT ||
|
|
$foldertype == SYNC_FOLDER_TYPE_NOTE || $foldertype == SYNC_FOLDER_TYPE_JOURNAL) ? true:false;
|
|
}
|
|
|
|
/**
|
|
* Our own utf7_decode function because imap_utf7_decode converts a string
|
|
* into ISO-8859-1 encoding which doesn't have euro sign (it will be converted
|
|
* into two chars: [space](ascii 32) and "¬" ("not sign", ascii 172)). Also
|
|
* php iconv function expects '+' as delimiter instead of '&' like in IMAP.
|
|
*
|
|
* @param string $string IMAP folder name
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
static public function Utf7_iconv_decode($string) {
|
|
//do not alter string if there aren't any '&' or '+' chars because
|
|
//it won't have any utf7-encoded chars and nothing has to be escaped.
|
|
if (strpos($string, '&') === false && strpos($string, '+') === false ) return $string;
|
|
|
|
//Get the string length and go back through it making the replacements
|
|
//necessary
|
|
$len = strlen($string) - 1;
|
|
while ($len > 0) {
|
|
//look for '&-' sequence and replace it with '&'
|
|
if ($len > 0 && $string[$len-1] == '&' && $string[$len] == '-') {
|
|
$string = substr_replace($string, '&', $len - 1, 2);
|
|
$len--; //decrease $len as this char has alreasy been processed
|
|
}
|
|
//search for '&' which weren't found in if clause above and
|
|
//replace them with '+' as they mark an utf7-encoded char
|
|
if ($len > 0 && $string[($len-1)] == '&') {
|
|
$string = substr_replace($string, '+', $len - 1, 1);
|
|
$len--; //decrease $len as this char has alreasy been processed
|
|
}
|
|
//finally "escape" all remaining '+' chars
|
|
if ($len > 0 && $string[$len-1] == '+') {
|
|
$string = substr_replace($string, '+-', $len - 1, 1);
|
|
}
|
|
$len--;
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Our own utf7_encode function because the string has to be converted from
|
|
* standard UTF7 into modified UTF7 (aka UTF7-IMAP).
|
|
*
|
|
* @param string $str IMAP folder name
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
static public function Utf7_iconv_encode($string) {
|
|
//do not alter string if there aren't any '&' or '+' chars because
|
|
//it won't have any utf7-encoded chars and nothing has to be escaped.
|
|
if (strpos($string, '&') === false && strpos($string, '+') === false ) return $string;
|
|
|
|
//Get the string length and go back through it making the replacements
|
|
//necessary
|
|
$len = strlen($string) - 1;
|
|
while ($len > 0) {
|
|
//look for '&-' sequence and replace it with '&'
|
|
if ($len > 0 && $string[$len-1] == '+' && $string[$len] == '-') {
|
|
$string = substr_replace($string, '+', $len - 1, 2);
|
|
$len--; //decrease $len as this char has alreasy been processed
|
|
}
|
|
//search for '&' which weren't found in if clause above and
|
|
//replace them with '+' as they mark an utf7-encoded char
|
|
if ($len > 0 && $string[$len-1] == '+') {
|
|
$string = substr_replace($string, '&', $len - 1, 1);
|
|
$len--; //decrease $len as this char has alreasy been processed
|
|
}
|
|
//finally "escape" all remaining '+' chars
|
|
if ($len > 0 && $string[$len-1] == '&') {
|
|
$string = substr_replace($string, '&-', $len - 1, 1);
|
|
}
|
|
$len--;
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Converts an UTF-7 encoded string into an UTF-8 string.
|
|
*
|
|
* @param string $string to convert
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
static public function Utf7_to_utf8($string) {
|
|
if (function_exists("iconv")){
|
|
return @iconv("UTF-7", "UTF-8", $string);
|
|
}
|
|
else
|
|
ZLog::Write(LOGLEVEL_WARN, "Utils::Utf7_to_utf8() 'iconv' is not available. Charset conversion skipped.");
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Converts an UTF-8 encoded string into an UTF-7 string.
|
|
*
|
|
* @param string $string to convert
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
static public function Utf8_to_utf7($string) {
|
|
if (function_exists("iconv")){
|
|
return @iconv("UTF-8", "UTF-7", $string);
|
|
}
|
|
else
|
|
ZLog::Write(LOGLEVEL_WARN, "Utils::Utf8_to_utf7() 'iconv' is not available. Charset conversion skipped.");
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Checks for valid email addresses
|
|
* The used regex actually only checks if a valid email address is part of the submitted string
|
|
* it also returns true for the mailbox format, but this is not checked explicitly
|
|
*
|
|
* @param string $email address to be checked
|
|
*
|
|
* @access public
|
|
* @return boolean
|
|
*/
|
|
static public function CheckEmail($email) {
|
|
return (bool) preg_match('#([a-zA-Z0-9_\-])+(\.([a-zA-Z0-9_\-])+)*@((\[(((([0-1])?([0-9])?[0-9])|(2[0-4][0-9])|(2[0-5][0-5])))\.(((([0-1])?([0-9])?[0-9])|(2[0-4][0-9])|(2[0-5][0-5])))\.(((([0-1])?([0-9])?[0-9])|(2[0-4][0-9])|(2[0-5][0-5])))\.(((([0-1])?([0-9])?[0-9])|(2[0-4][0-9])|(2[0-5][0-5]))\]))|((([a-zA-Z0-9])+(([\-])+([a-zA-Z0-9])+)*\.)+([a-zA-Z])+(([\-])+([a-zA-Z0-9])+)*)|localhost)#', $email);
|
|
}
|
|
|
|
/**
|
|
* Checks for valid empty group of email
|
|
* e.g.: undisclosed-recipients:;
|
|
*
|
|
* @param string $email
|
|
*
|
|
* @access public
|
|
* @return boolean
|
|
*/
|
|
static public function CheckEmailEmptyGroup($email) {
|
|
return (bool) preg_match('/.*:;/', $email);
|
|
}
|
|
|
|
/**
|
|
* Checks if a string is base64 encoded
|
|
*
|
|
* @param string $string the string to be checked
|
|
*
|
|
* @access public
|
|
* @return boolean
|
|
*/
|
|
static public function IsBase64String($string) {
|
|
return (bool) preg_match("#^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+/]{4})?$#", $string);
|
|
}
|
|
|
|
/**
|
|
* Decodes base64 encoded query parameters. Based on dw2412 contribution.
|
|
*
|
|
* @param string $query the query to decode
|
|
*
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
static public function DecodeBase64URI($query) {
|
|
/*
|
|
* The query string has a following structure. Number in () is position:
|
|
* 1 byte - protocoll version (0)
|
|
* 1 byte - command code (1)
|
|
* 2 bytes - locale (2)
|
|
* 1 byte - device ID length (4)
|
|
* variable - device ID (4+device ID length)
|
|
* 1 byte - policy key length (5+device ID length)
|
|
* 0 or 4 bytes - policy key (5+device ID length + policy key length)
|
|
* 1 byte - device type length (6+device ID length + policy key length)
|
|
* variable - device type (6+device ID length + policy key length + device type length)
|
|
* variable - command parameters, array which consists of:
|
|
* 1 byte - tag
|
|
* 1 byte - length
|
|
* variable - value of the parameter
|
|
*
|
|
*/
|
|
$decoded = base64_decode($query);
|
|
$devIdLength = ord($decoded[4]); //device ID length
|
|
$polKeyLength = ord($decoded[5+$devIdLength]); //policy key length
|
|
$devTypeLength = ord($decoded[6+$devIdLength+$polKeyLength]); //device type length
|
|
//unpack the decoded query string values
|
|
$unpackedQuery = unpack("CProtVer/CCommand/vLocale/CDevIDLen/H".($devIdLength*2)."DevID/CPolKeyLen".($polKeyLength == 4 ? "/VPolKey" : "")."/CDevTypeLen/A".($devTypeLength)."DevType", $decoded);
|
|
|
|
//get the command parameters
|
|
$pos = 7 + $devIdLength + $polKeyLength + $devTypeLength;
|
|
$decoded = substr($decoded, $pos);
|
|
while (strlen($decoded) > 0) {
|
|
$paramLength = ord($decoded[1]);
|
|
$unpackedParam = unpack("CParamTag/CParamLength/A".$paramLength."ParamValue", $decoded);
|
|
$unpackedQuery[ord($decoded[0])] = $unpackedParam['ParamValue'];
|
|
//remove parameter from decoded query string
|
|
$decoded = substr($decoded, 2 + $paramLength);
|
|
}
|
|
return $unpackedQuery;
|
|
}
|
|
|
|
/**
|
|
* Encode a string or a stream into a base64 string
|
|
*
|
|
* @param string or stream $input
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public static function EncodeBase64($input) {
|
|
if (is_resource($input)) {
|
|
ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): is_resource");
|
|
if (defined('BUG68532FIXED') && BUG68532FIXED === true) {
|
|
ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): BUG68532FIXED");
|
|
$stream = $input;
|
|
} elseif ( ($meta = stream_get_meta_data($input)) && $meta['stream_type'] === 'STDIO') {
|
|
ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): STDIO");
|
|
$stream = $input;
|
|
} else {
|
|
ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): else");
|
|
//because of bug #68532, we can't work with memory stream,
|
|
//so we copy input to a tmpfile
|
|
$stream = tmpfile();
|
|
stream_copy_to_stream($input, $stream);
|
|
fclose($input);
|
|
rewind($stream);
|
|
}
|
|
$base64filter = stream_filter_append($stream, 'convert.base64-encode');
|
|
$base64 = stream_get_contents($stream);
|
|
stream_filter_remove($base64filter);
|
|
fclose($stream);
|
|
return $base64;
|
|
} elseif (is_string($input)) {
|
|
ZLog::Write(LOGLEVEL_DEBUG, "Utils::EncodeBase64(): is_string");
|
|
return base64_encode($input);
|
|
} else {
|
|
throw new Exception("unsupported type : ".gettype($input));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a command string for a given command code.
|
|
*
|
|
* @param int $code
|
|
*
|
|
* @access public
|
|
* @return string or false if code is unknown
|
|
*/
|
|
public static function GetCommandFromCode($code) {
|
|
switch ($code) {
|
|
case ZPush::COMMAND_SYNC: return 'Sync';
|
|
case ZPush::COMMAND_SENDMAIL: return 'SendMail';
|
|
case ZPush::COMMAND_SMARTFORWARD: return 'SmartForward';
|
|
case ZPush::COMMAND_SMARTREPLY: return 'SmartReply';
|
|
case ZPush::COMMAND_GETATTACHMENT: return 'GetAttachment';
|
|
case ZPush::COMMAND_FOLDERSYNC: return 'FolderSync';
|
|
case ZPush::COMMAND_FOLDERCREATE: return 'FolderCreate';
|
|
case ZPush::COMMAND_FOLDERDELETE: return 'FolderDelete';
|
|
case ZPush::COMMAND_FOLDERUPDATE: return 'FolderUpdate';
|
|
case ZPush::COMMAND_MOVEITEMS: return 'MoveItems';
|
|
case ZPush::COMMAND_GETITEMESTIMATE: return 'GetItemEstimate';
|
|
case ZPush::COMMAND_MEETINGRESPONSE: return 'MeetingResponse';
|
|
case ZPush::COMMAND_SEARCH: return 'Search';
|
|
case ZPush::COMMAND_SETTINGS: return 'Settings';
|
|
case ZPush::COMMAND_PING: return 'Ping';
|
|
case ZPush::COMMAND_ITEMOPERATIONS: return 'ItemOperations';
|
|
case ZPush::COMMAND_PROVISION: return 'Provision';
|
|
case ZPush::COMMAND_RESOLVERECIPIENTS: return 'ResolveRecipients';
|
|
case ZPush::COMMAND_VALIDATECERT: return 'ValidateCert';
|
|
|
|
// Deprecated commands (AS >= 14)
|
|
case ZPush::COMMAND_GETHIERARCHY: return 'GetHierarchy';
|
|
|
|
// Webservice commands
|
|
case ZPush::COMMAND_WEBSERVICE_DEVICE: return 'WebserviceDevice';
|
|
case ZPush::COMMAND_WEBSERVICE_USERS: return 'WebserviceUsers';
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a command code for a given command.
|
|
*
|
|
* @param string $command
|
|
*
|
|
* @access public
|
|
* @return int or false if command is unknown
|
|
*/
|
|
public static function GetCodeFromCommand($command) {
|
|
switch ($command) {
|
|
case 'Sync': return ZPush::COMMAND_SYNC;
|
|
case 'SendMail': return ZPush::COMMAND_SENDMAIL;
|
|
case 'SmartForward': return ZPush::COMMAND_SMARTFORWARD;
|
|
case 'SmartReply': return ZPush::COMMAND_SMARTREPLY;
|
|
case 'GetAttachment': return ZPush::COMMAND_GETATTACHMENT;
|
|
case 'FolderSync': return ZPush::COMMAND_FOLDERSYNC;
|
|
case 'FolderCreate': return ZPush::COMMAND_FOLDERCREATE;
|
|
case 'FolderDelete': return ZPush::COMMAND_FOLDERDELETE;
|
|
case 'FolderUpdate': return ZPush::COMMAND_FOLDERUPDATE;
|
|
case 'MoveItems': return ZPush::COMMAND_MOVEITEMS;
|
|
case 'GetItemEstimate': return ZPush::COMMAND_GETITEMESTIMATE;
|
|
case 'MeetingResponse': return ZPush::COMMAND_MEETINGRESPONSE;
|
|
case 'Search': return ZPush::COMMAND_SEARCH;
|
|
case 'Settings': return ZPush::COMMAND_SETTINGS;
|
|
case 'Ping': return ZPush::COMMAND_PING;
|
|
case 'ItemOperations': return ZPush::COMMAND_ITEMOPERATIONS;
|
|
case 'Provision': return ZPush::COMMAND_PROVISION;
|
|
case 'ResolveRecipients': return ZPush::COMMAND_RESOLVERECIPIENTS;
|
|
case 'ValidateCert': return ZPush::COMMAND_VALIDATECERT;
|
|
|
|
// Deprecated commands (AS >= 14)
|
|
case 'GetHierarchy': return ZPush::COMMAND_GETHIERARCHY;
|
|
|
|
// Webservice commands
|
|
case 'WebserviceDevice': return ZPush::COMMAND_WEBSERVICE_DEVICE;
|
|
case 'WebserviceUsers': return ZPush::COMMAND_WEBSERVICE_USERS;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Normalize the given timestamp to the start of the day
|
|
*
|
|
* @param long $timestamp
|
|
*
|
|
* @access private
|
|
* @return long
|
|
*/
|
|
public static function getDayStartOfTimestamp($timestamp) {
|
|
return $timestamp - ($timestamp % (60 * 60 * 24));
|
|
}
|
|
|
|
/**
|
|
* Returns a formatted string output from an optional timestamp.
|
|
* If no timestamp is sent, NOW is used.
|
|
*
|
|
* @param long $timestamp
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public static function GetFormattedTime($timestamp = false) {
|
|
if (!$timestamp)
|
|
return @strftime("%d/%m/%Y %H:%M:%S");
|
|
else
|
|
return @strftime("%d/%m/%Y %H:%M:%S", $timestamp);
|
|
}
|
|
|
|
/**
|
|
* Returns a formatted string output from an optional timestamp with microseconds.
|
|
* If no timestamp is sent, NOW is used.
|
|
*
|
|
* @param float $timestamp
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public static function GetFormattedMicroTime($timestamp = false) {
|
|
if(!$timestamp)
|
|
$timestamp = microtime(true);
|
|
|
|
$t = explode('.',number_format($timestamp,6,'.',''),2);
|
|
return strftime("%Y-%m-%dT%H:%M:%S", $t[0]).'.'.$t[1];
|
|
}
|
|
|
|
/**
|
|
* Get charset name from a codepage
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/dd317756(VS.85).aspx
|
|
*
|
|
* Table taken from common/codepage.cpp
|
|
*
|
|
* @param integer codepage Codepage
|
|
*
|
|
* @access public
|
|
* @return string iconv-compatible charset name
|
|
*/
|
|
public static function GetCodepageCharset($codepage) {
|
|
$codepages = array(
|
|
20106 => "DIN_66003",
|
|
20108 => "NS_4551-1",
|
|
20107 => "SEN_850200_B",
|
|
950 => "big5",
|
|
50221 => "csISO2022JP",
|
|
51932 => "euc-jp",
|
|
51936 => "euc-cn",
|
|
51949 => "euc-kr",
|
|
949 => "euc-kr",
|
|
936 => "gb18030",
|
|
52936 => "csgb2312",
|
|
852 => "ibm852",
|
|
866 => "ibm866",
|
|
50220 => "iso-2022-jp",
|
|
50222 => "iso-2022-jp",
|
|
50225 => "iso-2022-kr",
|
|
1252 => "windows-1252",
|
|
28591 => "iso-8859-1",
|
|
28592 => "iso-8859-2",
|
|
28593 => "iso-8859-3",
|
|
28594 => "iso-8859-4",
|
|
28595 => "iso-8859-5",
|
|
28596 => "iso-8859-6",
|
|
28597 => "iso-8859-7",
|
|
28598 => "iso-8859-8",
|
|
28599 => "iso-8859-9",
|
|
28603 => "iso-8859-13",
|
|
28605 => "iso-8859-15",
|
|
20866 => "koi8-r",
|
|
21866 => "koi8-u",
|
|
932 => "shift-jis",
|
|
1200 => "unicode",
|
|
1201 => "unicodebig",
|
|
65000 => "utf-7",
|
|
65001 => "utf-8",
|
|
1250 => "windows-1250",
|
|
1251 => "windows-1251",
|
|
1253 => "windows-1253",
|
|
1254 => "windows-1254",
|
|
1255 => "windows-1255",
|
|
1256 => "windows-1256",
|
|
1257 => "windows-1257",
|
|
1258 => "windows-1258",
|
|
874 => "windows-874",
|
|
20127 => "us-ascii"
|
|
);
|
|
|
|
if(isset($codepages[$codepage])) {
|
|
return $codepages[$codepage];
|
|
} else {
|
|
// Defaulting to iso-8859-15 since it is more likely for someone to make a mistake in the codepage
|
|
// when using west-european charsets then when using other charsets since utf-8 is binary compatible
|
|
// with the bottom 7 bits of west-european
|
|
return "iso-8859-15";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts a string encoded with codepage into an UTF-8 string
|
|
*
|
|
* @param int $codepage
|
|
* @param string $string
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public static function ConvertCodepageStringToUtf8($codepage, $string) {
|
|
if (function_exists("iconv")) {
|
|
$charset = self::GetCodepageCharset($codepage);
|
|
return iconv($charset, "utf-8", $string);
|
|
}
|
|
else
|
|
ZLog::Write(LOGLEVEL_WARN, "Utils::ConvertCodepageStringToUtf8() 'iconv' is not available. Charset conversion skipped.");
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Converts a string to another charset.
|
|
*
|
|
* @param int $in
|
|
* @param int $out
|
|
* @param string $string
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public static function ConvertCodepage($in, $out, $string) {
|
|
// do nothing if both charsets are the same
|
|
if ($in == $out)
|
|
return $string;
|
|
|
|
if (function_exists("iconv")) {
|
|
$inCharset = self::GetCodepageCharset($in);
|
|
$outCharset = self::GetCodepageCharset($out);
|
|
return iconv($inCharset, $outCharset, $string);
|
|
}
|
|
else
|
|
ZLog::Write(LOGLEVEL_WARN, "Utils::ConvertCodepage() 'iconv' is not available. Charset conversion skipped.");
|
|
|
|
return $string;
|
|
}
|
|
|
|
/**
|
|
* Returns the best match of preferred body preference types.
|
|
*
|
|
* @param array $bpTypes
|
|
*
|
|
* @access public
|
|
* @return int
|
|
*/
|
|
public static function GetBodyPreferenceBestMatch($bpTypes) {
|
|
// The best choice is RTF, then HTML and then MIME in order to save bandwidth
|
|
// because MIME is a complete message including the headers and attachments
|
|
if (in_array(SYNC_BODYPREFERENCE_RTF, $bpTypes)) return SYNC_BODYPREFERENCE_RTF;
|
|
if (in_array(SYNC_BODYPREFERENCE_HTML, $bpTypes)) return SYNC_BODYPREFERENCE_HTML;
|
|
if (in_array(SYNC_BODYPREFERENCE_MIME, $bpTypes)) return SYNC_BODYPREFERENCE_MIME;
|
|
return SYNC_BODYPREFERENCE_PLAIN;
|
|
}
|
|
|
|
/* BEGIN fmbiete's contribution r1516, ZP-318 */
|
|
/**
|
|
* Converts a html string into a plain text string
|
|
*
|
|
* @param string $html
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public static function ConvertHtmlToText($html) {
|
|
// remove css-style tags
|
|
$plaintext = preg_replace("/<style.*?<\/style>/is", "", $html);
|
|
// remove all other html
|
|
$plaintext = strip_tags($plaintext);
|
|
|
|
return $plaintext;
|
|
}
|
|
/* END fmbiete's contribution r1516, ZP-318 */
|
|
|
|
/**
|
|
* Checks if a file has the same owner and group as the parent directory.
|
|
* If not, owner and group are fixed (being updated to the owner/group of the directory).
|
|
* Function code contributed by Robert Scheck aka rsc.
|
|
*
|
|
* @param string $file
|
|
*
|
|
* @access public
|
|
* @return boolean
|
|
*/
|
|
public static function FixFileOwner($file) {
|
|
if(posix_getuid() == 0 && file_exists($file)) {
|
|
$dir = dirname($file);
|
|
$perm_dir = stat($dir);
|
|
$perm_log = stat($file);
|
|
|
|
if($perm_dir[4] !== $perm_log[4] || $perm_dir[5] !== $perm_log[5]) {
|
|
chown($file, $perm_dir[4]);
|
|
chgrp($file, $perm_dir[5]);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns AS-style LastVerbExecuted value from the server value.
|
|
*
|
|
* @param int $verb
|
|
*
|
|
* @access public
|
|
* @return int
|
|
*/
|
|
public static function GetLastVerbExecuted($verb) {
|
|
switch ($verb) {
|
|
case NOTEIVERB_REPLYTOSENDER: return AS_REPLYTOSENDER;
|
|
case NOTEIVERB_REPLYTOALL: return AS_REPLYTOALL;
|
|
case NOTEIVERB_FORWARD: return AS_FORWARD;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the local part from email address.
|
|
*
|
|
* @param string $email
|
|
*
|
|
* @access public
|
|
* @return string
|
|
*/
|
|
public static function GetLocalPartFromEmail($email) {
|
|
$pos = strpos($email, '@');
|
|
if ($pos === false) {
|
|
return $email;
|
|
}
|
|
return substr($email, 0, $pos);
|
|
}
|
|
|
|
/**
|
|
* Generate date object from string and timezone.
|
|
*
|
|
* @param string $value
|
|
* @param string $timezone
|
|
*
|
|
* @access public
|
|
* @return int epoch
|
|
*/
|
|
public static function MakeUTCDate($value, $timezone = null) {
|
|
$tz = null;
|
|
if ($timezone) {
|
|
$tz = timezone_open($timezone);
|
|
}
|
|
if (!$tz) {
|
|
//If there is no timezone set, we use the default timezone
|
|
$tz = timezone_open(date_default_timezone_get());
|
|
}
|
|
//20110930T090000Z
|
|
$date = date_create_from_format('Ymd\THis\Z', $value, timezone_open("UTC"));
|
|
if (!$date) {
|
|
//20110930T090000
|
|
$date = date_create_from_format('Ymd\THis', $value, $tz);
|
|
}
|
|
if (!$date) {
|
|
//20110930 (Append T000000Z to the date, so it starts at midnight)
|
|
$date = date_create_from_format('Ymd\THis\Z', $value . "T000000Z", $tz);
|
|
}
|
|
return date_timestamp_get($date);
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate a tzid from various formats
|
|
*
|
|
* @param str $timezone
|
|
*
|
|
* @access public
|
|
* @return timezone id
|
|
*/
|
|
public static function ParseTimezone($timezone) {
|
|
//(GMT+01.00) Amsterdam / Berlin / Bern / Rome / Stockholm / Vienna
|
|
if (preg_match('/GMT(\\+|\\-)0(\d)/', $timezone, $matches)) {
|
|
return "Etc/GMT" . $matches[1] . $matches[2];
|
|
}
|
|
//(GMT+10.00) XXX / XXX / XXX / XXX
|
|
if (preg_match('/GMT(\\+|\\-)1(\d)/', $timezone, $matches)) {
|
|
return "Etc/GMT" . $matches[1] . "1" . $matches[2];
|
|
}
|
|
///inverse.ca/20101018_1/Europe/Amsterdam or /inverse.ca/20101018_1/America/Argentina/Buenos_Aires
|
|
if (preg_match('/\/[.[:word:]]+\/\w+\/(\w+)\/([\w\/]+)/', $timezone, $matches)) {
|
|
return $matches[1] . "/" . $matches[2];
|
|
}
|
|
return TimezoneUtil::getMSTZnameFromTZName(trim($timezone, '"'));
|
|
}
|
|
|
|
/**
|
|
* Safely write data to disk, using an unique tmp file (concurrent write),
|
|
* and using rename for atomicity
|
|
*
|
|
* If you use safe_put_contents, you can safely use file_get_contents
|
|
* (you will always read a fully written file)
|
|
*
|
|
* @param string $filename
|
|
* @param string $data
|
|
* @return boolean|int
|
|
*/
|
|
public static function safe_put_contents($filename, $data) {
|
|
//put the 'tmp' as a prefix (and not suffix) so all glob call will not see temp files
|
|
$tmp = dirname($filename) . DIRECTORY_SEPARATOR . 'tmp-' . getmypid() . '-' . basename($filename);
|
|
if (($res = file_put_contents($tmp, $data)) !== false)
|
|
if (rename($tmp, $filename) !== true)
|
|
$res = false;
|
|
|
|
return $res;
|
|
}
|
|
} |