Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2513231
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
53 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/ext/Syncroton/Command/Search.php b/lib/ext/Syncroton/Command/Search.php
index 2c6d956..871d5be 100644
--- a/lib/ext/Syncroton/Command/Search.php
+++ b/lib/ext/Syncroton/Command/Search.php
@@ -1,68 +1,82 @@
<?php
/**
* Syncroton
*
* @package Syncroton
* @subpackage Command
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Search command
*
* @package Syncroton
* @subpackage Command
*/
class Syncroton_Command_Search extends Syncroton_Command_Wbxml
-{
+{
const STATUS_SUCCESS = 1;
const STATUS_SERVER_ERROR = 3;
-
+
protected $_defaultNameSpace = 'uri:Search';
protected $_documentElement = 'Search';
-
+
/**
* store data
- *
- * @var array
+ *
+ * @var Syncroton_Model_StoreRequest
*/
- protected $_store = array();
-
+ protected $_store;
+
/**
* parse search command request
*
*/
public function handle()
{
$xml = simplexml_import_dom($this->_requestBody);
-
- $this->_store = array(
- 'name' => (string) $xml->Store->Name
- );
-
- if ($this->_logger instanceof Zend_Log)
- $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " stores: " . print_r($this->_store, true));
+
+ $this->_store = new Syncroton_Model_StoreRequest($xml->Store);
+
+ if ($this->_logger instanceof Zend_Log)
+ $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " stores: " . print_r($this->_store, true));
}
-
+
/**
* generate search command response
- *
+ *
*/
public function getResponse()
{
+ $storeResponse = new Syncroton_Model_StoreResponse(array(
+ 'status' => self::STATUS_SUCCESS,
+ ));
+/*
+ if ($this->_store->name == Syncroton_Data_Factory::STORE_GAL || $this->_store->name == Syncroton_Data_Factory::STORE_EMAIL) {
+ $dataController = Syncroton_Data_Factory::factory($this->_store->name, $this->_device);
+
+ try {
+ $results = $dataController->search($this->_store->query, $this->_store->options);
+
+ foreach ($results as $res) {
+ $storeResponse->result[] = $res;
+ }
+ }
+ catch (Exception $e) {
+ $storeResponse->status = self::STATUS_SERVER_ERROR;
+ }
+ }
+*/
$search = $this->_outputDom->documentElement;
+
$search->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', self::STATUS_SUCCESS));
-
+
$response = $search->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Response'));
- $store = $response->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Store'));
- $store->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', self::STATUS_SUCCESS));
- $store->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Total', 0));
-
- $result = $store->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Result', 0));
-
+ $storeResponse->appendXML($response);
+
return $this->_outputDom;
}
}
diff --git a/lib/ext/Syncroton/Data/Factory.php b/lib/ext/Syncroton/Data/Factory.php
index d7297b3..fcfc8d6 100644
--- a/lib/ext/Syncroton/Data/Factory.php
+++ b/lib/ext/Syncroton/Data/Factory.php
@@ -1,69 +1,70 @@
<?php
/**
* Syncroton
*
* @package Model
* @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
* @copyright Copyright (c) 2009-2012 Metaways Infosystems GmbH (http://www.metaways.de)
* @author Lars Kneschke <l.kneschke@metaways.de>
*/
/**
* class to handle ActiveSync Sync command
*
* @package Model
*/
class Syncroton_Data_Factory
{
const CLASS_CALENDAR = 'Calendar';
const CLASS_CONTACTS = 'Contacts';
const CLASS_EMAIL = 'Email';
const CLASS_TASKS = 'Tasks';
const STORE_EMAIL = 'Mailbox';
+ const STORE_GAL = 'GAL';
protected static $_classMap = array();
/**
* @param unknown_type $_class
* @param Syncroton_Model_IDevice $_device
* @param DateTime $_timeStamp
* @throws InvalidArgumentException
* @return Syncroton_Data_IData
*/
public static function factory($_classFactory, Syncroton_Model_IDevice $_device, DateTime $_timeStamp)
{
switch($_classFactory) {
case self::CLASS_CALENDAR:
$className = Syncroton_Registry::get(Syncroton_Registry::CALENDAR_DATA_CLASS);
break;
case self::CLASS_CONTACTS:
$className = Syncroton_Registry::get(Syncroton_Registry::CONTACTS_DATA_CLASS);
break;
case self::STORE_EMAIL:
case self::CLASS_EMAIL:
$className = Syncroton_Registry::get(Syncroton_Registry::EMAIL_DATA_CLASS);
break;
case self::CLASS_TASKS:
$className = Syncroton_Registry::get(Syncroton_Registry::TASKS_DATA_CLASS);
break;
default:
throw new InvalidArgumentException('invalid class type provided');
breeak;
}
$class = new $className($_device, $_timeStamp);
if (! $class instanceof Syncroton_Data_IData) {
throw new RuntimeException('class must be instanceof Syncroton_Data_IData');
}
return $class;
}
}
diff --git a/lib/ext/Syncroton/Model/StoreRequest.php b/lib/ext/Syncroton/Model/StoreRequest.php
new file mode 100644
index 0000000..c65bceb
--- /dev/null
+++ b/lib/ext/Syncroton/Model/StoreRequest.php
@@ -0,0 +1,242 @@
+<?php
+/**
+ * Syncroton
+ *
+ * @package Model
+ * @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
+ * @copyright Copyright (c) 2012-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright Copyright (c) 2012 Kolab Systems AG (http://kolabsys.com)
+ * @author Lars Kneschke <l.kneschke@metaways.de>
+ * @author Aleksander Machniak <machniak@kolabsys.com>
+ */
+
+/**
+ * class to handle ActiveSync Search Store request
+ *
+ * @package Model
+ * @property string name
+ * @property array options
+ * @property array query
+ */
+class Syncroton_Model_StoreRequest
+{
+ protected $_store = array();
+
+ protected $_xmlStore;
+
+ public function __construct($properties = null)
+ {
+ if ($properties instanceof SimpleXMLElement) {
+ $this->setFromSimpleXMLElement($properties);
+ } elseif (is_array($properties)) {
+ $this->setFromArray($properties);
+ }
+ }
+
+ public function setFromArray(array $properties)
+ {
+ $this->_store = array(
+ 'options' => array(
+// 'filterType' => Syncroton_Command_Sync::FILTER_NOTHING,
+ 'mimeSupport' => Syncroton_Command_Sync::MIMESUPPORT_DONT_SEND_MIME,
+ 'mimeTruncation' => Syncroton_Command_Sync::TRUNCATE_NOTHING,
+ 'bodyPreferences' => array()
+ ),
+ );
+
+ foreach ($properties as $key => $value) {
+ try {
+ $this->$key = $value; //echo __LINE__ . PHP_EOL;
+ } catch (InvalidArgumentException $iae) {
+ //ignore invalid properties
+ //echo __LINE__ . PHP_EOL;
+ }
+ }
+ }
+
+ /**
+ *
+ * @param SimpleXMLElement $xmlStore
+ * @throws InvalidArgumentException
+ */
+ public function setFromSimpleXMLElement(SimpleXMLElement $xmlStore)
+ {
+ if ($xmlStore->getName() !== 'Store') {
+ throw new InvalidArgumentException('Unexpected element name: ' . $xmlStore->getName());
+ }
+
+ $this->_xmlStore = $xmlStore;
+
+ $this->_store = array(
+ 'name' => (string) $xmlStore->Name,
+ 'options' => array(
+// 'filterType' => Syncroton_Command_Sync::FILTER_NOTHING,
+ 'mimeSupport' => Syncroton_Command_Sync::MIMESUPPORT_DONT_SEND_MIME,
+ 'mimeTruncation' => Syncroton_Command_Sync::TRUNCATE_NOTHING,
+ 'bodyPreferences' => array(),
+ ),
+ );
+
+ // Process Query
+ if ($this->_store['name'] == 'GAL') {
+ // @FIXME: In GAL search request Query is a string:
+ // <Store><Name>GAL</Name><Query>string</Query><Options><Range>0-11</Range></Options></Store>
+ if (isset($xmlStore->Query)) {
+ $this->_store['query'] = (string) $xmlStore->Query;
+ }
+ }
+ else if (isset($xmlStore->Query)) {
+ if (isset($xmlStore->Query->And)) {
+ if (isset($xmlStore->Query->And->FreeText)) {
+ $this->_store['query']['and']['freetext'] = (string) $xmlStore->Query->And->FreeText;
+ }
+ if (isset($xmlStore->Query->And->ConversationId)) {
+ $this->_store['query']['and']['conversationId'] = (string) $xmlStore->Query->And->ConversationId;
+ }
+ if (isset($xmlStore->Query->And->GreaterThan)) {
+ if (isset($xmlStore->Query->And->GreaterThan->Value)) {
+ $this->_store['query']['and']['greaterThan']['value'] = (string) $xmlStore->Query->And->GreaterThan->Value;
+ }
+
+ $email = $xmlStore->Query->And->GreaterThan->children('uri:Email');
+ if (isset($email->DateReceived)) {
+ $this->_store['query']['and']['greaterThan']['dateReceived'] = new DateTime((string) $email->DateReceived, new DateTimeZone('UTC'));
+ }
+ }
+ if (isset($xmlStore->Query->And->LessThan)) {
+ if (isset($xmlStore->Query->And->LessThan->Value)) {
+ $this->_store['query']['and']['lessThan']['value'] = (string) $xmlStore->Query->And->LessThan->Value;
+ }
+
+ $email = $xmlStore->Query->And->LessThan->children('uri:Email');
+ if (isset($email->DateReceived)) {
+ $this->_store['query']['and']['leasThan']['dateReceived'] = new DateTime((string) $email->DateReceived, new DateTimeZone('UTC'));
+ }
+ }
+
+ $airSync = $xmlStore->Query->children('uri:AirSync');
+
+ if (isset($airSync->Class)) {
+ $this->_store['query']['and']['class'] = (string) $airSync->Class;
+ }
+ if (isset($airSync->CollectionId)) {
+ $this->_store['query']['and']['collectionId'] = (string) $airSync->CollectionId;
+ }
+ }
+
+ if (isset($xmlStore->Query->EqualTo)) {
+ if (isset($xmlStore->Query->EqualTo->Value)) {
+ $this->_store['query']['equalTo']['value'] = (string) $xmlStore->Query->EqualTo->Value;
+ }
+
+ $doclib = $xmlStore->Query->EqualTo->children('uri:DocumentLibrary');
+ if (isset($doclib->LinkId)) {
+ $this->_store['query']['equalTo']['linkId'] = (string) $doclib->LinkId;
+ }
+ }
+ }
+
+ // Process options
+ if (isset($xmlStore->Options)) {
+ // optional parameters
+ if (isset($xmlStore->Options->DeepTraversal)) {
+ $this->_store['options']['deepTraversal'] = true;
+ }
+
+ if (isset($xmlStore->Options->RebuildResults)) {
+ $this->_store['options']['rebuildResult'] = true;
+ }
+
+ if (isset($xmlStore->Options->UserName)) {
+ $this->_store['options']['userName'] = (string) $xmlStore->Options->UserName;
+ }
+
+ if (isset($xmlStore->Options->Password)) {
+ $this->_store['options']['password'] = (string) $xmlStore->Options->Password;
+ }
+
+ if (isset($xmlStore->Options->Picture)) {
+ if (isset($xmlStore->Options->Picture->MaxSize)) {
+ $this->_store['options']['picture']['maxSize'] = (int) $xmlStore->Options->Picture->MaxSize;
+ }
+ if (isset($xmlStore->Options->Picture->MaxPctures)) {
+ $this->_store['options']['picture']['maxPictures'] = (int) $xmlStore->Options->Picture->MaxPictures;
+ }
+ }
+
+ if (!empty($xmlStore->Options->Range)) {
+ $this->_store['options']['range'] = (string) $xmlStore->Options->Range;
+ }
+ else {
+ switch ($this->_store['name']) {
+ case 'DocumentLibrary':
+ case 'Document Library': //?
+ '0-999';
+ break;
+ case 'Mailbox':
+ case 'GAL':
+ default:
+ '0-99';
+ break;
+ }
+ }
+
+ $this->_store['options']['range'] = explode('-', $this->_store['options']['range']);
+
+ if (isset($xmlStore->Options->MIMESupport)) {
+ $this->_store['options']['mimeSupport'] = (int) $xmlStore->Options->MIMESupport;
+ }
+ if (isset($xmlStore->Options->MIMETruncation)) {
+ $this->_store['options']['mimeTruncation'] = (int)$xmlStore->Options->MIMETruncation;
+ }
+ if (isset($xmlStore->Options->Class)) {
+ $this->_store['options']['class'] = (string)$xmlStore->Options->Class;
+ }
+
+ // try to fetch element from AirSyncBase:BodyPreference
+ $airSyncBase = $xmlStore->Options->children('uri:AirSyncBase');
+
+ if (isset($airSyncBase->BodyPreference)) {
+ foreach ($airSyncBase->BodyPreference as $bodyPreference) {
+ $type = (int) $bodyPreference->Type;
+ $this->_store['options']['bodyPreferences'][$type] = array(
+ 'type' => $type
+ );
+
+ // optional
+ if (isset($bodyPreference->TruncationSize)) {
+ $this->_store['options']['bodyPreferences'][$type]['truncationSize'] = (int) $bodyPreference->TruncationSize;
+ }
+ }
+ }
+
+ if (isset($airSyncBase->BodyPartPreference)) {
+ // process BodyPartPreference elements
+ }
+ }
+ }
+
+ public function &__get($name)
+ {
+ if (array_key_exists($name, $this->_store)) {
+ return $this->_store[$name];
+ }
+ //echo $name . PHP_EOL;
+ return null;
+ }
+
+ public function __set($name, $value)
+ {
+ $this->_store[$name] = $value;
+ }
+
+ public function __isset($name)
+ {
+ return isset($this->_store[$name]);
+ }
+
+ public function __unset($name)
+ {
+ unset($this->_store[$name]);
+ }
+}
\ No newline at end of file
diff --git a/lib/ext/Syncroton/Model/StoreResponse.php b/lib/ext/Syncroton/Model/StoreResponse.php
new file mode 100644
index 0000000..b0e5f48
--- /dev/null
+++ b/lib/ext/Syncroton/Model/StoreResponse.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * Syncroton
+ *
+ * @package Model
+ * @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3
+ * @copyright Copyright (c) 2012-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author Lars Kneschke <l.kneschke@metaways.de>
+ */
+
+/**
+ * class to handle ActiveSync event
+ *
+ * @package Model
+ * @property int status
+ * @property array result
+ * @property array range
+ * @property int total
+ */
+
+class Syncroton_Model_StoreResponse extends Syncroton_Model_AEntry
+{
+ /**
+ * status constants
+ */
+ const STATUS_SUCCESS = 1;
+ const STATUS_INVALIDREQUEST = 2;
+ const STATUS_SERVERERROR = 3;
+ const STATUS_BADLINK = 4;
+ const STATUS_ACCESSDENIED = 5;
+ const STATUS_NOTFOUND = 6;
+ const STATUS_CONNECTIONFAILED = 7;
+ const STATUS_TOOCOMPLEX = 8;
+ const STATUS_TIMEDOUT = 10;
+ const STATUS_FOLDERSYNCREQUIRED = 11;
+ const STATUS_ENDOFRANGE = 12;
+ const STATUS_ACCESSBLOCKED = 13;
+ const STATUS_CREDENTIALSREQUIRED = 14;
+
+ protected $_xmlBaseElement = 'ApplicationData';
+
+ protected $_properties = array(
+ 'Search' => array(
+ 'Status' => array('type' => 'number'),
+ 'Result' => array('type' => 'container'),
+ 'Range' => array('type' => 'string'),
+ 'Total' => array('type' => 'number'),
+ )
+ );
+
+ public function appendXML(DOMElement $_domParrent)
+ {
+ $this->_addXMLNamespaces($_domParrent);
+
+ $this->_elements['Total'] = count($this->_elements['Result']);
+
+ foreach ($this->_elements as $elementName => $value) {
+ // skip empty values
+ if ($value === null || $value === '') {
+ continue;
+ }
+
+ list ($nameSpace, $elementProperties) = $this->_getElementProperties($elementName);
+
+ $nameSpace = 'uri:' . $nameSpace;
+
+ switch ($elementName) {
+ case 'Result':
+ $element = $_domParrent->ownerDocument->createElementNS($nameSpace, $elementName);
+
+ foreach ($value as $result) {
+ $resultElement = $_domParrent->ownerDocument->createElementNS($nameSpace, 'Result');
+ $result->appendXML($resultElement);
+ $element->appendChild($resultElement);
+ }
+
+ $_domParrent->appendChild($element);
+ break;
+
+ case 'Range':
+ if (is_array($value) && count($value) == 2) {
+ $value = implode('-', $value);
+ }
+
+ default:
+ $element = $_domParrent->ownerDocument->createElementNS($nameSpace, $elementName);
+ $element->appendChild($_domParrent->ownerDocument->createTextNode($value));
+ $_domParrent->appendChild($element);
+ }
+ }
+ }
+}
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index df457b2..4a9cac0 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -1,1032 +1,1036 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
/**
*
*/
class kolab_sync_data_email extends kolab_sync_data
{
/**
* Mapping from ActiveSync Email namespace fields
*/
protected $mapping = array(
'Cc' => 'cc',
//'ContentClass' => 'contentclass',
'DateReceived' => 'internaldate',
//'DisplayTo' => 'displayto', //?
//'Flag' => 'flag',
//'FlagType' => 'flagtype', // child of Flag element
'From' => 'from',
//'Importance' => 'importance',
'InternetCPID' => 'charset',
//'MessageClass' => 'messageclass',
'ReplyTo' => 'replyto',
//'Read' => 'read',
'Subject' => 'subject',
//'ThreadTopic' => 'threadtopic',
'To' => 'to',
);
/**
* Kolab object type
*
* @var string
*/
protected $modelName = 'mail';
/**
* Type of the default folder
*
* @var int
*/
protected $defaultFolderType = Syncroton_Command_FolderSync::FOLDERTYPE_INBOX;
/**
* Default container for new entries
*
* @var string
*/
protected $defaultFolder = 'INBOX';
/**
* Type of user created folders
*
* @var int
*/
protected $folderType = Syncroton_Command_FolderSync::FOLDERTYPE_MAIL_USER_CREATED;
/**
* Default namespace
*
* @var string
*/
protected $defaultNS = 'Email';
/**
* Field to sort search results by
*
* @var string
*/
protected $sortField = 'n_fileas';
/**
* the constructor
*
* @param Syncroton_Model_IDevice $device
* @param DateTime $syncTimeStamp
*/
public function __construct(Syncroton_Model_IDevice $device, DateTime $syncTimeStamp)
{
parent::__construct($device, $syncTimeStamp);
$this->storage = rcube::get_instance()->get_storage();
}
/**
* Creates model object
*
* @param Syncroton_Model_SyncCollection $collection Collection data
* @param string $serverId Local entry identifier
*
* @return Syncroton_Model_Email Email object
*/
public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId)
{
$message = $this->getObject($serverId);
if (empty($message)) {
// @TODO: exception
return;
}
$msg = $this->parseMessageId($serverId);
$headers = $message->headers; // rcube_message_header
// Calendar namespace fields
foreach ($this->mapping as $key => $name) {
$value = null;
switch ($name) {
case 'internaldate':
$value = self::date_from_kolab(rcube_imap_generic::strToTime($headers->internaldate));
break;
case 'cc':
case 'to':
case 'replyto':
case 'from':
$addresses = rcube_mime::decode_address_list($headers->$name, null, true, $headers->charset);
foreach ($addresses as $idx => $part) {
$name = $part['name'];
$mailto = $part['mailto'];
$string = $part['string'];
// @TODO: convert to utf8?
// @FIXME: set name + address or address only?
//rcube_utils::idn_to_utf8();
$addresses[$idx] = format_email_recipient($mailto, $name);
}
$value = implode(',', $addresses);
break;
case 'subject':
$value = $headers->get('subject');
break;
case 'charset':
$value = self::charset_to_cp($headers->charset);
break;
}
if (empty($value) || is_array($value)) {
continue;
}
$result[$key] = $value;
}
// $result['ConversationId'] = 'FF68022058BD485996BE15F6F6D99320';
// $result['ConversationIndex'] = 'CA2CFA8A23';
// Read flag
$result['Read'] = intval(!empty($headers->flags['SEEN']));
// Flagged message
if (!empty($headers->flags['FLAGGED'])) {
// Use FollowUp flag which is used in Android when message is marked with a star
/*
@TODO:
$result['Flag'] = array(
'FlagType' => 'FollowUp',
'Status' => 1,
);
*/
}
// Importance/Priority
if ($headers->priority) {
if ($headers->priority < 3) {
$result['importance'] = 2; // High
}
else if ($headers->priority > 3) {
$result['Importance'] = 0; // Low
}
}
// get truncation
$truncateAt = null;
$opts = $collection->options;
$prefs = $opts['bodyPreferences'];
if ($opts['mimeSupport'] == 2 && ($this->asversion <= 12 || !empty($prefs[4]))) {
if (!empty($prefs[4]) && !empty($prefs[4]['truncationSize'])) {
$truncateAt = $prefs[4]['truncationSize'];
}
$airSyncBaseType = 4;
}
else if (!empty($prefs[2])) {
if (!empty($prefs[2]['truncationSize'])) {
$truncateAt = $prefs[2]['truncationSize'];
}
$airSyncBaseType = 2;
}
else {
+ // @TODO: In Sync examples there's one in which bodyPreferences is not defined
+ // in such case Truncated=1 and there's no body sent to the client
+ // only it's estimated size
+
if (!empty($prefs[1]) && !empty($prefs[1]['truncationSize'])) {
$truncateAt = $prefs[1]['truncationSize'];
}
$airSyncBaseType = 1;
}
if (isset($opts['mimeTruncation']) && $opts['mimeTruncation'] < 8) {
switch ($opts['mimeTruncation']) {
case Syncroton_Command_Sync::TRUNCATE_ALL:
$truncateAt = 0;
break;
case Syncroton_Command_Sync::TRUNCATE_4096:
$truncateAt = 4096;
break;
case Syncroton_Command_Sync::TRUNCATE_5120:
$truncateAt = 5120;
break;
case Syncroton_Command_Sync::TRUNCATE_7168:
$truncateAt = 7168;
break;
case Syncroton_Command_Sync::TRUNCATE_10240:
$truncateAt = 10240;
break;
case Syncroton_Command_Sync::TRUNCATE_20480:
$truncateAt = 20480;
break;
case Syncroton_Command_Sync::TRUNCATE_51200:
$truncateAt = 51200;
break;
case Syncroton_Command_Sync::TRUNCATE_102400:
$truncateAt = 102400;
break;
}
}
// Message body
if ($airSyncBaseType == 4) {
$messageBody = $this->storage->get_raw_body($message->uid);
/*
if ($this->asversion < 12) {
// if the email contains non 7bit ascii characters we can't transfer
// them via MIMEData xml and we need to fall back to plain text
if (preg_match('/[^\x00-\x7F]/', $messageBody)) {
$airSyncBaseType = 1;
$messageBody = $this->getMessageBody($message);
}
}
*/
}
else {
$messageBody = $this->getMessageBody($message, $airSyncBaseType == 2);
}
// strip out any non utf-8 characters
$messageBody = rcube_charset::clean($messageBody);
$real_length = $body_length = strlen($messageBody);
if ($truncateAt !== null && $body_length > $truncateAt) {
$messageBody = substr($messageBody, 0, $truncateAt);
$body_length = $truncateAt;
$isTruncacted = 1;
}
else {
$isTruncacted = 0;
}
if ($body_length > 0) {
$body_params = array(
'Type' => $airSyncBaseType,
'Truncated' => $isTruncacted,
);
if ($isTruncated) {
$body_params['EstimatedDataSize'] = $real_length;
}
$result['Body'] = $this->setBody($messageBody, $body_params);
/*
if ($this->asversion >= 12) {
$body = $domParrent->appendChild(new DOMElement('Body', null, 'uri:AirSyncBase'));
$body->appendChild(new DOMElement('Type', $airSyncBaseType, 'uri:AirSyncBase'));
$body->appendChild(new DOMElement('Truncated', $isTruncacted, 'uri:AirSyncBase'));
$body->appendChild(new DOMElement('EstimatedDataSize', $data->size, 'uri:AirSyncBase'));
$dataTag = $body->appendChild(new DOMElement('Data', null, 'uri:AirSyncBase'));
$dataTag->appendChild(new DOMText($messageBody));
}
else {
if ($airSyncBaseType == 4) {
$domParrent->appendChild(new DOMElement('MIMETruncated', $isTruncacted, 'uri:Email'));
$body = $domParrent->appendChild(new DOMElement('MIMEData', null, 'uri:Email'));
$body->appendChild(new DOMText($messageBody));
}
else {
$domParrent->appendChild(new DOMElement('BodyTruncated', $isTruncacted, 'uri:Email'));
$body = $domParrent->appendChild(new DOMElement('Body', null, 'uri:Email'));
$body->appendChild(new DOMText($messageBody));
}
}
*/
}
$result['NativeBodyType'] = intval($message->has_html_part(false));
// Message class
$result['MessageClass'] = 'IPM.Note' . ($airSyncBaseType == 4 ? '.SMIME' : '');
$result['ContentClass'] = 'urn:content-classes:message';
// attachments
$attachments = array_merge($message->attachments, $message->inline_parts);
if (!empty($attachments)) {
$result['Attachments'] = array();
foreach ($attachments as $attachment) {
$att = array();
$filename = $attachment->filename;
if (empty($filename) && $attachment->mimetype == 'text/html') {
$filename = 'HTML Part';
}
$att['DisplayName'] = $filename;
$att['FileReference'] = $serverId . '::' . $attachment->mime_id;
$att['Method'] = 1;
$att['EstimatedDataSize'] = $attachment->size;
if (!empty($attachment->content_id)) {
$att['ContentId'] = $attachment->content_id;
}
if (!empty($attachment->content_location)) {
$att['ContentLocation'] = $attachment->content_location;
}
if (in_array($attachment, $message->inline_parts)) {
$att['IsInline'] = 1;
}
$result['Attachments'][] = new Syncroton_Model_EmailAttachment($att);
}
}
// @TODO: flags
return new Syncroton_Model_Email($result);
}
/**
* convert contact from xml to libkolab array
*
* @param Syncroton_Model_IEntry $data Contact to convert
* @param string $folderid Folder identifier
* @param array $entry Existing entry
*
* @return array
*/
public function toKolab(Syncroton_Model_IEntry $data, $folderid, $entry = null)
{
// does nothing => you can't add emails via ActiveSync
}
/**
* Returns filter query array according to specified ActiveSync FilterType
*
* @param int $filter_type Filter type
*
* @param array Filter query
*/
protected function filter($filter_type = 0)
{
$filter = array();
switch ($filter_type) {
case Syncroton_Command_Sync::FILTER_1_DAY_BACK:
$mod = '-1 day';
break;
case Syncroton_Command_Sync::FILTER_3_DAYS_BACK:
$mod = '-3 days';
break;
case Syncroton_Command_Sync::FILTER_2_WEEKS_BACK:
$mod = '-2 weeks';
break;
case Syncroton_Command_Sync::FILTER_1_MONTH_BACK:
$mod = '-1 month';
break;
}
if (!empty($mod)) {
$dt = new DateTime('now', new DateTimeZone('UTC'));
$dt->modify($mod);
// RFC3501: IMAP SEARCH
$filter[] = 'SINCE ' . $dt->format('d-m-Y');
}
return $filter;
}
/**
* Returns default folder for current class type.
*/
protected function getDefaultFolder()
{
// There's always INBOX
$real_id = $this->backend->folder_id('INBOX', 'mail.inbox');
$folder_id = $this->defaultRootFolder;
return array(
'displayname' => 'Inbox',
'realid' => $real_id,
'folderid' => $folder_id,
'parentid' => 0,
'type' => $this->defaultFolderType,
);
}
public function moveItem($srcFolderId, $serverId, $dstFolderId)
{
$msg = $this->parseMessageId($serverId);
$dstname = $this->backend->folder_id2name($dstFolderId, $this->device->deviceid);
if ($dstname === null) {
return;
}
if (!$this->storage->move_message($msg['uid'], $dstname, $msg['foldername'])) {
return;
}
// Use COPYUID feature (RFC2359) to get the new UID of the copied message
$copyuid = $this->storage->conn->data['COPYUID'];
if (is_array($copyuid) && ($uid = $copyuid[1])) {
return $uid;
}
}
/**
* add entry from xml data
*
* @param string $folderId
* @param Syncroton_Model_IEntry $entry
*
* @return array
*/
public function createEntry($folderId, Syncroton_Model_IEntry $entry)
{
}
/**
* update existing entry
*
* @param string $folderId
* @param string $serverId
* @param Syncroton_Model_IEntry $entry
* @return array
*/
public function updateEntry($folderId, $serverId, Syncroton_Model_IEntry $entry)
{
$msg = $this->parseMessageId($serverId);
$message = $this->getObject($serverId);
if (isset($entry->Read)) {
// here we update only Read flag
$flag = (((int)$entry->Read != 1) ? 'UN' : '') . 'SEEN';
$this->storage->set_flag($msg['uid'], $flag, $msg['foldername']);
}
$is_flagged = !empty($message->headers->flags['FLAGGED']);
// @TODO: Flag change
if (empty($entry->Flag)) {
if ($is_flagged) {
$this->storage->set_flag($msg['uid'], 'UNFLAGGED', $msg['foldername']);
}
}
else if (!$is_flagged) {
/*
if ($entry->Flag->FlagType && preg_match('/^follow\s*up/i', $entry->Flag->FlagType)) {
$this->storage->set_flag($msg['uid'], 'FLAGGED', $msg['foldername']);
}
*/
}
}
/**
* delete entry
*
* @param string $folderId
* @param string $serverId
* @param Syncroton_Model_SyncCollection $collection
*/
public function deleteEntry($folderId, $serverId, $collection)
{
$trash = kolab_sync::get_instance()->config->get('trash_mbox');
$msg = $this->parseMessageId($serverId);
// move message to trash folder
if ($collection->deletesAsMoves
&& strlen($trash)
&& $trash != $msg['foldername']
&& $this->storage->folder_exists($trash)
) {
$this->storage->move_message($msg['uid'], $trash, $msg['foldername']);
}
// set delete flag
else {
$this->storage->set_flag($msg['uid'], 'DELETED', $msg['foldername']);
}
}
public function sendEmail($body, $saveInSent)
{
$sent = $this->backend->send_message($body, $smtp_error);
if ($sent && $saveInSent) {
$this->saveInSent($body);
}
}
public function forwardEmail($collectionId, $itemId, $body, $saveInSent)
{
/*
@TODO:
The SmartForward command can be applied to a meeting. When SmartForward is applied to a recurring meeting,
the InstanceId element (section 2.2.3.83.2) specifies the ID of a particular occurrence in the recurring meeting.
If SmartForward is applied to a recurring meeting and the InstanceId element is absent, the server SHOULD
forward the entire recurring meeting. If the value of the InstanceId element is invalid, the server responds
with Status element (section 2.2.3.162.15) value 104, as specified in section 2.2.4.
When the SmartForward command is used for an appointment, the original message is included by the server
as an attachment to the outgoing message. When the SmartForward command is used for a normal message
or a meeting, the behavior of the SmartForward command is the same as that of the SmartReply command (section 2.2.2.18).
*/
/*
@TODO:
The LongId element is an optional child element of the Source element in SmartForward command requests and
SmartReply command requests that specifies the long ID for the source message, which is returned in the Search
command response message (section 2.2.2.14.2). If the LongId element is present, the FolderId (section 2.2.3.69),
ItemId (section 2.2.3.84), and InstanceId (section 2.2.3.83.2) elements are not present.
The LongId element value can be up to 256 characters in length.
*/
$msg = $this->parseMessageId($itemId);
$message = $this->getObject($itemId);
// Parse message
list($headers, $body) = $this->backend->parse_mime($body, true);
// Get original message body
// @TODO: here we're assuming that reply message is in text/plain format
// So, original message will be converted to plain text if needed
// @TODO: what about forward-as-attachment?
$message_body = $this->getMessageBody($message, false);
$message_body = trim($message_body);
// Join bodies
$body = rtrim($body) . "\n\n" . ltrim($message_body);
// @TODO: add attachments from the original message
// Create complete message source
$body = $this->backend->build_mime($headers, $body);
// Send message
$sent = $this->backend->send_message($body, $smtp_error);
// Set FORWARDED flag on the replied message
if ($sent && empty($message->headers->flags['FORWARDED'])) {
$this->storage->set_flag($msg['uid'], 'FORWARDED', $msg['foldername']);
}
// Save sent message in Sent folder
if ($sent && $saveInSent) {
$this->saveInSent($body);
}
}
public function replyEmail($collectionId, $itemId, $body, $saveInSent)
{
$msg = $this->parseMessageId($itemId);
$message = $this->getObject($itemId);
// Parse message
// @TODO: messages with attachments
list($headers, $body) = $this->backend->parse_mime($body, true);
// Get original message body
// @TODO: here we're assuming that reply message is in text/plain format
// So, original message will be converted to plain text if needed
$message_body = $this->getMessageBody($message, false);
// Quote original message body
$message_body = self::wrap_and_quote(trim($message_body), 72);
// Join bodies
$body = rtrim($body) . "\n" . ltrim($message_body);
// Create complete message source
$body = $this->backend->build_mime($headers, $body);
// Send message
$sent = $this->backend->send_message($body, $smtp_error);
// Set ANSWERED flag on the replied message
if ($sent && empty($message->headers->flags['ANSWERED'])) {
$this->storage->set_flag($msg['uid'], 'ANSWERED', $msg['foldername']);
}
// Save sent message in Sent folder
if ($sent && $saveInSent) {
$this->saveInSent($body);
}
}
/**
* Append the message into Sent folder
*
* @param string $message Complete message source
*
* @return int|bool Appended message UID or True on success, False on error
*/
protected function saveInSent($message)
{
$sent_folder = kolab_sync::get_instance()->config->get('sent_mbox');
if (strlen($sent_folder) && $this->storage->folder_exists($sent_folder)) {
return $this->storage->save_message($sent_folder, $message);
}
return false;
}
/**
* Search for existing entries
*
* @param string $folderid
* @param array $filter
* @param int $result_type Type of the result (see RESULT_* constants)
*
* @return array|int Search result as count or array of uids/objects
*/
public function search($folderid, $filter = array(), $result_type = self::RESULT_UID)
{
if ($folderid == $this->defaultRootFolder) {
$folders = $this->backend->folders_list($this->device->deviceid, $this->modelName);
$folders = array_keys($folders);
}
else {
$folders = array($folderid);
}
$filter_str = 'ALL UNDELETED';
// convert filter into one IMAP search string
foreach ($filter as $idx => $filter_item) {
if (is_array($filter_item)) {
// @TODO
// convert 'changed' entries into IMAP search string
// for now we just return empty result
return $result_type == self::RESULT_COUNT ? 0 : array();
}
else {
$filter_str .= ' ' . $filter_item;
}
}
$result = $result_type == self::RESULT_COUNT ? 0 : array();
// no sorting for best performance
$sort_by = null;
foreach ($folders as $folderid) {
$foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
if ($foldername === null) {
continue;
}
// $this->storage->set_folder($foldername);
$this->storage->folder_sync($foldername);
$this->storage->search($foldername, $filter_str, RCMAIL_CHARSET, $sort_by);
$search = $this->storage->get_search_set();
$search = $search[1];
if (!($search instanceof rcube_result_index)) {
continue;
}
switch ($result_type) {
case self::RESULT_COUNT:
$result += (int) $search->count();
break;
case self::RESULT_UID:
if ($uids = $search->get()) {
foreach ($uids as $idx => $uid) {
$uids[$idx] = $this->createMessageId($folderid, $uid);
}
$result = array_merge($result, $uids);
}
break;
/*
case self::RESULT_OBJECT:
default:
if ($objects = $folder->select($filter)) {
$result = array_merge($result, $objects);
}
*/
}
}
return $result;
}
/**
* Fetches the entry from the backend
*/
protected function getObject($entryid, &$folder = null)
{
$message = $this->parseMessageId($entryid);
if (empty($message)) {
// @TODO: exception?
return null;
}
// set current folder
$this->storage->set_folder($message['foldername']);
// get message
$message = new rcube_message($message['uid']);
return $message;
}
/**
* @return Syncroton_Model_FileReference
*/
public function getFileReference($fileReference)
{
list($folderid, $uid, $part_id) = explode('::', $fileReference);
$message = $this->getObject($fileReference);
$part = $message->mime_parts[$part_id];
$body = $message->get_part_content($part_id);
$content_type = $part->mimetype;
return new Syncroton_Model_FileReference(array(
'ContentType' => $content_type,
'Data' => $body,
));
}
/**
* Parses entry ID to get folder name and UID of the message
*/
protected function parseMessageId($entryid)
{
list($folderid, $uid) = explode('::', $entryid);
$foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid);
if ($foldername === null || $foldername === false) {
// @TODO exception?
return null;
}
return array(
'uid' => $uid,
'folderid' => $folderid,
'foldername' => $foldername,
);
}
/**
* Creates entry ID of the message
*/
public function createMessageId($folderid, $uid)
{
return $folderid . '::' . $uid;
}
/**
* Returns body of the message in specified format
*/
protected function getMessageBody($message, $html = false)
{
if (!is_array($message->parts) && empty($message->body)) {
return '';
}
if (!empty($message->parts)) {
foreach ($message->parts as $part) {
// skip no-content and attachment parts (#1488557)
if ($part->type != 'content' || !$part->size || $message->is_attachment($part)) {
continue;
}
return $this->getMessagePartBody($message, $part, $html);
}
}
return $this->getMessagePartBody($message, $message, $html);
}
/**
* Returns body of the message part in specified format
*/
protected function getMessagePartBody($message, $part, $html = false)
{
// Check if we have enough memory to handle the message in it
// @FIXME: we need up to 5x more memory than the body
if (!rcube_utils::mem_check($part->size * 5)) {
return '';
}
if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset'])) {
$part->ctype_parameters['charset'] = $message->headers->charset;
}
// fetch part if not available
if (!isset($part->body)) {
$part->body = $message->get_part_content($part->mime_id);
}
// message is cached but not exists, or other error
if ($part->body === false) {
return '';
}
$body = $part->body;
if ($html) {
if ($part->ctype_secondary == 'html') {
}
else {
$body = '<pre>' . $body . '</pre>';
}
}
else {
if ($part->ctype_secondary == 'html') {
$txt = new html2text($body, false, true);
$body = $txt->get_text();
}
else {
if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') {
$body = rcube_mime::unfold_flowed($body);
}
}
}
return $body;
}
public static function charset_to_cp($charset)
{
// @TODO: ?????
// The body is converted to utf-8 in get_part_content(), what about headers?
return 65001; // UTF-8
$aliases = array(
'asmo708' => 708,
'shiftjis' => 932,
'gb2312' => 936,
'ksc56011987' => 949,
'big5' => 950,
'utf16' => 1200,
'utf16le' => 1200,
'unicodefffe' => 1201,
'utf16be' => 1201,
'johab' => 1361,
'macintosh' => 10000,
'macjapanese' => 10001,
'macchinesetrad' => 10002,
'mackorean' => 10003,
'macarabic' => 10004,
'machebrew' => 10005,
'macgreek' => 10006,
'maccyrillic' => 10007,
'macchinesesimp' => 10008,
'macromanian' => 10010,
'macukrainian' => 10017,
'macthai' => 10021,
'macce' => 10029,
'macicelandic' => 10079,
'macturkish' => 10081,
'maccroatian' => 10082,
'utf32' => 12000,
'utf32be' => 12001,
'chinesecns' => 20000,
'chineseeten' => 20002,
'ia5' => 20105,
'ia5german' => 20106,
'ia5swedish' => 20107,
'ia5norwegian' => 20108,
'usascii' => 20127,
'ibm273' => 20273,
'ibm277' => 20277,
'ibm278' => 20278,
'ibm280' => 20280,
'ibm284' => 20284,
'ibm285' => 20285,
'ibm290' => 20290,
'ibm297' => 20297,
'ibm420' => 20420,
'ibm423' => 20423,
'ibm424' => 20424,
'ebcdickoreanextended' => 20833,
'ibmthai' => 20838,
'koi8r' => 20866,
'ibm871' => 20871,
'ibm880' => 20880,
'ibm905' => 20905,
'ibm00924' => 20924,
'cp1025' => 21025,
'koi8u' => 21866,
'iso88591' => 28591,
'iso88592' => 28592,
'iso88593' => 28593,
'iso88594' => 28594,
'iso88595' => 28595,
'iso88596' => 28596,
'iso88597' => 28597,
'iso88598' => 28598,
'iso88599' => 28599,
'iso885913' => 28603,
'iso885915' => 28605,
'xeuropa' => 29001,
'iso88598i' => 38598,
'iso2022jp' => 50220,
'csiso2022jp' => 50221,
'iso2022jp' => 50222,
'iso2022kr' => 50225,
'eucjp' => 51932,
'euccn' => 51936,
'euckr' => 51949,
'hzgb2312' => 52936,
'gb18030' => 54936,
'isciide' => 57002,
'isciibe' => 57003,
'isciita' => 57004,
'isciite' => 57005,
'isciias' => 57006,
'isciior' => 57007,
'isciika' => 57008,
'isciima' => 57009,
'isciigu' => 57010,
'isciipa' => 57011,
'utf7' => 65000,
'utf8' => 65001,
);
$charset = strtolower($charset);
$charset = preg_replace(array('/^x-/', '/[^a-z0-9]/'), '', $charset);
if (isset($aliases[$charset])) {
return $aliases[$charset];
}
if (preg_match('/^(ibm|dos|cp|windows|win)[0-9]+/', $charset, $m)) {
return substr($charset, strlen($m[1]) + 1);
}
}
/**
* Wrap text to a given number of characters per line
* but respect the mail quotation of replies messages (>).
* Finally add another quotation level by prepending the lines
* with >
*
* @param string $text Text to wrap
* @param int $length The line width
*
* @return string The wrapped text
*/
protected static function wrap_and_quote($text, $length = 72)
{
// Function stolen from Roundcube ;)
// Rebuild the message body with a maximum of $max chars, while keeping quoted message.
$max = min(77, $length + 8);
$lines = preg_split('/\r?\n/', trim($text));
$out = '';
foreach ($lines as $line) {
// don't wrap already quoted lines
if ($line[0] == '>') {
$line = '>' . rtrim($line);
}
else if (mb_strlen($line) > $max) {
$newline = '';
foreach (explode("\n", rcube_mime::wordwrap($line, $length - 2)) as $l) {
if (strlen($l)) {
$newline .= '> ' . $l . "\n";
}
else {
$newline .= ">\n";
}
}
$line = rtrim($newline);
}
else {
$line = '> ' . $line;
}
// Append the line
$out .= $line . "\n";
}
return $out;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Apr 18, 10:12 AM (3 h, 17 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
435645
Default Alt Text
(53 KB)
Attached To
Mode
R4 syncroton
Attached
Detach File
Event Timeline
Log In to Comment