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);
......
This diff is collapsed.
......@@ -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