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