# Mail Calendar Sync for Nextcloud Automatically apply calendar invitation responses (iMIP) received via email to your Nextcloud calendar events. ## What it does When someone responds to a calendar invitation you sent (Accept, Decline, Tentative), this app automatically updates the attendee's participation status in your calendar — no manual action needed. ### Supported iMIP methods | Method | Behavior | |--------|----------| | **REPLY** | Updates attendee status (ACCEPTED/DECLINED/TENTATIVE) on an **existing** event in your calendar. The event must already exist (matched by UID). | | **REQUEST** | Updates an existing event with new details (time changes, etc). Optionally auto-adds new invitations to your calendar. | | **CANCEL** | Marks an existing event as CANCELLED when the organizer cancels it. | ### Key safety feature **The app always checks that the event UID from the email response already exists in your calendar before applying any changes.** This prevents stale or spoofed responses from creating phantom events. ## Requirements - Nextcloud 28 or later - [Nextcloud Mail app](https://apps.nextcloud.com/apps/mail) installed and configured with at least one account - At least one writable calendar - Background jobs (cron) configured and running ## Installation 1. Copy the `mail_calendar_sync` folder to your Nextcloud `apps/` directory: ```bash cp -r mail_calendar_sync /var/www/nextcloud/apps/ ``` 2. Set proper permissions: ```bash chown -R www-data:www-data /var/www/nextcloud/apps/mail_calendar_sync ``` 3. Enable the app: ```bash sudo -u www-data php occ app:enable mail_calendar_sync ``` 4. Run the database migration: ```bash sudo -u www-data php occ migrations:migrate mail_calendar_sync ``` ## Configuration Each user configures the app individually: 1. Go to **Settings → Groupware → Mail Calendar Sync** 2. Enable the sync 3. Select which **Mail account** to scan for responses 4. Select which **Calendar** to apply updates to 5. Optionally enable **auto-accept** to automatically add new invitations 6. Click **Save** ### Manual sync Click the **"Sync now"** button in settings to trigger an immediate scan instead of waiting for the background job. ## How it works ``` ┌─────────────────┐ ┌────────────────┐ ┌────────────────────┐ │ Background Job │ │ Mail Service │ │ Calendar Service │ │ (every 10 min) │────>│ (IMAP fetch) │────>│ (CalDAV lookup) │ └─────────────────┘ └────────────────┘ └────────────────────┘ │ │ │ │ 1. Load user config │ 2. Find iMip messages │ 3. Search by UID │ │ in INBOX │ in user calendars │ │ │ │ │ 4. Extract ICS from │ 5. Verify event exists │ │ MIME parts │ before updating │ │ │ │ │ │ 6. Update attendee │ │ │ PARTSTAT └───────────────────────┴────────────────────────┘ ``` 1. A **background job** runs every 10 minutes 2. For each user with sync enabled, it queries the **Mail app's database** for recent messages flagged as iMip 3. For unprocessed messages, it connects via **IMAP** to fetch the actual email and extract `text/calendar` MIME parts 4. It parses the ICS data using the **sabre/vobject** library 5. Based on the METHOD (REPLY/REQUEST/CANCEL), it: - Searches the user's calendars for the event by **UID** - Only proceeds if the event **already exists** (for REPLY and CANCEL) - Updates the attendee participation status or event data - Writes the updated event back via `ICreateFromString` 6. Each processed message is tracked to avoid re-processing ## Architecture ``` mail_calendar_sync/ ├── appinfo/ │ ├── info.xml # App metadata & dependencies │ └── routes.php # API routes ├── lib/ │ ├── AppInfo/ │ │ └── Application.php # App bootstrap │ ├── BackgroundJob/ │ │ └── ProcessImipResponsesJob.php # Cron job (every 10 min) │ ├── Controller/ │ │ └── SettingsController.php # REST API for settings UI │ ├── Db/ │ │ ├── Config.php # User config entity │ │ ├── ConfigMapper.php # Config DB mapper │ │ ├── LogEntry.php # Activity log entity │ │ ├── LogMapper.php # Log DB mapper │ │ ├── ProcessedMessage.php # Processed msg entity │ │ └── ProcessedMessageMapper.php # Processed msg DB mapper │ ├── Migration/ │ │ └── Version1000Date20250209000000.php # DB schema │ ├── Service/ │ │ ├── CalendarService.php # Calendar lookup & update │ │ ├── MailService.php # IMAP fetch & ICS extraction │ │ └── SyncService.php # Orchestrates the full flow │ └── Settings/ │ └── PersonalSettings.php # Settings page registration ├── templates/ │ └── personal-settings.php # Settings page HTML ├── js/ │ └── personal-settings.js # Settings page JavaScript ├── css/ │ └── personal-settings.css # Settings page styles ├── composer.json └── README.md ``` ## Database tables | Table | Purpose | |-------|---------| | `oc_mcs_config` | Per-user configuration (enabled, mail account, calendar, auto-accept) | | `oc_mcs_log` | Activity log of all processed responses (kept 30 days) | | `oc_mcs_processed` | Tracks which email messages have been processed (kept 90 days) | ## Troubleshooting ### No messages being processed - Ensure the Mail app has synced recent messages (check Mail app directly) - Verify that incoming invitation responses have `text/calendar` MIME parts - Check that the Mail app flags messages with `$imip` (this happens during Mail sync) - Try clicking "Sync now" in the settings to trigger an immediate scan ### Events not being updated - The event **must already exist** in the selected calendar (matched by UID) - The attendee email in the response must match an attendee in the existing event - Check the activity log in settings for "SKIPPED" entries with explanations ### Check server logs ```bash sudo -u www-data php occ log:tail --level=debug | grep -i "mail_calendar_sync\|MailCalendarSync" ``` ## License AGPL-3.0-or-later