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
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,10 +2533,16 @@ 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"];
......@@ -2557,8 +2563,7 @@ class MAPIProvider {
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
......@@ -239,10 +245,12 @@ class Mail_mimeDecode
$this->_charset = isset($params['charset']) ?
strtolower($params['charset']) : 'utf-8';
if (is_string($this->_decode_headers) && !function_exists('iconv')) {
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);
if ($structure === false) {
......@@ -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':
......@@ -385,7 +401,7 @@ class Mail_mimeDecode
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.
// 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];
$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;
......@@ -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;
if ($detectCharset && strtolower($charset) != $this->_charset) {
$conv = @iconv($charset, $this->_charset, $input);
$input = ($conv === false) ? $input : $conv;
}
/**
* 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;
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);
}
}
......
......@@ -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