Skip to content

Commit

Permalink
Merge branch 'eas16'
Browse files Browse the repository at this point in the history
  • Loading branch information
mrubinsk committed Feb 1, 2016
2 parents bd5727a + 48f5e63 commit 4bc99a9
Show file tree
Hide file tree
Showing 24 changed files with 1,347 additions and 359 deletions.
35 changes: 32 additions & 3 deletions framework/ActiveSync/lib/Horde/ActiveSync.php
Expand Up @@ -139,13 +139,36 @@ class Horde_ActiveSync
const AIRSYNCBASE_ISINLINE = 'AirSyncBase:IsInline';
const AIRSYNCBASE_NATIVEBODYTYPE = 'AirSyncBase:NativeBodyType';
const AIRSYNCBASE_CONTENTTYPE = 'AirSyncBase:ContentType';
const AIRSYNCBASE_LOCATION = 'AirSyncBase:Location';

// 14.0
const AIRSYNCBASE_PREVIEW = 'AirSyncBase:Preview';

// 14.1
const AIRSYNCBASE_BODYPARTPREFERENCE = 'AirSyncBase:BodyPartPreference';
const AIRSYNCBASE_BODYPART = 'AirSyncBase:BodyPart';
const AIRSYNCBASE_STATUS = 'AirSyncBase:Status';

// 16.0
const AIRSYNCBASE_ADD = 'AirSyncBase:Add';
const AIRSYNCBASE_DELETE = 'AirSyncBase:Delete';
const AIRSYNCBASE_CLIENTID = 'AirSyncBase:ClientId';
const AIRSYNCBASE_CONTENT = 'AirSyncBase:Content';
const AIRSYNCBASE_ANNOTATION = 'AirSyncBase:Annotation';
const AIRSYNCBASE_STREET = 'AirSyncBase:Street';
const AIRSYNCBASE_CITY = 'AirSyncBase:City';
const AIRSYNCBASE_STATE = 'AirSyncBase:State';
const AIRSYNCBASE_COUNTRY = 'AirSyncBase:Country';
const AIRSYNCBASE_POSTALCODE = 'AirSyncBase:PostalCode';
const AIRSYNCBASE_LATITUDE = 'AirSyncBase:Latitude';
const AIRSYNCBASE_LONGITUDE = 'AirSyncBase:Longitude';
const AIRSYNCBASE_ACCURACY = 'AirSyncBase:Accuracy';
const AIRSYNCBASE_ALTITUDE = 'AirSyncBase:Altitude';
const AIRSYNCBASE_ALTITUDEACCURACY = 'AirSyncBase:AltitudeAccuracy';
const AIRSYNCBASE_LOCATIONURI = 'AirSyncBase:LocationUri';
const AIRSYNCBASE_INSTANCEID = 'AirSyncBase:InstanceId';


/* Body type prefs */
const BODYPREF_TYPE_PLAIN = 1;
const BODYPREF_TYPE_HTML = 2;
Expand Down Expand Up @@ -274,6 +297,7 @@ class Horde_ActiveSync
const VERSION_TWELVEONE = '12.1';
const VERSION_FOURTEEN = '14.0';
const VERSION_FOURTEENONE = '14.1';
const VERSION_SIXTEEN = '16.0';

const MIME_SUPPORT_NONE = 0;
const MIME_SUPPORT_SMIME = 1;
Expand Down Expand Up @@ -318,7 +342,7 @@ class Horde_ActiveSync
*
* @var float
*/
protected $_maxVersion = self::VERSION_FOURTEENONE;
protected $_maxVersion = self::VERSION_SIXTEEN;

/**
* The actual version we are supporting.
Expand Down Expand Up @@ -413,7 +437,8 @@ class Horde_ActiveSync
self::VERSION_TWELVE,
self::VERSION_TWELVEONE,
self::VERSION_FOURTEEN,
self::VERSION_FOURTEENONE
self::VERSION_FOURTEENONE,
self::VERSION_SIXTEEN
);

/**
Expand Down Expand Up @@ -958,7 +983,10 @@ public function activeSyncHeader()
header('MS-Server-ActiveSync: 14.0');
break;
case self::VERSION_FOURTEENONE:
header('MS-Server-ActiveSync: 14.2');
header('MS-Server-ActiveSync: 14.1');
break;
case self::VERSION_SIXTEEN:
header('MS-Server-ActiveSync: 16.0');
}
}

Expand Down Expand Up @@ -1005,6 +1033,7 @@ public function getSupportedCommands()
case self::VERSION_TWELVEONE:
case self::VERSION_FOURTEEN:
case self::VERSION_FOURTEENONE:
case self::VERSION_SIXTEEN:
return 'Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Search,Settings,Ping,ItemOperations,Provision,ResolveRecipients,ValidateCert';
}
}
Expand Down
51 changes: 43 additions & 8 deletions framework/ActiveSync/lib/Horde/ActiveSync/Collections.php
Expand Up @@ -342,7 +342,7 @@ public function addCollection(array $collection, $requireSyncKey = false)
* @param $id The uid.
*
* @return string The backend server id.
* @throws Horde_ActiveSync_Exception
* @throws Horde_ActiveSync_Exception_FolderGone
*/
public function getBackendIdForFolderUid($folderid)
{
Expand All @@ -357,7 +357,7 @@ public function getBackendIdForFolderUid($folderid)
$this->_logger->err(
sprintf('[%s] Horde_ActiveSync_Collections::getBackendIdForFolderUid failed because folder was not found in cache.',
$this->_procid));
throw new Horde_ActiveSync_Exception('Folder not found in cache.');
throw new Horde_ActiveSync_Exception_FolderGone('Folder not found in cache.');
}
}

Expand Down Expand Up @@ -500,6 +500,13 @@ public function getCollectionClass($id)
if ($id == 'RI') {
return $id;
}

// First try existing, loaded collections.
if (!empty($this->_collections[$collection['id']])) {
return $this->_collections[$collection['id']]['class'];
}

// Next look in the SyncCache.
if (isset($this->_cache->folders[$id]['class'])) {
$class = $this->_cache->folders[$id]['class'];
$this->_logger->info(sprintf(
Expand All @@ -511,6 +518,29 @@ public function getCollectionClass($id)
return false;
}

public function getCollectionType($id)
{
if ($id == 'RI') {
return $id;
}

// First try existing, loaded collections.
if (!empty($this->_collections[$collection['id']])) {
return $this->_collections[$collection['id']]['type'];
}

// Next look in the SyncCache.
if (isset($this->_cache->folders[$id]['type'])) {
$type = $this->_cache->folders[$id]['type'];
$this->_logger->info(sprintf(
'[%s] Obtaining collection type of %s for collection id %s',
$this->_procid, $type, $id));
return $type;
}

return false;
}

/**
* Determine if we have any syncable collections either locally or in the
* sync cache.
Expand Down Expand Up @@ -1026,26 +1056,31 @@ public function save($preserve_folders = false)
* throw exception if it's not present.
*
* @throws Horde_ActiveSync_Exception_InvalidRequest
* @throws Horde_ActiveSync_Exception_FolderGone
*/
public function initCollectionState(array $collection, $requireSyncKey = false)
public function initCollectionState(array &$collection, $requireSyncKey = false)
{
// Clear the changes cache.
$this->_changes = null;

// Ensure we have a collection class.
if (empty($collection['class'])) {
if (!empty($this->_collections[$collection['id']])) {
$collection['class'] = $this->_collections[$collection['id']]['class'];
} else {
if (!($collection['class'] = $this->getCollectionClass($collection['id']))) {
throw new Horde_ActiveSync_Exception_FolderGone('Could not load collection class for ' . $collection['id']);
}
}

// Load the collection's type if we can.
if (empty($collection['type'])) {
$collection['type'] = $this->getCollectionType($collection['id']);
}

// Get the backend serverid.
if (empty($collection['serverid'])) {
$f = $this->_cache->getFolder($collection['id']);
$collection['serverid'] = $f['serverid'];
$collection['serverid'] = $this->getBackendIdForFolderUid($collection['id']);
}


if ($requireSyncKey && empty($collection['synckey'])) {
throw new Horde_ActiveSync_Exception_InvalidRequest(sprintf(
'[%s] Empty synckey for %s.',
Expand Down
82 changes: 64 additions & 18 deletions framework/ActiveSync/lib/Horde/ActiveSync/Connector/Importer.php
Expand Up @@ -97,7 +97,7 @@ public function __construct(Horde_ActiveSync $as)
}

/**
* Initialize the exporter for this collection
* Initialize the importer for this collection
*
* @param Horde_ActiveSync_State_Base $state The state machine.
* @param string $folderId The collection's uid.
Expand Down Expand Up @@ -132,8 +132,10 @@ public function setLogger($logger)
* @param Horde_ActiveSync_Device $device A device descriptor
* @param integer $clientid Client id sent from client.
* on message addition.
* @param string $class The collection class (only needed for SMS).
* @since 2.6.0
* @param string $class The collection class (only needed for SMS).
* @since 2.6.0
* @param string $synckey The synckey currently being processed.
* @since 2.31.0
*
* @todo Revisit passing $class for SMS. Probably pass class in the
* const'r.
Expand All @@ -143,7 +145,8 @@ public function setLogger($logger)
*/
public function importMessageChange(
$id, Horde_ActiveSync_Message_Base $message,
Horde_ActiveSync_Device $device, $clientid, $class = null)
Horde_ActiveSync_Device $device, $clientid, $class = false,
$synckey = false)
{
// Don't support SMS, but can't tell client that. Send back a phoney
// UID for any imported SMS objects.
Expand All @@ -152,17 +155,35 @@ public function importMessageChange(
}

// Changing an existing object
if ($id && $this->_flags == Horde_ActiveSync::CONFLICT_OVERWRITE_PIM) {
$conflict = $this->_isConflict(
Horde_ActiveSync::CHANGE_TYPE_CHANGE,
$this->_folderId,
$id);
if ($conflict) {
$this->_logger->notice(sprintf(
'[%s] Conflict when updating %s, will overwrite client version on next sync.',
$this->_procid, $id)
);
return array($id, Horde_ActiveSync_Request_Sync::STATUS_CONFLICT);
if ($id && $synckey &&
($message instanceof Horde_ActiveSync_Message_Appointment) &&
$this->_flags == Horde_ActiveSync::CONFLICT_OVERWRITE_PIM) {

if ($this->_state->isDuplicatePIMChange($id, $synckey)) {}

} elseif ($id && $this->_flags == Horde_ActiveSync::CONFLICT_OVERWRITE_PIM) {
// This is complicated by the fact that in EAS 16.0, clients
// will send a CHANGE for adding/editing an exception along with
// a seperate change with the entire appointment - even if nothing
// else has changed. This leads to conficts (since the appointment
// is marked as changed after the first edit). Sniff that out here
// and prevent the conflict check for those messages.
if (!$synckey ||
!(($message instanceof Horde_ActiveSync_Message_Appointment) &&
$this->_state->isDuplicatePIMChange($id, $synckey))) {

$conflict = $this->_isConflict(
Horde_ActiveSync::CHANGE_TYPE_CHANGE,
$this->_folderId,
$id
);
if ($conflict) {
$this->_logger->notice(sprintf(
'[%s] Conflict when updating %s, will overwrite client version on next sync.',
$this->_procid, $id)
);
return array($id, Horde_ActiveSync_Request_Sync::STATUS_CONFLICT);
}
}
} elseif (!$id && $uid = $this->_state->isDuplicatePIMAddition($clientid)) {
// Already saw this addition, but client never received UID
Expand Down Expand Up @@ -208,18 +229,43 @@ public function importMessageChange(
* Import message deletions. This may conflict if the local object has been
* modified.
*
* @param array $ids Server message uids to delete
* @param string $class The server collection class.
* @param array $ids Server message uids to delete
* @param string $class The server collection class.
* @param boolean $instanceids If true, $ids is a hash of
* instanceids => uids. @since 2.31.0
*
* @return array An array containing ids of successfully deleted messages.
*/
public function importMessageDeletion(array $ids, $class)
public function importMessageDeletion(array $ids, $class, $instanceids = false)
{
// Don't support SMS, but can't tell client that.
if ($class == Horde_ActiveSync::CLASS_SMS) {
return array();
}

if ($instanceids) {
foreach ($ids as $uid => $iid) {
$mod = $this->_as->driver->getSyncStamp($this->_folderId);
$this->_as->driver->deleteMessage($this->_folderId, array($uid => $iid), true);
$change = array(
'id' => $uid,
'mod' => $mod,
'serverid' => $this->_folderId
);
// Log this as a change in the state, not a deletion because
// these are actually instances of recurring series being
// deleted, not the entire item being deleted.
$this->_state->updateState(
Horde_ActiveSync::CHANGE_TYPE_CHANGE,
$change,
Horde_ActiveSync::CHANGE_ORIGIN_PIM,
$this->_as->driver->getUser()
);
}

return $ids;
}

// Ask the backend to delete the message.
$mod = $this->_as->driver->getSyncStamp($this->_folderId);
$ids = $this->_as->driver->deleteMessage($this->_folderId, $ids);
Expand Down

0 comments on commit 4bc99a9

Please sign in to comment.