indent -kr -i8 -brf -bbb -fnc -l132 -nce on all of webcit-classic
[citadel.git] / webcit / dav_propfind.c
1
2 /*
3  * Handles GroupDAV and CalDAV PROPFIND requests.
4  *
5  * A few notes about our XML output:
6  *
7  * --> Yes, we are spewing tags directly instead of using an XML library.
8  *     Whining about it will be summarily ignored.
9  *
10  * --> XML is deliberately output with no whitespace/newlines between tags.
11  *     This makes it difficult to read, but we have discovered clients which
12  *     crash when you try to pretty it up.
13  *
14  * References:
15  * http://www.ietf.org/rfc/rfc4791.txt
16  * http://blogs.nologin.es/rickyepoderi/index.php?/archives/14-Introducing-CalDAV-Part-I.html
17  * https://msdn.microsoft.com/en-us/library/aa142960(v=exchg.65).aspx
18  *
19  * Copyright (c) 2005-2017 by the citadel.org team
20  *
21  * This program is open source software; you can redistribute it and/or
22  * modify it under the terms of the GNU General Public License version 3.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  */
29
30 #include "webcit.h"
31 #include "webserver.h"
32 #include "dav.h"
33
34 /*
35  * Given an encoded UID, translate that to an unencoded Citadel EUID and
36  * then search for it in the current room.  Return a message number or -1
37  * if not found.
38  *
39  */
40 long locate_message_by_uid(const char *uid) {
41         char buf[256];
42         char decoded_uid[1024];
43         long retval = (-1L);
44
45         /* decode the UID */
46         euid_unescapize(decoded_uid, uid);
47
48         /* ask Citadel if we have this one */
49         serv_printf("EUID %s", decoded_uid);
50         serv_getln(buf, sizeof buf);
51         if (buf[0] == '2') {
52                 retval = atol(&buf[4]);
53         }
54
55         return (retval);
56 }
57
58
59 /*
60  * IgnoreFloor: set to 0 or 1 _nothing else_
61  * Subfolders: direct child floors will be put here.
62  */
63 const folder *GetRESTFolder(int IgnoreFloor, HashList * Subfolders) {
64         wcsession *WCC = WC;
65         void *vFolder;
66         const folder *ThisFolder = NULL;
67         const folder *FoundFolder = NULL;
68         const folder *BestGuess = NULL;
69         int nBestGuess = 0;
70         HashPos *itd, *itfl;
71         StrBuf *Dir;
72         void *vDir;
73         long len;
74         const char *Key;
75         int iRoom, jURL, urlp;
76         int delta;
77
78 /*
79  * Guess room: if the full URL matches a room, list thats it. We also need to remember direct sub rooms.
80  * if the URL is longer, we need to find the "best guess" so we can find the room we're in, and the rest
81  * of the URL will be uids and so on.
82  */
83         itfl = GetNewHashPos(WCC->Floors, 0);
84         urlp = GetCount(WCC->Directory);
85
86         while (GetNextHashPos(WCC->Floors, itfl, &len, &Key, &vFolder) && (ThisFolder == NULL)) {
87                 ThisFolder = vFolder;
88                 if (!IgnoreFloor &&     /* so we can handle legacy URLS... */
89                     (ThisFolder->Floor != WCC->CurrentFloor))
90                         continue;
91
92                 if (ThisFolder->nRoomNameParts > 1) {
93                         /*TODO: is that number all right? */
94 //                      if (urlp - ThisFolder->nRoomNameParts != 2) {
95 //                              if (BestGuess != NULL)
96 //                                      continue;
97 //ThisFolder->name
98 //                              itd  = GetNewHashPos(WCC->Directory, 0);
99 //                              GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir); //TODO: how many to fast forward?
100 //                      }
101                         itd = GetNewHashPos(WCC->Directory, 0);
102                         GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir); //TODO: how many to fast forward?
103
104                         for (iRoom = 0, /* Fast forward the floorname as we checked it above: */ jURL = IgnoreFloor;
105                              (iRoom <= ThisFolder->nRoomNameParts) && (jURL <= urlp);
106                              iRoom++, jURL++, GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir)) {
107                                 Dir = (StrBuf *) vDir;
108                                 if (strcmp(ChrPtr(ThisFolder->RoomNameParts[iRoom]), ChrPtr(Dir)) != 0) {
109                                         DeleteHashPos(&itd);
110                                         continue;
111                                 }
112                         }
113                         DeleteHashPos(&itd);
114                         /* Gotcha? */
115                         if ((iRoom == ThisFolder->nRoomNameParts) && (jURL == urlp)) {
116                                 FoundFolder = ThisFolder;
117                         }
118                         /* URL got more parts then this room, so we remember it for the best guess */
119                         else if ((jURL <= urlp) && (ThisFolder->nRoomNameParts <= nBestGuess)) {
120                                 BestGuess = ThisFolder;
121                                 nBestGuess = jURL - 1;
122                         }
123                         /* Room has more parts than the URL, it might be a sub-room? */
124                         else if (iRoom < ThisFolder->nRoomNameParts) {  //// TODO: ThisFolder->nRoomNameParts == urlp - IgnoreFloor???
125                                 Put(Subfolders, SKEY(ThisFolder->name),
126                                     /* Cast away const, its a reference. */
127                                     (void *) ThisFolder, reference_free_handler);
128                         }
129                 }
130                 else {
131                         delta = GetCount(WCC->Directory) - ThisFolder->nRoomNameParts;
132                         if ((delta != 2) && (nBestGuess > 1))
133                                 continue;
134
135                         itd = GetNewHashPos(WCC->Directory, 0);
136
137                         if (!GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir) || (vDir == NULL)) {
138                                 DeleteHashPos(&itd);
139
140                                 syslog(LOG_DEBUG, "5\n");
141                                 continue;
142                         }
143                         DeleteHashPos(&itd);
144                         Dir = (StrBuf *) vDir;
145                         if (strcmp(ChrPtr(ThisFolder->name), ChrPtr(Dir))
146                             != 0) {
147                                 DeleteHashPos(&itd);
148
149                                 syslog(LOG_DEBUG, "5\n");
150                                 continue;
151                         }
152                         DeleteHashPos(&itfl);
153                         DeleteHashPos(&itd);
154                         if (delta != 2) {
155                                 nBestGuess = 1;
156                                 BestGuess = ThisFolder;
157                         }
158                         else
159                                 FoundFolder = ThisFolder;
160                 }
161         }
162
163 /* TODO: Subfolders: remove patterns not matching the best guess or thisfolder */
164         DeleteHashPos(&itfl);
165         if (FoundFolder != NULL)
166                 return FoundFolder;
167         else
168                 return BestGuess;
169 }
170
171
172
173
174 long GotoRestRoom(HashList * SubRooms) {
175         int IgnoreFloor = 0;    /* deprecated... */
176         wcsession *WCC = WC;
177         long Count;
178         long State;
179         const folder *ThisFolder;
180
181         State = REST_TOPLEVEL;
182
183         if (WCC->Hdr->HR.Handler != NULL)
184                 State |= REST_IN_NAMESPACE;
185
186         Count = GetCount(WCC->Directory);
187
188         if (Count == 0)
189                 return State;
190
191         if (Count >= 1)
192                 State |= REST_IN_FLOOR;
193         if (Count == 1)
194                 return State;
195
196         /* 
197          * More than 3 params and no floor found? 
198          * -> fall back to old non-floored notation
199          */
200         if ((Count >= 3) && (WCC->CurrentFloor == NULL))
201                 IgnoreFloor = 1;
202         if (Count >= 3) {
203                 IgnoreFloor = 0;
204                 State |= REST_IN_FLOOR;
205
206                 ThisFolder = GetRESTFolder(IgnoreFloor, SubRooms);
207                 if (ThisFolder != NULL) {
208                         if (WCC->ThisRoom != NULL)
209                                 if (CompareRooms(WCC->ThisRoom, ThisFolder) != 0)
210                                         gotoroom(ThisFolder->name);
211                         State |= REST_IN_ROOM;
212
213                 }
214                 if (GetCount(SubRooms) > 0)
215                         State |= REST_HAVE_SUB_ROOMS;
216         }
217         if ((WCC->ThisRoom != NULL) && (Count + IgnoreFloor > 3)) {
218                 if (WCC->Hdr->HR.Handler->RID(ExistsID, IgnoreFloor)) {
219                         State |= REST_GOT_LOCAL_PART;
220                 }
221                 else {
222                         /// WHOOPS, not there???
223                         State |= REST_NONEXIST;
224                 }
225
226
227         }
228         return State;
229 }
230
231
232
233 /*
234  * List rooms (or "collections" in DAV terminology) which contain
235  * interesting groupware objects.
236  */
237 void dav_collection_list(void) {
238         wcsession *WCC = WC;
239         char buf[256];
240         char roomname[256];
241         int view;
242         char datestring[256];
243         time_t now;
244         time_t mtime;
245         int is_groupware_collection = 0;
246
247         int starting_point = 1;         /**< 0 for /, 1 for /groupdav/ */
248
249         if (WCC->Hdr->HR.Handler == NULL) {
250                 starting_point = 0;
251         }
252         else if (StrLength(WCC->Hdr->HR.ReqLine) == 0) {
253                 starting_point = 1;
254         }
255         else {
256                 starting_point = 2;
257         }
258
259         now = time(NULL);
260         http_datestring(datestring, sizeof datestring, now);
261
262         /*
263          * Be rude.  Completely ignore the XML request and simply send them
264          * everything we know about.  Let the client sort it out.
265          */
266         hprintf("HTTP/1.0 207 Multi-Status\r\n");
267         dav_common_headers();
268         hprintf("Date: %s\r\n", datestring);
269         hprintf("Content-type: text/xml\r\n");
270         if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))
271                 hprintf("Content-encoding: identity\r\n");
272
273         begin_burst();
274
275         wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>" "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">");
276
277         /*
278          * If the client is requesting the root, show a root node.
279          */
280         if (starting_point == 0) {
281                 wc_printf("<response>");
282                 wc_printf("<href>");
283                 dav_identify_host();
284                 wc_printf("/");
285                 wc_printf("</href>");
286                 wc_printf("<propstat>");
287                 wc_printf("<status>HTTP/1.1 200 OK</status>");
288                 wc_printf("<prop>");
289                 wc_printf("<displayname>/</displayname>");
290                 wc_printf("<resourcetype><collection/></resourcetype>");
291                 wc_printf("<getlastmodified>");
292                 escputs(datestring);
293                 wc_printf("</getlastmodified>");
294                 wc_printf("</prop>");
295                 wc_printf("</propstat>");
296                 wc_printf("</response>");
297         }
298
299         /*
300          * If the client is requesting "/groupdav", show a /groupdav subdirectory.
301          */
302         if ((starting_point + WCC->Hdr->HR.dav_depth) >= 1) {
303                 wc_printf("<response>");
304                 wc_printf("<href>");
305                 dav_identify_host();
306                 wc_printf("/groupdav");
307                 wc_printf("</href>");
308                 wc_printf("<propstat>");
309                 wc_printf("<status>HTTP/1.1 200 OK</status>");
310                 wc_printf("<prop>");
311                 wc_printf("<displayname>GroupDAV</displayname>");
312                 wc_printf("<resourcetype><collection/></resourcetype>");
313                 wc_printf("<getlastmodified>");
314                 escputs(datestring);
315                 wc_printf("</getlastmodified>");
316                 wc_printf("</prop>");
317                 wc_printf("</propstat>");
318                 wc_printf("</response>");
319         }
320
321         /*
322          * Now go through the list and make it look like a DAV collection
323          */
324         serv_puts("LKRA");
325         serv_getln(buf, sizeof buf);
326         if (buf[0] == '1')
327                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
328
329                         extract_token(roomname, buf, 0, '|', sizeof roomname);
330                         view = extract_int(buf, 7);
331                         mtime = extract_long(buf, 8);
332                         http_datestring(datestring, sizeof datestring, mtime);
333
334                         /*
335                          * For now, only list rooms that we know a GroupDAV client
336                          * might be interested in.  In the future we may add
337                          * the rest.
338                          *
339                          * We determine the type of objects which are stored in each
340                          * room by looking at the *default* view for the room.  This
341                          * allows, for example, a Calendar room to appear as a
342                          * GroupDAV calendar even if the user has switched it to a
343                          * Calendar List view.
344                          */
345                         if ((view == VIEW_CALENDAR) ||
346                             (view == VIEW_TASKS) ||
347                             (view == VIEW_ADDRESSBOOK) || (view == VIEW_NOTES) || (view == VIEW_JOURNAL) || (view == VIEW_WIKI)
348                             ) {
349                                 is_groupware_collection = 1;
350                         }
351                         else {
352                                 is_groupware_collection = 0;
353                         }
354
355                         if ((is_groupware_collection) && ((starting_point + WCC->Hdr->HR.dav_depth) >= 2)) {
356                                 wc_printf("<response>");
357
358                                 wc_printf("<href>");
359                                 dav_identify_host();
360                                 wc_printf("/groupdav/");
361                                 urlescputs(roomname);
362                                 wc_printf("/</href>");
363
364                                 wc_printf("<propstat>");
365                                 wc_printf("<status>HTTP/1.1 200 OK</status>");
366                                 wc_printf("<prop>");
367                                 wc_printf("<displayname>");
368                                 escputs(roomname);
369                                 wc_printf("</displayname>");
370                                 wc_printf("<resourcetype><collection/>");
371
372                                 switch (view) {
373                                 case VIEW_CALENDAR:
374                                         wc_printf("<G:vevent-collection />");
375                                         break;
376                                 case VIEW_TASKS:
377                                         wc_printf("<G:vtodo-collection />");
378                                         break;
379                                 case VIEW_ADDRESSBOOK:
380                                         wc_printf("<G:vcard-collection />");
381                                         break;
382                                 case VIEW_NOTES:
383                                         wc_printf("<G:vnotes-collection />");
384                                         break;
385                                 case VIEW_JOURNAL:
386                                         wc_printf("<G:vjournal-collection />");
387                                         break;
388                                 case VIEW_WIKI:
389                                         wc_printf("<G:wiki-collection />");
390                                         break;
391                                 }
392
393                                 wc_printf("</resourcetype>");
394                                 wc_printf("<getlastmodified>");
395                                 escputs(datestring);
396                                 wc_printf("</getlastmodified>");
397                                 wc_printf("</prop>");
398                                 wc_printf("</propstat>");
399                                 wc_printf("</response>");
400                         }
401                 }
402         wc_printf("</multistatus>\n");
403
404         end_burst();
405 }
406
407
408 void propfind_xml_start(void *data, const char *supplied_el, const char **attr) {
409         // syslog(LOG_DEBUG, "<%s>", supplied_el);
410 }
411
412 void propfind_xml_end(void *data, const char *supplied_el) {
413         // syslog(LOG_DEBUG, "</%s>", supplied_el);
414 }
415
416
417
418 /*
419  * The pathname is always going to be /groupdav/room_name/msg_num
420  */
421 void dav_propfind(void) {
422         wcsession *WCC = WC;
423         StrBuf *dav_roomname;
424         StrBuf *dav_uid;
425         StrBuf *MsgNum;
426         long BufLen;
427         long dav_msgnum = (-1);
428         char uid[256];
429         char encoded_uid[256];
430         long *msgs = NULL;
431         int num_msgs = 0;
432         int i;
433         char datestring[256];
434         time_t now;
435
436         now = time(NULL);
437         http_datestring(datestring, sizeof datestring, now);
438
439         int parse_success = 0;
440         XML_Parser xp = XML_ParserCreateNS(NULL, '|');
441         if (xp) {
442                 // XML_SetUserData(xp, XXX);
443                 XML_SetElementHandler(xp, propfind_xml_start, propfind_xml_end);
444                 // XML_SetCharacterDataHandler(xp, xrds_xml_chardata);
445
446                 const char *req = ChrPtr(WCC->upload);
447                 if (req) {
448                         req = strchr(req, '<'); /* hunt for the first tag */
449                 }
450                 if (!req) {
451                         req = "ERROR";  /* force it to barf */
452                 }
453
454                 i = XML_Parse(xp, req, strlen(req), 1);
455                 if (!i) {
456                         syslog(LOG_DEBUG, "XML_Parse() failed: %s", XML_ErrorString(XML_GetErrorCode(xp)));
457                         XML_ParserFree(xp);
458                         parse_success = 0;
459                 }
460                 else {
461                         parse_success = 1;
462                 }
463         }
464
465         if (!parse_success) {
466                 hprintf("HTTP/1.1 500 Internal Server Error\r\n");
467                 dav_common_headers();
468                 hprintf("Date: %s\r\n", datestring);
469                 hprintf("Content-Type: text/plain\r\n");
470                 wc_printf("An internal error has occurred at %s:%d.\r\n", __FILE__, __LINE__);
471                 end_burst();
472                 return;
473         }
474
475         dav_roomname = NewStrBuf();
476         dav_uid = NewStrBuf();
477         StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
478         StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');
479
480         syslog(LOG_DEBUG, "PROPFIND requested for '%s' at depth %d", ChrPtr(dav_roomname), WCC->Hdr->HR.dav_depth);
481
482         /*
483          * If the room name is blank, the client is requesting a folder list.
484          */
485         if (StrLength(dav_roomname) == 0) {
486                 dav_collection_list();
487                 FreeStrBuf(&dav_roomname);
488                 FreeStrBuf(&dav_uid);
489                 return;
490         }
491
492         /* Go to the correct room. */
493         if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
494                 gotoroom(dav_roomname);
495         }
496         if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
497                 hprintf("HTTP/1.1 404 not found\r\n");
498                 dav_common_headers();
499                 hprintf("Date: %s\r\n", datestring);
500                 hprintf("Content-Type: text/plain\r\n");
501                 wc_printf("There is no folder called \"%s\" on this server.\r\n", ChrPtr(dav_roomname));
502                 end_burst();
503                 FreeStrBuf(&dav_roomname);
504                 FreeStrBuf(&dav_uid);
505                 return;
506         }
507
508         /* If dav_uid is non-empty, client is requesting a PROPFIND on
509          * a specific item in the room.  This is not valid GroupDAV, but
510          * it is valid WebDAV (and probably CalDAV too).
511          */
512         if (StrLength(dav_uid) != 0) {
513
514                 dav_msgnum = locate_message_by_uid(ChrPtr(dav_uid));
515                 if (dav_msgnum < 0) {
516                         hprintf("HTTP/1.1 404 not found\r\n");
517                         dav_common_headers();
518                         hprintf("Content-Type: text/plain\r\n");
519                         wc_printf("Object \"%s\" was not found in the \"%s\" folder.\r\n", ChrPtr(dav_uid), ChrPtr(dav_roomname)
520                             );
521                         end_burst();
522                         FreeStrBuf(&dav_roomname);
523                         FreeStrBuf(&dav_uid);
524                         return;
525                 }
526
527                 /* Be rude.  Completely ignore the XML request and simply send them
528                  * everything we know about (which is going to simply be the ETag and
529                  * nothing else).  Let the client-side parser sort it out.
530                  */
531                 hprintf("HTTP/1.0 207 Multi-Status\r\n");
532                 dav_common_headers();
533                 hprintf("Date: %s\r\n", datestring);
534                 hprintf("Content-type: text/xml\r\n");
535                 if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))
536                         hprintf("Content-encoding: identity\r\n");
537
538                 begin_burst();
539
540                 wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>" "<multistatus xmlns=\"DAV:\">");
541
542                 wc_printf("<response>");
543
544                 wc_printf("<href>");
545                 dav_identify_host();
546                 wc_printf("/groupdav/");
547                 urlescputs(ChrPtr(WCC->CurRoom.name));
548                 euid_escapize(encoded_uid, ChrPtr(dav_uid));
549                 wc_printf("/%s", encoded_uid);
550                 wc_printf("</href>");
551                 wc_printf("<propstat>");
552                 wc_printf("<status>HTTP/1.1 200 OK</status>");
553                 wc_printf("<prop>");
554                 wc_printf("<getetag>\"%ld\"</getetag>", dav_msgnum);
555                 wc_printf("<getlastmodified>");
556                 escputs(datestring);
557                 wc_printf("</getlastmodified>");
558                 wc_printf("</prop>");
559                 wc_printf("</propstat>");
560
561                 wc_printf("</response>\n");
562                 wc_printf("</multistatus>\n");
563                 end_burst();
564                 FreeStrBuf(&dav_roomname);
565                 FreeStrBuf(&dav_uid);
566                 return;
567         }
568         FreeStrBuf(&dav_roomname);
569         FreeStrBuf(&dav_uid);
570
571
572         /*
573          * If we get to this point the client is performing a PROPFIND on the room itself.
574          *
575          * We call it a room; DAV calls it a "collection."  We have to give it some properties
576          * of the room itself and then offer a list of all items contained therein.
577          *
578          * Be rude.  Completely ignore the XML request and simply send them
579          * everything we know about (which is going to simply be the ETag and
580          * nothing else).  Let the client-side parser sort it out.
581          */
582         //syslog(LOG_DEBUG, "BE RUDE AND IGNORE: \033[31m%s\033[0m", ChrPtr(WC->upload) );
583         hprintf("HTTP/1.0 207 Multi-Status\r\n");
584         dav_common_headers();
585         hprintf("Date: %s\r\n", datestring);
586         hprintf("Content-type: text/xml\r\n");
587         if (DisableGzip || (!WCC->Hdr->HR.gzip_ok)) {
588                 hprintf("Content-encoding: identity\r\n");
589         }
590         begin_burst();
591
592         wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
593                   "<D:multistatus "
594                   "xmlns:D=\"DAV:\" " "xmlns:G=\"http://groupdav.org/\" " "xmlns:C=\"urn:ietf:params:xml:ns:caldav\"" ">");
595
596         /* Transmit the collection resource */
597         wc_printf("<D:response>");
598
599         wc_printf("<D:href>");
600         dav_identify_host();
601         wc_printf("/groupdav/");
602         urlescputs(ChrPtr(WCC->CurRoom.name));
603         wc_printf("</D:href>");
604
605         wc_printf("<D:propstat>");
606         wc_printf("<D:status>HTTP/1.1 200 OK</D:status>");
607         wc_printf("<D:prop>");
608         wc_printf("<D:displayname>");
609         escputs(ChrPtr(WCC->CurRoom.name));
610         wc_printf("</D:displayname>");
611
612         wc_printf("<D:owner/>");        /* empty owner ought to be legal; see rfc3744 section 5.1 */
613
614         wc_printf("<D:resourcetype><D:collection/>");
615         switch (WCC->CurRoom.defview) {
616         case VIEW_CALENDAR:
617                 wc_printf("<G:vevent-collection />");
618                 wc_printf("<C:calendar />");
619                 break;
620         case VIEW_TASKS:
621                 wc_printf("<G:vtodo-collection />");
622                 break;
623         case VIEW_ADDRESSBOOK:
624                 wc_printf("<G:vcard-collection />");
625                 break;
626         }
627         wc_printf("</D:resourcetype>");
628
629         /* FIXME get the mtime
630            wc_printf("<D:getlastmodified>");
631            escputs(datestring);
632            wc_printf("</D:getlastmodified>");
633          */
634         wc_printf("</D:prop>");
635         wc_printf("</D:propstat>");
636         wc_printf("</D:response>");
637
638         /* If a depth greater than zero was specified, transmit the collection listing */
639
640         if (WCC->Hdr->HR.dav_depth > 0) {
641                 MsgNum = NewStrBuf();
642                 serv_puts("MSGS ALL");
643
644                 StrBuf_ServGetln(MsgNum);
645                 if (GetServerStatus(MsgNum, NULL) == 1)
646                         while (BufLen = StrBuf_ServGetln(MsgNum),
647                                ((BufLen >= 0) && ((BufLen != 3) || strcmp(ChrPtr(MsgNum), "000")))) {
648                                 msgs = realloc(msgs, ++num_msgs * sizeof(long));
649                                 msgs[num_msgs - 1] = StrTol(MsgNum);
650                         }
651
652                 if (num_msgs > 0)
653                         for (i = 0; i < num_msgs; ++i) {
654
655                                 syslog(LOG_DEBUG, "PROPFIND enumerating message # %ld", msgs[i]);
656                                 strcpy(uid, "");
657                                 now = (-1);
658                                 serv_printf("MSG0 %ld|3", msgs[i]);
659                                 StrBuf_ServGetln(MsgNum);
660                                 if (GetServerStatus(MsgNum, NULL) == 1)
661                                         while (BufLen = StrBuf_ServGetln(MsgNum),
662                                                ((BufLen >= 0) && ((BufLen != 3) || strcmp(ChrPtr(MsgNum), "000")))) {
663                                                 if (!strncasecmp(ChrPtr(MsgNum), "exti=", 5)) {
664                                                         strcpy(uid, &ChrPtr(MsgNum)[5]);
665                                                 }
666                                                 else if (!strncasecmp(ChrPtr(MsgNum), "time=", 5)) {
667                                                         now = atol(&ChrPtr(MsgNum)[5]);
668                                                 }
669                                         }
670
671                                 if (!IsEmptyStr(uid)) {
672                                         wc_printf("<D:response>");
673                                         wc_printf("<D:href>");
674                                         dav_identify_host();
675                                         wc_printf("/groupdav/");
676                                         urlescputs(ChrPtr(WCC->CurRoom.name));
677                                         euid_escapize(encoded_uid, uid);
678                                         wc_printf("/%s", encoded_uid);
679                                         wc_printf("</D:href>");
680                                         wc_printf("<D:propstat>");
681                                         wc_printf("<D:status>HTTP/1.1 200 OK</D:status>");
682                                         wc_printf("<D:prop>");
683                                         wc_printf("<D:getetag>\"%ld\"</D:getetag>", msgs[i]);
684                                         switch (WCC->CurRoom.defview) {
685                                         case VIEW_CALENDAR:
686                                                 wc_printf("<D:getcontenttype>text/x-ical</D:getcontenttype>");
687                                                 break;
688                                         case VIEW_TASKS:
689                                                 wc_printf("<D:getcontenttype>text/x-ical</D:getcontenttype>");
690                                                 break;
691                                         case VIEW_ADDRESSBOOK:
692                                                 wc_printf("<D:getcontenttype>text/x-vcard</D:getcontenttype>");
693                                                 break;
694                                         }
695                                         if (now > 0L) {
696                                                 http_datestring(datestring, sizeof datestring, now);
697                                                 wc_printf("<D:getlastmodified>");
698                                                 escputs(datestring);
699                                                 wc_printf("</D:getlastmodified>");
700                                         }
701                                         wc_printf("</D:prop>");
702                                         wc_printf("</D:propstat>");
703                                         wc_printf("</D:response>");
704                                 }
705                         }
706                 FreeStrBuf(&MsgNum);
707         }
708
709         wc_printf("</D:multistatus>\n");
710         end_burst();
711
712         if (msgs != NULL) {
713                 free(msgs);
714         }
715 }
716
717
718
719 int ParseMessageListHeaders_EUID(StrBuf * Line,
720                                  const char **pos, message_summary * Msg, StrBuf * ConversionBuffer, void **ViewSpecific) {
721         Msg->euid = NewStrBuf();
722         StrBufExtract_NextToken(Msg->euid, Line, pos, '|');
723         Msg->date = StrBufExtractNext_long(Line, pos, '|');
724
725         return StrLength(Msg->euid) > 0;
726 }
727
728 int DavUIDL_GetParamsGetServerCall(SharedMessageStatus * Stat,
729                                    void **ViewSpecific, long oper, char *cmd, long len, char *filter, long flen) {
730         Stat->defaultsortorder = 0;
731         Stat->sortit = 0;
732         Stat->load_seen = 0;
733         Stat->maxmsgs = 9999999;
734
735         snprintf(cmd, len, "MSGS ALL|||2");
736         return 200;
737 }
738
739 int DavUIDL_RenderView_or_Tail(SharedMessageStatus * Stat, void **ViewSpecific, long oper) {
740
741         DoTemplate(HKEY("msg_listview"), NULL, &NoCtx);
742
743         return 0;
744 }
745
746 int DavUIDL_Cleanup(void **ViewSpecific) {
747         /* Note: wDumpContent() will output one additional </div> tag. */
748         /* We ought to move this out into template */
749         wDumpContent(1);
750
751         return 0;
752 }
753
754
755
756
757 void InitModule_PROPFIND(void) {
758         RegisterReadLoopHandlerset(eReadEUIDS,
759                                    DavUIDL_GetParamsGetServerCall,
760                                    NULL,
761                                    NULL, ParseMessageListHeaders_EUID, NULL, DavUIDL_RenderView_or_Tail, DavUIDL_Cleanup, NULL);
762
763 }