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