Bonjour,
Je cherche à créer des événements dans un calendrier géré sous Zimbra depuis une application écrite en C#.
J'utilise pour se faire le protocole CalDAV (RFC 4791).
Pour le moment, j'arrive sans problème à interroger le serveur pour trouver l'URI du calendrier de l'utilisateur courant, à lister les événements du calendrier pour un intervalle de dates données.
En revanche, lorsque je tente de créer un événement, ça marche plus : erreur 401.
Voici l'exemple de la RFC :
[fixed]
PUT /home/lisa/calendars/events/qwue23489.ics HTTP/1.1
If-None-Match: *
Host: cal.example.com
Content-Type: text/calendar
Content-Length: xxxx
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:20010712T182145Z-123401@example.com
DTSTAMP:20060712T182145Z
DTSTART:20060714T170000Z
DTEND:20060715T040000Z
SUMMARY:Bastille Day Party
END:VEVENT
END:VCALENDAR
[/fixed]
Donc moi j'ai un peu adapté, en modifiant notamment l'URI du fichier ICS à créer, de façon à ce qu'il corresponde au même pattern que ceux déjà existants (que je récupère en chargement les événements).
[fixed]
PUT /dav/<user>/Calendar/test1.ics HTTP/1.1
If-None-Match: *
Host: <host>
Content-Type: text/calendar
Content-Length: <length>
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN
BEGIN:VEVENT
UID:20010712T182145Z-123401@example.com
DTSTAMP:20140712T182145Z
DTSTART:20140714T170000Z
DTEND:20140715T040000Z
SUMMARY:Bastille Day Party
END:VEVENT
END:VCALENDAR
[/fixed]
Mais l'erreur subsiste.
Voici mon code :
Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public string CreateEvent(CalDAVEvent evt) { List<KeyValuePair<string, string>> headers = new List<KeyValuePair<string, string>>(); headers.Add(new KeyValuePair<string, string>("If-None-Match", "*")); return SendQuery("PUT", evt.Serialize(), new Uri(string.Format("{0}test{1}.ics", CalendarAddress.ToString(), evt.ETAG)), headers, "text/calendar"); } private string SendQuery(string method, string query, Uri address, List<KeyValuePair<string, string>> headers, string contenttype) { HttpWebRequest hr = HttpWebRequest.Create(address) as HttpWebRequest; hr.Credentials = Credentials; hr.Method = method; hr.Host = address.Host; hr.ContentType = contenttype; foreach (KeyValuePair<string, string> kvp in headers) { hr.Headers.Add(kvp.Key, kvp.Value); } byte[] bytes = Encoding.UTF8.GetBytes((string)query); hr.ContentLength = bytes.Length; Stream RequestStream = hr.GetRequestStream(); RequestStream.Write(bytes, 0, bytes.Length); RequestStream.Close(); HttpWebResponse Response = (HttpWebResponse)hr.GetResponse(); // Ça pète ici Stream ResponseStream = Response.GetResponseStream(); byte[] buffer = new byte[4096]; StringBuilder sb = new StringBuilder(); int r = 0; while ((r = ResponseStream.Read(buffer, 0, buffer.Length)) > 0) { sb.Append(Encoding.UTF8.GetString(buffer, 0, r)); } ResponseStream.Close(); return sb.ToString(); }
Pourtant, quand j'appelle SendQuery() pour charger les événements, j'ai pas de souci :
Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 public List<CalDAVEvent> GetEventsFromLastModified(DateTime from) { List<CalDAVEvent> ret = new List<CalDAVEvent>(); string range = string.Format("<C:prop-filter name=\"DTSTAMP\"><C:time-range start=\"{0}\" end=\"{1}\"/></C:prop-filter>", ISO8601DateTime.ToString(from), ISO8601DateTime.ToString(DateTime.Now.AddDays(1))); List<KeyValuePair<string, string>> headers = new List<KeyValuePair<string, string>>(); headers.Add(new KeyValuePair<string, string>("Depth", "1")); string res = SendQuery("REPORT", string.Format("<?xml version=\"1.0\" encoding=\"utf-8\" ?><C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\"><D:prop xmlns:D=\"DAV:\"><D:getetag/><C:calendar-data/></D:prop><C:filter><C:comp-filter name=\"VCALENDAR\"><C:comp-filter name=\"VEVENT\">{0}</C:comp-filter></C:comp-filter></C:filter></C:calendar-query>", range), CalendarAddress, headers, "application/xml; charset=\"utf-8\""); XmlDocument doc = new XmlDocument(); doc.LoadXml(res); XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("D", "DAV:"); nsmgr.AddNamespace("C", "urn:ietf:params:xml:ns:caldav"); foreach (XmlNode el in doc.SelectNodes("/D:multistatus/D:response/D:propstat/D:prop", nsmgr)) { ret.Add(new CalDAVEvent(el.SelectSingleNode("C:calendar-data", nsmgr).InnerText, el.SelectSingleNode("D:getetag", nsmgr).InnerText)); } return ret; }
C'est donc bien le PUT ou l'adresse qui lui plaît pas.
J'ai raté un truc dans la RFC ? Notamment en ce qui concerne l'adresse... L'exemple d'ajout a une adresse complètement différente des autres exemple. Donc pas évident de trouver une logique de nommage.
Quand j'essaie de faire Calendar/events/test1.ics j'ai doit à un 404, signe que "events" n'existe pas, et que j'ai bel et bien pas le droit de créer test1.ics dans Calendar.
Si je vire le "If-Not-Match" en header, j'ai toujours l'erreur : d'après la RFC, dans ce cas, je devrais écraser un événement déjà existant sans erreur. Donc c'est pas à cause d'un événement déjà existant...
J'ai installé sur mon PC le client CalDav "eM Client" et lui il arrive très bien à créer des événements. C'est donc pas une configuration qui bloque sur Zimbra, mais visiblement une subtilité qui m'échappe...If the client intends to create a new
non-collection resource, such as a new VEVENT, the client SHOULD use
the HTTP request header "If-None-Match: *" on the PUT request. The
Request-URI on the PUT request MUST include the target collection,
where the resource is to be created, plus the name of the resource in
the last path segment. The "If-None-Match: *" request header ensures
that the client will not inadvertently overwrite an existing resource
if the last path segment turned out to already be used.
Partager