7.0 KiB
7.0 KiB
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 installed and configured with at least one account
- At least one writable calendar
- Background jobs (cron) configured and running
Installation
- Copy the
mail_calendar_syncfolder to your Nextcloudapps/directory:
cp -r mail_calendar_sync /var/www/nextcloud/apps/
- Set proper permissions:
chown -R www-data:www-data /var/www/nextcloud/apps/mail_calendar_sync
- Enable the app:
sudo -u www-data php occ app:enable mail_calendar_sync
- Run the database migration:
sudo -u www-data php occ migrations:migrate mail_calendar_sync
Configuration
Each user configures the app individually:
- Go to Settings → Groupware → Mail Calendar Sync
- Enable the sync
- Select which Mail account to scan for responses
- Select which Calendar to apply updates to
- Optionally enable auto-accept to automatically add new invitations
- 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
└───────────────────────┴────────────────────────┘
- A background job runs every 10 minutes
- For each user with sync enabled, it queries the Mail app's database for recent messages flagged as iMip
- For unprocessed messages, it connects via IMAP to fetch the actual email and extract
text/calendarMIME parts - It parses the ICS data using the sabre/vobject library
- 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
- 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/calendarMIME 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
sudo -u www-data php occ log:tail --level=debug | grep -i "mail_calendar_sync\|MailCalendarSync"
License
AGPL-3.0-or-later