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