[commits] [Wiki] changed: ActiveSync/Development

Wiki wikiguest at horde.org
Thu Jun 25 09:20:40 UTC 2026


tdannhauer  Thu, 25 Jun 2026 09:20:40 +0000

Modified page: https://wiki.horde.org/ActiveSync/Development
New Revision:  13
Change log:  H6 Update

@@ -1,365 +1,109 @@
-# [ActiveSync](ActiveSync) Library Technical Information
+# ActiveSync Development


-This page is designed to give anyone working on the  
[ActiveSync](ActiveSync) library in Horde 5.x a general overview of  
logic flow and what happens where and when.
+Developer documentation for the Horde ActiveSync stack. **Horde 6 /  
ActiveSync 3.x.**

-## General Library Structure
+## Where to read what

+| Topic | Location |
+| --- | --- |
+| Protocol versions, EAS 16 features, deployment config, integrator  
API | [horde/activesync  
`README.md`](https://github.com/horde/activesync/blob/FRAMEWORK_6_0/README.md)  
-- **canonical**, versioned with the library |
+| Request flow, sync keys, class roles, tests | Same README --  
*Architecture* and *Development and tests* sections |
+| Open refactor work and Horde 6 breaking plans |  
[`doc/todo.md`](https://github.com/horde/activesync/blob/FRAMEWORK_6_0/doc/todo.md) in the package repo  
|
+| Operator setup, Autodiscover, web server | [ActiveSync](ActiveSync) wiki |
+| Client quirks and server workarounds | [Broken Client  
Behavior](ActiveSync/BrokenClientBehavior) |
+| Symptoms and unfixed client bugs | [Known Issues](ActiveSync/KnownIssues) |
+| Feature matrix vs other servers | [Feature Grid](ActiveSync/FeatureGrid) |

-The logic for handling [ActiveSync](ActiveSync) requests is split  
between 3 different libraries currently - *Horde\_Rpc*, *Horde\_Core*,  
and obviously *Horde\_ActiveSync*. Horde\_Rpc only handles the initial  
request and basically just passes on control to Horde\_ActiveSync so  
this page will focus only on the other two libraries.
+**Recommendation:** keep **architecture and protocol depth** in the  
git `README.md` so it stays aligned with releases. This wiki page  
covers **Horde deployment wiring** and points to the repo for  
everything else. The old long-form class tour and 2016 synclog  
walkthrough have been retired here in favour of the README.

-### Horde\_ActiveSync
+## Package split

+ActiveSync logic spans two Composer packages in a normal Horde install:

-This library contains the main logic for decoding WBXML from EAS  
requests, passing the request to the appropriate controller and then  
sending properly encoded responses back to the client.
+### `horde/activesync` -- protocol engine

-The following is a general overview of the important classes in this  
library and what they are responsible for:
+Decodes WBXML, dispatches `Horde_ActiveSync_Request_*` handlers,  
manages device
+state and sync keys, encodes responses. No direct Turba/Kronolith/IMP  
calls --
+all data access goes through `Horde_ActiveSync_Driver_Base`.

-**Horde\_ActiveSync**: This is the main "server" and the entry point  
for the library. Also contains some getters for various objects used  
throughout.
-**Horde\_ActiveSync\_Collections**: This class is essentially a  
manager for "collections". A collection is what we call a group of  
properties related to a single synchable data store \(such as an email  
folder, or a contact list\). It is the entry point for the SYNC and  
PING handlers for asking for changes. It manages the SyncCache, is  
responsible for ensuring we have all needed information when handling  
empty SYNC requests, detects concurrent requests, and a bunch more.
-**Horde\_ActiveSync\_Device**: Manages device information such as OS  
and version. Also contains code that works around certain quirks that  
are specific to certain clients.
-**Horde\_ActiveSync\_Mime**: A wrapper around Horde\_Mime\_Part that  
adds to and overrides existing behavior to make it more specific to  
[ActiveSync](ActiveSync). There is also  
Horde\_ActiveSync\_Mime\_Iterator that is changed from the the normal  
Horde\_Mime\_Iterator behavior in that we don't include what EAS  
considers attachments to be returned, and we also include the base  
part when iterating \(whereas Horde\_Mime does not\). Objects are  
instantiated from Horde\_ActiveSync\_Imap\_\* classes.
-**Horde\_ActiveSync\_Rfc822**: Deals with handling RFC822 email  
message strings/streams in the context of EAS. E.g., used when  
handling incoming SENDMAIL commands.
-**Horde\_ActiveSync\_SyncCache**: This class is responsible for  
storing/loading the SyncCache data from the storage/state driver. The  
SyncCache started life as a way of caching data from SYNC and PING  
requests that would be needed if the client issued an "empty"  
SYNC/PING request. Empty requests are required to use the same values  
as the last non-empty request. It has since grown to be essentially a  
shared data store that helps keep collection data up to date between  
any and all running requests. Most \(if not all\) interaction with  
this class is through the Horde\_ActiveSync\_Collections object.
-**Horde\_ActiveSync\_Connector**: These classes are responsible for  
sending/receiving the structured WBXML for certain requests. E.g., the  
H\_AS\_C\_Exporter\_Sync object contain methods that are called from  
the H\_AS\_Request\_Sync object that send the WBXML needed for sending  
each change. It is passed the message object, encodes it to WBXML, and  
wraps it with any needed response WBXML before sending it down the  
output stream. Likewise, the H\_AS\_C\_Importer object reads the  
structured WBXML for importing changes. These methods are called from  
the Horde\_ActiveSync\_Request\_\* classes after the changes have been  
fetched.
-**Horde\_ActiveSync\_Driver\_Base**: This is the base class that  
Horde\_Core\_ActiveSync\_Driver extends.
-**Horde\_ActiveSync\_Folder\_\***: These classes represent a specific  
collection's state. It's an abstraction around the state data and  
represents the point in time of the sync represented by the synckey.  
For non-email collections, this basically stores the last known  
syncstamp/time along with a few other details. For email folders, this  
class stores things like the HIGHESTMODSEQ, UIDVALIDITY, NEXTUID, and  
the list of IMAP UIDS that the are on the client etc... If not using a  
server that supports CONDSTORE then this will also contain flag  
information. Also contains logic used to set/get/calculate what UIDs  
have changed/vanished etc... These objects are used to transport the  
change set back and forth from the backend driver to the  
[ActiveSync](ActiveSync) code that fetches and sends each detected  
change.
-**Horde\_ActiveSync\_Imap\_\***: These classes interact with the IMAP  
server. H\_AS\_I\_Adapter contains the bulk of the logic for fetching  
changes, messages, etc.... The Message object wraps a single IMAP  
message and the MessageBodyData object abstracts access to the  
message's body taking various things into account \(if the client  
needs HTML body, if it needs truncation\) and ensuring the data is  
proper UTF-8 data.
-**Horde\_ActiveSync\_Message\_\***: These classes represent either an  
actual message item i.e., a Calendar item or a part of a message item  
that is contained by a message item i.e., and Attendee or a Flag. Each  
class is responsible for knowing how to encode itself into WBXML or to  
decode a WBXML stream into the item's properties.
-**Horde\_ActiveSync\_State\_\***: This is the state/storage driver.  
Responsible for interacting with the persistent storage used by  
[ActiveSync](ActiveSync) \(normally Sql\). This is another one of  
those classes that has grown too big for it's original name and needs  
to be broken down for H6. Started as a driver for maintaining the  
state/synckey "sync-points" but has grown to encompass managing all  
storage needs. E.g., the SyncCache is actually persisted to storage  
using this class.
-### Horde\_Core
+Key areas: `Request/`, `Message/`, `State/`, `Wbxml/`, `Collections`,
+`SyncCache`, `Connector/`, `Folder/`, `Imap/`, `Device.php`.

+### `horde/core` -- Horde groupware backend

-Core contains any code specific to handling Horde Groupware  
collections. This is where requests for information and changes to  
information are actually handled.
+`Horde_Core_ActiveSync_Driver` implements the driver for IMP  
\(mail\), Kronolith,
+Turba, Nag, and Mnemo. Supporting classes:

-**Horde\_Core\_ActiveSync\_Auth**: Wraps authentication driver for  
use in [ActiveSync](ActiveSync) \(see Authentication section below\).
-**Horde\_Core\_ActiveSync\_Connector**: Wraps calls to the various  
application APIs \(except for IMP\) from  
Horde\_Core\_ActiveSync\_Driver.
-**Horde\_Core\_ActiveSync\_Driver**: Concrete implementation of  
Horde\_ActiveSync\_Driver\_Base.
-**Horde\_Core\_ActiveSync\_Mail**: Handles sending/replying/forwarding email.
-#### Factories
-**\[\[# activesyncserver  
Horde\_Core\_Factory\_ActiveSyncServer\]\]**: Creates the main  
*Horde\_ActiveSync* object. Injects the  
*Horde\_Core\_ActiveSync\_Driver*, the WBXML encoder/decoder objects,  
the *Horde\_ActiveSync\_State\_\[Sql\|Mongo\]* storage object and the  
*Horde\_Controller\_Request* object.
-**Horde\_Core\_Factory\_ActiveSyncState**: Creates the state storage  
handler. The name is misleading \(and will change in Horde 6\) as this  
class has evolved to be more of a general storage handler and now  
handles more than just device state. We will assume a Sql storage  
backend for this document.
-**Horde\_Core\_Factory\_ActiveSyncBackend**: Creates the  
*Horde\_Core\_ActiveSync\_Driver* backend driver. Injects  
*Horde\_Core\_ActiveSync\_Connector*,  
*Horde\_Core\_ActiveSync\_Imap\_Factory* \(if needed\), the state  
storage driver, and the *Horde\_Core\_ActiveSync\_Auth* object.
-**Horde\_Core\_ActiveSync\_Imap\_Factory**: Responsible for  
constructing the Horde\_ActiveSync\_Imap\_Adapter, for returning the  
folder tree, and information on available user-defined flags. This  
factory is injected into the backend driver.
-**Horde\_Core\_ActiveSync\_Logger\_Factory**: Obviously, provides the  
logger object used for the synclog.
-## Protocol Overview
+| Class | Role |
+| --- | --- |
+| `Horde_Core_ActiveSync_Auth` | Auth driver wrapper \(Basic +  
optional certificate transparent driver\) |
+| `Horde_Core_ActiveSync_Connector` | Registry calls into Horde apps |
+| `Horde_Core_ActiveSync_Mail` | Send / reply / forward |
+| `Horde_Core_ActiveSync_Imap_Factory` | IMAP adapter and folder tree  
for mail |

+### Entry point

-It's beyond the scope to explain the [ActiveSync](ActiveSync)  
protocol in detail. For that, there is the official  
[documentation](https://msdn.microsoft.com/en-us/library/cc425499(v=exchg.80).aspx). The most useful of these  
are
-**MS-ASCMD**: Details every command/request and it's schema.
-**MS-ASHTTP**: Documents the requirements and flow of the HTTP  
protocol as used in the EAS protocol.
+`web/horde/rpc.php` -> `Horde_Rpc_ActiveSync` -> injector factories:

-There are also the individual documents for each collection type,  
such as *MS-ASEMAIL*, *MS-ASTASKS* etc... These documents are the best  
place to start when trying to track down issues such as "Protocol  
Error" issues with certain clients.
+- `Horde_Core_Factory_ActiveSyncServer` -- builds `Horde_ActiveSync`,  
applies global `conf['activesync']['version']`
+- `Horde_Core_Factory_ActiveSyncBackend` -- `Horde_Core_ActiveSync_Driver`
+- `Horde_Core_Factory_ActiveSyncState` -- SQL or Mongo state storage

-The basic bit to know about the EAS protocol is that it is encoded  
using WBXML. That is, binary encoded XML data. Again, the  
structure/schema of WBXML data is beyond the scope of this page.  
Instead of decoding the entire request first and then handling it,  
Horde\_ActiveSync decodes the data and handles it on the fly. That is,  
we decode each individual message/object as it comes in and handle it  
in-line, so to speak. This is to avoid keeping more in memory than is  
necessary. The same is true for encoding - we don't wait to enocde the  
entire response - we encode and send the response to the output stream  
as soon as we can. The actual encoding/decoding is done in the  
*Horde\_ActiveSync\_Wbxml\_\** classes. The codepages/schema is  
defined in *Horde\_ActiveSync\_Wbxml::.
+EAS requests are **sessionless** (`$session_control = 'None'`); each HTTP hit
+bootstraps Horde without a web session cookie.

-++ Logic Flow
+`OPTIONS` and **Autodiscover** \(v1 XML and v2 JSON\) are handled in the RPC
+layer before the normal command loop.

-I have a long-standing item on my todo list to generate an activity  
diagram for the program flow of an EAS request, but in the meantime  
here is a description of what happens.
+## Authentication note

-EAS requests hit rpc.php first. There are a number of ways that these  
are differentiated from other RPC requests, but the main give-away is  
the REQUEST\_URI containing *Microsoft-Server-ActiveSync*. A few  
things happen here before continuing. First, EAS requests are  
session-less, meaning that the entire Horde environment needs to be  
setup for each request. This is handled by explicitly setting  
$session\_control = 'None' to force the use of the Null session driver  
in Horde. From there, we perform the same logic and checks like every  
other Horde RPC request. This includes having to initialize the Horde  
environment with NO authentication.
+Clients send HTTP Basic credentials \(or certificates with a transparent auth
+driver\). `Horde_ActiveSync_Credentials` normalises usernames --  
Autodiscover and
+some mobile clients use the **email address**;  
`getUsernameFromEmail()` in the
+core driver maps to the Horde account. This is controlled in part by
+`conf['activesync']['autodiscovery']` \(name is historical; used  
beyond Autodiscover\).

-From here, we instantiate a *Horde\_Rpc\_ActiveSync* object and  
inject a *Horde\_ActiveSync* object \(which is created using  
*Horde\_Core\_Factory\_ActiveSyncServer*\).
+See README *Protocol version configuration* for per-user permissions and the
+`activesync_device_version` hook.

-Here, we perform some sanity checking on the request and sniff out  
what type of request we are handling. OPTIONS and Autodiscover  
requests are handled a little differently, but for now we will  
concentrate on the "normal" request handling.
+## Debugging

-The client must send certain data, apart from the WBXML structure,  
with each request. This data is either present as "normal" GET  
variables or is sent as BASE64 encoded binary data sent in  
QUERY\_STRING \(see ***Horde\_ActiveSync::getGetVars\(\)***\). The  
format of this binary data is beyond the scope of this page, but the  
data is decoded in ***Horde\_ActiveSync\_Utils::decodeBase64\(\)***.
+1. Enable ActiveSync logging in Horde administration or `conf.php`
+   \(`logging.type = perdevice` writes one file per device id under the
+   configured log path\).
+2. Reproduce with a single collection where possible.
+3. Read README *Request flow* and *Client pairing lifecycle* to interpret
+   `SyncKey`, `FolderSync`, and status codes.
+4. Check [Broken Client Behavior](ActiveSync/BrokenClientBehavior) before
+   assuming a server bug.

-: Cmd : This is the command or request type. E.g., SYNC, PING, FOLDESYNC
-: DeviceId : This is a unique identifier for the client. This value  
is only unique to the client, not to the account. I.e., the same  
physical device/application will have the same DeviceId. Multiple  
users can be associated with the same DeviceId.
+Deployment log paths and the author's test device id are documented in the
+deployment `AGENTS.md` / project rules -- not repeated here.

-Flow is turned over to ***Horde\_ActiveSync::handleRequest\(\)***.  
This is where the interesting stuff starts to happen.
+## Running tests

-+++ Authentication
-First, we perform some checks, normalization, and call the  
versionCallback hook if it's present. If all is well, we finally  
attempt authentication. Authentication to Horde is a massive topic and  
it's only complicated more by layering [ActiveSync](ActiveSync) on top  
of it. I will try to give a brief overview here. For a more detailed  
description of the general Horde authentication layer, see Jan's  
excellent series of posts on his \[http:*janschneider.de blog\].
+From a Composer deployment root:

-Thanks to broken clients, different supported authentication  
mechanisms and other idiosyncrasies, we need to perform some magic to  
make sure we have the user's credentials. For this, there is  
*Horde\_ActiveSync\_Credentials*. This class, when constructed and  
injected with the *Horde\_ActiveSync* object, will have two properties  
set: *username* and *password*. For the purposes of this page, we will  
assume a typical setup where the client properly sends credentials  
using HTTP BASIC and we are NOT using X509 certificates.
+```bash
+vendor/bin/phpunit -c vendor/horde/activesync/phpunit.xml.dist
+```
+
+Calendar EAS tests:  
`vendor/horde/kronolith/test/Kronolith/Unit/EventActiveSyncTest.php`.
+See README for bootstrap details and integration fixtures under
+`vendor/horde/activesync/test/`.

-Once we have found the credentials, we call  
***Horde\_ActiveSync::authenticate\(\)***. This performs a few checks  
then ultimately passes control to  
***Horde\_Core\_ActiveSync\_Driver::authenticate\(\)***. Since some  
broken clients always send the email address as the username, plus the  
fact the Autodiscover requests ALWAYS use the email address, we need  
to normalize the username to the correct form. This is handled in  
***Horde\_Core\_ActiveSync\_Driver::getUsernameFromEmail\(\)*** and is  
partially affected by the  
$GLOBALS\['conf'\]\['activesync'\]\['autodiscovery'\] setting \(yes,  
this name is misleading since it is now used for more than just  
autodiscovery\).
-
-Once in ***Horde\_Core\_ActiveSync\_Driver::authenticate\(\)*** we  
use the *Horde\_Core\_ActiveSync\_Auth* object that was injected when  
the driver was created to perform the actual authentication. The  
reason for this authentication wrapper is to allow for combinations of  
a "normal" Horde auth driver along with a transparent driver like X509  
to support clients that allow for certificates along WITH credentials.  
For this page, we will assume a "normal" authentication - and as such,  
the actual authentication task is delegated to the 'base\_driver' in  
*Horde\_Core\_ActiveSync\_Auth*. That is, the authentication driver  
that is returned by
-```
-
-$injector->getInstance('Horde_Core_Factory_Auth')->create()
-```
-### Device
-
-Note that for BC reasons we refer to the client as "Device". We treat  
these two terms are interchangeable unless explicitly mentioned that  
we are talking about the physical device that the client is running on.
-
-Once we have gotten authentication out the way, we perform some  
checks on the device/client. This includes things like making sure the  
device entry exists in our storage backend \(or gets created if it  
doesn't\), checking to see if the maximum protocol version we can  
handle has changed - and if so, notifying the client. We also call  
some hooks and callbacks to allow checking various permissions etc...  
All of this is handled in *Horde\_ActiveSync::\_handleDevice\(\)*. At  
the end of that call \(if successful\), we know we have a device  
object available and the client is allowed to connect. Any failure  
here will result in an Exception being thrown and subsequently an  
appropriate HTTP error code to be sent back to the client.
-
-### Provisioning
-
-Now we take care of setting the provisioning flag and read the WBXML  
header in from the input stream. Provisioning support allows the  
server to take control of certain security settings on the client and  
also enables the ability to remote-wipe the client from the server.  
The provisioning flag is from Horde's permissions system and indicates  
the level of security required:
-
-**Enable**: This means the only clients that support full  
provisioning are allowed to connect to the server. Clients that don't  
support this or that have broken support \(early Android clients\)  
will NOT work.
-**Allow**: This means that we enforce provisioning on clients that  
support it, but also allow non-compliant clients to connect.
-**Disable**: No provisioning is done.
-### Multipart
-
-Next we check to see if the client accepts Multipart responses. This  
is typically used when fetching large amounts of data like e.g, an  
email attachment. This is indicated by the presence of a specific  
header or GET variable. We will assume this is not the case for this  
page.
-
-### Command
-
-
-Now we can take care of the actual command the client is attempting  
to perform. These are handled by Horde\_ActiveSync\_Request\_\*  
objects. Again, the name of these objects are a bit outdated and are  
planned to be renamed in H6. The object is instantiated and the  
handle\(\) request is called. If all goes well, a value of true is  
returned and control is returned back to the RPC layer to finish up.
-
-The Horde\_ActiveSync\_Request\_\* objects are responsible for  
enforcing the schema of whatever request is being handled, passing off  
any incoming additions/deletions/changes to the backend and delegating  
the responsibility for storing any data that needs to be persisted.  
This includes the collection state and the sync cache. This are  
explained in the section on the client life cycle.
-
-I should be noted here that [ActiveSync](ActiveSync) clients are able  
to issue requests while other requests are still running. I.e., if the  
user changes an entry, a new SYNC request can be issued, even though  
there is currently a PING request running. After the SYNC request is  
handled, the client can then issue another PING reqeust etc... The  
sync cache is used to track this and detect these concurrent requests  
and kill off the most appropriate request.
-
-There are almost 2 dozen different types of requests that handle  
things from synchronizing the folder structure to validating S/MIME  
certificates. The more common request objects are:
-
-**Horde\_ActiveSync\_Request\_Sync**: This is the main code  
responsible for accepting and sending object changes, handling  
"hanging syncs" \(which are SYNC requests that also act as PING  
requests\) as well as handling certain options that the client sets  
\(such as truncation, sync window etc...\).
-**Horde\_ActiveSync\_Request\_Ping**: This handles PING requests,  
which basically just continuously check for a change in the backend.  
It doesn't care what the change is, or how many there are. Much less  
resource intensive than a SYNC for this reason.
-
-This is where the bulk of the action is performed and this  
description is of course a gross over-simplification. There are many  
other objects that are involved in handling the request - the  
responsibility of each of these objects are outlined in the General  
Library Structure section.
-
-
-## Life Cycle of a Client.
-
-
-What we will describe is the life cycle of a client-server pairing  
from the initial connection to be able to synchronize changes. Let's  
start with a fresh, never before connected client. The details and  
order of things vary from EAS version to version. This is designed as  
a general overview and most closely resembles what happens in EAS  
version \>= 14.
-
-### OPTIONS
-
-The first thing that happens is the OPTIONS request. This essentially  
tells the server what protocol versions the client supports and the  
server responds with \(among other things\) the version that it will  
be using. This is usually where the device object is first created.  
TODO: Flesh this out with more details.
-
-### SETTINGS
-
-Second will normally be a SETTINGS request. This is where the client  
can give the server details about the device and/or OS, application  
name etc... Normally this is responded to with server details about  
the user account. The following is an example synclog of the SETTINGS  
request and response using a Windows 10 Mail client:
-
-```
-
-2016-11-14T17:03:59+00:00 INFO: [10030]  
Horde_Core_ActiveSync_Driver::authenticate() attempt for mike
-2016-11-14T17:03:59+00:00 INFO: [10030] SETTINGS request received for  
user mike
-2016-11-14T17:03:59+00:00 INFO: [10030] Device entry exists for  
340B5431BBC4241BB73F622B4C6D18E9, updating userAgent, version, and  
supported.
-2016-11-14T17:03:59+00:00 INFO: [10030] GET VARIABLES: Array
-(
-    [ProtVer] => 14.1
-    [Cmd] => Settings
-    [Locale] => 1033
-    [DeviceId] => 340b5431bbc4241bb73f622b4c6d18e9
-    [PolicyKey] => 0
-    [DeviceType] => WindowsMail
-)
-
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I  <Settings:Settings>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I   <Settings:DeviceInformation>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I    <Settings:Set>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:Model>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I       VMware Virtual Platform
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     </Settings:Model>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:IMEI />
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:FriendlyName>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I       DESKTOP-E96J0OF
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     </Settings:FriendlyName>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:OS>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I       Windows 10.0.14393
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     </Settings:OS>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:OSLanguage>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I       English
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     </Settings:OSLanguage>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:PhoneNumber />
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:UserAgent>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I       MSFT-WIN-3/10.0.14393
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     </Settings:UserAgent>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:EnableOutboundSMS>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     <Settings:MobileOperator>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I       OperatorName
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I     </Settings:MobileOperator>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I    </Settings:Set>
-2016-11-14T17:03:59+00:00 INFO: [10030] Device entry exists for  
340B5431BBC4241BB73F622B4C6D18E9, updating userAgent, version, and  
supported.
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I   </Settings:DeviceInformation>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I   <Settings:UserInformation>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I    <Settings:Get />
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I   </Settings:UserInformation>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] I  </Settings:Settings>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O  <Settings:Settings>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O   <Settings:Status>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O    1
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O   </Settings:Status>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O   <Settings:DeviceInformation>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O    <Settings:Status>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O     1
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O    </Settings:Status>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O   </Settings:DeviceInformation>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O   <Settings:UserInformation>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O    <Settings:Status>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O     1
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O    </Settings:Status>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O    <Settings:Get>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O     <Settings:Accounts>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O      <Settings:Account>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O       <Settings:AccountName>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O        Default Identity
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O       </Settings:AccountName>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O       <Settings:EmailAddresses>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O         
<Settings:PrimarySmtpAddress>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O         mike at theupstairsroom.com
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O         
</Settings:PrimarySmtpAddress>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O        <Settings:SmtpAddress>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O         mike at theupstairsroom.com
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O        </Settings:SmtpAddress>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O       </Settings:EmailAddresses>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O      </Settings:Account>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O     </Settings:Accounts>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O    </Settings:Get>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O   </Settings:UserInformation>
-2016-11-14T17:03:59+00:00 DEBUG: [10030] O  </Settings:Settings>
-```
-### FOLDERSYNC
-
-For the purposes of this page, we are going to ignore the  
PROVISIONING stuff. The next step is the FOLDERSYNC request:
-```
-
-2016-11-14T17:03:59+00:00 DEBUG: [10029] I  <FolderHierarchy:FolderSync>
-2016-11-14T17:03:59+00:00 DEBUG: [10029] I   <FolderHierarchy:SyncKey>
-2016-11-14T17:03:59+00:00 DEBUG: [10029] I     0
-2016-11-14T17:03:59+00:00 DEBUG: [10029] I   </FolderHierarchy:SyncKey>
-2016-11-14T17:03:59+00:00 DEBUG: [10029] I  </FolderHierarchy:FolderSync>
-```
-
-First, the client requests a SyncKey of 0. This indicates it is a  
fresh start and that the server should discard any previous sets of  
state it may have for this client and user. The server fetches the  
list of folders that are available. For this purpose each groupware  
share \(address book, calendar etc...\) is considered a folder. The  
entry point for this is  
***Horde\_Core\_ActiveSync\_Driver::getFolderList\(\)***.
-
-At this point we should talk about the backend's id and the  
[ActiveSync](ActiveSync) UID for each folder. From the client's point  
of view, all it cares about is the uid for the folder. We create new  
UIDs whenever a new folder is encountered. The mapping of UIDs to  
backend ids is managed by the Horde\_ActiveSync\_State driver. So, you  
will see something like this in the sync log:
-
-```
-
-2016-11-14T17:03:59+00:00 INFO: [10029]  
Horde_Core_ActiveSync_Driver::getFolderList()
-2016-11-14T17:03:59+00:00 INFO: [10029] Device entry exists for  
340B5431BBC4241BB73F622B4C6D18E9, updating userAgent, version, and  
supported.
-2016-11-14T12:04:00-05:00 INFO: [10029] Creating new folder uuid for  
Calendar:1w_UP1B0Fbgun61fg3dAqLJ: A2603e3ee
-2016-11-14T12:04:00-05:00 INFO: [10029] Creating new folder uuid for  
@Contacts@: Cd1b7031f
-2016-11-14T12:04:00-05:00 INFO: [10029] Creating new folder uuid for  
@Tasks@: Tb666e6ce
-2016-11-14T12:04:00-05:00 INFO: [10029] Creating new folder uuid for  
@Notes@: N6d6ecbe3
-2016-11-14T12:04:00-05:00 INFO: [10029] Polling  
Horde_Core_ActiveSync_Driver::_getMailFolders()
-2016-11-14T12:04:00-05:00 INFO: [10029] Creating new folder uuid for  
INBOX: Fe4d57a40
-```
-
-Now we send the folder tree to the client, along with a synckey  
\(only showing one folder for brevity's sake\):
-
-```
-
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O  <FolderHierarchy:FolderSync>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O   <FolderHierarchy:Status>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O    1
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O   </FolderHierarchy:Status>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O   <FolderHierarchy:SyncKey>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O     
{5829ee7f-b60c-405a-822f-272dc0a80160}1
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O   </FolderHierarchy:SyncKey>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O   <FolderHierarchy:Changes>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O    <FolderHierarchy:Count>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O     87
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O    </FolderHierarchy:Count>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O    <FolderHierarchy:Add>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O      
<FolderHierarchy:ServerEntryId>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O      Tb666e6ce
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O      
</FolderHierarchy:ServerEntryId>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O     <FolderHierarchy:ParentId>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O      0
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O     </FolderHierarchy:ParentId>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O     <FolderHierarchy:DisplayName>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O      Tasks
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O     </FolderHierarchy:DisplayName>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O     <FolderHierarchy:Type>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O      7
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O     </FolderHierarchy:Type>
-2016-11-14T12:04:00-05:00 DEBUG: [10029] O    </FolderHierarchy:Add>
-2016-11-14T12:04:01-05:00 DEBUG: [10029] O   </FolderHierarchy:Changes>
-2016-11-14T12:04:01-05:00 DEBUG: [10029] O  </FolderHierarchy:FolderSync>
-```
-
-This is a good time to mention the SyncKey. The SyncKey is the unique  
identifier a specific set of state. It represents the state of the  
collection being synced at that point in time. It always has the  
following form:
-
-\{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\}y where y is a continuously  
increasing counter. That increments any time changes are received from  
or sent to the client. The GUID-looking value in front of the counter  
will remain constant after it's generated for each collection.
-
-### SYNC
-
-Next, the client can issue SYNC requests for each folder it wants to  
populate. Unlike the FOLDERSYNC request, the SYNC request doesn't send  
the actual changes back with the 1st synckey. E.g., the first SYNC  
request for a mail folder contains some options such as the filtertype  
\(how far back to sync\), bodytype preferences etc... The server sets  
those options in the SyncCache for that collection, creates a new  
synckey, stores it in the collection's state and sends back the  
initial response.
-
-```
-
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I  <Synchronize>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I   <Folders>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I    <Folder>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I     <SyncKey>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       0
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I     </SyncKey>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I     <FolderId>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       Fe4d57a40
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I     </FolderId>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I     <Options>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      <FilterType>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I        5
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      </FilterType>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      <AirSyncBase:BodyPreference>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       <AirSyncBase:Type>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I         1
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       </AirSyncBase:Type>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       <AirSyncBase:Preview>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I         128
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       </AirSyncBase:Preview>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      </AirSyncBase:BodyPreference>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      <AirSyncBase:BodyPreference>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       <AirSyncBase:Type>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I         2
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       </AirSyncBase:Type>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       <AirSyncBase:Preview>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I         128
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       </AirSyncBase:Preview>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      </AirSyncBase:BodyPreference>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      <MIMESupport>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I        1
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      </MIMESupport>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      <AirSyncBase:BodyPreference>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       <AirSyncBase:Type>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I         4
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       </AirSyncBase:Type>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       <AirSyncBase:TruncationSize>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I         107520
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I        
</AirSyncBase:TruncationSize>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       <AirSyncBase:Preview>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I         128
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       </AirSyncBase:Preview>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      </AirSyncBase:BodyPreference>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      <Conflict>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I        1
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I      </Conflict>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       
<RightsManagement:RightsManagementSupport>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I        1
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I       
</RightsManagement:RightsManagementSupport>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I     </Options>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I    </Folder>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I   </Folders>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] I  </Synchronize>
-
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O  <Synchronize>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O   <Status>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O    1
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O   </Status>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O   <Folders>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O    <Folder>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O     <SyncKey>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O       
{5829ee83-11e8-472d-bc66-272ec0a80160}1
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O     </SyncKey>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O     <FolderId>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O      Fe4d57a40
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O     </FolderId>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O     <Status>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O      1
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O     </Status>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O    </Folder>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O   </Folders>
-2016-11-14T17:04:03+00:00 DEBUG: [10030] O  </Synchronize>
-```
+## Contributing

-Now, the next SYNC request for this folder will be sent with the  
SyncKey of \{5829ee83-11e8-472d-bc66-272ec0a80160\}1. At this point we  
know it's the "first sync" i.e., all objects are "new" from the  
client's point of view,  so we do some optimizations and take  
advantage of this. Otherwise, all the future SYNC requests will behave  
the same way - the client sends the last SyncKey it knows about, the  
server uses that to get the state of whatever collection we are  
syncing, calculates the changes, increments the SyncKey counter, sends  
it all and saves the new state.
+- Library changes:  
[horde/activesync](https://github.com/horde/activesync) on branch  
`FRAMEWORK_6_0`
+- Horde driver / admin UI:  
[horde/core](https://github.com/horde/core),  
[horde/horde](https://github.com/horde/horde)
+- App-specific mapping \(calendar, contacts, tasks\): respective app repos

-The general flow for getting server changes would be something like this:
+Update `README.md` when adding commands, changing negotiation, or  
altering state
+shape. Add a short wiki note only when operators need new config or behaviour
+is client-visible.

-In ActiveSync\_Request\_Sync::\_handle\(\) ---\>
-  call into Horde\_ActiveSync\_Collections::getCollectionChanges\(\) ---\>
-    which calls into Horde\_ActiveSync\_State\_Base::getChanges\(\) ---\>
-        which finally calls  
Horde\_Core\_ActiveSync\_Driver::getServerChanges\(\) to get the  
changes.
+Bug reports: [bugs.horde.org](http://bugs.horde.org/queue/sync) -- include
+`MS-ASProtocolVersion`, `DeviceType`, `User-Agent`, and redacted  
per-device log
+excerpts.

-Once we have the change set in H\_AS\_Request\_Sync::\_handle\(\), we  
set it into the exporter object the loop and send each change \(see  
Horde\_ActiveSync\_Connector\_Exporter\_Sync::sendNextChange\(\)\).




More information about the commits mailing list