ceb78fdac73a131b3a1b5c2dffe873ec7452e62d
[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 open source 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 extern int DisableGzip;
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         if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))     
294                 hprintf("Content-encoding: identity\r\n");
295
296         begin_burst();
297
298         wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
299                 "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
300         );
301
302         /*
303          * If the client is requesting the root, show a root node.
304          */
305         if (starting_point == 0) {
306                 wc_printf("<response>");
307                         wc_printf("<href>");
308                                 groupdav_identify_host();
309                                 wc_printf("/");
310                         wc_printf("</href>");
311                         wc_printf("<propstat>");
312                                 wc_printf("<status>HTTP/1.1 200 OK</status>");
313                                 wc_printf("<prop>");
314                                         wc_printf("<displayname>/</displayname>");
315                                         wc_printf("<resourcetype><collection/></resourcetype>");
316                                         wc_printf("<getlastmodified>");
317                                                 escputs(datestring);
318                                         wc_printf("</getlastmodified>");
319                                 wc_printf("</prop>");
320                         wc_printf("</propstat>");
321                 wc_printf("</response>");
322         }
323
324         /*
325          * If the client is requesting "/groupdav", show a /groupdav subdirectory.
326          */
327         if ((starting_point + WCC->Hdr->HR.dav_depth) >= 1) {
328                 wc_printf("<response>");
329                         wc_printf("<href>");
330                                 groupdav_identify_host();
331                                 wc_printf("/groupdav");
332                         wc_printf("</href>");
333                         wc_printf("<propstat>");
334                                 wc_printf("<status>HTTP/1.1 200 OK</status>");
335                                 wc_printf("<prop>");
336                                         wc_printf("<displayname>GroupDAV</displayname>");
337                                         wc_printf("<resourcetype><collection/></resourcetype>");
338                                         wc_printf("<getlastmodified>");
339                                                 escputs(datestring);
340                                         wc_printf("</getlastmodified>");
341                                 wc_printf("</prop>");
342                         wc_printf("</propstat>");
343                 wc_printf("</response>");
344         }
345
346         /*
347          * Now go through the list and make it look like a DAV collection
348          */
349         serv_puts("LKRA");
350         serv_getln(buf, sizeof buf);
351         if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
352
353                 extract_token(roomname, buf, 0, '|', sizeof roomname);
354                 view = extract_int(buf, 7);
355                 mtime = extract_long(buf, 8);
356                 http_datestring(datestring, sizeof datestring, mtime);
357
358                 /*
359                  * For now, only list rooms that we know a GroupDAV client
360                  * might be interested in.  In the future we may add
361                  * the rest.
362                  *
363                  * We determine the type of objects which are stored in each
364                  * room by looking at the *default* view for the room.  This
365                  * allows, for example, a Calendar room to appear as a
366                  * GroupDAV calendar even if the user has switched it to a
367                  * Calendar List view.
368                  */
369                 if (    (view == VIEW_CALENDAR) || 
370                         (view == VIEW_TASKS) || 
371                         (view == VIEW_ADDRESSBOOK) ||
372                         (view == VIEW_NOTES) ||
373                         (view == VIEW_JOURNAL) ||
374                         (view == VIEW_WIKI)
375                 ) {
376                         is_groupware_collection = 1;
377                 }
378                 else {
379                         is_groupware_collection = 0;
380                 }
381
382                 if ( (is_groupware_collection) && ((starting_point + WCC->Hdr->HR.dav_depth) >= 2) ) {
383                         wc_printf("<response>");
384
385                         wc_printf("<href>");
386                         groupdav_identify_host();
387                         wc_printf("/groupdav/");
388                         urlescputs(roomname);
389                         wc_printf("/</href>");
390
391                         wc_printf("<propstat>");
392                         wc_printf("<status>HTTP/1.1 200 OK</status>");
393                         wc_printf("<prop>");
394                         wc_printf("<displayname>");
395                         escputs(roomname);
396                         wc_printf("</displayname>");
397                         wc_printf("<resourcetype><collection/>");
398
399                         switch(view) {
400                         case VIEW_CALENDAR:
401                                 wc_printf("<G:vevent-collection />");
402                                 break;
403                         case VIEW_TASKS:
404                                 wc_printf("<G:vtodo-collection />");
405                                 break;
406                         case VIEW_ADDRESSBOOK:
407                                 wc_printf("<G:vcard-collection />");
408                                 break;
409                         case VIEW_NOTES:
410                                 wc_printf("<G:vnotes-collection />");
411                                 break;
412                         case VIEW_JOURNAL:
413                                 wc_printf("<G:vjournal-collection />");
414                                 break;
415                         case VIEW_WIKI:
416                                 wc_printf("<G:wiki-collection />");
417                                 break;
418                         }
419
420                         wc_printf("</resourcetype>");
421                         wc_printf("<getlastmodified>");
422                                 escputs(datestring);
423                         wc_printf("</getlastmodified>");
424                         wc_printf("</prop>");
425                         wc_printf("</propstat>");
426                         wc_printf("</response>");
427                 }
428         }
429         wc_printf("</multistatus>\n");
430
431         end_burst();
432 }
433
434
435
436 /*
437  * The pathname is always going to be /groupdav/room_name/msg_num
438  */
439 void groupdav_propfind(void) 
440 {
441 #ifdef DEV_RESTDAV
442         HashList *SubRooms = NULL;
443         long State;
444 #endif
445         wcsession *WCC = WC;
446         StrBuf *dav_roomname;
447         StrBuf *dav_uid;
448         StrBuf *MsgNum;
449         long BufLen;
450         long dav_msgnum = (-1);
451         char uid[256];
452         char encoded_uid[256];
453         long *msgs = NULL;
454         int num_msgs = 0;
455         int i;
456         char datestring[256];
457         time_t now;
458
459         now = time(NULL);
460         http_datestring(datestring, sizeof datestring, now);
461
462         dav_roomname = NewStrBuf();
463         dav_uid = NewStrBuf();
464         StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
465         StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');
466 #ifdef DEV_RESTDAV
467         /*
468          * If the room name is blank, the client is requesting a
469          * folder list.
470          */
471         SubRooms = NewHash(1, Flathash);
472         State = GotoRestRoom(SubRooms);
473         if (((State & REST_IN_ROOM) == 0) ||
474             (((State & (REST_GOT_LOCAL_PART)) == 0) &&
475              (WCC->Hdr->HR.dav_depth == 0)))
476         {
477                 now = time(NULL);
478                 http_datestring(datestring, sizeof datestring, now);
479
480                 /*
481                  * Be rude.  Completely ignore the XML request and simply send them
482                  * everything we know about.  Let the client sort it out.
483                  */
484                 hprintf("HTTP/1.0 207 Multi-Status\r\n");
485                 groupdav_common_headers();
486                 hprintf("Date: %s\r\n", datestring);
487                 hprintf("Content-type: text/xml\r\n");
488                 if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))     
489                         hprintf("Content-encoding: identity\r\n");
490
491                 begin_burst();
492
493
494                 /*
495                  * If the client is requesting the root, show a root node.
496                  */
497                 do_template("dav_propfind_top", NULL);
498                 end_burst();
499                 FreeStrBuf(&dav_roomname);
500                 FreeStrBuf(&dav_uid);
501                 FreeHashList(&SubRooms);
502                 return;
503         }
504
505         if ((State & (REST_GOT_LOCAL_PART)) == 0) {
506                 readloop(headers, eReadEUIDS);
507                 FreeHashList(&SubRooms);
508                 return;
509
510         }
511
512
513         
514         FreeHashList(&SubRooms);
515
516 #endif
517
518         /*
519          * If the room name is blank, the client is requesting a
520          * folder list.
521          */
522         if (StrLength(dav_roomname) == 0) {
523                 groupdav_collection_list();
524                 FreeStrBuf(&dav_roomname);
525                 FreeStrBuf(&dav_uid);
526                 return;
527         }
528
529         /* Go to the correct room. */
530         if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
531                 gotoroom(dav_roomname);
532         }
533         if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
534                 hprintf("HTTP/1.1 404 not found\r\n");
535                 groupdav_common_headers();
536                 hprintf("Date: %s\r\n", datestring);
537                 hprintf("Content-Type: text/plain\r\n");
538                 wc_printf("There is no folder called \"%s\" on this server.\r\n",
539                         ChrPtr(dav_roomname)
540                 );
541                 end_burst();
542                 FreeStrBuf(&dav_roomname);
543                 FreeStrBuf(&dav_uid);
544                 return;
545         }
546
547         /* If dav_uid is non-empty, client is requesting a PROPFIND on
548          * a specific item in the room.  This is not valid GroupDAV, but
549          * it is valid WebDAV.
550          */
551         if (StrLength(dav_uid) != 0) {
552
553                 dav_msgnum = locate_message_by_uid(ChrPtr(dav_uid));
554                 if (dav_msgnum < 0) {
555                         hprintf("HTTP/1.1 404 not found\r\n");
556                         groupdav_common_headers();
557                         hprintf("Content-Type: text/plain\r\n");
558                         wc_printf("Object \"%s\" was not found in the \"%s\" folder.\r\n",
559                                 ChrPtr(dav_uid),
560                                 ChrPtr(dav_roomname)
561                         );
562                         end_burst();
563                         FreeStrBuf(&dav_roomname);
564                         FreeStrBuf(&dav_uid);
565                         return;
566                 }
567
568                 /* Be rude.  Completely ignore the XML request and simply send them
569                  * everything we know about (which is going to simply be the ETag and
570                  * nothing else).  Let the client-side parser sort it out.
571                  */
572                 hprintf("HTTP/1.0 207 Multi-Status\r\n");
573                 groupdav_common_headers();
574                 hprintf("Date: %s\r\n", datestring);
575                 hprintf("Content-type: text/xml\r\n");
576                 if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))     
577                         hprintf("Content-encoding: identity\r\n");
578         
579                 begin_burst();
580         
581                 wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
582                         "<multistatus xmlns=\"DAV:\">"
583                 );
584
585                 wc_printf("<response>");
586                 
587                 wc_printf("<href>");
588                 groupdav_identify_host();
589                 wc_printf("/groupdav/");
590                 urlescputs(ChrPtr(WCC->CurRoom.name));
591                 euid_escapize(encoded_uid, ChrPtr(dav_uid));
592                 wc_printf("/%s", encoded_uid);
593                 wc_printf("</href>");
594                 wc_printf("<propstat>");
595                 wc_printf("<status>HTTP/1.1 200 OK</status>");
596                 wc_printf("<prop>");
597                 wc_printf("<getetag>\"%ld\"</getetag>", dav_msgnum);
598                 wc_printf("<getlastmodified>");
599                 escputs(datestring);
600                 wc_printf("</getlastmodified>");
601                 wc_printf("</prop>");
602                 wc_printf("</propstat>");
603
604                 wc_printf("</response>\n");
605                 wc_printf("</multistatus>\n");
606                 end_burst();
607                 FreeStrBuf(&dav_roomname);
608                 FreeStrBuf(&dav_uid);
609                 return;
610         }
611         FreeStrBuf(&dav_roomname);
612         FreeStrBuf(&dav_uid);
613
614
615         /*
616          * We got to this point, which means that the client is requesting
617          * a 'collection' (i.e. a list of all items in the room).
618          *
619          * Be rude.  Completely ignore the XML request and simply send them
620          * everything we know about (which is going to simply be the ETag and
621          * nothing else).  Let the client-side parser sort it out.
622          */
623         hprintf("HTTP/1.0 207 Multi-Status\r\n");
624         groupdav_common_headers();
625         hprintf("Date: %s\r\n", datestring);
626         hprintf("Content-type: text/xml\r\n");
627         if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))     
628                 hprintf("Content-encoding: identity\r\n");
629
630         begin_burst();
631
632         wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
633                 "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
634         );
635
636
637         /* Transmit the collection resource (FIXME check depth and starting point) */
638         wc_printf("<response>");
639
640         wc_printf("<href>");
641         groupdav_identify_host();
642         wc_printf("/groupdav/");
643         urlescputs(ChrPtr(WCC->CurRoom.name));
644         wc_printf("</href>");
645
646         wc_printf("<propstat>");
647         wc_printf("<status>HTTP/1.1 200 OK</status>");
648         wc_printf("<prop>");
649         wc_printf("<displayname>");
650         escputs(ChrPtr(WCC->CurRoom.name));
651         wc_printf("</displayname>");
652         wc_printf("<resourcetype><collection/>");
653
654         switch(WCC->CurRoom.defview) {
655                 case VIEW_CALENDAR:
656                         wc_printf("<G:vevent-collection />");
657                         break;
658                 case VIEW_TASKS:
659                         wc_printf("<G:vtodo-collection />");
660                         break;
661                 case VIEW_ADDRESSBOOK:
662                         wc_printf("<G:vcard-collection />");
663                         break;
664         }
665
666         wc_printf("</resourcetype>");
667         /* FIXME get the mtime
668         wc_printf("<getlastmodified>");
669                 escputs(datestring);
670         wc_printf("</getlastmodified>");
671         */
672         wc_printf("</prop>");
673         wc_printf("</propstat>");
674         wc_printf("</response>");
675
676         /* Transmit the collection listing (FIXME check depth and starting point) */
677
678         MsgNum = NewStrBuf();
679         serv_puts("MSGS ALL");
680
681         StrBuf_ServGetln(MsgNum);
682         if (GetServerStatus(MsgNum, NULL) == 1)
683                 while (BufLen = StrBuf_ServGetln(MsgNum), strcmp(ChrPtr(MsgNum), "000"))  {
684                         msgs = realloc(msgs, ++num_msgs * sizeof(long));
685                         msgs[num_msgs-1] = StrTol(MsgNum);
686                 }
687
688         if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
689
690                 strcpy(uid, "");
691                 now = (-1);
692                 serv_printf("MSG0 %ld|3", msgs[i]);
693                 StrBuf_ServGetln(MsgNum);
694                 if (GetServerStatus(MsgNum, NULL) == 1)
695                         while (BufLen = StrBuf_ServGetln(MsgNum), strcmp(ChrPtr(MsgNum), "000")) 
696                         {
697                                 if (!strncasecmp(ChrPtr(MsgNum), "exti=", 5)) {
698                                         strcpy(uid, &ChrPtr(MsgNum)[5]);
699                                 }
700                                 else if (!strncasecmp(ChrPtr(MsgNum), "time=", 5)) {
701                                         now = atol(&ChrPtr(MsgNum)[5]);
702                         }
703                 }
704
705                 if (!IsEmptyStr(uid)) {
706                         wc_printf("<response>");
707                                 wc_printf("<href>");
708                                         groupdav_identify_host();
709                                         wc_printf("/groupdav/");
710                                         urlescputs(ChrPtr(WCC->CurRoom.name));
711                                         euid_escapize(encoded_uid, uid);
712                                         wc_printf("/%s", encoded_uid);
713                                 wc_printf("</href>");
714                                 switch(WCC->CurRoom.defview) {
715                                 case VIEW_CALENDAR:
716                                         wc_printf("<getcontenttype>text/x-ical</getcontenttype>");
717                                         break;
718                                 case VIEW_TASKS:
719                                         wc_printf("<getcontenttype>text/x-ical</getcontenttype>");
720                                         break;
721                                 case VIEW_ADDRESSBOOK:
722                                         wc_printf("<getcontenttype>text/x-vcard</getcontenttype>");
723                                         break;
724                                 }
725                                 wc_printf("<propstat>");
726                                         wc_printf("<status>HTTP/1.1 200 OK</status>");
727                                         wc_printf("<prop>");
728                                                 wc_printf("<getetag>\"%ld\"</getetag>", msgs[i]);
729                                         if (now > 0L) {
730                                                 http_datestring(datestring, sizeof datestring, now);
731                                                 wc_printf("<getlastmodified>");
732                                                 escputs(datestring);
733                                                 wc_printf("</getlastmodified>");
734                                         }
735                                         wc_printf("</prop>");
736                                 wc_printf("</propstat>");
737                         wc_printf("</response>");
738                 }
739         }
740         FreeStrBuf(&MsgNum);
741
742         wc_printf("</multistatus>\n");
743         end_burst();
744
745         if (msgs != NULL) {
746                 free(msgs);
747         }
748 }
749
750
751
752 int ParseMessageListHeaders_EUID(StrBuf *Line, 
753                                  const char **pos, 
754                                  message_summary *Msg, 
755                                  StrBuf *ConversionBuffer)
756 {
757         Msg->euid = NewStrBuf();
758         StrBufExtract_NextToken(Msg->euid,  Line, pos, '|');
759         Msg->date = StrBufExtractNext_long(Line, pos, '|');
760         
761         return StrLength(Msg->euid) > 0;
762 }
763
764 int DavUIDL_GetParamsGetServerCall(SharedMessageStatus *Stat, 
765                                     void **ViewSpecific, 
766                                     long oper, 
767                                     char *cmd, 
768                                     long len)
769 {
770         Stat->defaultsortorder = 0;
771         Stat->sortit = 0;
772         Stat->load_seen = 0;
773         Stat->maxmsgs  = 9999999;
774
775         snprintf(cmd, len, "MSGS ALL|||2");
776         return 200;
777 }
778
779 int DavUIDL_RenderView_or_Tail(SharedMessageStatus *Stat, 
780                                 void **ViewSpecific, 
781                                 long oper)
782 {
783         
784         DoTemplate(HKEY("msg_listview"),NULL,&NoCtx);
785         
786         return 0;
787 }
788
789 int DavUIDL_Cleanup(void **ViewSpecific)
790 {
791         /* Note: wDumpContent() will output one additional </div> tag. */
792         /* We ought to move this out into template */
793         wDumpContent(1);
794
795         return 0;
796 }
797
798
799
800
801 void 
802 InitModule_PROPFIND
803 (void)
804 {
805         RegisterReadLoopHandlerset(
806                 eReadEUIDS,
807                 DavUIDL_GetParamsGetServerCall,
808                 NULL, /// TODO: is this right?
809                 ParseMessageListHeaders_EUID,
810                 NULL, //// ""
811                 DavUIDL_RenderView_or_Tail,
812                 DavUIDL_Cleanup);
813
814 }