PHPackages                             horde/activesync - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. horde/activesync

ActiveHorde-library[Utility &amp; Helpers](/categories/utility)

horde/activesync
================

ActiveSync server library

v3.0.0(2d ago)61.6k6[1 issues](https://github.com/horde/ActiveSync/issues)[1 PRs](https://github.com/horde/ActiveSync/pulls)4GPL-2.0-onlyPHPPHP ^7.4 || ^8CI failing

Since Oct 15Pushed 2w ago6 watchersCompare

[ Source](https://github.com/horde/ActiveSync)[ Packagist](https://packagist.org/packages/horde/activesync)[ Docs](https://www.horde.org/libraries/Horde_ActiveSync)[ RSS](/packages/horde-activesync/feed)WikiDiscussions FRAMEWORK\_6\_0 Synced today

READMEChangelog (10)Dependencies (62)Versions (187)Used By (4)

Horde ActiveSync
================

[](#horde-activesync)

PHP library implementing the Microsoft Exchange ActiveSync (EAS) protocol. It decodes WBXML requests from mobile clients, drives synchronization through a pluggable backend driver, and encodes WBXML responses.

In a typical Horde deployment, this package is the **protocol engine**. The **data backend** lives in `horde/core` as `Horde_Core_ActiveSync_Driver`, which talks to IMAP (mail), Kronolith (calendar), Turba (contacts), Nag (tasks), and Mnemo (notes).

Open work and the Horde 6 roadmap are tracked in [`doc/Horde/ActiveSync/todo.md`](doc/Horde/ActiveSync/todo.md).

How it fits together
--------------------

[](#how-it-fits-together)

```
Mobile client (Outlook, iOS Mail, …)
        │  HTTPS POST, WBXML body
        ▼
Web server  →  /Microsoft-Server-ActiveSync  (rewritten to Horde rpc.php)
        │
        ▼
Horde_Rpc_ActiveSync
        │
        ▼
Horde_ActiveSync                 ← this library
  ├── Request handlers (Sync, FolderSync, Ping, …)
  ├── Message classes (Appointment, Mail, Contact, …)
  ├── WBXML encoder/decoder
  └── Horde_ActiveSync_Driver_Base  (abstract backend API)
        │
        ▼
Horde_Core_ActiveSync_Driver     ← horde/core (Horde deployment)
  └── Horde_Core_ActiveSync_Connector → Horde apps / IMAP

```

### Main classes

[](#main-classes)

ClassRole`Horde_ActiveSync`Server entry point: auth, version negotiation, request dispatch`Horde_ActiveSync_Request_*`One class per EAS command (`Sync`, `FolderSync`, `Find`, …)`Horde_ActiveSync_Message_*`Typed WBXML property maps for each item type`Horde_ActiveSync_Driver_Base`Abstract backend all data access goes through`Horde_ActiveSync_State_Sql` / `_Mongo`Device state, sync keys, change maps`Horde_ActiveSync_Device`Per-device metadata (type, policy key, remote wipe)`Horde_ActiveSync_Wbxml_*`Low-level WBXML encode/decode and protocol loggingMessage objects are version-aware: constructors accept `protocolversion` and adjust their property maps for the negotiated EAS level.

Protocol versions
-----------------

[](#protocol-versions)

The library defines constants for EAS **2.5**, **12.0**, **12.1**, **14.0**, **14.1**, **16.0**, and **16.1**.

VersionStatus in this tree2.5 – 14.1Mature; long-standing Horde support**16.0**Supported end-to-end for production use (see below)**16.1**Supported; extends 16.0 with meeting proposals and account-only wipe (see below)### How version negotiation works

[](#how-version-negotiation-works)

Two related values matter on each request:

1. **Server ceiling** (`Horde_ActiveSync::$_maxVersion`, set via `setSupportedVersion()`). Controls what the server **advertises** in `OPTIONS` / `MS-ASProtocolVersions` and `MS-Server-ActiveSync`, and which command sets are available.
2. **Session protocol version** (client header `MS-ASProtocolVersion`, or `ProtVer` in GET for very old clients). The level actually used for WBXML encoding/decoding on that request. The client must not exceed what it offered and what the server supports.

Clients normally negotiate down: a device that sends `MS-ASProtocolVersion: 16.0`against a server ceiling of `14.1` will sync at 14.1.

In a Horde deployment the ceiling is applied in **layers** (see next section). The library itself only exposes `setSupportedVersion()`; per-user and per-device policy is implemented in `Horde_Core_ActiveSync_Driver::versionCallback()`and invoked at the start of every `Horde_ActiveSync::handleRequest()` call, before authentication completes.

### Protocol version configuration (Horde)

[](#protocol-version-configuration-horde)

Three mechanisms stack together. None of them are personal **preferences**(users cannot change their own EAS version under Preferences); per-user limits are **administrator permissions**.

#### 1. Global ceiling (all users, default)

[](#1-global-ceiling-all-users-default)

Set in Horde administration → ActiveSync → *What is the highest version of EAS that Horde should support?*, or in `conf.php`:

```
$conf['activesync']['version'] = '16.1';
```

`Horde_Core_Factory_ActiveSyncServer` calls `setSupportedVersion()` with this value when the server object is created. This is the baseline for every request.

#### 2. Per-user ceiling (permissions)

[](#2-per-user-ceiling-permissions)

Default mode: `version_mode` is **`user`** when unset.

Administrators can assign **Maximum ActiveSync protocol version**(`horde:activesync:version`) per user or group under Horde administration → Permissions → ActiveSync. Allowed values: `2.5`, `12.0`, `12.1`, `14.0`, `14.1`, `16.0`, and `16.1`.

On each request, `versionCallback()` resolves the authenticated Horde username (from HTTP Basic credentials, the `User` GET parameter, or the registry) and reads that permission. If set, it calls `setSupportedVersion()` again for this request only.

SituationEffective ceiling for this requestPermission empty / permission tree not definedGlobal `conf['activesync']['version']` onlyUser permission **lower** than global (e.g. user `14.1`, global `16.0`)User value — caps that user below the site defaultUser permission **higher** than global (e.g. user `16.0`, global `14.1`)User value — can raise the advertised ceiling above the admin default for that userUser in multiple groups with different values**Lowest** (most restrictive) allowed versionThe last row matters for group-based permissions: if one group allows `16.0` and another `14.1`, the user syncs at `14.1`.

#### 3. Per-device ceiling (hook)

[](#3-per-device-ceiling-hook)

For device-specific policy (pilot devices, problematic clients, lab handsets), set in `conf.php`:

```
$conf['activesync']['version_mode'] = 'device';
```

Then implement `activesync_device_version()` in `config/hooks.php` (see `vendor/horde/horde/config/hooks.php.dist`):

```
public function activesync_device_version($deviceId, $user)
{
    // $deviceId is normalised to uppercase.
    $map = [
        'OLD-OUTLOOK-DEVICE-ID' => '14.1',
        'TEST-IPHONE-ID'        => '16.0',
    ];

    return $map[$deviceId] ?? null;
}
```

Hook return values:

ReturnMeaningString, e.g. `'16.0'`Use this ceiling for the deviceArray of version strings**Lowest** (most restrictive) entry is used`null`, `false`, `''`, or `-1`No override; fall back to global / user permission behaviour`DeviceId` must be present in the request (standard on all sync commands). If the hook is not defined or returns no override, behaviour depends on `version_mode`: in **`device`** mode with no hook result, no permission override is applied; switch back to **`user`** mode to use group permissions as the primary per-principal control.

#### Choosing `user` vs `device` mode

[](#choosing-user-vs-device-mode)

`version_mode`Source of per-request override`user` (default)`horde:activesync:version` permission`device``activesync_device_version` hookOnly one mode is active per installation. Use **permissions** for account/class-of-user policy; use the **hook** when the device ID is the right key (e.g. force an old Outlook build to `14.1` while everyone else stays on `16.0`).

#### Custom / non-Horde drivers

[](#custom--non-horde-drivers)

Any backend driver may implement `versionCallback(Horde_ActiveSync $server)` the same way as `Horde_Core_ActiveSync_Driver`. The library checks `is_callable([$driver, 'versionCallback'])` on every request. Integrators without Horde permissions can set policy entirely inside that method.

EAS commands
------------

[](#eas-commands)

Commands advertised for EAS ≥ 12.0 (including 16.0):

`Sync`, `SendMail`, `SmartForward`, `SmartReply`, `GetAttachment`, `GetHierarchy`, `CreateCollection`, `DeleteCollection`, `MoveCollection`, `FolderSync`, `FolderCreate`, `FolderDelete`, `FolderUpdate`, `MoveItems`, `GetItemEstimate`, `MeetingResponse`, `Search`, `Settings`, `Ping`, `ItemOperations`, `Provision`, `ResolveRecipients`, `ValidateCert`, **`Find`**

EAS 2.5 omits `Settings`, `ItemOperations`, and `Find`.

`OPTIONS` and **Autodiscover** are handled outside the normal command loop via `Horde_Rpc_ActiveSync`.

Implemented feature set
-----------------------

[](#implemented-feature-set)

### Mail (EAS `Email` class)

[](#mail-eas-email-class)

- Folder hierarchy sync, message sync, flags, categories
- Send, reply, forward (`SendMail`, `SmartReply`, `SmartForward`)
- Attachments (`GetAttachment`, `ItemOperations:Fetch`)
- Meeting requests embedded in mail
- Body preferences and truncation (`AirSyncBase:Body`)
- Draft folder sync; **EAS 16.0** draft edit detection (`CHANGE_TYPE_DRAFT`) and draft send via `POOMMAIL2:Send`
- **EAS 16.0** `Forwardee` objects on forward/reply
- GAL search (`Search`, `ResolveRecipients`)
- **EAS 16.0** mailbox `Find` with KQL parser (`Horde_ActiveSync_Find_Kql`) supporting boolean operators, property restrictions, dates, and size

### Calendar (EAS `Calendar` class)

[](#calendar-eas-calendar-class)

Handled in `horde/kronolith` (`Kronolith_Event::fromASAppointment()` / `toASAppointment()`) with logic in this library's `Message/Appointment` and `Message/Exception` classes.

- Create, update, delete appointments; recurrence and exceptions
- Attendees, reminders, categories, sensitivity, busy status
- Meeting responses (`MeetingResponse`)
- **EAS 16.0 instance model**: modified recurrence instances sync as separate items with top-level `InstanceId`; masters export only deleted-instance exceptions; `ClientUid` round-trip; bound exceptions visible in initial sync
- **EAS 16.0** `AirSyncBase:Location` (display name + coordinates)
- **EAS 16.0** inbound validation strips forbidden top-level fields (`uid`, `dtstamp`, `organizername`, `organizeremail`) instead of rejecting the item
- All-day event rules for 16.0 (date-only, no spurious timezone conversion)

### Contacts (`Contacts`)

[](#contacts-contacts)

- Personal address books and GAL
- Photo support via `ResolveRecipients` / Find picture options
- Standard vCard-style field mapping

### Tasks (`Tasks`) and Notes (`Notes`)

[](#tasks-tasks-and-notes-notes)

- Full folder sync and item CRUD through Nag and Mnemo
- Task recurrence (basic)

### Device management

[](#device-management)

- Provisioning and policy keys (`Provision`, `Settings`)
- Remote wipe status tracking
- Per-device logging (`perdevice` log type in Horde config)
- Device block/allow hooks (Horde `hooks.php`)

### State and performance

[](#state-and-performance)

- SQL (default) or MongoDB state backends
- Sync key / modseq change tracking
- `Ping` long-poll with configurable heartbeat bounds
- `SyncCache` for in-request collection state
- WBXML protocol logging at configurable verbosity

EAS 16.0 — what changed
-----------------------

[](#eas-160--what-changed)

Microsoft reworked several areas in 16.0. The following are implemented in this codebase:

AreaBehaviour**Calendar instances**Exceptions are first-class sync items, not only embedded in the series master**ClientUid**Persisted on events and exported on sync**Location**`AirSyncBase:Location` instead of plain string for 16.0+**Drafts**Content changes reported as `CHANGE_TYPE_DRAFT`; `send=true` sends via SMTP and removes draft**Find**Mailbox/GAL search; KQL parser maps common Outlook restrictions to IMAP search**SmartForward/Reply**`Forwardee` list support**Appointment validation**Forbidden inbound fields stripped per MS-ASCAL specHorde driver details (initial calendar UID list omits bound exceptions at 16.0+, `calendar_import()` unified return shape) live in `horde/core` and `horde/kronolith`.

EAS 16.1 — what changed
-----------------------

[](#eas-161--what-changed)

EAS 16.1 is a small delta on top of 16.0. The following are implemented in this codebase:

AreaBehaviour**Propose new time**`MeetingResponse` accepts `ProposedStartTime` / `ProposedEndTime`; outbound RFC5546 `METHOD=COUNTER`; inbound storage and sync of attendee proposals**DisallowNewTimeProposal**Exported on calendar appointments (≥14.0) from iCal `DISALLOW-COUNTER`; inbound proposals ignored when set**Account-only remote wipe**`Provision:AccountOnlyRemoteWipe` status flow; admin and user prefs UI (devices must negotiate ≥16.1)Horde driver, Kronolith, iTip, and IMP details live in `horde/core`, `horde/kronolith`, `horde/itip`, and `horde/imp`.

Using ActiveSync in a Horde deployment
--------------------------------------

[](#using-activesync-in-a-horde-deployment)

### 1. Enable and configure

[](#1-enable-and-configure)

In Horde administration → ActiveSync (or `var/config/horde/conf.php`):

```
$conf['activesync']['enabled'] = true;
$conf['activesync']['version'] = '16.1';   // global protocol ceiling (see above)
$conf['activesync']['storage'] = 'Sql';    // or 'Nosql' (Mongo)
$conf['activesync']['emailsync'] = true;
$conf['activesync']['auth']['type'] = 'basic';
// Optional: per-device version policy instead of per-user permissions
// $conf['activesync']['version_mode'] = 'device';
```

Per-user version limits are **not** preference keys — configure them under Permissions → ActiveSync → *Maximum ActiveSync protocol version*. See [Protocol version configuration](#protocol-version-configuration-horde).

Also configure IMAP/SMTP host hints for Autodiscover, logging path/level, and Ping heartbeat bounds. Full option descriptions are in `vendor/horde/horde/config/conf.xml` under the `activesync` tab.

History (`$conf['history']['enabled']`) must be enabled — ActiveSync relies on it for change timestamps.

### 2. Web server URL

[](#2-web-server-url)

Clients expect `/Microsoft-Server-ActiveSync`. Rewrite that path to Horde's RPC endpoint, for example:

```
/Microsoft-Server-ActiveSync  →  /horde/rpc.php

```

The RPC layer selects the ActiveSync backend when `server=ActiveSync` is passed (Apache/nginx configs usually add this; see [Horde ActiveSync wiki](http://wiki.horde.org/ActiveSync)).

Autodiscover is served from the same endpoint when the request URI contains `autodiscover/autodiscover`.

### 3. Client setup

[](#3-client-setup)

Point the device at your mail domain. With Autodiscover enabled (`autodiscovery` in config), iOS and Outlook discover the ActiveSync URL automatically. Otherwise configure the ActiveSync server URL manually.

Authentication is HTTP Basic against Horde by default (`auth.type = basic`).

### 4. Per-user access

[](#4-per-user-access)

Users need the **ActiveSync** permission in Horde. They can manage enrolled devices under Personal Preferences → ActiveSync (device list and wipe — not protocol version).

Administrators assign the maximum EAS version per user or group via **Permissions**, and optionally per device via `hooks.php` when `version_mode = device`.

For integrators — custom backends
---------------------------------

[](#for-integrators--custom-backends)

To use this library outside Horde (or with a minimal test stack):

1. Subclass `Horde_ActiveSync_Driver_Base` and implement the abstract methods for each collection class you support.
2. Provide a `Horde_ActiveSync_State_*` implementation (or use SQL/Mongo drivers from this package).
3. Instantiate the server:

```
$server = new Horde_ActiveSync(
    $driver,
    new Horde_ActiveSync_Wbxml_Decoder(fopen('php://input', 'r')),
    new Horde_ActiveSync_Wbxml_Encoder(fopen('php://output', 'w+')),
    $state,
    $httpRequest
);
$server->setSupportedVersion(Horde_ActiveSync::VERSION_SIXTEEN);
$server->setLogger($logger);
// Optional: implement versionCallback() on your driver for dynamic ceilings
$server->handleRequest($cmd, $device);
```

`Horde_ActiveSync_Driver_Mock` plus `Horde_ActiveSync_Driver_MockConnector` in this package provide a reference stack for unit and integration tests.

Import/export of calendar data is normally done by converting between `Horde_ActiveSync_Message_Appointment` and your domain model (in Horde, `Kronolith_Event`).

Development and tests
---------------------

[](#development-and-tests)

**Requirements:** PHP `^7.4 || ^8`, plus Horde packages listed in `composer.json`. Suggested packages for a full stack: `horde/imap_client`, `horde/db`, `horde/mail`.

Run unit tests from the package directory:

```
cd vendor/horde/activesync
php ../../../vendor/bin/phpunit --bootstrap ../../../vendor/autoload.php
```

Calendar EAS 16.0 import/export tests live in `horde/kronolith`:

```
php vendor/bin/phpunit \
  --bootstrap vendor/horde/kronolith/test/bootstrap.php \
  vendor/horde/kronolith/test/Kronolith/Unit/EventActiveSyncTest.php
```

WBXML fixtures and protocol-level tests are under `test/unit/` and `test/integration/`.

Enable protocol logging (`logging.level` / per-device log files) when debugging client issues — the logger records command names, collection IDs, and decoded metadata without dumping full message bodies at low levels.

Package layout
--------------

[](#package-layout)

```
lib/Horde/ActiveSync.php          Server class and protocol constants
lib/Horde/ActiveSync/
  Request/                        EAS command handlers
  Message/                        Item type WBXML mappings
  State/                          Sync state persistence (SQL, Mongo)
  Wbxml/                          Encoder, decoder, code pages
  Find/                           EAS 16.0 Find command helpers
  Driver/                         Base, Mock backends
migration/                        SQL schema for state tables
test/unit/                        PHPUnit tests
doc/Horde/ActiveSync/todo.md      Open work and Horde 6 refactor notes

```

License
-------

[](#license)

GPL-2.0-only. See [LICENSE](LICENSE).

References
----------

[](#references)

- [Microsoft Exchange ActiveSync protocol docs](https://learn.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-ascntc30)
- [Horde ActiveSync wiki](http://wiki.horde.org/ActiveSync)

###  Health Score

62

↑

FairBetter than 99% of packages

Maintenance94

Actively maintained with recent releases

Popularity29

Limited adoption so far

Community37

Small or concentrated contributor base

Maturity82

Battle-tested with a long release history

 Bus Factor1

Top contributor holds 89.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~32 days

Recently: every ~5 days

Total

146

Last Release

2d ago

Major Versions

2.41.5 → 3.0.0alpha22021-02-24

2.41.6 → v3.0.0alpha42022-05-18

2.41.9 → v3.0.0alpha62026-03-07

PHP version history (6 changes)2.8.5PHP &gt;=5.3.0

2.15.0PHP &gt;=5.3.0,&lt;=6.0.0alpha1

2.30.4PHP &gt;=5.3.0,&lt;=8.0.0alpha1

2.38.3PHP ^5.3 || ^7

3.0.0alpha2PHP ^7

v3.0.0alpha4PHP ^7.4 || ^8

### Community

Maintainers

![](https://www.gravatar.com/avatar/c943a083635c28520599075eaea7ede2d743b7697b76e84d6bdc37e52cc8249b?d=identicon)[yunosh](/maintainers/yunosh)

![](https://www.gravatar.com/avatar/c931cd02664859360478593450d6c473a05bb12b209dfacfc534cd13257cc7ef?d=identicon)[ralflang](/maintainers/ralflang)

![](https://www.gravatar.com/avatar/e4f6c6771993db2ed500959b42353f6cf6a2ca0406d9617f7ae680f4504faa4a?d=identicon)[horde](/maintainers/horde)

![](https://www.gravatar.com/avatar/a7767adb66b45f2f05bcd44d49bc4e67efacd9ce05b161ce2d481d5dd6af025c?d=identicon)[mrubinsk](/maintainers/mrubinsk)

![](https://www.gravatar.com/avatar/816e2b926f25f8cd2939054c7a7173011b4303d690e25ab61bf33cf8c7cf71ae?d=identicon)[tdannhauer](/maintainers/tdannhauer)

---

Top Contributors

[![mrubinsk](https://avatars.githubusercontent.com/u/66822?v=4)](https://github.com/mrubinsk "mrubinsk (3989 commits)")[![yunosh](https://avatars.githubusercontent.com/u/379318?v=4)](https://github.com/yunosh "yunosh (282 commits)")[![TDannhauer](https://avatars.githubusercontent.com/u/6716033?v=4)](https://github.com/TDannhauer "TDannhauer (63 commits)")[![ralflang](https://avatars.githubusercontent.com/u/646976?v=4)](https://github.com/ralflang "ralflang (56 commits)")[![slusarz](https://avatars.githubusercontent.com/u/381003?v=4)](https://github.com/slusarz "slusarz (41 commits)")[![amulet1](https://avatars.githubusercontent.com/u/18431541?v=4)](https://github.com/amulet1 "amulet1 (12 commits)")[![thomasjfox](https://avatars.githubusercontent.com/u/1146758?v=4)](https://github.com/thomasjfox "thomasjfox (11 commits)")[![wrobel](https://avatars.githubusercontent.com/u/10232?v=4)](https://github.com/wrobel "wrobel (6 commits)")[![midahp](https://avatars.githubusercontent.com/u/19747890?v=4)](https://github.com/midahp "midahp (3 commits)")[![renan](https://avatars.githubusercontent.com/u/28046?v=4)](https://github.com/renan "renan (2 commits)")[![powercycle](https://avatars.githubusercontent.com/u/2614846?v=4)](https://github.com/powercycle "powercycle (1 commits)")[![Bullja](https://avatars.githubusercontent.com/u/3464071?v=4)](https://github.com/Bullja "Bullja (1 commits)")[![cheese1](https://avatars.githubusercontent.com/u/6437726?v=4)](https://github.com/cheese1 "cheese1 (1 commits)")[![martament](https://avatars.githubusercontent.com/u/38855494?v=4)](https://github.com/martament "martament (1 commits)")[![aluxnimm](https://avatars.githubusercontent.com/u/12834530?v=4)](https://github.com/aluxnimm "aluxnimm (1 commits)")

---

Tags

exchangesynccontactsoutlookeas

### Embed Badge

![Health badge](/badges/horde-activesync/health.svg)

```
[![Health](https://phpackages.com/badges/horde-activesync/health.svg)](https://phpackages.com/packages/horde-activesync)
```

###  Alternatives

[horde/kronolith

Calendar and scheduling application

101.5k4](/packages/horde-kronolith)[horde/horde

Horde base application

583.0k70](/packages/horde-horde)[horde/imp

Webmail application

261.3k](/packages/horde-imp)[horde/imap_client

IMAP client library

275.5k19](/packages/horde-imap-client)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
