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