d8dfc211e98f02f583ff2744d2f80188003fa362
[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), 
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, 
138                                             itd, &len, &Key, &vDir) ||
139                             (vDir == NULL))
140                         {
141                                 DeleteHashPos(&itd);
142                                 
143                                 lprintf(0, "5\n");
144                                 continue;
145                         }
146                         DeleteHashPos(&itd);
147                         Dir = (StrBuf*) vDir;
148                         if (strcmp(ChrPtr(ThisFolder->name), 
149                                                ChrPtr(Dir))
150                             != 0)
151                         {
152                                 DeleteHashPos(&itd);
153                                 
154                                 lprintf(0, "5\n");
155                                 continue;
156                         }
157                         DeleteHashPos(&itfl);
158                         DeleteHashPos(&itd);
159                         if (delta != 2) {
160                                 nBestGuess = 1;
161                                 BestGuess = ThisFolder;
162                         }
163                         else 
164                                 FoundFolder = ThisFolder;
165                 }
166         }
167
168 /* TODO: Subfolders: remove patterns not matching the best guess or thisfolder */
169         DeleteHashPos(&itfl);
170         if (FoundFolder != NULL)
171                 return FoundFolder;
172         else
173                 return BestGuess;
174 }
175
176
177
178
179 long GotoRestRoom(HashList *SubRooms)
180 {
181         int IgnoreFloor = 0; /* deprecated... */
182         wcsession *WCC = WC;
183         long Count;
184         long State;
185         const folder *ThisFolder;
186
187         State = REST_TOPLEVEL;
188
189         if (WCC->Hdr->HR.Handler != NULL) 
190                 State |= REST_IN_NAMESPACE;
191
192         Count = GetCount(WCC->Directory);
193         
194         if (Count == 0) return State;
195
196         if (Count >= 1) State |=REST_IN_FLOOR;
197         if (Count == 1) return State;
198         
199         /* 
200          * More than 3 params and no floor found? 
201          * -> fall back to old non-floored notation
202          */
203         if ((Count >= 3) && (WCC->CurrentFloor == NULL))
204                 IgnoreFloor = 1;
205         if (Count >= 3)
206         {
207                 IgnoreFloor = 0;
208                 State |= REST_IN_FLOOR;
209
210                 ThisFolder = GetRESTFolder(IgnoreFloor, SubRooms);
211                 if (ThisFolder != NULL)
212                 {
213                         if (WCC->ThisRoom != NULL)
214                                 if (CompareRooms(WCC->ThisRoom, ThisFolder) != 0)
215                                         gotoroom(ThisFolder->name);
216                         State |= REST_IN_ROOM;
217                         
218                 }
219                 if (GetCount(SubRooms) > 0)
220                         State |= REST_HAVE_SUB_ROOMS;
221         }
222         if ((WCC->ThisRoom != NULL) && 
223             (Count + IgnoreFloor > 3))
224         {
225                 if (WCC->Hdr->HR.Handler->RID(ExistsID, IgnoreFloor))
226                 {
227                         State |= REST_GOT_LOCAL_PART;
228                 }
229                 else {
230                         /// WHOOPS, not there???
231                         State |= REST_NONEXIST;
232                 }
233
234
235         }
236         return State;
237 }
238
239
240
241 /*
242  * List rooms (or "collections" in DAV terminology) which contain
243  * interesting groupware objects.
244  */
245 void groupdav_collection_list(void)
246 {
247         wcsession *WCC = WC;
248         char buf[256];
249         char roomname[256];
250         int view;
251         char datestring[256];
252         time_t now;
253         time_t mtime;
254         int is_groupware_collection = 0;
255         int starting_point = 1;         /**< 0 for /, 1 for /groupdav/ */
256
257         if (WCC->Hdr->HR.Handler == NULL) {
258                 starting_point = 0;
259         }
260         else if (StrLength(WCC->Hdr->HR.ReqLine) == 0) {
261                 starting_point = 1;
262         }
263         else {
264                 starting_point = 2;
265         }
266
267         now = time(NULL);
268         http_datestring(datestring, sizeof datestring, now);
269
270         /*
271          * Be rude.  Completely ignore the XML request and simply send them
272          * everything we know about.  Let the client sort it out.
273          */
274         hprintf("HTTP/1.0 207 Multi-Status\r\n");
275         groupdav_common_headers();
276         hprintf("Date: %s\r\n", datestring);
277         hprintf("Content-type: text/xml\r\n");
278         hprintf("Content-encoding: identity\r\n");
279
280         begin_burst();
281
282         wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
283                 "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
284         );
285
286         /*
287          * If the client is requesting the root, show a root node.
288          */
289         if (starting_point == 0) {
290                 wc_printf("<response>");
291                         wc_printf("<href>");
292                                 groupdav_identify_host();
293                                 wc_printf("/");
294                         wc_printf("</href>");
295                         wc_printf("<propstat>");
296                                 wc_printf("<status>HTTP/1.1 200 OK</status>");
297                                 wc_printf("<prop>");
298                                         wc_printf("<displayname>/</displayname>");
299                                         wc_printf("<resourcetype><collection/></resourcetype>");
300                                         wc_printf("<getlastmodified>");
301                                                 escputs(datestring);
302                                         wc_printf("</getlastmodified>");
303                                 wc_printf("</prop>");
304                         wc_printf("</propstat>");
305                 wc_printf("</response>");
306         }
307
308         /*
309          * If the client is requesting "/groupdav", show a /groupdav subdirectory.
310          */
311         if ((starting_point + WCC->Hdr->HR.dav_depth) >= 1) {
312                 wc_printf("<response>");
313                         wc_printf("<href>");
314                                 groupdav_identify_host();
315                                 wc_printf("/groupdav");
316                         wc_printf("</href>");
317                         wc_printf("<propstat>");
318                                 wc_printf("<status>HTTP/1.1 200 OK</status>");
319                                 wc_printf("<prop>");
320                                         wc_printf("<displayname>GroupDAV</displayname>");
321                                         wc_printf("<resourcetype><collection/></resourcetype>");
322                                         wc_printf("<getlastmodified>");
323                                                 escputs(datestring);
324                                         wc_printf("</getlastmodified>");
325                                 wc_printf("</prop>");
326                         wc_printf("</propstat>");
327                 wc_printf("</response>");
328         }
329
330         /*
331          * Now go through the list and make it look like a DAV collection
332          */
333         serv_puts("LKRA");
334         serv_getln(buf, sizeof buf);
335         if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
336
337                 extract_token(roomname, buf, 0, '|', sizeof roomname);
338                 view = extract_int(buf, 7);
339                 mtime = extract_long(buf, 8);
340                 http_datestring(datestring, sizeof datestring, mtime);
341
342                 /*
343                  * For now, only list rooms that we know a GroupDAV client
344                  * might be interested in.  In the future we may add
345                  * the rest.
346                  *
347                  * We determine the type of objects which are stored in each
348                  * room by looking at the *default* view for the room.  This
349                  * allows, for example, a Calendar room to appear as a
350                  * GroupDAV calendar even if the user has switched it to a
351                  * Calendar List view.
352                  */
353                 if (    (view == VIEW_CALENDAR) || 
354                         (view == VIEW_TASKS) || 
355                         (view == VIEW_ADDRESSBOOK) ||
356                         (view == VIEW_NOTES) ||
357                         (view == VIEW_JOURNAL) ||
358                         (view == VIEW_WIKI)
359                 ) {
360                         is_groupware_collection = 1;
361                 }
362                 else {
363                         is_groupware_collection = 0;
364                 }
365
366                 if ( (is_groupware_collection) && ((starting_point + WCC->Hdr->HR.dav_depth) >= 2) ) {
367                         wc_printf("<response>");
368
369                         wc_printf("<href>");
370                         groupdav_identify_host();
371                         wc_printf("/groupdav/");
372                         urlescputs(roomname);
373                         wc_printf("/</href>");
374
375                         wc_printf("<propstat>");
376                         wc_printf("<status>HTTP/1.1 200 OK</status>");
377                         wc_printf("<prop>");
378                         wc_printf("<displayname>");
379                         escputs(roomname);
380                         wc_printf("</displayname>");
381                         wc_printf("<resourcetype><collection/>");
382
383                         switch(view) {
384                         case VIEW_CALENDAR:
385                                 wc_printf("<G:vevent-collection />");
386                                 break;
387                         case VIEW_TASKS:
388                                 wc_printf("<G:vtodo-collection />");
389                                 break;
390                         case VIEW_ADDRESSBOOK:
391                                 wc_printf("<G:vcard-collection />");
392                                 break;
393                         case VIEW_NOTES:
394                                 wc_printf("<G:vnotes-collection />");
395                                 break;
396                         case VIEW_JOURNAL:
397                                 wc_printf("<G:vjournal-collection />");
398                                 break;
399                         case VIEW_WIKI:
400                                 wc_printf("<G:wiki-collection />");
401                                 break;
402                         }
403
404                         wc_printf("</resourcetype>");
405                         wc_printf("<getlastmodified>");
406                                 escputs(datestring);
407                         wc_printf("</getlastmodified>");
408                         wc_printf("</prop>");
409                         wc_printf("</propstat>");
410                         wc_printf("</response>");
411                 }
412         }
413         wc_printf("</multistatus>\n");
414
415         end_burst();
416 }
417
418
419
420 /*
421  * The pathname is always going to be /groupdav/room_name/msg_num
422  */
423 void groupdav_propfind(void) 
424 {
425         HashList *SubRooms = NULL;
426         wcsession *WCC = WC;
427         StrBuf *dav_roomname;
428         StrBuf *dav_uid;
429         StrBuf *MsgNum;
430         long BufLen;
431         long dav_msgnum = (-1);
432         char uid[256];
433         char encoded_uid[256];
434         long *msgs = NULL;
435         int num_msgs = 0;
436         int i;
437         char datestring[256];
438         time_t now;
439         long State;
440
441         now = time(NULL);
442         http_datestring(datestring, sizeof datestring, now);
443
444         dav_roomname = NewStrBuf();
445         dav_uid = NewStrBuf();
446         StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
447         StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');
448 #ifdef DEV_RESTDAV
449         /*
450          * If the room name is blank, the client is requesting a
451          * folder list.
452          */
453         SubRooms = NewHash(1, Flathash);
454         State = GotoRestRoom(SubRooms);
455         if (((State & REST_IN_ROOM) == 0) ||
456             (((State & (REST_GOT_LOCAL_PART)) == 0) &&
457              (WCC->Hdr->HR.dav_depth == 0)))
458         {
459                 now = time(NULL);
460                 http_datestring(datestring, sizeof datestring, now);
461
462                 /*
463                  * Be rude.  Completely ignore the XML request and simply send them
464                  * everything we know about.  Let the client sort it out.
465                  */
466                 hprintf("HTTP/1.0 207 Multi-Status\r\n");
467                 groupdav_common_headers();
468                 hprintf("Date: %s\r\n", datestring);
469                 hprintf("Content-type: text/xml\r\n");
470                 hprintf("Content-encoding: identity\r\n");
471
472                 begin_burst();
473
474
475                 /*
476                  * If the client is requesting the root, show a root node.
477                  */
478                 do_template("dav_propfind_top", NULL);
479                 end_burst();
480                 FreeStrBuf(&dav_roomname);
481                 FreeStrBuf(&dav_uid);
482                 FreeHashList(&SubRooms);
483                 return;
484         }
485
486         if ((State & (REST_GOT_LOCAL_PART)) == 0) {
487                 readloop(headers, eReadEUIDS);
488                 FreeHashList(&SubRooms);
489                 return;
490
491         }
492
493
494         
495         FreeHashList(&SubRooms);
496
497 #endif
498
499         /*
500          * If the room name is blank, the client is requesting a
501          * folder list.
502          */
503         if (StrLength(dav_roomname) == 0) {
504                 groupdav_collection_list();
505                 FreeStrBuf(&dav_roomname);
506                 FreeStrBuf(&dav_uid);
507                 return;
508         }
509
510         /* Go to the correct room. */
511         if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
512                 gotoroom(dav_roomname);
513         }
514         if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
515                 hprintf("HTTP/1.1 404 not found\r\n");
516                 groupdav_common_headers();
517                 hprintf("Date: %s\r\n", datestring);
518                 hprintf("Content-Type: text/plain\r\n");
519                 wc_printf("There is no folder called \"%s\" on this server.\r\n",
520                         ChrPtr(dav_roomname)
521                 );
522                 end_burst();
523                 FreeStrBuf(&dav_roomname);
524                 FreeStrBuf(&dav_uid);
525                 return;
526         }
527
528         /* If dav_uid is non-empty, client is requesting a PROPFIND on
529          * a specific item in the room.  This is not valid GroupDAV, but
530          * it is valid WebDAV.
531          */
532         if (StrLength(dav_uid) != 0) {
533
534                 dav_msgnum = locate_message_by_uid(ChrPtr(dav_uid));
535                 if (dav_msgnum < 0) {
536                         hprintf("HTTP/1.1 404 not found\r\n");
537                         groupdav_common_headers();
538                         hprintf("Content-Type: text/plain\r\n");
539                         wc_printf("Object \"%s\" was not found in the \"%s\" folder.\r\n",
540                                 ChrPtr(dav_uid),
541                                 ChrPtr(dav_roomname)
542                         );
543                         end_burst();
544                         FreeStrBuf(&dav_roomname);
545                         FreeStrBuf(&dav_uid);
546                         return;
547                 }
548
549                 /* Be rude.  Completely ignore the XML request and simply send them
550                  * everything we know about (which is going to simply be the ETag and
551                  * nothing else).  Let the client-side parser sort it out.
552                  */
553                 hprintf("HTTP/1.0 207 Multi-Status\r\n");
554                 groupdav_common_headers();
555                 hprintf("Date: %s\r\n", datestring);
556                 hprintf("Content-type: text/xml\r\n");
557                 hprintf("Content-encoding: identity\r\n");
558         
559                 begin_burst();
560         
561                 wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
562                         "<multistatus xmlns=\"DAV:\">"
563                 );
564
565                 wc_printf("<response>");
566                 
567                 wc_printf("<href>");
568                 groupdav_identify_host();
569                 wc_printf("/groupdav/");
570                 urlescputs(ChrPtr(WCC->CurRoom.name));
571                 euid_escapize(encoded_uid, ChrPtr(dav_uid));
572                 wc_printf("/%s", encoded_uid);
573                 wc_printf("</href>");
574                 wc_printf("<propstat>");
575                 wc_printf("<status>HTTP/1.1 200 OK</status>");
576                 wc_printf("<prop>");
577                 wc_printf("<getetag>\"%ld\"</getetag>", dav_msgnum);
578                 wc_printf("<getlastmodified>");
579                 escputs(datestring);
580                 wc_printf("</getlastmodified>");
581                 wc_printf("</prop>");
582                 wc_printf("</propstat>");
583
584                 wc_printf("</response>\n");
585                 wc_printf("</multistatus>\n");
586                 end_burst();
587                 FreeStrBuf(&dav_roomname);
588                 FreeStrBuf(&dav_uid);
589                 return;
590         }
591         FreeStrBuf(&dav_roomname);
592         FreeStrBuf(&dav_uid);
593
594
595         /*
596          * We got to this point, which means that the client is requesting
597          * a 'collection' (i.e. a list of all items in the room).
598          *
599          * Be rude.  Completely ignore the XML request and simply send them
600          * everything we know about (which is going to simply be the ETag and
601          * nothing else).  Let the client-side parser sort it out.
602          */
603         hprintf("HTTP/1.0 207 Multi-Status\r\n");
604         groupdav_common_headers();
605         hprintf("Date: %s\r\n", datestring);
606         hprintf("Content-type: text/xml\r\n");
607         hprintf("Content-encoding: identity\r\n");
608
609         begin_burst();
610
611         wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
612                 "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
613         );
614
615
616         /* Transmit the collection resource (FIXME check depth and starting point) */
617         wc_printf("<response>");
618
619         wc_printf("<href>");
620         groupdav_identify_host();
621         wc_printf("/groupdav/");
622         urlescputs(ChrPtr(WCC->CurRoom.name));
623         wc_printf("</href>");
624
625         wc_printf("<propstat>");
626         wc_printf("<status>HTTP/1.1 200 OK</status>");
627         wc_printf("<prop>");
628         wc_printf("<displayname>");
629         escputs(ChrPtr(WCC->CurRoom.name));
630         wc_printf("</displayname>");
631         wc_printf("<resourcetype><collection/>");
632
633         switch(WCC->CurRoom.defview) {
634                 case VIEW_CALENDAR:
635                         wc_printf("<G:vevent-collection />");
636                         break;
637                 case VIEW_TASKS:
638                         wc_printf("<G:vtodo-collection />");
639                         break;
640                 case VIEW_ADDRESSBOOK:
641                         wc_printf("<G:vcard-collection />");
642                         break;
643         }
644
645         wc_printf("</resourcetype>");
646         /* FIXME get the mtime
647         wc_printf("<getlastmodified>");
648                 escputs(datestring);
649         wc_printf("</getlastmodified>");
650         */
651         wc_printf("</prop>");
652         wc_printf("</propstat>");
653         wc_printf("</response>");
654
655         /* Transmit the collection listing (FIXME check depth and starting point) */
656
657         MsgNum = NewStrBuf();
658         serv_puts("MSGS ALL");
659
660         StrBuf_ServGetln(MsgNum);
661         if (GetServerStatus(MsgNum, NULL) == 1)
662                 while (BufLen = StrBuf_ServGetln(MsgNum), strcmp(ChrPtr(MsgNum), "000"))  {
663                         msgs = realloc(msgs, ++num_msgs * sizeof(long));
664                         msgs[num_msgs-1] = StrTol(MsgNum);
665                 }
666
667         if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
668
669                 strcpy(uid, "");
670                 now = (-1);
671                 serv_printf("MSG0 %ld|3", msgs[i]);
672                 StrBuf_ServGetln(MsgNum);
673                 if (GetServerStatus(MsgNum, NULL) == 1)
674                         while (BufLen = StrBuf_ServGetln(MsgNum), strcmp(ChrPtr(MsgNum), "000")) 
675                         {
676                                 if (!strncasecmp(ChrPtr(MsgNum), "exti=", 5)) {
677                                         strcpy(uid, &ChrPtr(MsgNum)[5]);
678                                 }
679                                 else if (!strncasecmp(ChrPtr(MsgNum), "time=", 5)) {
680                                         now = atol(&ChrPtr(MsgNum)[5]);
681                         }
682                 }
683
684                 if (!IsEmptyStr(uid)) {
685                         wc_printf("<response>");
686                                 wc_printf("<href>");
687                                         groupdav_identify_host();
688                                         wc_printf("/groupdav/");
689                                         urlescputs(ChrPtr(WCC->CurRoom.name));
690                                         euid_escapize(encoded_uid, uid);
691                                         wc_printf("/%s", encoded_uid);
692                                 wc_printf("</href>");
693                                 switch(WCC->CurRoom.defview) {
694                                 case VIEW_CALENDAR:
695                                         wc_printf("<getcontenttype>text/x-ical</getcontenttype>");
696                                         break;
697                                 case VIEW_TASKS:
698                                         wc_printf("<getcontenttype>text/x-ical</getcontenttype>");
699                                         break;
700                                 case VIEW_ADDRESSBOOK:
701                                         wc_printf("<getcontenttype>text/x-vcard</getcontenttype>");
702                                         break;
703                                 }
704                                 wc_printf("<propstat>");
705                                         wc_printf("<status>HTTP/1.1 200 OK</status>");
706                                         wc_printf("<prop>");
707                                                 wc_printf("<getetag>\"%ld\"</getetag>", msgs[i]);
708                                         if (now > 0L) {
709                                                 http_datestring(datestring, sizeof datestring, now);
710                                                 wc_printf("<getlastmodified>");
711                                                 escputs(datestring);
712                                                 wc_printf("</getlastmodified>");
713                                         }
714                                         wc_printf("</prop>");
715                                 wc_printf("</propstat>");
716                         wc_printf("</response>");
717                 }
718         }
719         FreeStrBuf(&MsgNum);
720
721         wc_printf("</multistatus>\n");
722         end_burst();
723
724         if (msgs != NULL) {
725                 free(msgs);
726         }
727 }
728
729
730
731 int ParseMessageListHeaders_EUID(StrBuf *Line, 
732                                  const char **pos, 
733                                  message_summary *Msg, 
734                                  StrBuf *ConversionBuffer)
735 {
736         Msg->euid = NewStrBuf();
737         StrBufExtract_NextToken(Msg->euid,  Line, pos, '|');
738         Msg->date = StrBufExtractNext_long(Line, pos, '|');
739         
740         return StrLength(Msg->euid) > 0;
741 }
742
743 int DavUIDL_GetParamsGetServerCall(SharedMessageStatus *Stat, 
744                                     void **ViewSpecific, 
745                                     long oper, 
746                                     char *cmd, 
747                                     long len)
748 {
749         Stat->defaultsortorder = 0;
750         Stat->sortit = 0;
751         Stat->load_seen = 0;
752         Stat->maxmsgs  = 9999999;
753
754         snprintf(cmd, len, "MSGS ALL|||2");
755         return 200;
756 }
757
758 int DavUIDL_RenderView_or_Tail(SharedMessageStatus *Stat, 
759                                 void **ViewSpecific, 
760                                 long oper)
761 {
762         
763         DoTemplate(HKEY("msg_listview"),NULL,&NoCtx);
764         
765         return 0;
766 }
767
768 int DavUIDL_Cleanup(void **ViewSpecific)
769 {
770         /* Note: wDumpContent() will output one additional </div> tag. */
771         /* We ought to move this out into template */
772         wDumpContent(1);
773
774         return 0;
775 }
776
777
778
779
780 void 
781 InitModule_PROPFIND
782 (void)
783 {
784         RegisterReadLoopHandlerset(
785                 eReadEUIDS,
786                 DavUIDL_GetParamsGetServerCall,
787                 NULL, /// TODO: is this right?
788                 ParseMessageListHeaders_EUID,
789                 NULL, //// ""
790                 DavUIDL_RenderView_or_Tail,
791                 DavUIDL_Cleanup);
792
793 }