Commit d4418b8f authored by Manfred Kutas's avatar Manfred Kutas

Merge branch 'develop' into bugfix/ZP-1267-change-summed-mapi-tags

parents 210380ec afec4917
...@@ -171,6 +171,20 @@ USE_FULLEMAIL_FOR_LOGIN If this is set to "true", AutoDiscover will attempt to ...@@ -171,6 +171,20 @@ USE_FULLEMAIL_FOR_LOGIN If this is set to "true", AutoDiscover will attempt to
address sent by the client. If disabled (default), the address sent by the client. If disabled (default), the
local part of the email address is used. local part of the email address is used.
AUTODISCOVER_LOGIN_TYPE If the local part of the email address doesn't match the
user name, this parameter helps to convert it for some common cases.
Possible values:
AUTODISCOVER_LOGIN_EMAIL uses the local part of the email address
as provided when setting up the account.
AUTODISCOVER_LOGIN_NO_DOT removes the '.' from email address:
email: first.last@domain.com -> resulting username: firstlast
AUTODISCOVER_LOGIN_F_NO_DOT_LAST cuts the first part before '.' after the first letter
and removes the '.' from email address:
email: first.last@domain.com -> resulting username: flast
AUTODISCOVER_LOGIN_F_DOT_LAST cuts the part before '.' after the first letter and
leaves the part after '.' as is:
email: first.last@domain.com -> resulting username: f.last
LOGFILEDIR The directory where logfiles are created. LOGFILEDIR The directory where logfiles are created.
LOGFILE The default AutoDiscover log file. LOGFILE The default AutoDiscover log file.
......
...@@ -190,11 +190,25 @@ class ZPushAutodiscover { ...@@ -190,11 +190,25 @@ class ZPushAutodiscover {
// the local part only. // the local part only.
if (USE_FULLEMAIL_FOR_LOGIN) { if (USE_FULLEMAIL_FOR_LOGIN) {
$username = $incomingXml->Request->EMailAddress; $username = $incomingXml->Request->EMailAddress;
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Using the complete email address for login: '%s'", $username)); ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAutodiscover->login(): Using the complete email address for login: '%s'", $username));
} }
else { else {
$username = Utils::GetLocalPartFromEmail($incomingXml->Request->EMailAddress); $username = Utils::GetLocalPartFromEmail($incomingXml->Request->EMailAddress);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("Using the username only for login: '%s'", $username)); if (defined('AUTODISCOVER_LOGIN_TYPE') && AUTODISCOVER_LOGIN_TYPE != AUTODISCOVER_LOGIN_EMAIL) {
switch (AUTODISCOVER_LOGIN_TYPE) {
case AUTODISCOVER_LOGIN_NO_DOT:
$username = str_replace('.', '', $username);
break;
case AUTODISCOVER_LOGIN_F_NO_DOT_LAST:
$username = str_replace('.', '', substr_replace($username, '', 1, strpos($username, '.') - 1));
break;
case AUTODISCOVER_LOGIN_F_DOT_LAST:
$username = substr_replace($username, '', 1, strpos($username, '.') - 1);
break;
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAutodiscover->login(): AUTODISCOVER_LOGIN_TYPE is set to %d", AUTODISCOVER_LOGIN_TYPE));
}
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAutodiscover->login(): Using the username only for login: '%s'", $username));
} }
// Mobile devices send Authorization header using UTF-8 charset. Outlook sends it using ISO-8859-1 encoding. // Mobile devices send Authorization header using UTF-8 charset. Outlook sends it using ISO-8859-1 encoding.
......
...@@ -45,6 +45,28 @@ ...@@ -45,6 +45,28 @@
*/ */
define('USE_FULLEMAIL_FOR_LOGIN', false); define('USE_FULLEMAIL_FOR_LOGIN', false);
/*
* AutoDiscover requires the username to match either the email address
* or the local part of the email address.
* This is not always possible as the username might have a different
* schema than email address. Configure this parameter to match your
* username settings.
* @see https://wiki.z-hub.io/display/ZP/Configuring+Z-Push+Autodiscover#ConfiguringZ-PushAutodiscover-Configuration
* @see https://jira.z-hub.io/browse/ZP-1209
*
* Possible values:
* AUTODISCOVER_LOGIN_EMAIL - uses the email address as provided when setting up the account
* AUTODISCOVER_LOGIN_NO_DOT - removes the '.' from email address:
* email: first.last@domain.com -> resulting username: firstlast
* AUTODISCOVER_LOGIN_F_NO_DOT_LAST - cuts the first part before '.' after the first letter and
* removes the '.' from email address:
* email: first.last@domain.com -> resulting username: flast
* AUTODISCOVER_LOGIN_F_DOT_LAST - cuts the part before '.' after the first letter and
* leaves the part after '.' as is:
* email: first.last@domain.com -> resulting username: f.last
*/
define('AUTODISCOVER_LOGIN_TYPE', AUTODISCOVER_LOGIN_EMAIL);
/********************************************************************************** /**********************************************************************************
* Logging settings * Logging settings
* Possible LOGLEVEL and LOGUSERLEVEL values are: * Possible LOGLEVEL and LOGUSERLEVEL values are:
......
...@@ -190,7 +190,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider { ...@@ -190,7 +190,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the new message")); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the new message"));
$mobj = new Mail_mimeDecode($sm->mime); $mobj = new Mail_mimeDecode($sm->mime);
$message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); $message = $mobj->decode(array('decode_headers' => 'utf-8', 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8'));
unset($mobj); unset($mobj);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the From and To")); ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the From and To"));
...@@ -485,7 +485,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider { ...@@ -485,7 +485,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider {
} }
$mobj = new Mail_mimeDecode($mail); $mobj = new Mail_mimeDecode($mail);
$message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); $message = $mobj->decode(array('decode_headers' => 'utf-8', 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8'));
if (!isset($message->parts)) { if (!isset($message->parts)) {
throw new StatusException(sprintf("BackendIMAP->GetAttachmentData('%s'): Error, message without parts. Requesting part key: '%d'", $attname, $part), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT); throw new StatusException(sprintf("BackendIMAP->GetAttachmentData('%s'): Error, message without parts. Requesting part key: '%d'", $attname, $part), SYNC_ITEMOPERATIONSSTATUS_INVALIDATT);
...@@ -1032,7 +1032,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider { ...@@ -1032,7 +1032,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider {
} }
$mobj = new Mail_mimeDecode($mail); $mobj = new Mail_mimeDecode($mail);
$message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); $message = $mobj->decode(array('decode_headers' => 'utf-8', 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8'));
Utils::CheckAndFixEncodingInHeaders($mail, $message); Utils::CheckAndFixEncodingInHeaders($mail, $message);
...@@ -1672,7 +1672,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider { ...@@ -1672,7 +1672,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider {
// Get the original calendar request, so we don't need to create it from scratch // Get the original calendar request, so we don't need to create it from scratch
$mobj = new Mail_mimeDecode($mail); $mobj = new Mail_mimeDecode($mail);
unset($mail); unset($mail);
$message = $mobj->decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8')); $message = $mobj->decode(array('decode_headers' => 'utf-8', 'decode_bodies' => true, 'include_bodies' => true, 'rfc_822bodies' => true, 'charset' => 'utf-8'));
unset($mobj); unset($mobj);
$body_part = null; $body_part = null;
......
...@@ -1135,6 +1135,12 @@ define('PR_EC_STATS_SESSION_LOCKED' ,mapi_prop_tag(PT_BOOLEAN, ...@@ -1135,6 +1135,12 @@ define('PR_EC_STATS_SESSION_LOCKED' ,mapi_prop_tag(PT_BOOLEAN,
define('PR_EC_STATS_SESSION_BUSYSTATES' ,mapi_prop_tag(PT_MV_STRING8, 0x6747)); define('PR_EC_STATS_SESSION_BUSYSTATES' ,mapi_prop_tag(PT_MV_STRING8, 0x6747));
define('PR_EC_COMPANY_NAME' ,mapi_prop_tag(PT_STRING8, 0x6748)); define('PR_EC_COMPANY_NAME' ,mapi_prop_tag(PT_STRING8, 0x6748));
/* kopano specific properties for optimization of imap functionality */
define('PR_EC_IMAP_EMAIL' ,mapi_prop_tag(PT_BINARY, PR_EC_BASE+0x8C)); // the complete rfc822 email
define('PR_EC_IMAP_EMAIL_SIZE' ,mapi_prop_tag(PT_LONG, PR_EC_BASE+0x8D));
define('PR_EC_IMAP_BODY' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x8E)); // simplified bodystructure (mostly unused by clients)
define('PR_EC_IMAP_BODYSTRUCTURE' ,mapi_prop_tag(PT_STRING8, PR_EC_BASE+0x8F)); // extended bodystructure (often used by clients)
/* user features */ /* user features */
define('PR_EC_ENABLED_FEATURES' ,mapi_prop_tag(PT_MV_TSTRING, 0x67B3)); define('PR_EC_ENABLED_FEATURES' ,mapi_prop_tag(PT_MV_TSTRING, 0x67B3));
define('PR_EC_ENABLED_FEATURES_A' ,mapi_prop_tag(PT_MV_STRING8, 0x67B3)); define('PR_EC_ENABLED_FEATURES_A' ,mapi_prop_tag(PT_MV_STRING8, 0x67B3));
......
...@@ -2533,10 +2533,16 @@ class MAPIProvider { ...@@ -2533,10 +2533,16 @@ class MAPIProvider {
* @return boolean * @return boolean
*/ */
private function imtoinet($mapimessage, &$message) { private function imtoinet($mapimessage, &$message) {
if (function_exists("mapi_inetmapi_imtoinet")) { $mapiEmail = mapi_getprops($mapimessage, array(PR_EC_IMAP_EMAIL));
$stream = false;
if (isset($mapiEmail[PR_EC_IMAP_EMAIL]) || MAPIUtils::GetError(PR_EC_IMAP_EMAIL, $mapiEmail) == MAPI_E_NOT_ENOUGH_MEMORY) {
$stream = mapi_openproperty($mapimessage, PR_EC_IMAP_EMAIL, IID_IStream, 0, 0);
ZLog::Write(LOGLEVEL_DEBUG, "MAPIProvider->imtoinet(): using PR_EC_IMAP_EMAIL as full RFC822 message");
}
else {
$addrbook = $this->getAddressbook(); $addrbook = $this->getAddressbook();
$stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $mapimessage, array('use_tnef' => -1)); $stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $mapimessage, array('use_tnef' => -1));
}
if (is_resource($stream)) { if (is_resource($stream)) {
$mstreamstat = mapi_stream_stat($stream); $mstreamstat = mapi_stream_stat($stream);
$streamsize = $mstreamstat["cb"]; $streamsize = $mstreamstat["cb"];
...@@ -2557,8 +2563,7 @@ class MAPIProvider { ...@@ -2557,8 +2563,7 @@ class MAPIProvider {
return true; return true;
} }
} }
ZLog::Write(LOGLEVEL_ERROR, sprintf("MAPIProvider->imtoinet(): got no stream or content from mapi_inetmapi_imtoinet()")); ZLog::Write(LOGLEVEL_ERROR, "MAPIProvider->imtoinet(): got no stream or content from mapi_inetmapi_imtoinet()");
}
return false; return false;
} }
......
...@@ -116,7 +116,7 @@ class BackendMaildir extends BackendDiff { ...@@ -116,7 +116,7 @@ class BackendMaildir extends BackendDiff {
// Parse e-mail // Parse e-mail
$rfc822 = file_get_contents($this->getPath() . "/$fn"); $rfc822 = file_get_contents($this->getPath() . "/$fn");
$message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8')); $message = Mail_mimeDecode::decode(array('decode_headers' => 'utf-8', 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8'));
$attachment = new SyncItemOperationsAttachment(); $attachment = new SyncItemOperationsAttachment();
$attachment->data = StringStreamWrapper::Open($message->parts[$part]->body); $attachment->data = StringStreamWrapper::Open($message->parts[$part]->body);
...@@ -333,7 +333,7 @@ class BackendMaildir extends BackendDiff { ...@@ -333,7 +333,7 @@ class BackendMaildir extends BackendDiff {
// Parse e-mail // Parse e-mail
$rfc822 = file_get_contents($this->getPath() . "/" . $fn); $rfc822 = file_get_contents($this->getPath() . "/" . $fn);
$message = Mail_mimeDecode::decode(array('decode_headers' => true, 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8')); $message = Mail_mimeDecode::decode(array('decode_headers' => 'utf-8', 'decode_bodies' => true, 'include_bodies' => true, 'input' => $rfc822, 'crlf' => "\n", 'charset' => 'utf-8'));
Utils::CheckAndFixEncodingInHeaders($mail, $message); Utils::CheckAndFixEncodingInHeaders($mail, $message);
......
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
* @author Sean Coates <sean@php.net> * @author Sean Coates <sean@php.net>
* @copyright 2003-2006 PEAR <pear-group@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net>
* @license http://www.opensource.org/licenses/bsd-license.php BSD License * @license http://www.opensource.org/licenses/bsd-license.php BSD License
* @version CVS: $Id: mimeDecode.php 335147 2014-10-27 08:41:39Z alan_k $ * @version CVS: $Id: mimeDecode.php 337165 2015-07-15 09:42:08Z alan_k $
* @link http://pear.php.net/package/Mail_mime * @link http://pear.php.net/package/Mail_mime
*/ */
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
* implemented automated decoding of strings from mail charset * implemented automated decoding of strings from mail charset
* *
* Reference implementation used: * Reference implementation used:
* http://download.pear.php.net/package/Mail_mimeDecode-1.5.5.tgz * http://download.pear.php.net/package/Mail_mimeDecode-1.5.6.tgz
* *
* used "old" method of checking if called statically, as this is deprecated between php 5.0.0 and 5.3.0 * used "old" method of checking if called statically, as this is deprecated between php 5.0.0 and 5.3.0
* (isStatic of decode() around line 215) * (isStatic of decode() around line 215)
...@@ -154,8 +154,8 @@ class Mail_mimeDecode ...@@ -154,8 +154,8 @@ class Mail_mimeDecode
/** /**
* Flag to determine whether to decode headers * Flag to determine whether to decode headers
* * (set to UTF8 to iconv convert headers)
* @var boolean * @var mixed
* @access private * @access private
*/ */
var $_decode_headers; var $_decode_headers;
...@@ -190,6 +190,11 @@ class Mail_mimeDecode ...@@ -190,6 +190,11 @@ class Mail_mimeDecode
$this->_include_bodies = true; $this->_include_bodies = true;
$this->_rfc822_bodies = false; $this->_rfc822_bodies = false;
} }
// BC
function Mail_mimeDecode($input)
{
$this->__construct($input);
}
/** /**
* Begins the decoding process. If called statically * Begins the decoding process. If called statically
...@@ -202,7 +207,8 @@ class Mail_mimeDecode ...@@ -202,7 +207,8 @@ class Mail_mimeDecode
* object. * object.
* decode_bodies - Whether to decode the bodies * decode_bodies - Whether to decode the bodies
* of the parts. (Transfer encoding) * of the parts. (Transfer encoding)
* decode_headers - Whether to decode headers * decode_headers - Whether to decode headers,
* - use "UTF8//IGNORE" to convert charset.
* *
* input - If called statically, this will be treated * input - If called statically, this will be treated
* as the input * as the input
...@@ -239,10 +245,12 @@ class Mail_mimeDecode ...@@ -239,10 +245,12 @@ class Mail_mimeDecode
$this->_charset = isset($params['charset']) ? $this->_charset = isset($params['charset']) ?
strtolower($params['charset']) : 'utf-8'; strtolower($params['charset']) : 'utf-8';
if (is_string($this->_decode_headers)) {
if (is_string($this->_decode_headers) && !function_exists('iconv')) { if (!function_exists('iconv')) {
$this->raiseError('header decode conversion requested, however iconv is missing'); $this->raiseError('header decode conversion requested, however iconv is missing');
} }
$this->_decode_headers = strtolower($this->_decode_headers);
}
$structure = $this->_decode($this->_header, $this->_body); $structure = $this->_decode($this->_header, $this->_body);
if ($structure === false) { if ($structure === false) {
...@@ -339,10 +347,18 @@ class Mail_mimeDecode ...@@ -339,10 +347,18 @@ class Mail_mimeDecode
break; break;
case 'multipart/signed': // PGP case 'multipart/signed': // PGP
$parts = $this->_boundarySplit($body, $content_type['other']['boundary'], true);
$return->parts['msg_body'] = $parts[0];
list($part_header, $part_body) = $this->_splitBodyHeader($parts[1]);
$return->parts['sig_hdr'] = $part_header;
$return->parts['sig_body'] = $part_body;
break;
case 'multipart/encrypted': // #190 encrypted parts will be treated as normal ones case 'multipart/encrypted': // #190 encrypted parts will be treated as normal ones
case 'multipart/parallel': case 'multipart/parallel':
case 'multipart/appledouble': // Appledouble mail case 'multipart/appledouble': // Appledouble mail
case 'multipart/report': // RFC1892 case 'multipart/report': // RFC1892
case 'multipart/signed': // PGP
case 'multipart/digest': case 'multipart/digest':
case 'multipart/alternative': case 'multipart/alternative':
case 'multipart/related': case 'multipart/related':
...@@ -385,7 +401,7 @@ class Mail_mimeDecode ...@@ -385,7 +401,7 @@ class Mail_mimeDecode
break; break;
// #190, KD 2015-06-09 - Add type for S/MIME Encrypted messages; these must have the filename set explicitly (it won't work otherwise) // #190, KD 2015-06-09 - Add type for S/MIME Encrypted messages; these must have the filename set explicitly (it won't work otherwise)
//and then falls through for the rest on purpose. // and then falls through for the rest on purpose.
case 'application/x-pkcs7-mime': case 'application/x-pkcs7-mime':
case 'application/pkcs7-mime': case 'application/pkcs7-mime':
if (!isset($content_transfer_encoding['value'])) { if (!isset($content_transfer_encoding['value'])) {
...@@ -781,7 +797,7 @@ class Mail_mimeDecode ...@@ -781,7 +797,7 @@ class Mail_mimeDecode
* @return string Decoded header value * @return string Decoded header value
* @access private * @access private
*/ */
function _decodeHeader($input) function _decodeHeader($input, $default_charset=false)
{ {
if (!$this->_decode_headers) { if (!$this->_decode_headers) {
return $input; return $input;
...@@ -789,19 +805,14 @@ class Mail_mimeDecode ...@@ -789,19 +805,14 @@ class Mail_mimeDecode
// Remove white space between encoded-words // Remove white space between encoded-words
$input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input); $input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
$encodedwords = false;
$charset = '';
// For each encoded-word... // For each encoded-word...
while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) { while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
$encodedwords = true;
$encoded = $matches[1]; $encoded = $matches[1];
$charset = $matches[2]; $charset = strtolower($matches[2]);
$encoding = $matches[3]; $encoding = strtolower($matches[3]);
$text = $matches[4]; $text = $matches[4];
switch (strtolower($encoding)) { switch ($encoding) {
case 'b': case 'b':
$text = base64_decode($text); $text = base64_decode($text);
break; break;
...@@ -813,13 +824,16 @@ class Mail_mimeDecode ...@@ -813,13 +824,16 @@ class Mail_mimeDecode
$text = str_replace('=' . $value, chr(hexdec($value)), $text); $text = str_replace('=' . $value, chr(hexdec($value)), $text);
break; break;
} }
if (is_string($this->_decode_headers) && $charset != $this->_decode_headers) {
$text = $this->_autoconvert_encoding($text, $charset); $conv = @iconv($charset, $this->_decode_headers, $text);
$text = ($conv === false) ? $text : $conv;
}
$input = str_replace($encoded, $text, $input); $input = str_replace($encoded, $text, $input);
} }
if (!$encodedwords) { if ($default_charset && is_string($this->_decode_headers) && $charset != $this->_decode_headers) {
$input = $this->_autoconvert_encoding($input, $charset); $conv = @iconv($charset, $this->_decode_headers, $input); // TODO: shouldn't this be $default_charset ?
$input = ($conv === false) ? $input : $conv;
} }
return $input; return $input;
...@@ -846,67 +860,13 @@ class Mail_mimeDecode ...@@ -846,67 +860,13 @@ class Mail_mimeDecode
case 'base64': case 'base64':
$input = base64_decode($input); $input = base64_decode($input);
break; break;
case '7bit':
case '8bit':
default:
break;
}
return $detectCharset ? $this->_autoconvert_encoding($input, $charset) : $input;
} }
if ($detectCharset && strtolower($charset) != $this->_charset) {
/** $conv = @iconv($charset, $this->_charset, $input);
* Error handler dummy for _autoconvert_encoding $input = ($conv === false) ? $input : $conv;
*
* @param integer $errno
* @param string $errstr
* @return boolean true
* @access public static
*/
static function _iconv_notice_handler($errno, $errstr) {
return true;
} }
/** return $input;
* Autoconvert the text from any encoding. THIS WILL NEVER WORK 100%.
* Will ignore the E_NOTICE for iconv when detecting ilegal charsets
*
* @param string $input Input string to convert
* @param string $supposed_encoding Encoding that the text is possibly using
* @return string Converted string
* @access private
*/
function _autoconvert_encoding($input, $supposed_encoding = "UTF-8") {
$input_converted = $input;
if (function_exists("mb_detect_order")) {
$mb_order = array_merge(array($supposed_encoding), mb_detect_order());
set_error_handler('Mail_mimeDecode::_iconv_notice_handler');
// Default value in case of error
$detected_encoding = $supposed_encoding;
try {
$detected_encoding = mb_detect_encoding($input, $mb_order, true);
// In some cases mb_detect_encoding returns an empty string
if ($detected_encoding === false || strlen($detected_encoding) == 0) {
$detected_encoding = $supposed_encoding;
}
$input_converted = iconv($detected_encoding, $this->_charset, $input);
}
catch(Exception $ex) {
$this->raiseError($ex->getMessage());
}
restore_error_handler();
if ($input_converted === false || mb_strlen($input_converted, $this->_charset) !== mb_strlen($input, $detected_encoding)) {
ZLog::Write(LOGLEVEL_DEBUG, "Mail_mimeDecode()::_autoconvert_encoding(): Text cannot be correctly decoded, using original text. This will be ok if the part is not text, otherwise expect encoding errors");
$input_converted = $input;
}
}
return $input_converted;
} }
/** /**
...@@ -1008,7 +968,7 @@ class Mail_mimeDecode ...@@ -1008,7 +968,7 @@ class Mail_mimeDecode
/** /**
* Get all parts in the message with specified type and concatenate them together, unless the * Get all parts in the message with specified type and concatenate them together, unless the
* Content-Disposition is 'attachment', in which case the text is apparently an attachment * Content-Disposition is 'attachment', in which case the text is apparently an attachment.
* *
* @param string $message mimedecode message(part) * @param string $message mimedecode message(part)
* @param string $message message subtype * @param string $message message subtype
...@@ -1019,8 +979,9 @@ class Mail_mimeDecode ...@@ -1019,8 +979,9 @@ class Mail_mimeDecode
* @access public * @access public
*/ */
static function getBodyRecursive($message, $subtype, &$body, $replace_nr = false) { static function getBodyRecursive($message, $subtype, &$body, $replace_nr = false) {
if(!isset($message->ctype_primary)) return; // TODO: move this function into general utils
if(strcasecmp($message->ctype_primary, "text") == 0 && strcasecmp($message->ctype_secondary, $subtype) == 0 && isset($message->body)) { if (!isset($message->ctype_primary)) return;
if (strcasecmp($message->ctype_primary, "text") == 0 && strcasecmp($message->ctype_secondary, $subtype) == 0 && isset($message->body)) {
if ($replace_nr) { if ($replace_nr) {
$body .= str_replace("\n", "\r\n", str_replace("\r", "", $message->body)); $body .= str_replace("\n", "\r\n", str_replace("\r", "", $message->body));
} }
...@@ -1029,12 +990,12 @@ class Mail_mimeDecode ...@@ -1029,12 +990,12 @@ class Mail_mimeDecode
} }
} }
if(strcasecmp($message->ctype_primary,"multipart")==0 && isset($message->parts) && is_array($message->parts)) { if (strcasecmp($message->ctype_primary,"multipart") == 0 && isset($message->parts) && is_array($message->parts)) {
foreach($message->parts as $part) { foreach($message->parts as $part) {
// Check testing/samples/m1009.txt // Check testing/samples/m1009.txt
// Content-Type: text/plain; charset=us-ascii; name="hareandtoroise.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="hareandtoroise.txt" // Content-Type: text/plain; charset=us-ascii; name="hareandtoroise.txt" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="hareandtoroise.txt"
// We don't want to show that file text (outlook doesn't show it), so if we have content-disposition we don't apply recursivity // We don't want to show that file text (outlook doesn't show it), so if we have content-disposition we don't apply recursivity
if(!isset($part->disposition)) { if (!isset($part->disposition)) {
Mail_mimeDecode::getBodyRecursive($part, $subtype, $body, $replace_nr); Mail_mimeDecode::getBodyRecursive($part, $subtype, $body, $replace_nr);
} }
} }
......
...@@ -1053,3 +1053,8 @@ define("NOTEIVERB_FORWARD", 104); ...@@ -1053,3 +1053,8 @@ define("NOTEIVERB_FORWARD", 104);
define("AS_REPLYTOSENDER", 1); define("AS_REPLYTOSENDER", 1);
define("AS_REPLYTOALL", 2); define("AS_REPLYTOALL", 2);
define("AS_FORWARD", 3); define("AS_FORWARD", 3);
define('AUTODISCOVER_LOGIN_EMAIL', 0);
define('AUTODISCOVER_LOGIN_NO_DOT', 1);
define('AUTODISCOVER_LOGIN_F_NO_DOT_LAST', 2);
define('AUTODISCOVER_LOGIN_F_DOT_LAST', 3);
...@@ -28,6 +28,11 @@ class ZPushAdmin { ...@@ -28,6 +28,11 @@ class ZPushAdmin {
* //TODO resync of a foldertype for all users (e.g. Appointment) * //TODO resync of a foldertype for all users (e.g. Appointment)
*/ */
const STATUS_SUCCESS = 0;
const STATUS_DEVICE_SYNCED_AFTER_DAYSOLD = 1;
public static $status = self::STATUS_SUCCESS;
/** /**
* List devices known to Z-Push. * List devices known to Z-Push.
* If no user is given, all devices are listed * If no user is given, all devices are listed
...@@ -219,11 +224,13 @@ class ZPushAdmin { ...@@ -219,11 +224,13 @@ class ZPushAdmin {
* *
* @param string $user (opt) user of the device * @param string $user (opt) user of the device
* @param string $devid (opt) device id which should be removed * @param string $devid (opt) device id which should be removed
* @param int $daysOld (opt) devices which haven't synced for $daysOld days
* @param int $time (opt) unix timestamp to use with $daysOld
* *
* @return boolean * @return boolean
* @access public * @access public
*/ */
static public function RemoveDevice($user = false, $devid = false) { static public function RemoveDevice($user = false, $devid = false, $daysOld = false, $time = false) {
if ($user === false && $devid === false) if ($user === false && $devid === false)
return false; return false;
...@@ -232,7 +239,7 @@ class ZPushAdmin { ...@@ -232,7 +239,7 @@ class ZPushAdmin {
$devicesIds = ZPush::GetStateMachine()->GetAllDevices($user); $devicesIds = ZPush::GetStateMachine()->GetAllDevices($user);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::RemoveDevice(): all '%d' devices for user '%s' found to be removed", count($devicesIds), $user)); ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::RemoveDevice(): all '%d' devices for user '%s' found to be removed", count($devicesIds), $user));
foreach ($devicesIds as $deviceid) { foreach ($devicesIds as $deviceid) {
if (!self::RemoveDevice($user, $deviceid)) { if (!self::RemoveDevice($user, $deviceid, $daysOld, $time)) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::RemoveDevice(): removing devices failed for device '%s' of user '%s'. Aborting", $deviceid, $user)); ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::RemoveDevice(): removing devices failed for device '%s' of user '%s'. Aborting", $deviceid, $user));
return false; return false;
} }
...@@ -243,7 +250,7 @@ class ZPushAdmin { ...@@ -243,7 +250,7 @@ class ZPushAdmin {
$users = self::ListUsers($devid); $users = self::ListUsers($devid);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::RemoveDevice(): device '%d' is used by '%d' users and will be removed", $devid, count($users))); ZLog::Write(LOGLEVEL_DEBUG, sprintf("ZPushAdmin::RemoveDevice(): device '%d' is used by '%d' users and will be removed", $devid, count($users)));
foreach ($users as $aUser) { foreach ($users as $aUser) {
if (!self::RemoveDevice($aUser, $devid)) { if (!self::RemoveDevice($aUser, $devid, $daysOld, $time)) {
ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::RemoveDevice(): removing user '%s' from device '%s' failed. Aborting", $aUser, $devid)); ZLog::Write(LOGLEVEL_ERROR, sprintf("ZPushAdmin::RemoveDevice(): removing user '%s' from device '%s' failed. Aborting", $aUser, $devid));
return false; return false;
} }
...@@ -260,6 +267,21 @@ class ZPushAdmin { ...@@ -260,6 +267,21 @@ class ZPushAdmin {
$device->SetData($devicedata, false); $device->SetData($devicedata, false);
if (!isset($devicedata->devices)) if (!isset($devicedata->devices))
throw new StateInvalidException("No devicedata stored in ASDevice"); throw new StateInvalidException("No devicedata stored in ASDevice");
if ($daysOld) {
if (!$time) {
$time = time();
}
$lastSynced = floor(($time - $device->getLastupdatetime()) / 86400);
if ($daysOld > $lastSynced) {
ZLog::Write(LOGLEVEL_INFO,
sprintf("ZPushAdmin::RemoveDevice(): device '%s' of user '%s' synced %d day(s) ago but only devices which synced more than %d days ago will be removed. Skipping.",
$devid, $user, $lastSynced, $daysOld));
self::$status = self::STATUS_DEVICE_SYNCED_AFTER_DAYSOLD;
return true;
}
}
$devices = $devicedata->devices; $devices = $devicedata->devices;
} }
catch (StateNotFoundException $e) { catch (StateNotFoundException $e) {
......
...@@ -92,6 +92,7 @@ class ZPushAdminCLI { ...@@ -92,6 +92,7 @@ class ZPushAdminCLI {
static private $device = false; static private $device = false;
static private $type = false; static private $type = false;
static private $errormessage; static private $errormessage;
static private $daysold = false;
/** /**
* Returns usage instructions * Returns usage instructions
...@@ -102,7 +103,8 @@ class ZPushAdminCLI { ...@@ -102,7 +103,8 @@ class ZPushAdminCLI {
static public function UsageInstructions() { static public function UsageInstructions() {
return "Usage:\n\tz-push-admin.php -a ACTION [options]\n\n" . return "Usage:\n\tz-push-admin.php -a ACTION [options]\n\n" .
"Parameters:\n\t-a list/lastsync/wipe/remove/resync/clearloop/fixstates\n\t[-u] username\n\t[-d] deviceid\n" . "Parameters:\n\t-a list/lastsync/wipe/remove/resync/clearloop/fixstates\n\t[-u] username\n\t[-d] deviceid\n" .
"\t[-t] type\tthe following types are available: '".self::TYPE_OPTION_EMAIL."', '".self::TYPE_OPTION_CALENDAR."', '".self::TYPE_OPTION_CONTACT."', '".self::TYPE_OPTION_TASK."', '".self::TYPE_OPTION_NOTE."', '".self::TYPE_OPTION_HIERARCHY."' of '".self::TYPE_OPTION_GAB."' (for KOE) or a folder id.\n\n" . "\t[-t] type\tthe following types are available: '".self::TYPE_OPTION_EMAIL."', '".self::TYPE_OPTION_CALENDAR."', '".self::TYPE_OPTION_CONTACT."', '".self::TYPE_OPTION_TASK."', '".self::TYPE_OPTION_NOTE."', '".self::TYPE_OPTION_HIERARCHY."' of '".self::TYPE_OPTION_GAB."' (for KOE) or a folder id.\n" .
"\t[--days-old] n\tshow or remove profiles older than n days with lastsync or remove. n must be a positive integer.\n\n".
"Actions:\n" . "Actions:\n" .
"\tlist\t\t\t\t\t Lists all devices and synchronized users\n" . "\tlist\t\t\t\t\t Lists all devices and synchronized users\n" .
"\tlist -u USER\t\t\t\t Lists all devices of user USER\n" . "\tlist -u USER\t\t\t\t Lists all devices of user USER\n" .
...@@ -150,7 +152,7 @@ class ZPushAdminCLI { ...@@ -150,7 +152,7 @@ class ZPushAdminCLI {
if (self::$errormessage) if (self::$errormessage)
return; return;
$options = getopt("u:d:a:t:"); $options = getopt("u:d:a:t:", array('user:', 'device:', 'action:', 'type:', 'days-old:', 'days-ago:'));
// get 'user' // get 'user'
if (isset($options['u']) && !empty($options['u'])) if (isset($options['u']) && !empty($options['u']))
...@@ -177,6 +179,19 @@ class ZPushAdminCLI { ...@@ -177,6 +179,19 @@ class ZPushAdminCLI {
elseif (isset($options['type']) && !empty($options['type'])) elseif (isset($options['type']) && !empty($options['type']))
self::$type = strtolower(trim($options['type'])); self::$type = strtolower(trim($options['type']));
if (isset($options['days-ago']) && !empty($options['days-ago'])) {
$options['days-old'] = $options['days-ago'];
}
if (isset($options['days-old']) && !empty($options['days-old'])) {
if (!is_numeric($options['days-old']) || $options['days-old'] < 0) {
self::$errormessage = "--days-old parameter must be a positive integer\n";
self::$command = null;
return;
}
self::$daysold = trim($options['days-old']);
}
// if type is set, it must be one of known types or a 44 or 48 byte long folder id // if type is set, it must be one of known types or a 44 or 48 byte long folder id
if (self::$type !== false) { if (self::$type !== false) {
if (self::$type !== self::TYPE_OPTION_EMAIL && if (self::$type !== self::TYPE_OPTION_EMAIL &&
...@@ -438,17 +453,22 @@ class ZPushAdminCLI { ...@@ -438,17 +453,22 @@ class ZPushAdminCLI {
echo "\tno devices found\n"; echo "\tno devices found\n";
else { else {
echo "All known devices and users and their last synchronization time\n\n"; echo "All known devices and users and their last synchronization time\n\n";
echo str_pad("Device id", 36) . str_pad("Synchronized user", 31) . str_pad("Last sync time", 20) . "Short Ids\n"; echo str_pad("Device id", 36) . str_pad("Synchronized user", 31) . str_pad("Last sync time", 33) . "Short Ids\n";
echo "-----------------------------------------------------------------------------------------------------\n"; echo "------------------------------------------------------------------------------------------------------------------\n";
} }
$now = time();
foreach ($devicelist as $deviceId) { foreach ($devicelist as $deviceId) {
$users = ZPushAdmin::ListUsers($deviceId); $users = ZPushAdmin::ListUsers($deviceId);
foreach ($users as $user) { foreach ($users as $user) {
$device = ZPushAdmin::GetDeviceDetails($deviceId, $user); $device = ZPushAdmin::GetDeviceDetails($deviceId, $user);
$lastsync = $device->GetLastSyncTime() ? strftime("%Y-%m-%d %H:%M", $device->GetLastSyncTime()) : "never"; $daysOld = floor(($now - $device->GetLastSyncTime()) / 86400);
if (self::$daysold > $daysOld) {
continue;
}
$lastsync = $device->GetLastSyncTime() ? strftime("%Y-%m-%d %H:%M", $device->GetLastSyncTime()) . ' (' . str_pad($daysOld, 3, ' ', STR_PAD_LEFT) . ' days ago)' : "never";
$hasShortFolderIds = $device->HasFolderIdMapping() ? "Yes":"No"; $hasShortFolderIds = $device->HasFolderIdMapping() ? "Yes":"No";
echo str_pad($deviceId, 36) . str_pad($user, 30) . " " . str_pad($lastsync, 20) . $hasShortFolderIds . "\n"; echo str_pad($deviceId, 36) . str_pad($user, 30) . " " . str_pad($lastsync, 33) . $hasShortFolderIds . "\n";
} }
} }
} }
...@@ -505,20 +525,24 @@ class ZPushAdminCLI { ...@@ -505,20 +525,24 @@ class ZPushAdminCLI {
} }
/** /**
* Command "Remove device" * Command "Remove device".
* Remove a device of that user from the device list * Removes a device of that user from the device list.
* *
* @return * @return
* @access public * @access public
*/ */
static public function CommandRemoveDevice() { static public function CommandRemoveDevice() {
$stat = ZPushAdmin::RemoveDevice(self::$user, self::$device); $stat = ZPushAdmin::RemoveDevice(self::$user, self::$device, self::$daysold, time());
if (self::$user === false) if (self::$user === false)
echo sprintf("State data of device '%s' removed: %s", self::$device, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; echo sprintf("State data of device '%s' removed: %s", self::$device, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n";
elseif (self::$device === false) elseif (self::$device === false)
echo sprintf("State data of all devices of user '%s' removed: %s", self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; echo sprintf("State data of all devices of user '%s' removed: %s", self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n";
else else
echo sprintf("State data of device '%s' of user '%s' removed: %s", self::$device, self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n"; echo sprintf("State data of device '%s' of user '%s' removed: %s", self::$device, self::$user, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n";
if (ZPushAdmin::$status == ZPushAdmin::STATUS_DEVICE_SYNCED_AFTER_DAYSOLD) {
print("Some devices might not have been removed because of --days-old parameter. Check Z-Push log file for more details.\n");
}
} }
/** /**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment