Перейти к содержанию

Провайдер CalDAV

Документ описывает провайдер CalDAV: поддерживаемые серверы (стандартный CalDAV, Яндекс, Kerio, CommuniGate), подключение через почтовый ящик и Basic Auth, операции с событиями по протоколу CalDAV, формат ICS и маппинг полей. Для администраторов и инженеров, настраивающих интеграцию внешних календарей с 1Формой.

1. Архитектура

Провайдер CalDAV состоит из двух уровней:

  • высокоуровневый провайдер — адаптер между календарём 1Формы и протоколом CalDAV (преобразует события 1Ф в формат CalDAV и обратно);
  • низкоуровневый HTTP-клиент — реализация протокола CalDAV под конкретный тип сервера (стандартный, Яндекс, Kerio, CommuniGate).

Над ними — сервисный слой, который по почтовому ящику пользователя выбирает нужный тип клиента.

2. Поддерживаемые серверы

В таблице — особенности подключения для каждого типа CalDAV-сервера:

Сервер Особенности
Стандартный CalDAV Стандартная реализация протокола без особенностей
Яндекс Обнаружение календарей идёт через HTTP GET (не PROPFIND), ответ разбирается как HTML. Не принимает участников при создании — событие записывается в два приёма
Kerio Отбрасывает @домен из логина. Чтение через запрос REPORT. Идентификатор (UID) берётся из адреса события — UID в теле может отличаться
CommuniGate К адресу добавляется /CalDav/. Чтение — REPORT с откатом на PROPFIND. Удаление из серии помечает событие как отменённое (CANCELLED), не удаляя его

Google Calendar не поддерживается: только Basic Auth, OAuth2 не реализован.

3. Аутентификация и подключение

Подключение выполняется через почтовый ящик (mailbox) пользователя с авторизацией Basic Auth (логин и пароль). Пароль хранится в зашифрованном виде. Адрес CalDAV берётся из персональной настройки ящика или из настроек почтового сервера.

Учитываются только почтовые ящики, у которых:

  • задан CalDAV-логин;
  • на почтовом сервере включён признак CalDAV;
  • ящик не отключён.

Связанные таблицы БД:

Таблица Назначение
CalDavProviders Справочник типов CalDAV-серверов (Id, Name)
EmailMailServers Почтовые серверы; признак IsCalDav и адрес CalDavAddress, ссылка на тип через CalDavProviderId
EmailMailBoxes (ящик пользователя) CalDavLogin, CalDavPassword (зашифрован), CalDavAddress (персональный адрес)

4. Операции с событиями

Все операции выполняются по протоколу CalDAV (HTTP-запросы PUT / PROPFIND / REPORT / DELETE).

Создание:

  1. Выбирается календарь (по наличию «calendar» в адресе, иначе — первый доступный).
  2. Событие сериализуется в формат ICS.
  3. Выполняется HTTP PUT события по адресу {адрес-календаря}/{идентификатор}.ics.
  4. Для Яндекса — обходной путь: событие создаётся без участников, затем перечитывается и обновляется с участниками.

Чтение:

  • одно событие — запрос PROPFIND, разбор и десериализация ICS;
  • список — запрос REPORT с calendar-query.

Изменение:

Текущее событие читается, изменяется и записывается обратно. Для отдельного экземпляра повторяющейся серии создаётся изменённый экземпляр с RecurrenceId, при необходимости правится список исключений (EXDATE). Записывается полный ICS (все экземпляры в одном VCALENDAR).

Удаление:

  • одиночное событие — HTTP DELETE;
  • из серии — добавление даты в список исключений (EXDATE) или удаление изменённого экземпляра с последующей записью.

Ответ на приглашение:

Действие Организатор Участник
Принять Статус ответа → ACCEPTED
Отклонить = удаление Статус ответа → DECLINED
Принять под вопросом Статус ответа → TENTATIVE
Отменить = удаление

5. Формат ICS

Порядок полей события (VEVENT):

BEGIN:VEVENT
  UID, ATTENDEE[], CATEGORIES, RRULE[], CLASS, CREATED,
  DESCRIPTION, DTEND, DTSTAMP, DTSTART,
  LAST-MODIFIED, LOCATION, ORGANIZER, PRIORITY, SEQUENCE,
  RECURRENCE-ID, EXDATE[], STATUS, SUMMARY, TRANSP, URL,
  [пользовательские свойства], VALARM[]
END:VEVENT

Формат дат: yyyyMMddTHHmmssZ (UTC). Строки длиннее 75 символов переносятся по правилам ICS (line folding).

Признак «весь день» определяется как Начало == Конец или Начало + 1 день == Конец (второе условие — для Яндекса).

6. Маппинг полей

Соответствие полей события 1Ф и полей VEVENT в ICS:

Поле 1Forma Поле VEVENT Примечание
Start DTSTART В UTC
End DTEND В UTC
Subject SUMMARY
TextBody DESCRIPTION Без конвертации HTML → текст (см. FAQ: HTML-теги в описании)
IsAllDayEvent IsAllDay
Location LOCATION
IsPrivate CLASS PRIVATE / PUBLIC
Organizer ORGANIZER По справочнику пользователей
RequiredAttendees[] ATTENDEE[] Email + имя, статус ответа = NeedsAction
Recurrence RRULE По типу повторяемости

7. Механизм синхронизации

Модель: чтение по запросу. Автоматической синхронизации нет:

  • при каждом открытии календаря выполняется полный запрос к CalDAV-серверу;
  • метки изменений (Ctag / SyncToken) считываются, но для инкрементальной синхронизации не используются;
  • push-подписок на CalDAV-сервер нет;
  • повторяющиеся события разворачиваются на стороне 1Формы, а не на сервере.

Статус занятости (absence) подтягивается тоже по запросу: для каждого пользователя запрашиваются события на сегодня, обработка идёт пачками по 20 человек.

Уведомления интерфейса отправляются разово после каждой операции создания / изменения / удаления (не потоково).

8. Ограничения и известные проблемы

Критичные

Ограничения, заметные пользователю или влияющие на работоспособность интеграции:

# Проблема Детали
1 HTML в DESCRIPTION Описание пишется в поле DESCRIPTION как есть, а по стандарту RFC 5545 это обычный текст. Сторонние календари показывают HTML-теги. См. FAQ: HTML-теги в описании
2 Нет инкрементальной синхронизации Каждый запрос — полный REPORT/PROPFIND. На больших календарях дорого
3 Google Calendar не поддерживается Только Basic Auth, нет OAuth2

Архитектурные

Особенности реализации, которые стоит учитывать при настройке:

# Проблема Детали
4 Развёртывание повторяемости нестандартное Не полностью соответствует RFC; нет относительного ежемесячного правила (например, «третий понедельник месяца»)
5 Нет VTIMEZONE Все даты переводятся в UTC, информация о часовом поясе в ICS не передаётся
6 Потеря вложений Вложения (ATTACH) читаются, но при записи не сохраняются
7 Шифрование паролей Единый ключ шифрования на всю систему, не отдельный на пользователя

Серверо-специфичные

Особенности отдельных типов CalDAV-серверов:

# Проблема Детали
8 Яндекс: участники при создании Не принимаются — запись в два приёма (создание + обновление)
9 Kerio: расхождение UID UID в адресе и в теле события могут отличаться — берётся из адреса
10 CommuniGate: смешанные запросы REPORT с откатом на PROPFIND
11 Выбор календаря Эвристика — поиск «calendar» в адресе; отдельного выбора в интерфейсе нет
12 Ложный признак «весь день» Правило Начало + 1 день == Конец срабатывает на событиях ровно с полуночи

9. Диагностика

Частые симптомы при работе CalDAV-интеграции и где искать причину:

Симптом Где смотреть
Пустой список событий Логи CalDAV-запросов (HTTP-ответы). Проверить ящик: CalDavLogin, CalDavAddress, признак CalDAV на сервере
Встреча не создаётся при включённом CalDAV Проверить выбор календаря (поиск «calendar» в адресе) и участников для Яндекса
HTML-теги в описании Известное ограничение, см. FAQ
Ошибки аутентификации Логин и пароль (Basic Auth) в настройках почтового ящика

Диагностический SQL

Запросы для проверки настроек CalDAV у пользователя:

-- Почтовые ящики пользователя с CalDAV
select m.ID, m.EmailLogin, m.CalDavLogin, m.CalDavAddress,
       s.Name as ServerName, s.CalDavAddress as ServerCalDavAddress,
       s.IsCalDav, p.Name as ProviderName
from EmailMailBoxes m
join EmailMailServers s on m.MailServerID = s.ID
left join CalDavProviders p on s.CalDavProviderId = p.Id
where m.UserID = @userId
  and m.CalDavLogin is not null
  and s.IsCalDav = 1
  and m.Disabled = 0;

-- Справочник типов CalDAV-серверов
select * from CalDavProviders;

Связанные документы: