{"id":16243756,"url":"https://github.com/mhthies/ews-caldav-sync","last_synced_at":"2025-03-19T17:32:49.528Z","repository":{"id":95230751,"uuid":"407844394","full_name":"mhthies/ews-caldav-sync","owner":"mhthies","description":"Python script for incremental one-way synchronization of a Microsoft Exchange calendar to a CalDAV server","archived":false,"fork":false,"pushed_at":"2024-02-10T17:09:06.000Z","size":32,"stargazers_count":27,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-17T09:45:33.034Z","etag":null,"topics":["caldav","calendar","ews","exchange","ical"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mhthies.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-09-18T11:42:47.000Z","updated_at":"2025-02-17T22:33:50.000Z","dependencies_parsed_at":"2024-02-10T18:26:46.292Z","dependency_job_id":"cdcf9eb7-1305-4b0c-a522-e15244ce64ea","html_url":"https://github.com/mhthies/ews-caldav-sync","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mhthies%2Fews-caldav-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mhthies%2Fews-caldav-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mhthies%2Fews-caldav-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mhthies%2Fews-caldav-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mhthies","download_url":"https://codeload.github.com/mhthies/ews-caldav-sync/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244474122,"owners_count":20458590,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caldav","calendar","ews","exchange","ical"],"created_at":"2024-10-10T14:16:12.298Z","updated_at":"2025-03-19T17:32:49.235Z","avatar_url":"https://github.com/mhthies.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Exchange Calendar to CalDAV sync\n\nThis script synchronizes the default calendar of a Microsoft Exchange account with a calendar on a CalDAV server.\nCurrently, only one-way synchronization from Exchange to CalDAV is supported.\nThe synchronization is restricted to events – no tasks – but includes event series.\n\nThe script uses the prodigious [exchangelib](https://github.com/ecederstrand/exchangelib) to connect to Exchange's EWS interface and fetch incremental changes from the calendar folder.\nIt then uses the iCal representation in the `mimedata` field, provided by Exchange, of each changed calendar entry to add or update the entry in the CalDAV calendar.\n[python-caldav](https://github.com/python-caldav/caldav) is used for interfacing with the CalDAV server.\n\nBefore storing the entry, a few changes are made to the data, using the [icalendar](https://github.com/collective/icalendar) library:\n\n- A new custom property `X-EWSSYNC-ITEMID` with the EWS ItemId is added to query items for propageting item deletion from the Exchange server\n- The `TRANSP` property is set to `TRANSPARENT` when the event's status is \"FREE\" according to the `'X-MICROSOFT-CDO-BUSYSTATUS` property\n- The `STATUS` property is set to `TENTATIVE` when the event's status is \"TENTATIVE\" according to the `'X-MICROSOFT-CDO-BUSYSTATUS` property\n\n## Setup \u0026 Usage\n\n- Clone this repository\n- Optional: create a Python virtual environment and activate it\n  ```bash\n  python3 -m virtualenv -p python3 venv \u0026\u0026 . venv/bin/activate\n  ```\n- Install the dependencies\n  ```bash\n  pip install -r requirements.txt\n  ```\n- Copy and customize the config file with the credentials of your Exchange and CalDAV accounts\n  ```bash\n  cp config.example.toml config.toml \u0026\u0026 $EDITOR config.toml\n  ``` \n- Run `ews_calendar_sync.py` regularly to synchronize changes in the EWS calendar to the CalDAV calendar.\n  Typically, you would do this using a cron job (`crontab`, etc.) or a systemd Timer Unit.\n  Make sure to use the Python interpreter from the virtualenv (if you use one) and run the process in the working directory where the config.toml file is located.\n\nAlternatively, using the Docker container:\n\n- Download the [config file template](./config.example.toml) from this repository, save it as `config.toml` and customize it with the credentials of your Exchange and CalDAV accounts\n- Run `ews_calendar_sync` via docker:\n  ```bash\n  docker run -it --rm --name ews-caldav-sync -v ./config.toml:/app/config.toml -v ./syncstate.txt:/app/syncstate.txt ghcr.io/mhthies/ews-caldav-sync:latest\n  ```\n\nOn the first execution, the script will synchronize all contents of the Exchange Calendar to the CalDAV calendar.\nThis may take a while (currently, there is no parallelization implemented).\nOn every subsequent execution, only changes (new events, updated events, deleted events) since the last execution are propagated to the CalDAV server.\nThis is achieved by using the EWS SyncFolderitems service and storing the sync_state token from the Exchange server in the `statefile` as defined in the config file.\n\n## Known Issues\n\nIssues when syncing to a Nextcloud calendar:\n- Nextcloud (since version 22) forbids recreating/updating a calendar item with the id of a previously deleted item.\n  (\"400 Bad Request: Deleted calendar object with uid already exists in this calendar collection.\")\n  Updates/recreations of previously deleted events in the Exchange calendar – which often happen due to meeting invitation updates – will result in an exception of the ews_calendar_sync script and the update being ignored in the CalDAV calendar.\n  This issue is caused by Nextcloud's new calendar recycle bin feature and is already reported to Nextcloud: https://github.com/nextcloud/server/issues/30096\n- ~~Nextcloud sets ContentType to `text/html` in emtpy responses. The caldav library logs this as a warning to the console (\"unexpected content type from server: text/html; charset=UTF-8. Please raise an issue[…]\").\n  Reported to caldav library: https://github.com/python-caldav/caldav/issues/142~~\n  Fixed with `caldav` version 0.8.1.\n\nGeneral issues:\n- ~~Events with with a slash in their iCal UID cause errors when being uploaded to the CalDAV server, due to unescaped slashes in the CalDAV object path.\n  Exchange does not create such UIDs itself, but they may be imported with existing iCal datasets into Outlook/Exchange\n  Reported to caldav library: https://github.com/python-caldav/caldav/issues/143~~\n  Mostly fixed with `caldav` version 0.8.1.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmhthies%2Fews-caldav-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmhthies%2Fews-caldav-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmhthies%2Fews-caldav-sync/lists"}