Commit d4418b8f authored by Manfred Kutas's avatar Manfred Kutas

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

parents 210380ec afec4917
......@@ -170,7 +170,21 @@ USE_FULLEMAIL_FOR_LOGIN If this is set to "true", AutoDiscover will attempt to
login on the collaboration server with the full email
address sent by the client. If disabled (default), the
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.
LOGFILE The default AutoDiscover log file.
......
......@@ -190,11 +190,25 @@ class ZPushAutodiscover {
// the local part only.
if (USE_FULLEMAIL_FOR_LOGIN) {
$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 {
$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.
......
......@@ -45,6 +45,28 @@
*/
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
* Possible LOGLEVEL and LOGUSERLEVEL values are:
......
......@@ -190,7 +190,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider {
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the new message"));
$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);
ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendIMAP->SendMail(): We get the From and To"));
......@@ -485,7 +485,7 @@ class BackendIMAP extends BackendDiff implements ISearchProvider {
}
$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)) {
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 {
}
$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);
......@@ -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
$mobj = new Mail_mimeDecode($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);
$body_part = null;
......
......@@ -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_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 */
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));
......
......@@ -2533,32 +2533,37 @@ class MAPIProvider {
* @return boolean
*/
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();
$stream = mapi_inetmapi_imtoinet($this->session, $addrbook, $mapimessage, array('use_tnef' => -1));
if (is_resource($stream)) {
$mstreamstat = mapi_stream_stat($stream);
$streamsize = $mstreamstat["cb"];
if (isset($streamsize)) {
if (Request::GetProtocolVersion() >= 12.0) {
if (!isset($message->asbody))
$message->asbody = new SyncBaseBody();
$message->asbody->data = MAPIStreamWrapper::Open($stream);
$message->asbody->estimatedDataSize = $streamsize;
$message->asbody->truncated = 0;
}
else {
$message->mimedata = MAPIStreamWrapper::Open($stream);
$message->mimesize = $streamsize;
$message->mimetruncated = 0;
}
unset($message->body, $message->bodytruncated);
return true;
}
if (is_resource($stream)) {
$mstreamstat = mapi_stream_stat($stream);
$streamsize = $mstreamstat["cb"];
if (isset($streamsize)) {
if (Request::GetProtocolVersion() >= 12.0) {
if (!isset($message->asbody))
$message->asbody = new SyncBaseBody();
$message->asbody->data = MAPIStreamWrapper::Open($stream);
$message->asbody->estimatedDataSize = $streamsize;
$message->asbody->truncated = 0;
}
else {
$message->mimedata = MAPIStreamWrapper::Open($stream);
$message->mimesize = $streamsize;
$message->mimetruncated = 0;
}
unset($message->body, $message->bodytruncated);
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;
}
......
......@@ -116,7 +116,7 @@ class BackendMaildir extends BackendDiff {
// Parse e-mail
$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->data = StringStreamWrapper::Open($message->parts[$part]->body);
......@@ -333,7 +333,7 @@ class BackendMaildir extends BackendDiff {
// Parse e-mail
$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);
......
......@@ -52,7 +52,7 @@
* @author Sean Coates <sean@php.net>
* @copyright 2003-2006 PEAR <pear-group@php.net>
* @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
*/
......@@ -64,7 +64,7 @@
* implemented automated decoding of strings from mail charset
*
* 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
* (isStatic of decode() around line 215)
......@@ -154,8 +154,8 @@ class Mail_mimeDecode
/**
* Flag to determine whether to decode headers
*
* @var boolean
* (set to UTF8 to iconv convert headers)
* @var mixed
* @access private
*/
var $_decode_headers;
......@@ -190,6 +190,11 @@ class Mail_mimeDecode
$this->_include_bodies = true;
$this->_rfc822_bodies = false;
}
// BC
function Mail_mimeDecode($input)
{
$this->__construct($input);
}
/**
* Begins the decoding process. If called statically
......@@ -202,7 +207,8 @@ class Mail_mimeDecode
* object.
* decode_bodies - Whether to decode the bodies
* 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
* as the input
......@@ -216,7 +222,7 @@ class Mail_mimeDecode
$isStatic = empty($this) || !is_a($this, __CLASS__);
// Have we been called statically?
// If so, create an object and pass details to that.
// If so, create an object and pass details to that.
if ($isStatic AND isset($params['input'])) {
$obj = new Mail_mimeDecode($params['input']);
......@@ -229,19 +235,21 @@ class Mail_mimeDecode
// Called via an object
} else {
$this->_include_bodies = isset($params['include_bodies']) ?
$params['include_bodies'] : false;
$params['include_bodies'] : false;
$this->_decode_bodies = isset($params['decode_bodies']) ?
$params['decode_bodies'] : false;
$params['decode_bodies'] : false;
$this->_decode_headers = isset($params['decode_headers']) ?
$params['decode_headers'] : false;
$params['decode_headers'] : false;
$this->_rfc822_bodies = isset($params['rfc_822bodies']) ?
$params['rfc_822bodies'] : false;
$params['rfc_822bodies'] : false;
$this->_charset = isset($params['charset']) ?
strtolower($params['charset']) : 'utf-8';
if (is_string($this->_decode_headers) && !function_exists('iconv')) {
$this->raiseError('header decode conversion requested, however iconv is missing');
if (is_string($this->_decode_headers)) {
if (!function_exists('iconv')) {
$this->raiseError('header decode conversion requested, however iconv is missing');
}
$this->_decode_headers = strtolower($this->_decode_headers);
}
$structure = $this->_decode($this->_header, $this->_body);
......@@ -339,10 +347,18 @@ class Mail_mimeDecode
break;
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/parallel':
case 'multipart/appledouble': // Appledouble mail
case 'multipart/report': // RFC1892
case 'multipart/signed': // PGP
case 'multipart/digest':
case 'multipart/alternative':
case 'multipart/related':
......@@ -384,8 +400,8 @@ class Mail_mimeDecode
$return->disposition = 'inline';
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)
//and then falls through for the rest on purpose.
// #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.
case 'application/x-pkcs7-mime':
case 'application/pkcs7-mime':
if (!isset($content_transfer_encoding['value'])) {
......@@ -781,7 +797,7 @@ class Mail_mimeDecode
* @return string Decoded header value
* @access private
*/
function _decodeHeader($input)
function _decodeHeader($input, $default_charset=false)
{
if (!$this->_decode_headers) {
return $input;
......@@ -789,19 +805,14 @@ class Mail_mimeDecode
// Remove white space between encoded-words
$input = preg_replace('/(=\?[^?]+\?(q|b)\?[^?]*\?=)(\s)+=\?/i', '\1=?', $input);
$encodedwords = false;
$charset = '';
// For each encoded-word...
while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)/i', $input, $matches)) {
$encodedwords = true;
$encoded = $matches[1];
$charset = $matches[2];
$encoding = $matches[3];
$text = $matches[4];
$encoded = $matches[1];
$charset = strtolower($matches[2]);
$encoding = strtolower($matches[3]);
$text = $matches[4];
switch (strtolower($encoding)) {
switch ($encoding) {
case 'b':
$text = base64_decode($text);
break;
......@@ -813,13 +824,16 @@ class Mail_mimeDecode
$text = str_replace('=' . $value, chr(hexdec($value)), $text);
break;
}
$text = $this->_autoconvert_encoding($text, $charset);
if (is_string($this->_decode_headers) && $charset != $this->_decode_headers) {
$conv = @iconv($charset, $this->_decode_headers, $text);
$text = ($conv === false) ? $text : $conv;
}
$input = str_replace($encoded, $text, $input);
}
if (!$encodedwords) {
$input = $this->_autoconvert_encoding($input, $charset);
if ($default_charset && is_string($this->_decode_headers) && $charset != $this->_decode_headers) {
$conv = @iconv($charset, $this->_decode_headers, $input); // TODO: shouldn't this be $default_charset ?
$input = ($conv === false) ? $input : $conv;
}
return $input;
......@@ -836,7 +850,7 @@ class Mail_mimeDecode
* @return string Decoded body
* @access private
*/
function _decodeBody($input, $encoding = '7bit', $charset = '', $detectCharset = true)
function _decodeBody($input, $encoding = '7bit', $charset = '', $detectCharset = true)
{
switch (strtolower($encoding)) {
case 'quoted-printable':
......@@ -846,67 +860,13 @@ class Mail_mimeDecode
case 'base64':
$input = base64_decode($input);
break;
case '7bit':
case '8bit':
default:
break;
}
return $detectCharset ? $this->_autoconvert_encoding($input, $charset) : $input;
}
/**
* Error handler dummy for _autoconvert_encoding
*
* @param integer $errno
* @param string $errstr
* @return boolean true
* @access public static
*/
static function _iconv_notice_handler($errno, $errstr) {
return true;
}
/**
* 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;
}
if ($detectCharset && strtolower($charset) != $this->_charset) {
$conv = @iconv($charset, $this->_charset, $input);
$input = ($conv === false) ? $input : $conv;
}
return $input_converted;
return $input;
}
/**
......@@ -1008,7 +968,7 @@ class Mail_mimeDecode
/**
* 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 message subtype
......@@ -1019,8 +979,9 @@ class Mail_mimeDecode
* @access public
*/
static function getBodyRecursive($message, $subtype, &$body, $replace_nr = false) {
if(!isset($message->ctype_primary)) return;
if(strcasecmp($message->ctype_primary, "text") == 0 && strcasecmp($message->ctype_secondary, $subtype) == 0 && isset($message->body)) {
// TODO: move this function into general utils
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) {
$body .= str_replace("\n", "\r\n", str_replace("\r", "", $message->body));
}
......@@ -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) {
// 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"
// 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);
}
}
......@@ -1215,4 +1176,4 @@ class Mail_mimeDecode
return false;
}
} // End of class
} // End of class
\ No newline at end of file
......@@ -1053,3 +1053,8 @@ define("NOTEIVERB_FORWARD", 104);
define("AS_REPLYTOSENDER", 1);
define("AS_REPLYTOALL", 2);
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 {
* //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.
* If no user is given, all devices are listed
......@@ -219,11 +224,13 @@ class ZPushAdmin {
*
* @param string $user (opt) user of the device
* @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
* @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)
return false;
......@@ -232,7 +239,7 @@ class ZPushAdmin {
$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));
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));
return false;
}
......@@ -243,7 +250,7 @@ class ZPushAdmin {
$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)));
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));
return false;
}
......@@ -260,6 +267,21 @@ class ZPushAdmin {
$device->SetData($devicedata, false);
if (!isset($devicedata->devices))
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;
}
catch (StateNotFoundException $e) {
......
......@@ -92,6 +92,7 @@ class ZPushAdminCLI {
static private $device = false;
static private $type = false;
static private $errormessage;
static private $daysold = false;
/**
* Returns usage instructions
......@@ -102,7 +103,8 @@ class ZPushAdminCLI {
static public function UsageInstructions() {
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" .
"\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" .
"\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" .
......@@ -150,7 +152,7 @@ class ZPushAdminCLI {
if (self::$errormessage)
return;
$options = getopt("u:d:a:t:");
$options = getopt("u:d:a:t:", array('user:', 'device:', 'action:', 'type:', 'days-old:', 'days-ago:'));
// get 'user'
if (isset($options['u']) && !empty($options['u']))
......@@ -177,6 +179,19 @@ class ZPushAdminCLI {
elseif (isset($options['type']) && !empty($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 (self::$type !== false) {
if (self::$type !== self::TYPE_OPTION_EMAIL &&
......@@ -438,17 +453,22 @@ class ZPushAdminCLI {
echo "\tno devices found\n";
else {
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 "-----------------------------------------------------------------------------------------------------\n";
echo str_pad("Device id", 36) . str_pad("Synchronized user", 31) . str_pad("Last sync time", 33) . "Short Ids\n";
echo "------------------------------------------------------------------------------------------------------------------\n";
}
$now = time();
foreach ($devicelist as $deviceId) {
$users = ZPushAdmin::ListUsers($deviceId);
foreach ($users as $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";
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 {
}
/**
* Command "Remove device"
* Remove a device of that user from the device list
* Command "Remove device".
* Removes a device of that user from the device list.
*
* @return
* @access public
*/
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)
echo sprintf("State data of device '%s' removed: %s", self::$device, ($stat)?'OK':ZLog::GetLastMessage(LOGLEVEL_ERROR)). "\n";
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";
else
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