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
10 changes: 9 additions & 1 deletion .horde.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,13 @@ dependencies:
horde/mail: ^3
horde/mongo: ^2
horde/text_filter: ^3
horde/test: ^3
autoload:
psr-0:
Horde_ActiveSync: lib/
autoload-dev:
psr-4:
Horde\ActiveSync\: test/Horde/ActiveSync/
Horde\ActiveSync\Test\Helpers\: test/Horde/ActiveSync/Helpers/
files:
- test/Horde/ActiveSync/Helpers/ControllerRequestStub.php
vendor: horde
14 changes: 10 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,21 @@
},
"autoload-dev": {
"psr-4": {
"Horde\\ActiveSync\\Test\\": "test/"
}
"Horde\\ActiveSync\\": "test/Horde/ActiveSync/",
"Horde\\ActiveSync\\Test\\Helpers\\": "test/Horde/ActiveSync/Helpers/"
},
"files": [
"test/Horde/ActiveSync/Helpers/ControllerRequestStub.php"
]
},
"config": {
"allow-plugins": {}
"allow-plugins": {
"horde/horde-installer-plugin": true
}
},
"extra": {
"branch-alias": {
"dev-FRAMEWORK_6_0": "3.x-dev"
}
}
}
}
14 changes: 14 additions & 0 deletions lib/Horde/ActiveSync/Connector/Exporter/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ abstract class Horde_ActiveSync_Connector_Exporter_Base
*/
protected $_procid;

/**
* Logger instance.
*
* @var Horde_Log_Logger
*/
protected $_logger;

/**
* Array of seen objects during sync.
*
* @var array
*/
protected $_seenObjects = array();

/**
* Const'r
*
Expand Down
60 changes: 48 additions & 12 deletions lib/Horde/ActiveSync/Folder/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* @license http://www.horde.org/licenses/gpl GPLv2
*
* @copyright 2012-2020 Horde LLC (http://www.horde.org)
* @copyright 2012-2026 Horde LLC (http://www.horde.org)
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @package ActiveSync
*/
Expand All @@ -14,7 +14,7 @@
*
* @license http://www.horde.org/licenses/gpl GPLv2
*
* @copyright 2012-2020 Horde LLC (http://www.horde.org)
* @copyright 2012-2026 Horde LLC (http://www.horde.org)
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @package ActiveSync
*/
Expand Down Expand Up @@ -45,33 +45,34 @@ public function __toString()
}

/**
* Serialize this object.
* Serialize this object using modern PHP serialization.
*
* @return string The serialized data.
* @return array The data to serialize.
* @since 3.0.0-beta2
*/
public function serialize()
public function __serialize(): array
{
return json_encode(array(
return array(
's' => $this->_status,
'f' => $this->_serverid,
'c' => $this->_class,
'lsd' => $this->_lastSinceDate,
'sd' => $this->_softDelete,
'i' => $this->haveInitialSync,
'v' => self::VERSION)
'v' => self::VERSION
);
}

/**
* Reconstruct the object from serialized data.
* Reconstruct the object from serialized data using modern PHP serialization.
*
* @param string $data The serialized data.
* @param array $data The serialized data.
* @throws Horde_ActiveSync_Exception_StaleState
* @since 3.0.0-beta2
*/
public function unserialize($data)
public function __unserialize(array $data): void
{
$data = @json_decode($data, true);
if (!is_array($data) || empty($data['v']) || $data['v'] != self::VERSION) {
if (empty($data['v']) || $data['v'] != self::VERSION) {
throw new Horde_ActiveSync_Exception_StaleState('Cache version change');
}
$this->_status = $data['s'];
Expand All @@ -82,4 +83,39 @@ public function unserialize($data)
$this->_softDelete = empty($data['sd']) ? 0 : $data['sd'];
}

/**
* Serialize this object (legacy Serializable interface).
*
* Delegates to __serialize() and JSON-encodes for backward compatibility
* with old "C" format data storage.
*
* @return string The serialized data.
*/
public function serialize()
{
return json_encode($this->__serialize());
}

/**
* Reconstruct the object from serialized data (legacy Serializable interface).
*
* Supports both old "C" format (JSON-encoded) data and delegates to
* __unserialize() for processing.
*
* @param string $data The serialized data.
* @throws Horde_ActiveSync_Exception_StaleState
*/
public function unserialize($data)
{
$decoded = @json_decode($data, true);
if (!is_array($decoded)) {
throw new Horde_ActiveSync_Exception_StaleState('Invalid serialized data');
}
// Ensure version key exists for old data that might be missing it
if (!isset($decoded['v'])) {
$decoded['v'] = self::VERSION;
}
$this->__unserialize($decoded);
}

}
93 changes: 68 additions & 25 deletions lib/Horde/ActiveSync/Folder/Imap.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
/**
* Horde_ActiveSync_Folder_Imap::
*
* PHP Version 5
*
* @license http://www.horde.org/licenses/gpl GPLv2
*
* @copyright 2012-2020 Horde LLC (http://www.horde.org)
* @copyright 2012-2026 Horde LLC (http://www.horde.org)
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @package ActiveSync
*/
Expand All @@ -16,7 +14,7 @@
*
* @license http://www.horde.org/licenses/gpl GPLv2
*
* @copyright 2012-2020 Horde LLC (http://www.horde.org)
* @copyright 2012-2026 Horde LLC (http://www.horde.org)
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @package ActiveSync
*/
Expand Down Expand Up @@ -425,11 +423,12 @@ public function minuid()
}

/**
* Serialize this object.
* Serialize this object using modern PHP serialization.
*
* @return string The serialized data.
* @return array The data to serialize.
* @since 3.0.0-beta2
*/
public function serialize()
public function __serialize(): array
{
if (!empty($this->_status[self::HIGHESTMODSEQ])) {
$msgs = (count($this->_messages) > self::COMPRESSION_LIMIT) ?
Expand All @@ -439,45 +438,89 @@ public function serialize()
$msgs = $this->_messages;
}

return json_encode(array(
return array(
's' => $this->_status,
'm' => $msgs,
'f' => $this->_serverid,
'c' => $this->_class,
'lsd' => $this->_lastSinceDate,
'sd' => $this->_softDelete,
'hi' => $this->haveInitialSync,
'v' => self::VERSION)
'v' => self::VERSION
);
}

/**
* Reconstruct the object from serialized data.
* Reconstruct the object from serialized data using modern PHP serialization.
*
* @param array $data The serialized data.
* @throws Horde_ActiveSync_Exception_StaleState
* @since 3.0.0-beta2
*/
public function __unserialize(array $data): void
{
if (empty($data['v']) || $data['v'] != self::VERSION) {
throw new Horde_ActiveSync_Exception_StaleState('Cache version change');
}
$this->_status = $data['s'];
$this->_messages = $data['m'];
$this->_serverid = $data['f'];
$this->_class = $data['c'];
$this->_lastSinceDate = $data['lsd'];
$this->_softDelete = $data['sd'];
$this->haveInitialSync = empty($data['hi']) ? !empty($this->_messages) : $data['hi'];

if (!empty($this->_status[self::HIGHESTMODSEQ]) && is_string($this->_messages)) {
$this->_messages = $this->_fromSequenceString($this->_messages);
}
}

/**
* Serialize this object (legacy Serializable interface).
*
* Delegates to __serialize() and JSON-encodes for backward compatibility
* with old "C" format data storage. Also supports fallback to old
* serialization format for expensive email collection resyncs.
*
* @return string The serialized data.
*/
public function serialize()
{
return json_encode($this->__serialize());
}

/**
* Reconstruct the object from serialized data (legacy Serializable interface).
*
* Supports both old "C" format (JSON-encoded or PHP-serialized) data and
* delegates to __unserialize() for processing. Falls back to old
* serialization strategy to avoid expensive email collection resyncs.
*
* @param string $data The serialized data.
* @throws Horde_ActiveSync_Exception_StaleState
*/
public function unserialize($data)
{ $d_data = json_decode($data, true);
if (!is_array($d_data) || empty($d_data['v']) || $d_data['v'] != self::VERSION) {
{
$decoded = json_decode($data, true);
if (!is_array($decoded) || empty($decoded['v']) || $decoded['v'] != self::VERSION) {
// Try using the old serialization strategy, since this would save
// an expensive resync of email collections.
$d_data = @unserialize($data);
if (!is_array($d_data) || empty($d_data['v']) || $d_data['v'] != 1) {
$decoded = @unserialize($data);
if (!is_array($decoded) || empty($decoded['v']) || $decoded['v'] != 1) {
throw new Horde_ActiveSync_Exception_StaleState('Cache version change');
}
Copy link
Copy Markdown
Member

@amulet1 amulet1 Mar 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version 1 older data is not going to work (__unserialize will throw an exception) unless you add
$decoded['v'] = self::VERSION;

This was already in my version.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

// Upgrade version 1 data to current VERSION
$decoded['v'] = self::VERSION;

// Version 1 had messages as indexed array, but version 2 needs
// associative array (uid => flags) for non-MODSEQ servers
if (is_array($decoded['m']) && !empty($decoded['m']) &&
empty($decoded['s'][self::HIGHESTMODSEQ])) {
// Convert indexed array to associative: [100, 101] -> [100 => [], 101 => []]
$decoded['m'] = array_fill_keys($decoded['m'], []);
}
}
$this->_status = $d_data['s'];
$this->_messages = $d_data['m'];
$this->_serverid = $d_data['f'];
$this->_class = $d_data['c'];
$this->_lastSinceDate = $d_data['lsd'];
$this->_softDelete = $d_data['sd'];
$this->haveInitialSync = empty($d_data['hi']) ? !empty($this->_messages) : $d_data['hi'];

if (!empty($this->_status[self::HIGHESTMODSEQ]) && is_string($this->_messages)) {
$this->_messages = $this->_fromSequenceString($this->_messages);
}
$this->__unserialize($decoded);
}

/**
Expand Down
Loading
Loading