From 5858523c531fd07841099e602b5222ab916d1ed3 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 8 Jun 2016 11:59:11 +0200 Subject: [PATCH 01/13] z-push 2.3 requires all SyncBaseBody->data to be streams, using StringStreamWrapper::Open and stream_get_contents to convert from and to strings --- mail/inc/class.mail_zpush.inc.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/mail/inc/class.mail_zpush.inc.php b/mail/inc/class.mail_zpush.inc.php index f196a8c4fa..833d5fd8d6 100644 --- a/mail/inc/class.mail_zpush.inc.php +++ b/mail/inc/class.mail_zpush.inc.php @@ -939,9 +939,12 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__." bodypreference 4 requested"); $output->asbody->type = SYNC_BODYPREFERENCE_MIME;//4; // use Api\Mailer::convert to convert charset of all text parts to utf-8, which is a z-push or AS requirement! + // ToDo: check if above is true for mime-message, otherwise with could use a stream without conversion $Body = Api\Mailer::convert($this->mail->getMessageRawBody($id, '', $_folderName)); if ($this->debugLevel>2) ZLog::Write(LOGLEVEL_DEBUG, __METHOD__.__LINE__." Setting Mailobjectcontent to output:".$Body); - $output->asbody->data = $Body; + if ((string)$Body === '') $Body = ' '; + $output->asbody->data = StringStreamWrapper::Open($Body); + $output->asbody->estimatedDataSize = strlen($Body); } else if ($bpReturnType==2) //SYNC_BODYPREFERENCE_HTML { @@ -975,7 +978,8 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, } // output->nativebodytype is used as marker that the original message was of type ... but is now converted to, as type 2 is requested. $output->nativebodytype = 2; - $output->asbody->data = $htmlbody; + $output->asbody->data = StringStreamWrapper::Open($htmlbody); + $output->asbody->estimatedDataSize = strlen($htmlbody); } else { @@ -996,16 +1000,16 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, $plainBody = Utils::Utf8_truncate($plainBody, $truncsize); $output->asbody->truncated = 1; } - $output->asbody->data = $plainBody; + $output->asbody->data = StringStreamWrapper::Open((string)$plainBody !== '' ? $plainBody : ' '); + $output->asbody->estimatedDataSize = strlen($plainBody); } // In case we have nothing for the body, send at least a blank... // dw2412 but only in case the body is not rtf! - if ($output->asbody->type != 3 && (!isset($output->asbody->data) || strlen($output->asbody->data) == 0)) + if ($output->asbody->type != 3 && !isset($output->asbody->data)) { - $output->asbody->data = " "; + $output->asbody->data = StringStreamWrapper::Open(" "); + $output->asbody->estimatedDataSize = 1; } - // determine estimated datasize for all the above cases ... - $output->asbody->estimatedDataSize = strlen($output->asbody->data); } // end AS12 Stuff ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' gather Header info:'.$headers['SUBJECT'].' from:'.$headers['DATE']); From 3f9fd761bb9720fec7bb158e4ca9ded9ab22f25b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 8 Jun 2016 12:53:05 +0200 Subject: [PATCH 02/13] revert last commit to fix PHP Fatal error: Declaration of calendar_zpush::ChangeMessage($folderid, $_id, SyncAppointment $message, $contentParameters) must be compatible with activesync_plugin_write::ChangeMessage($folderid, $id, $message, $contentParameters) --- calendar/inc/class.calendar_zpush.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/calendar/inc/class.calendar_zpush.inc.php b/calendar/inc/class.calendar_zpush.inc.php index b126502299..0a6997c631 100644 --- a/calendar/inc/class.calendar_zpush.inc.php +++ b/calendar/inc/class.calendar_zpush.inc.php @@ -522,7 +522,7 @@ class calendar_zpush implements activesync_plugin_write, activesync_plugin_meeti * Note that this function will never be called on E-mail items as you can't change e-mail items, you * can only set them as 'read'. */ - public function ChangeMessage($folderid, $_id, SyncAppointment $message, $contentParameters) + public function ChangeMessage($folderid, $_id, $message, $contentParameters) { unset($contentParameters); // unused, but required by function signature From ab02df94d45feeb622572c1b98cfcd9aba9c6a28 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 9 Jun 2016 20:40:31 +0200 Subject: [PATCH 03/13] extract PDO code from Sqlfs streamwrapper into class Api\Db\Pdo, to be able to use it for z-push SqlStateMaschine --- api/src/Db/Pdo.php | 159 ++++++++++++++++++++++++++++ api/src/Vfs/Sqlfs/StreamWrapper.php | 118 +-------------------- 2 files changed, 160 insertions(+), 117 deletions(-) create mode 100644 api/src/Db/Pdo.php diff --git a/api/src/Db/Pdo.php b/api/src/Db/Pdo.php new file mode 100644 index 0000000000..6c3f40776c --- /dev/null +++ b/api/src/Db/Pdo.php @@ -0,0 +1,159 @@ + + * @copyright (c) 2008-16 by Ralf Becker + * @version $Id$ + */ + +namespace EGroupware\Api\Db; + +/** + * PDO database connection + */ +class Pdo +{ + /** + * Reference to the PDO object we use + * + * @var \PDO + */ + static protected $pdo; + + /** + * PDO database type: mysql, pgsl + * + * @var string + */ + public static $pdo_type; + + /** + * Case sensitive comparison operator, for mysql we use ' COLLATE utf8_bin =' + * + * @var string + */ + public static $case_sensitive_equal = '='; + + /** + * Get active PDO connection + * + * @return \PDO + * @throws \PDOException when opening PDO connection fails + * @throws Exception when opening regular db-connection fails + */ + static public function connection() + { + if (!isset(self::$pdo)) + { + self::reconnect(); + } + return self::$pdo; + } + + /** + * Reconnect to database + */ + static public function reconnect() + { + self::$pdo = self::_pdo(); + } + + /** + * Create pdo object / connection, as long as pdo is not generally used in eGW + * + * @return \PDO + */ + static protected function _pdo() + { + $egw_db = isset($GLOBALS['egw_setup']) ? $GLOBALS['egw_setup']->db : $GLOBALS['egw']->db; + + switch($egw_db->Type) + { + case 'mysqli': + case 'mysqlt': + case 'mysql': + self::$case_sensitive_equal = '= BINARY '; + self::$pdo_type = 'mysql'; + break; + default: + self::$pdo_type = $egw_db->Type; + break; + } + // get host used be egw_db + $egw_db->connect(); + $host = $egw_db->get_host(); + + $dsn = self::$pdo_type.':dbname='.$egw_db->Database.($host ? ';host='.$host.($egw_db->Port ? ';port='.$egw_db->Port : '') : ''); + // check once if pdo extension and DB specific driver is loaded or can be loaded + static $pdo_available=null; + if (is_null($pdo_available)) + { + foreach(array('pdo','pdo_'.self::$pdo_type) as $ext) + { + check_load_extension($ext,true); // true = throw Exception + } + $pdo_available = true; + } + // set client charset of the connection + switch(self::$pdo_type) + { + case 'mysql': + $dsn .= ';charset=utf8'; + break; + case 'pgsql': + $query = "SET NAMES 'utf-8'"; + break; + } + try { + self::$pdo = new \PDO($dsn,$egw_db->User,$egw_db->Password,array( + \PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION, + )); + } + catch(\PDOException $e) + { + unset($e); + // Exception reveals password, so we ignore the exception and connect again without pw, to get the right exception without pw + self::$pdo = new \PDO($dsn,$egw_db->User,'$egw_db->Password'); + } + if ($query) + { + self::$pdo->exec($query); + } + return self::$pdo; + } + + /** + * Just a little abstration 'til I know how to organise stuff like that with PDO + * + * @param mixed $time + * @return string Y-m-d H:i:s + */ + static public function _pdo_timestamp($time) + { + if (is_numeric($time)) + { + $time = date('Y-m-d H:i:s',$time); + } + return $time; + } + + /** + * Just a little abstration 'til I know how to organise stuff like that with PDO + * + * @param boolean $val + * @return string '1' or '0' for mysql, 'true' or 'false' for everyone else + */ + static public function _pdo_boolean($val) + { + if (self::$pdo_type == 'mysql') + { + return $val ? '1' : '0'; + } + return $val ? 'true' : 'false'; + } +} diff --git a/api/src/Vfs/Sqlfs/StreamWrapper.php b/api/src/Vfs/Sqlfs/StreamWrapper.php index afafdf95d5..eddd233c84 100644 --- a/api/src/Vfs/Sqlfs/StreamWrapper.php +++ b/api/src/Vfs/Sqlfs/StreamWrapper.php @@ -34,7 +34,7 @@ use EGroupware\Api; * * @link http://www.php.net/manual/en/function.stream-wrapper-register.php */ -class StreamWrapper implements Vfs\StreamWrapperIface +class StreamWrapper extends Api\Db\Pdo implements Vfs\StreamWrapperIface { /** * Mime type of directories, the old vfs uses 'Directory', while eg. WebDAV uses 'httpd/unix-directory' @@ -141,12 +141,6 @@ class StreamWrapper implements Vfs\StreamWrapperIface * @var array $path => info-array pairs */ static protected $stat_cache = array(); - /** - * Reference to the PDO object we use - * - * @var \PDO - */ - static protected $pdo; /** * Array with filenames of dir opened with dir_opendir * @@ -1624,116 +1618,6 @@ class StreamWrapper implements Vfs\StreamWrapperIface return $stat; } - public static $pdo_type; - /** - * Case sensitive comparison operator, for mysql we use ' COLLATE utf8_bin =' - * - * @var string - */ - public static $case_sensitive_equal = '='; - - /** - * Reconnect to database - */ - static public function reconnect() - { - self::$pdo = self::_pdo(); - } - - /** - * Create pdo object / connection, as long as pdo is not generally used in eGW - * - * @return \PDO - */ - static protected function _pdo() - { - $egw_db = isset($GLOBALS['egw_setup']) ? $GLOBALS['egw_setup']->db : $GLOBALS['egw']->db; - - switch($egw_db->Type) - { - case 'mysqli': - case 'mysqlt': - case 'mysql': - self::$case_sensitive_equal = '= BINARY '; - self::$pdo_type = 'mysql'; - break; - default: - self::$pdo_type = $egw_db->Type; - break; - } - // get host used be egw_db - $egw_db->connect(); - $host = $egw_db->get_host(); - - $dsn = self::$pdo_type.':dbname='.$egw_db->Database.($host ? ';host='.$host.($egw_db->Port ? ';port='.$egw_db->Port : '') : ''); - // check once if pdo extension and DB specific driver is loaded or can be loaded - static $pdo_available=null; - if (is_null($pdo_available)) - { - foreach(array('pdo','pdo_'.self::$pdo_type) as $ext) - { - check_load_extension($ext,true); // true = throw Exception - } - $pdo_available = true; - } - // set client charset of the connection - switch(self::$pdo_type) - { - case 'mysql': - $dsn .= ';charset=utf8'; - break; - case 'pgsql': - $query = "SET NAMES 'utf-8'"; - break; - } - try { - self::$pdo = new \PDO($dsn,$egw_db->User,$egw_db->Password,array( - \PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION, - )); - } - catch(\PDOException $e) - { - unset($e); - // Exception reveals password, so we ignore the exception and connect again without pw, to get the right exception without pw - self::$pdo = new \PDO($dsn,$egw_db->User,'$egw_db->Password'); - } - if ($query) - { - self::$pdo->exec($query); - } - return self::$pdo; - } - - /** - * Just a little abstration 'til I know how to organise stuff like that with PDO - * - * @param mixed $time - * @return string Y-m-d H:i:s - */ - static protected function _pdo_timestamp($time) - { - if (is_numeric($time)) - { - $time = date('Y-m-d H:i:s',$time); - } - return $time; - } - - /** - * Just a little abstration 'til I know how to organise stuff like that with PDO - * - * @param boolean $val - * @return string '1' or '0' for mysql, 'true' or 'false' for everyone else - */ - static protected function _pdo_boolean($val) - { - if (self::$pdo_type == 'mysql') - { - return $val ? '1' : '0'; - } - return $val ? 'true' : 'false'; - } - /** * Maximum value for a single hash element (should be 10^N): 10, 100 (default), 1000, ... * From 17e8473e6e7fa8a294ad7ba3fd64cb1a4d723f31 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 10 Jun 2016 08:31:09 +0200 Subject: [PATCH 04/13] store folder-hashes via state-maschine in db --- mail/inc/class.mail_zpush.inc.php | 71 ++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/mail/inc/class.mail_zpush.inc.php b/mail/inc/class.mail_zpush.inc.php index 833d5fd8d6..1539f0df8b 100644 --- a/mail/inc/class.mail_zpush.inc.php +++ b/mail/inc/class.mail_zpush.inc.php @@ -1970,37 +1970,78 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, private $folderHashes; + /** + * Statemaschine instance used to store folders + * + * @var activesync_statemaschine + */ + private $fh_state_maschine; + + /** + * state_type (and _key) used to store folder hashes + */ + const FOLDER_STATE_TYPE = 'folder_hashes'; + /** * Read hashfile from state dir */ private function readFolderHashes() { - if ((file_exists($file = $this->hashFile()) || file_exists($file = $this->hashFile(true))) && - ($hashes = file_get_contents($file))) + if (!isset($this->fh_state_maschine)) { - $this->folderHashes = json_decode($hashes,true); - // fallback in case hashes have been serialized instead of being json-encoded - if (json_last_error()!=JSON_ERROR_NONE) - { - //error_log(__METHOD__.__LINE__." error decoding with json"); - $this->folderHashes = unserialize($hashes); - } + $this->fh_state_maschine = new activesync_statemachine($this->backend); } - else - { - $this->folderHashes = array(); + try { + $this->folderHashes = $this->fh_state_maschine->getState(Request::GetDeviceID(), + self::FOLDER_STATE_TYPE, self::FOLDER_STATE_TYPE, 0); + } + catch (Exception $e) { + _egw_log_exception($e); + if ((file_exists($file = $this->hashFile()) || file_exists($file = $this->hashFile(true))) && + ($hashes = file_get_contents($file))) + { + $this->folderHashes = json_decode($hashes,true); + // fallback in case hashes have been serialized instead of being json-encoded + if (json_last_error()!=JSON_ERROR_NONE) + { + //error_log(__METHOD__.__LINE__." error decoding with json"); + $this->folderHashes = unserialize($hashes); + } + // store folder-hashes to state + try { + $this->storeFolderHashes(); + } + catch(Exception $e) { + + } + } + else + { + $this->folderHashes = array(); + } } } /** - * Store hashfile in state dir + * Store hashfile via state-maschine * * return int|boolean false on error */ private function storeFolderHashes() { - // make sure $this->folderHashes is an array otherwise json_encode may fail on decode for string,integer,float or boolean - return file_put_contents($this->hashFile(), json_encode((is_array($this->folderHashes)?$this->folderHashes:array($this->folderHashes)))); + if (!isset($this->fh_state_maschine)) + { + $this->fh_state_maschine = new activesync_statemachine($this->backend); + } + try { + $this->fh_state_maschine->setState($this->folderHashes, Request::GetDeviceID(), + self::FOLDER_STATE_TYPE, self::FOLDER_STATE_TYPE, 0); + } + catch (Exception $e) { + _egw_log_exception($e); + return false; + } + return true; } /** From c193325f4c07597b52ed87bac87a53de55a3e95c Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 10 Jun 2016 12:01:10 +0200 Subject: [PATCH 05/13] remove call to no longer existing method Api\Mailer::EncodeHeader --- mail/inc/class.mail_zpush.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mail/inc/class.mail_zpush.inc.php b/mail/inc/class.mail_zpush.inc.php index 1539f0df8b..616f8a1435 100644 --- a/mail/inc/class.mail_zpush.inc.php +++ b/mail/inc/class.mail_zpush.inc.php @@ -369,7 +369,7 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, // Horde SMTP Class uses utf-8 by default. as we set charset always to utf-8 $mailObject->Sender = $activeMailProfile['ident_email']; $mailObject->From = $activeMailProfile['ident_email']; - $mailObject->FromName = $mailObject->EncodeHeader(Mail::generateIdentityString($activeMailProfile,false)); + $mailObject->FromName = Mail::generateIdentityString($activeMailProfile,false); $mailObject->AddCustomHeader('X-Mailer: mail-Activesync'); @@ -665,7 +665,7 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, if ($this->debugLevel>0) ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' Key:'.$key.'->'.array2string($attachment)); $attachmentNames .= $attachment['name']."\n"; $attachmentData = $this->mail->getAttachment($uid, $attachment['partID'],0,false,false,$folder); - /*$x =*/ $mailObject->AddStringAttachment($attachmentData['attachment'], $mailObject->EncodeHeader($attachment['name']), $attachment['mimeType']); + /*$x =*/ $mailObject->AddStringAttachment($attachmentData['attachment'], $attachment['name'], $attachment['mimeType']); ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.__LINE__.' added part with number:'.$x); } } From 0f7ca734e385d0e2c080390fc22b3b6aaef79e80 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 10 Jun 2016 12:11:56 +0200 Subject: [PATCH 06/13] remove call to no longer existing method Api\Mailer::AddCustomHeader --- mail/inc/class.mail_zpush.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail/inc/class.mail_zpush.inc.php b/mail/inc/class.mail_zpush.inc.php index 616f8a1435..c49dcb6322 100644 --- a/mail/inc/class.mail_zpush.inc.php +++ b/mail/inc/class.mail_zpush.inc.php @@ -370,7 +370,7 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, $mailObject->Sender = $activeMailProfile['ident_email']; $mailObject->From = $activeMailProfile['ident_email']; $mailObject->FromName = Mail::generateIdentityString($activeMailProfile,false); - $mailObject->AddCustomHeader('X-Mailer: mail-Activesync'); + $mailObject->addHeader('X-Mailer', 'mail-Activesync'); // prepare addressee list; moved the adding of addresses to the mailobject down From 61b99bc32edaa9cadd7bb2813b8bad8274499ef9 Mon Sep 17 00:00:00 2001 From: leithoff Date: Fri, 10 Jun 2016 15:06:29 +0200 Subject: [PATCH 07/13] handle info_des correctly for android, prevent destroying info_des on IOS-devices --- infolog/inc/class.infolog_zpush.inc.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/infolog/inc/class.infolog_zpush.inc.php b/infolog/inc/class.infolog_zpush.inc.php index 4cc7ceb77a..1b2558eaa5 100644 --- a/infolog/inc/class.infolog_zpush.inc.php +++ b/infolog/inc/class.infolog_zpush.inc.php @@ -410,7 +410,13 @@ class infolog_zpush implements activesync_plugin_write switch ($attr) { case 'info_des': - $infolog[$attr] = $this->backend->messagenote2note($message->body, $message->rtf, $message->airsyncbasebody); + if (is_null($message->asbody)) $message->asbody = new SyncBaseBody(); + // only change info_des, if one given, as iOS5 skips description in ChangeMessage + // --> we ignore empty / not set description, so description get no longer lost, but you cant empty it via eSync + if (($description = $this->backend->messagenote2note($message->body, $message->rtf, $message->asbody))) + { + $infolog[$attr] = $description; + } break; case 'info_cat': From 4363e7289a60ef93289e3b2c2236720c60c4450e Mon Sep 17 00:00:00 2001 From: leithoff Date: Tue, 14 Jun 2016 16:41:50 +0200 Subject: [PATCH 08/13] use one function to get the data reqired on ItemOperationsGetAttachmentData AND/ot GetAttachmentData calls --- mail/inc/class.mail_zpush.inc.php | 66 +++++++++++++++++++------------ 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/mail/inc/class.mail_zpush.inc.php b/mail/inc/class.mail_zpush.inc.php index c49dcb6322..85e2f8cf56 100644 --- a/mail/inc/class.mail_zpush.inc.php +++ b/mail/inc/class.mail_zpush.inc.php @@ -172,6 +172,22 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, 'default' => 'sendifnocalnotif', 'admin' => False, ); +/* + $settings['mail-useSignature'] = array( + 'type' => 'select', + 'label' => 'control if and which available signature is added to outgoing mails', + 'name' => 'mail-useSignature', + 'help' => 'control the use of signatures', + 'values' => array( + 'sendifnocalnotif'=>'only send if there is no notification in calendar', + 'send'=>'yes, always add EGroupware signatures to outgoing mails', + 'nosend'=>'no, never add EGroupware signatures to outgoing mails', + ), + 'xmlrpc' => True, + 'default' => 'nosend', + 'admin' => False, + ); +*/ return $settings; } @@ -1190,29 +1206,11 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, * * @param string $fid - id * @param string $attname - should contain (folder)id - * @return true, prints the content of the attachment + * @return SyncItemOperationsAttachment-object */ function GetAttachmentData($fid,$attname) { - ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')".function_backtrace()); - //error_log(__METHOD__.__LINE__." Fid: $fid (attname: '$attname')"); - list($folderid, $id, $part) = explode(":", $attname); - - $this->splitID($folderid, $account, $folder); - - if (!isset($this->mail)) $this->mail = Mail::getInstance(false,self::$profileID,true,false,true); - - $this->mail->reopen($folder); - $attachment = $this->mail->getAttachment($id,$part,0,false,true,$folder); - $SIOattachment = new SyncItemOperationsAttachment(); - fseek($attachment['attachment'], 0, SEEK_SET); // z-push requires stream seeked to start - $SIOattachment->data = $attachment['attachment']; - //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname') Data:".$attachment['attachment']); - if (isset($attachment['type']) ) - $SIOattachment->contenttype = $attachment['type']; - - unset($attachment); - - return $SIOattachment; + ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')"); + return $this->_GetAttachmentData($fid,$attname); } /** @@ -1226,6 +1224,21 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, * @return SyncItemOperationsAttachment-object */ function ItemOperationsGetAttachmentData($fid,$attname) { + ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')"); + return $this->_GetAttachmentData($fid,$attname); + } + + /** + * _GetAttachmentData implements + * -ItemOperationsGetAttachmentData + * -GetAttachmentData + * + * @param string $fid - id + * @param string $attname - should contain (folder)id + * @return SyncItemOperationsAttachment-object + */ + private function _GetAttachmentData($fid,$attname) + { ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname')".function_backtrace()); //error_log(__METHOD__.__LINE__." Fid: $fid (attname: '$attname')"); list($folderid, $id, $part) = explode(":", $attname); @@ -1236,15 +1249,16 @@ class mail_zpush implements activesync_plugin_write, activesync_plugin_sendmail, $this->mail->reopen($folder); $attachment = $this->mail->getAttachment($id,$part,0,false,true,$folder); - $SIOattachment = new SyncItemOperationsAttachment(); + $SIOattachment = new SyncItemOperationsAttachment(); fseek($attachment['attachment'], 0, SEEK_SET); // z-push requires stream seeked to start - $SIOattachment->data = $attachment['attachment']; - if (isset($attachment['type']) ) - $SIOattachment->contenttype = $attachment['type']; + $SIOattachment->data = $attachment['attachment']; + //ZLog::Write(LOGLEVEL_DEBUG,__METHOD__.": $fid (attname: '$attname') Data:".$attachment['attachment']); + if (isset($attachment['type']) ) + $SIOattachment->contenttype = $attachment['type']; unset($attachment); - return $SIOattachment; + return $SIOattachment; } /** From 19d35063e41857fade4d628fd4a8265f7729c65c Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 14 Jun 2016 19:05:04 +0200 Subject: [PATCH 09/13] tell jQuery to remove $, as EGroupware does NOT use it and it conflicts with eg. mootools --- api/js/jsapi/egw.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/js/jsapi/egw.js b/api/js/jsapi/egw.js index cbb5f9927d..6cb0a64094 100644 --- a/api/js/jsapi/egw.js +++ b/api/js/jsapi/egw.js @@ -154,6 +154,9 @@ window.egw_LAB.script(include).wait(function() { + // tell jQuery to remove $, as EGroupware does NOT use it and it conflicts with eg. mootools + jQuery.noConflict(); + // call egw.link_handler, if attr specified var egw_redirect = egw_script.getAttribute('data-egw-redirect'); if (egw_redirect) From a12f6755abf5ca104bcf20cbecee1cb5515e49ec Mon Sep 17 00:00:00 2001 From: leithoff Date: Thu, 16 Jun 2016 12:00:07 +0200 Subject: [PATCH 10/13] handle fatal error Using $this when not in object context --- api/src/Mail.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/Mail.php b/api/src/Mail.php index 24e9a7d9e8..391b74b79e 100644 --- a/api/src/Mail.php +++ b/api/src/Mail.php @@ -282,7 +282,7 @@ class Mail } else { - throw new Exception(__METHOD__." failed to load the Profile for ProfileID for $_profileID / ".$this->profileID." with error:".$e->getMessage().($e->details?', '.$e->details:'')); + throw new Exception(__METHOD__." failed to load the Profile for ProfileID for $_profileID with error:".$e->getMessage().($e->details?', '.$e->details:'')); } } self::storeActiveProfileIDToPref(self::$instances[$_profileID]->icServer, $_profileID, $_validate ); From be27c65e98e60b5ef33a14c51d163e087d345ab1 Mon Sep 17 00:00:00 2001 From: leithoff Date: Thu, 16 Jun 2016 16:06:53 +0200 Subject: [PATCH 11/13] remove the pre-initialization of asbody for the messagenote2note call. its handeled in / with messagenote2note now --- infolog/inc/class.infolog_zpush.inc.php | 1 - 1 file changed, 1 deletion(-) diff --git a/infolog/inc/class.infolog_zpush.inc.php b/infolog/inc/class.infolog_zpush.inc.php index 1b2558eaa5..279f9d6795 100644 --- a/infolog/inc/class.infolog_zpush.inc.php +++ b/infolog/inc/class.infolog_zpush.inc.php @@ -410,7 +410,6 @@ class infolog_zpush implements activesync_plugin_write switch ($attr) { case 'info_des': - if (is_null($message->asbody)) $message->asbody = new SyncBaseBody(); // only change info_des, if one given, as iOS5 skips description in ChangeMessage // --> we ignore empty / not set description, so description get no longer lost, but you cant empty it via eSync if (($description = $this->backend->messagenote2note($message->body, $message->rtf, $message->asbody))) From e600eede2a1e77734800fe01199e0046c76d6d50 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 16 Jun 2016 18:14:02 +0200 Subject: [PATCH 12/13] fix iOS calendar crashed if event-organizer had no email address --- api/src/Accounts.php | 8 +++++++- calendar/inc/class.calendar_zpush.inc.php | 7 ++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/api/src/Accounts.php b/api/src/Accounts.php index 158ec81c27..c290345021 100644 --- a/api/src/Accounts.php +++ b/api/src/Accounts.php @@ -536,6 +536,7 @@ class Accounts $underscore = '_'; } if (!$domain) $domain = $GLOBALS['egw_info']['server']['mail_suffix']; + if (!$domain) $domain = $_SERVER['SERVER_NAME']; $email = str_replace(array('first','last','initial','account','dot','underscore','-'), array($first,$last,substr($first,0,1),$account,$dot,$underscore,''), @@ -736,9 +737,10 @@ class Accounts * * @param int|string $account_id numeric account_id or account_lid * @param string $which ='account_lid' type to convert to: account_lid (default), account_email, ... + * @param boolean $generate_email =false true: generate an email address, if user has none * @return string|boolean converted value or false on error ($account_id not found) */ - static function id2name($account_id, $which='account_lid') + static function id2name($account_id, $which='account_lid', $generate_email=false) { if (!is_numeric($account_id) && !($account_id = self::getInstance()->name2id($account_id))) { @@ -751,6 +753,10 @@ class Accounts unset($e); return false; } + if ($generate_email && $which === 'account_email' && empty($data[$which])) + { + return self::email($data['account_firstname'], $data['account_lastname'], $data['account_lid']); + } return $data[$which]; } diff --git a/calendar/inc/class.calendar_zpush.inc.php b/calendar/inc/class.calendar_zpush.inc.php index 0a6997c631..23c95c8e9e 100644 --- a/calendar/inc/class.calendar_zpush.inc.php +++ b/calendar/inc/class.calendar_zpush.inc.php @@ -1081,7 +1081,8 @@ class calendar_zpush implements activesync_plugin_write, activesync_plugin_meeti $message->md5body = md5($event['description']); $message->organizername = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_fullname'); - $message->organizeremail = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_email'); + // at least iOS calendar crashes, if organizer has no email address (true = generate an email, if user has none) + $message->organizeremail = $GLOBALS['egw']->accounts->id2name($event['owner'], 'account_email', true); $message->sensitivity = $event['public'] ? 0 : 2; // 0=normal, 1=personal, 2=private, 3=confidential @@ -1102,7 +1103,7 @@ class calendar_zpush implements activesync_plugin_write, activesync_plugin_meeti if (is_numeric($uid)) { $attendee->name = $GLOBALS['egw']->accounts->id2name($uid,'account_fullname'); - $attendee->email = $GLOBALS['egw']->accounts->id2name($uid,'account_email'); + $attendee->email = $GLOBALS['egw']->accounts->id2name($uid, 'account_email', true); } else { @@ -1113,7 +1114,7 @@ class calendar_zpush implements activesync_plugin_write, activesync_plugin_meeti if (!$info['email'] && $info['responsible']) { - $info['email'] = $GLOBALS['egw']->accounts->id2name($info['responsible'],'account_email'); + $info['email'] = $GLOBALS['egw']->accounts->id2name($info['responsible'], 'account_email', true); } $attendee->name = empty($info['cn']) ? $info['name'] : $info['cn']; $attendee->email = $info['email']; From c07b3b26e4c0721f503b7f4a2a27345a7c11c109 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 17 Jun 2016 08:42:49 +0200 Subject: [PATCH 13/13] remove old (from Andreas z-push fork) and no longer used md5body attribute --- calendar/inc/class.calendar_zpush.inc.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/calendar/inc/class.calendar_zpush.inc.php b/calendar/inc/class.calendar_zpush.inc.php index 23c95c8e9e..5c5c655a56 100644 --- a/calendar/inc/class.calendar_zpush.inc.php +++ b/calendar/inc/class.calendar_zpush.inc.php @@ -1078,8 +1078,6 @@ class calendar_zpush implements activesync_plugin_write, activesync_plugin_meeti $this->backend->note2messagenote($event['description'], $bodypreference, $message->asbody); } } - $message->md5body = md5($event['description']); - $message->organizername = $GLOBALS['egw']->accounts->id2name($event['owner'],'account_fullname'); // at least iOS calendar crashes, if organizer has no email address (true = generate an email, if user has none) $message->organizeremail = $GLOBALS['egw']->accounts->id2name($event['owner'], 'account_email', true);