Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions lib/Horde/ActiveSync/Device.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,21 @@
/**
* Horde_ActiveSync_Device:: Wraps all functionality related to device data.
*
* This class is the single home for client-specific behavior:
*
* - Hard client-conditional branches in request handlers, exporters or
* state code MUST go through hasQuirk() with a QUIRK_* constant defined
* here — never as inline deviceType/userAgent string comparisons at the
* call site.
* - Workarounds that are safe and correct for every client (e.g. output
* keepalive for clients with hard read timeouts) stay client-agnostic;
* the client that motivated them is named in a comment only.
*
* @license http://www.horde.org/licenses/gpl GPLv2
*
* @copyright 2010-2020 Horde LLC (http://www.horde.org)
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @author Torben Dannhauer <torben@dannhauer.de>
* @package ActiveSync
*
* @property string $id The device id.
Expand Down Expand Up @@ -98,6 +109,14 @@ class Horde_ActiveSync_Device
*/
public const QUIRK_SUPPORTS_TNEF = 3;

/**
* Outlook 2013 does not duplicate the destination email during
* MOVEITEMS requests; it reassigns the existing email the new UID
* instead, so the ADD command for the moved message must not be sent
* to clients with this quirk.
*/
public const QUIRK_REASSIGNS_UID_ON_MOVE = 4;

/**
* Device properties.
*
Expand Down Expand Up @@ -723,6 +742,9 @@ public function hasQuirk($quirk)
|| strpos($this->deviceType, 'WindowsOutlook') != false
|| strpos($this->userAgent, 'Outlook') !== false);

case self::QUIRK_REASSIGNS_UID_ON_MOVE:
return $this->deviceType == 'WindowsOutlook15';

default:
return false;
}
Expand Down
11 changes: 6 additions & 5 deletions lib/Horde/ActiveSync/State/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -768,13 +768,14 @@ public function getChanges(array $options = [])
// this stuff. (Needs BC breaking changes in
// storage/state classes).

// OL2013 is broken and duplicates the destination
// email during MOVEITEMS requests (instead it
// reassigns the existing email the new UID). Don't
// send the ADD command for these changes.
// Clients with QUIRK_REASSIGNS_UID_ON_MOVE (OL2013)
// do not duplicate the destination email during
// MOVEITEMS requests (they reassign the existing
// email the new UID). Don't send the ADD command
// for these changes.
if ($changes[$i]['type'] == Horde_ActiveSync::CHANGE_TYPE_CHANGE
&& $changes[$i]['flags'] == Horde_ActiveSync::FLAG_NEWMESSAGE
&& $this->_deviceInfo->deviceType != 'WindowsOutlook15') {
&& !$this->_deviceInfo->hasQuirk(Horde_ActiveSync_Device::QUIRK_REASSIGNS_UID_ON_MOVE)) {
$this->_changes[] = $changes[$i];
continue;
}
Expand Down
22 changes: 22 additions & 0 deletions test/unit/Horde/ActiveSync/DeviceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,28 @@ public function testSupported()
$this->assertEquals($device->supported, ['contacts' => ['one', 'two']]);
}

public function testReassignsUidOnMoveQuirk()
{
$state = $this->getMockBuilder('Horde_ActiveSync_State_Base')->disableOriginalConstructor()->getMock();

// Outlook 2013 has the quirk.
$fixture = [
'deviceType' => 'WindowsOutlook15',
'userAgent' => 'Outlook/15.0 (15.0.4675.1000; MSI; x86)',
];
$device = new Horde_ActiveSync_Device($state, $fixture);
$this->assertTrue($device->hasQuirk(Horde_ActiveSync_Device::QUIRK_REASSIGNS_UID_ON_MOVE));

// Other clients do not.
$fixture = [
'deviceType' => 'iPhone',
'userAgent' => 'Apple-iPhone6C1/1104.201',
'properties' => [Horde_ActiveSync_Device::OS => 'iOS 9.0.2 13A452'],
];
$device = new Horde_ActiveSync_Device($state, $fixture);
$this->assertFalse($device->hasQuirk(Horde_ActiveSync_Device::QUIRK_REASSIGNS_UID_ON_MOVE));
}

public function testIssetReportsNestedDeviceProperties()
{
$state = $this->getMockBuilder('Horde_ActiveSync_State_Base')->disableOriginalConstructor()->getMock();
Expand Down
Loading