Version number to 8.02 for upcoming release
[citadel.git] / webcit / groupdav_propfind.c
index 8e6154d611e18f032f714ad6e2d182bd6a4a5479..1580d494652cb0e8d45dafdabae50e1a71cf31db 100644 (file)
 /*
- * $Id$
- *
  * Handles GroupDAV PROPFIND requests.
  *
  * A few notes about our XML output:
  *
  * --> Yes, we are spewing tags directly instead of using an XML library.
- *     If you would like to rewrite this using libxml2, code it up and submit
- *     a patch.  Whining will be summarily ignored.
+ *     Whining about it will be summarily ignored.
  *
  * --> XML is deliberately output with no whitespace/newlines between tags.
  *     This makes it difficult to read, but we have discovered clients which
  *     crash when you try to pretty it up.
  *
+ * Copyright (c) 2005-2011 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "webcit.h"
 #include "webserver.h"
 #include "groupdav.h"
 
+extern int DisableGzip;
+
 /*
  * Given an encoded UID, translate that to an unencoded Citadel EUID and
  * then search for it in the current room.  Return a message number or -1
  * if not found.
  *
  */
-long locate_message_by_uid(char *uid) {
+long locate_message_by_uid(const char *uid) {
        char buf[256];
        char decoded_uid[1024];
        long retval = (-1L);
 
-       /* Decode the uid */
+       /* decode the UID */
        euid_unescapize(decoded_uid, uid);
 
-/**************  THE NEW WAY ***********************/
+       /* ask Citadel if we have this one */
        serv_printf("EUID %s", decoded_uid);
        serv_getln(buf, sizeof buf);
        if (buf[0] == '2') {
                retval = atol(&buf[4]);
        }
-/***************************************************/
 
-/**************  THE OLD WAY ***********************
-       serv_puts("MSGS ALL|0|1");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '8') {
-               serv_printf("exti|%s", decoded_uid);
-               serv_puts("000");
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       retval = atol(buf);
+       return(retval);
+}
+
+
+/*
+ * IgnoreFloor: set to 0 or 1 _nothing else_
+ * Subfolders: direct child floors will be put here.
+ */
+const folder *GetRESTFolder(int IgnoreFloor, HashList *Subfolders)
+{
+       wcsession  *WCC = WC;
+       void *vFolder;
+       const folder *ThisFolder = NULL;
+       const folder *FoundFolder = NULL;
+       const folder *BestGuess = NULL;
+       int nBestGuess = 0;
+       HashPos    *itd, *itfl;
+       StrBuf     * Dir;
+       void       *vDir;
+       long        len;
+        const char *Key;
+       int iRoom, jURL, urlp;
+       int delta;
+
+/*
+ * Guess room: if the full URL matches a room, list thats it. We also need to remember direct sub rooms.
+ * 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.
+ */
+       itfl = GetNewHashPos(WCC->Floors, 0);
+       urlp = GetCount(WCC->Directory);
+
+       while (GetNextHashPos(WCC->Floors, itfl, &len, &Key, &vFolder) && 
+              (ThisFolder == NULL))
+       {
+               ThisFolder = vFolder;
+               if (!IgnoreFloor && /* so we can handle legacy URLS... */
+                   (ThisFolder->Floor != WCC->CurrentFloor))
+                       continue;
+
+
+               if (ThisFolder->nRoomNameParts > 1) 
+               {
+                       /*TODO: is that number all right? */
+//                     if (urlp - ThisFolder->nRoomNameParts != 2) {
+//                             if (BestGuess != NULL)
+//                                     continue;
+//ThisFolder->name
+//                             itd  = GetNewHashPos(WCC->Directory, 0);
+//                             GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir); //TODO: how many to fast forward?
+//                     }
+                       itd  = GetNewHashPos(WCC->Directory, 0);
+                       GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir); //TODO: how many to fast forward?
+       
+                       for (iRoom = 0, /* Fast forward the floorname as we checked it above: */ jURL = IgnoreFloor; 
+
+                            (iRoom <= ThisFolder->nRoomNameParts) && (jURL <= urlp); 
+
+                            iRoom++, jURL++, GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir))
+                       {
+                               Dir = (StrBuf*)vDir;
+                               if (strcmp(ChrPtr(ThisFolder->RoomNameParts[iRoom]), 
+                                          ChrPtr(Dir)) != 0)
+                               {
+                                       DeleteHashPos(&itd);
+                                       continue;
+                               }
+                       }
+                       DeleteHashPos(&itd);
+                       /* Gotcha? */
+                       if ((iRoom == ThisFolder->nRoomNameParts) && (jURL == urlp))
+                       {
+                               FoundFolder = ThisFolder;
+                       }
+                       /* URL got more parts then this room, so we remember it for the best guess*/
+                       else if ((jURL <= urlp) &&
+                                (ThisFolder->nRoomNameParts <= nBestGuess))
+                       {
+                               BestGuess = ThisFolder;
+                               nBestGuess = jURL - 1;
+                       }
+                       /* Room has more parts than the URL, it might be a sub-room? */
+                       else if (iRoom <ThisFolder->nRoomNameParts) 
+                       {//// TODO: ThisFolder->nRoomNameParts == urlp - IgnoreFloor???
+                               Put(Subfolders, SKEY(ThisFolder->name), 
+                                   /* Cast away const, its a reference. */
+                                   (void*)ThisFolder, reference_free_handler);
+                       }
+               }
+               else {
+                       delta = GetCount(WCC->Directory) - ThisFolder->nRoomNameParts;
+                       if ((delta != 2) && (nBestGuess > 1))
+                           continue;
+                       
+                       itd  = GetNewHashPos(WCC->Directory, 0);
+                                               
+                       if (!GetNextHashPos(WCC->Directory, 
+                                           itd, &len, &Key, &vDir) ||
+                           (vDir == NULL))
+                       {
+                               DeleteHashPos(&itd);
+                               
+                               syslog(0, "5\n");
+                               continue;
+                       }
+                       DeleteHashPos(&itd);
+                       Dir = (StrBuf*) vDir;
+                       if (strcmp(ChrPtr(ThisFolder->name), 
+                                              ChrPtr(Dir))
+                           != 0)
+                       {
+                               DeleteHashPos(&itd);
+                               
+                               syslog(0, "5\n");
+                               continue;
+                       }
+                       DeleteHashPos(&itfl);
+                       DeleteHashPos(&itd);
+                       if (delta != 2) {
+                               nBestGuess = 1;
+                               BestGuess = ThisFolder;
+                       }
+                       else 
+                               FoundFolder = ThisFolder;
                }
        }
- ***************************************************/
 
-       return(retval);
+/* TODO: Subfolders: remove patterns not matching the best guess or thisfolder */
+       DeleteHashPos(&itfl);
+       if (FoundFolder != NULL)
+               return FoundFolder;
+       else
+               return BestGuess;
+}
+
+
+
+
+long GotoRestRoom(HashList *SubRooms)
+{
+       int IgnoreFloor = 0; /* deprecated... */
+       wcsession *WCC = WC;
+       long Count;
+       long State;
+       const folder *ThisFolder;
+
+       State = REST_TOPLEVEL;
+
+       if (WCC->Hdr->HR.Handler != NULL) 
+               State |= REST_IN_NAMESPACE;
+
+       Count = GetCount(WCC->Directory);
+       
+       if (Count == 0) return State;
+
+       if (Count >= 1) State |=REST_IN_FLOOR;
+       if (Count == 1) return State;
+       
+       /* 
+        * More than 3 params and no floor found? 
+        * -> fall back to old non-floored notation
+        */
+       if ((Count >= 3) && (WCC->CurrentFloor == NULL))
+               IgnoreFloor = 1;
+       if (Count >= 3)
+       {
+               IgnoreFloor = 0;
+               State |= REST_IN_FLOOR;
+
+               ThisFolder = GetRESTFolder(IgnoreFloor, SubRooms);
+               if (ThisFolder != NULL)
+               {
+                       if (WCC->ThisRoom != NULL)
+                               if (CompareRooms(WCC->ThisRoom, ThisFolder) != 0)
+                                       gotoroom(ThisFolder->name);
+                       State |= REST_IN_ROOM;
+                       
+               }
+               if (GetCount(SubRooms) > 0)
+                       State |= REST_HAVE_SUB_ROOMS;
+       }
+       if ((WCC->ThisRoom != NULL) && 
+           (Count + IgnoreFloor > 3))
+       {
+               if (WCC->Hdr->HR.Handler->RID(ExistsID, IgnoreFloor))
+               {
+                       State |= REST_GOT_LOCAL_PART;
+               }
+               else {
+                       /// WHOOPS, not there???
+                       State |= REST_NONEXIST;
+               }
+
+
+       }
+       return State;
 }
 
 
@@ -62,8 +257,9 @@ long locate_message_by_uid(char *uid) {
  * List rooms (or "collections" in DAV terminology) which contain
  * interesting groupware objects.
  */
-void groupdav_collection_list(char *dav_pathname, int dav_depth)
+void groupdav_collection_list(void)
 {
+       wcsession *WCC = WC;
        char buf[256];
        char roomname[256];
        int view;
@@ -73,84 +269,82 @@ void groupdav_collection_list(char *dav_pathname, int dav_depth)
        int is_groupware_collection = 0;
        int starting_point = 1;         /**< 0 for /, 1 for /groupdav/ */
 
-       if (!strcmp(dav_pathname, "/")) {
+       if (WCC->Hdr->HR.Handler == NULL) {
                starting_point = 0;
        }
-       else if (!strcasecmp(dav_pathname, "/groupdav")) {
-               starting_point = 1;
-       }
-       else if (!strcasecmp(dav_pathname, "/groupdav/")) {
+       else if (StrLength(WCC->Hdr->HR.ReqLine) == 0) {
                starting_point = 1;
        }
-       else if ( (!strncasecmp(dav_pathname, "/groupdav/", 10)) && (strlen(dav_pathname) > 10) ) {
+       else {
                starting_point = 2;
        }
 
        now = time(NULL);
        http_datestring(datestring, sizeof datestring, now);
 
-       /**
+       /*
         * Be rude.  Completely ignore the XML request and simply send them
         * everything we know about.  Let the client sort it out.
         */
-       wprintf("HTTP/1.0 207 Multi-Status\r\n");
+       hprintf("HTTP/1.0 207 Multi-Status\r\n");
        groupdav_common_headers();
-       wprintf("Date: %s\r\n", datestring);
-       wprintf("Content-type: text/xml\r\n");
-       wprintf("Content-encoding: identity\r\n");
+       hprintf("Date: %s\r\n", datestring);
+       hprintf("Content-type: text/xml\r\n");
+       if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))     
+               hprintf("Content-encoding: identity\r\n");
 
        begin_burst();
 
-       wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+       wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
        );
 
-       /**
-        *      If the client is requesting the root, show a root node.
+       /*
+        * If the client is requesting the root, show a root node.
         */
        if (starting_point == 0) {
-               wprintf("<response>");
-                       wprintf("<href>");
+               wc_printf("<response>");
+                       wc_printf("<href>");
                                groupdav_identify_host();
-                               wprintf("/");
-                       wprintf("</href>");
-                       wprintf("<propstat>");
-                               wprintf("<status>HTTP/1.1 200 OK</status>");
-                               wprintf("<prop>");
-                                       wprintf("<displayname>/</displayname>");
-                                       wprintf("<resourcetype><collection/></resourcetype>");
-                                       wprintf("<getlastmodified>");
+                               wc_printf("/");
+                       wc_printf("</href>");
+                       wc_printf("<propstat>");
+                               wc_printf("<status>HTTP/1.1 200 OK</status>");
+                               wc_printf("<prop>");
+                                       wc_printf("<displayname>/</displayname>");
+                                       wc_printf("<resourcetype><collection/></resourcetype>");
+                                       wc_printf("<getlastmodified>");
                                                escputs(datestring);
-                                       wprintf("</getlastmodified>");
-                               wprintf("</prop>");
-                       wprintf("</propstat>");
-               wprintf("</response>");
+                                       wc_printf("</getlastmodified>");
+                               wc_printf("</prop>");
+                       wc_printf("</propstat>");
+               wc_printf("</response>");
        }
 
-       /**
-        *      If the client is requesting "/groupdav", show a /groupdav subdirectory.
+       /*
+        * If the client is requesting "/groupdav", show a /groupdav subdirectory.
         */
-       if ((starting_point + dav_depth) >= 1) {
-               wprintf("<response>");
-                       wprintf("<href>");
+       if ((starting_point + WCC->Hdr->HR.dav_depth) >= 1) {
+               wc_printf("<response>");
+                       wc_printf("<href>");
                                groupdav_identify_host();
-                               wprintf("/groupdav");
-                       wprintf("</href>");
-                       wprintf("<propstat>");
-                               wprintf("<status>HTTP/1.1 200 OK</status>");
-                               wprintf("<prop>");
-                                       wprintf("<displayname>GroupDAV</displayname>");
-                                       wprintf("<resourcetype><collection/></resourcetype>");
-                                       wprintf("<getlastmodified>");
+                               wc_printf("/groupdav");
+                       wc_printf("</href>");
+                       wc_printf("<propstat>");
+                               wc_printf("<status>HTTP/1.1 200 OK</status>");
+                               wc_printf("<prop>");
+                                       wc_printf("<displayname>GroupDAV</displayname>");
+                                       wc_printf("<resourcetype><collection/></resourcetype>");
+                                       wc_printf("<getlastmodified>");
                                                escputs(datestring);
-                                       wprintf("</getlastmodified>");
-                               wprintf("</prop>");
-                       wprintf("</propstat>");
-               wprintf("</response>");
+                                       wc_printf("</getlastmodified>");
+                               wc_printf("</prop>");
+                       wc_printf("</propstat>");
+               wc_printf("</response>");
        }
 
-       /**
-        *      Now go through the list and make it look like a DAV collection
+       /*
+        * Now go through the list and make it look like a DAV collection
         */
        serv_puts("LKRA");
        serv_getln(buf, sizeof buf);
@@ -172,52 +366,67 @@ void groupdav_collection_list(char *dav_pathname, int dav_depth)
                 * GroupDAV calendar even if the user has switched it to a
                 * Calendar List view.
                 */
-               if ((view == VIEW_CALENDAR) || (view == VIEW_TASKS) || (view == VIEW_ADDRESSBOOK) ) {
+               if (    (view == VIEW_CALENDAR) || 
+                       (view == VIEW_TASKS) || 
+                       (view == VIEW_ADDRESSBOOK) ||
+                       (view == VIEW_NOTES) ||
+                       (view == VIEW_JOURNAL) ||
+                       (view == VIEW_WIKI)
+               ) {
                        is_groupware_collection = 1;
                }
                else {
                        is_groupware_collection = 0;
                }
 
-               if ( (is_groupware_collection) && ((starting_point + dav_depth) >= 2) ) {
-                       wprintf("<response>");
+               if ( (is_groupware_collection) && ((starting_point + WCC->Hdr->HR.dav_depth) >= 2) ) {
+                       wc_printf("<response>");
 
-                       wprintf("<href>");
+                       wc_printf("<href>");
                        groupdav_identify_host();
-                       wprintf("/groupdav/");
+                       wc_printf("/groupdav/");
                        urlescputs(roomname);
-                       wprintf("/</href>");
+                       wc_printf("/</href>");
 
-                       wprintf("<propstat>");
-                       wprintf("<status>HTTP/1.1 200 OK</status>");
-                       wprintf("<prop>");
-                       wprintf("<displayname>");
+                       wc_printf("<propstat>");
+                       wc_printf("<status>HTTP/1.1 200 OK</status>");
+                       wc_printf("<prop>");
+                       wc_printf("<displayname>");
                        escputs(roomname);
-                       wprintf("</displayname>");
-                       wprintf("<resourcetype><collection/>");
+                       wc_printf("</displayname>");
+                       wc_printf("<resourcetype><collection/>");
 
                        switch(view) {
-                               case VIEW_CALENDAR:
-                                       wprintf("<G:vevent-collection />");
-                                       break;
-                               case VIEW_TASKS:
-                                       wprintf("<G:vtodo-collection />");
-                                       break;
-                               case VIEW_ADDRESSBOOK:
-                                       wprintf("<G:vcard-collection />");
-                                       break;
+                       case VIEW_CALENDAR:
+                               wc_printf("<G:vevent-collection />");
+                               break;
+                       case VIEW_TASKS:
+                               wc_printf("<G:vtodo-collection />");
+                               break;
+                       case VIEW_ADDRESSBOOK:
+                               wc_printf("<G:vcard-collection />");
+                               break;
+                       case VIEW_NOTES:
+                               wc_printf("<G:vnotes-collection />");
+                               break;
+                       case VIEW_JOURNAL:
+                               wc_printf("<G:vjournal-collection />");
+                               break;
+                       case VIEW_WIKI:
+                               wc_printf("<G:wiki-collection />");
+                               break;
                        }
 
-                       wprintf("</resourcetype>");
-                       wprintf("<getlastmodified>");
+                       wc_printf("</resourcetype>");
+                       wc_printf("<getlastmodified>");
                                escputs(datestring);
-                       wprintf("</getlastmodified>");
-                       wprintf("</prop>");
-                       wprintf("</propstat>");
-                       wprintf("</response>");
+                       wc_printf("</getlastmodified>");
+                       wc_printf("</prop>");
+                       wc_printf("</propstat>");
+                       wc_printf("</response>");
                }
        }
-       wprintf("</multistatus>\n");
+       wc_printf("</multistatus>\n");
 
        end_burst();
 }
@@ -227,12 +436,18 @@ void groupdav_collection_list(char *dav_pathname, int dav_depth)
 /*
  * The pathname is always going to be /groupdav/room_name/msg_num
  */
-void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type, char *dav_content) {
-       char dav_roomname[256];
-       char dav_uid[256];
-       char msgnum[256];
+void groupdav_propfind(void) 
+{
+#ifdef DEV_RESTDAV
+       HashList *SubRooms = NULL;
+       long State;
+#endif
+       wcsession *WCC = WC;
+       StrBuf *dav_roomname;
+       StrBuf *dav_uid;
+       StrBuf *MsgNum;
+       long BufLen;
        long dav_msgnum = (-1);
-       char buf[256];
        char uid[256];
        char encoded_uid[256];
        long *msgs = NULL;
@@ -244,32 +459,88 @@ void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type
        now = time(NULL);
        http_datestring(datestring, sizeof datestring, now);
 
-       extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
-       extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
+       dav_roomname = NewStrBuf();
+       dav_uid = NewStrBuf();
+       StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
+       StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');
+#ifdef DEV_RESTDAV
+       /*
+        * If the room name is blank, the client is requesting a
+        * folder list.
+        */
+       SubRooms = NewHash(1, Flathash);
+       State = GotoRestRoom(SubRooms);
+       if (((State & REST_IN_ROOM) == 0) ||
+           (((State & (REST_GOT_LOCAL_PART)) == 0) &&
+            (WCC->Hdr->HR.dav_depth == 0)))
+       {
+               now = time(NULL);
+               http_datestring(datestring, sizeof datestring, now);
+
+               /*
+                * Be rude.  Completely ignore the XML request and simply send them
+                * everything we know about.  Let the client sort it out.
+                */
+               hprintf("HTTP/1.0 207 Multi-Status\r\n");
+               groupdav_common_headers();
+               hprintf("Date: %s\r\n", datestring);
+               hprintf("Content-type: text/xml\r\n");
+               if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))     
+                       hprintf("Content-encoding: identity\r\n");
+
+               begin_burst();
+
+
+               /*
+                * If the client is requesting the root, show a root node.
+                */
+               do_template("dav_propfind_top");
+               end_burst();
+               FreeStrBuf(&dav_roomname);
+               FreeStrBuf(&dav_uid);
+               FreeHashList(&SubRooms);
+               return;
+       }
+
+       if ((State & (REST_GOT_LOCAL_PART)) == 0) {
+               readloop(headers, eReadEUIDS);
+               FreeHashList(&SubRooms);
+               return;
+
+       }
+
+
+       
+       FreeHashList(&SubRooms);
+
+#endif
 
        /*
         * If the room name is blank, the client is requesting a
         * folder list.
         */
-       if (strlen(dav_roomname) == 0) {
-               groupdav_collection_list(dav_pathname, dav_depth);
+       if (StrLength(dav_roomname) == 0) {
+               groupdav_collection_list();
+               FreeStrBuf(&dav_roomname);
+               FreeStrBuf(&dav_uid);
                return;
        }
 
        /* Go to the correct room. */
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+       if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
                gotoroom(dav_roomname);
        }
-       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
-               wprintf("HTTP/1.1 404 not found\r\n");
+       if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
+               hprintf("HTTP/1.1 404 not found\r\n");
                groupdav_common_headers();
-               wprintf("Date: %s\r\n", datestring);
-               wprintf(
-                       "Content-Type: text/plain\r\n"
-                       "\r\n"
-                       "There is no folder called \"%s\" on this server.\r\n",
-                       dav_roomname
+               hprintf("Date: %s\r\n", datestring);
+               hprintf("Content-Type: text/plain\r\n");
+               wc_printf("There is no folder called \"%s\" on this server.\r\n",
+                       ChrPtr(dav_roomname)
                );
+               end_burst();
+               FreeStrBuf(&dav_roomname);
+               FreeStrBuf(&dav_uid);
                return;
        }
 
@@ -277,19 +548,20 @@ void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type
         * a specific item in the room.  This is not valid GroupDAV, but
         * it is valid WebDAV.
         */
-       if (strlen(dav_uid) > 0) {
+       if (StrLength(dav_uid) != 0) {
 
-               dav_msgnum = locate_message_by_uid(dav_uid);
+               dav_msgnum = locate_message_by_uid(ChrPtr(dav_uid));
                if (dav_msgnum < 0) {
-                       wprintf("HTTP/1.1 404 not found\r\n");
+                       hprintf("HTTP/1.1 404 not found\r\n");
                        groupdav_common_headers();
-                       wprintf(
-                               "Content-Type: text/plain\r\n"
-                               "\r\n"
-                               "Object \"%s\" was not found in the \"%s\" folder.\r\n",
-                               dav_uid,
-                               dav_roomname
+                       hprintf("Content-Type: text/plain\r\n");
+                       wc_printf("Object \"%s\" was not found in the \"%s\" folder.\r\n",
+                               ChrPtr(dav_uid),
+                               ChrPtr(dav_roomname)
                        );
+                       end_burst();
+                       FreeStrBuf(&dav_roomname);
+                       FreeStrBuf(&dav_uid);
                        return;
                }
 
@@ -297,37 +569,47 @@ void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type
                 * everything we know about (which is going to simply be the ETag and
                 * nothing else).  Let the client-side parser sort it out.
                 */
-               wprintf("HTTP/1.0 207 Multi-Status\r\n");
+               hprintf("HTTP/1.0 207 Multi-Status\r\n");
                groupdav_common_headers();
-               wprintf("Date: %s\r\n", datestring);
-               wprintf("Content-type: text/xml\r\n");
-               wprintf("Content-encoding: identity\r\n");
+               hprintf("Date: %s\r\n", datestring);
+               hprintf("Content-type: text/xml\r\n");
+               if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))     
+                       hprintf("Content-encoding: identity\r\n");
        
                begin_burst();
        
-               wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+               wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                        "<multistatus xmlns=\"DAV:\">"
                );
 
-               wprintf("<response>");
-
-               wprintf("<href>");
+               wc_printf("<response>");
+               
+               wc_printf("<href>");
                groupdav_identify_host();
-               wprintf("/groupdav/");
-               urlescputs(WC->wc_roomname);
-               euid_escapize(encoded_uid, dav_uid);
-               wprintf("/%s", encoded_uid);
-               wprintf("</href>");
-               wprintf("<propstat>");
-               wprintf("<status>HTTP/1.1 200 OK</status>");
-               wprintf("<prop><getetag>\"%ld\"</getetag></prop>", dav_msgnum);
-               wprintf("</propstat>");
-
-               wprintf("</response>\n");
-               wprintf("</multistatus>\n");
+               wc_printf("/groupdav/");
+               urlescputs(ChrPtr(WCC->CurRoom.name));
+               euid_escapize(encoded_uid, ChrPtr(dav_uid));
+               wc_printf("/%s", encoded_uid);
+               wc_printf("</href>");
+               wc_printf("<propstat>");
+               wc_printf("<status>HTTP/1.1 200 OK</status>");
+               wc_printf("<prop>");
+               wc_printf("<getetag>\"%ld\"</getetag>", dav_msgnum);
+               wc_printf("<getlastmodified>");
+               escputs(datestring);
+               wc_printf("</getlastmodified>");
+               wc_printf("</prop>");
+               wc_printf("</propstat>");
+
+               wc_printf("</response>\n");
+               wc_printf("</multistatus>\n");
                end_burst();
+               FreeStrBuf(&dav_roomname);
+               FreeStrBuf(&dav_uid);
                return;
        }
+       FreeStrBuf(&dav_roomname);
+       FreeStrBuf(&dav_uid);
 
 
        /*
@@ -338,101 +620,200 @@ void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type
         * everything we know about (which is going to simply be the ETag and
         * nothing else).  Let the client-side parser sort it out.
         */
-       wprintf("HTTP/1.0 207 Multi-Status\r\n");
+       hprintf("HTTP/1.0 207 Multi-Status\r\n");
        groupdav_common_headers();
-       wprintf("Date: %s\r\n", datestring);
-       wprintf("Content-type: text/xml\r\n");
-       wprintf("Content-encoding: identity\r\n");
+       hprintf("Date: %s\r\n", datestring);
+       hprintf("Content-type: text/xml\r\n");
+       if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))     
+               hprintf("Content-encoding: identity\r\n");
 
        begin_burst();
 
-       wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+       wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
                "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
        );
 
 
-       /** Transmit the collection resource (FIXME check depth and starting point) */
-       wprintf("<response>");
+       /* Transmit the collection resource (FIXME check depth and starting point) */
+       wc_printf("<response>");
 
-       wprintf("<href>");
-               groupdav_identify_host();
-               wprintf("/groupdav/");
-               urlescputs(WC->wc_roomname);
-       wprintf("</href>");
-
-       wprintf("<propstat>");
-       wprintf("<status>HTTP/1.1 200 OK</status>");
-       wprintf("<prop>");
-       wprintf("<displayname>");
-       escputs(WC->wc_roomname);
-       wprintf("</displayname>");
-       wprintf("<resourcetype><collection/>");
-
-       switch(WC->wc_default_view) {
+       wc_printf("<href>");
+       groupdav_identify_host();
+       wc_printf("/groupdav/");
+       urlescputs(ChrPtr(WCC->CurRoom.name));
+       wc_printf("</href>");
+
+       wc_printf("<propstat>");
+       wc_printf("<status>HTTP/1.1 200 OK</status>");
+       wc_printf("<prop>");
+       wc_printf("<displayname>");
+       escputs(ChrPtr(WCC->CurRoom.name));
+       wc_printf("</displayname>");
+       wc_printf("<resourcetype><collection/>");
+
+       switch(WCC->CurRoom.defview) {
                case VIEW_CALENDAR:
-                       wprintf("<G:vevent-collection />");
+                       wc_printf("<G:vevent-collection />");
                        break;
                case VIEW_TASKS:
-                       wprintf("<G:vtodo-collection />");
+                       wc_printf("<G:vtodo-collection />");
                        break;
                case VIEW_ADDRESSBOOK:
-                       wprintf("<G:vcard-collection />");
+                       wc_printf("<G:vcard-collection />");
                        break;
        }
 
-       wprintf("</resourcetype>");
+       wc_printf("</resourcetype>");
        /* FIXME get the mtime
-       wprintf("<getlastmodified>");
+       wc_printf("<getlastmodified>");
                escputs(datestring);
-       wprintf("</getlastmodified>");
+       wc_printf("</getlastmodified>");
        */
-       wprintf("</prop>");
-       wprintf("</propstat>");
-       wprintf("</response>");
+       wc_printf("</prop>");
+       wc_printf("</propstat>");
+       wc_printf("</response>");
 
-       /** Transmit the collection listing (FIXME check depth and starting point) */
+       /* Transmit the collection listing (FIXME check depth and starting point) */
 
+       MsgNum = NewStrBuf();
        serv_puts("MSGS ALL");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') while (serv_getln(msgnum, sizeof msgnum), strcmp(msgnum, "000")) {
-               msgs = realloc(msgs, ++num_msgs * sizeof(long));
-               msgs[num_msgs-1] = atol(msgnum);
-       }
+
+       StrBuf_ServGetln(MsgNum);
+       if (GetServerStatus(MsgNum, NULL) == 1)
+               while (BufLen = StrBuf_ServGetln(MsgNum), 
+                      ((BufLen >= 0) && 
+                       ((BufLen != 3) || strcmp(ChrPtr(MsgNum), "000"))  ))
+               {
+                       msgs = realloc(msgs, ++num_msgs * sizeof(long));
+                       msgs[num_msgs-1] = StrTol(MsgNum);
+               }
 
        if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
 
                strcpy(uid, "");
+               now = (-1);
                serv_printf("MSG0 %ld|3", msgs[i]);
-               serv_getln(buf, sizeof buf);
-               if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       if (!strncasecmp(buf, "exti=", 5)) {
-                               strcpy(uid, &buf[5]);
+               StrBuf_ServGetln(MsgNum);
+               if (GetServerStatus(MsgNum, NULL) == 1)
+                       while (BufLen = StrBuf_ServGetln(MsgNum), 
+                              ((BufLen >= 0) && 
+                               ((BufLen != 3) || strcmp(ChrPtr(MsgNum), "000")) ))
+                       {
+                               if (!strncasecmp(ChrPtr(MsgNum), "exti=", 5)) {
+                                       strcpy(uid, &ChrPtr(MsgNum)[5]);
+                               }
+                               else if (!strncasecmp(ChrPtr(MsgNum), "time=", 5)) {
+                                       now = atol(&ChrPtr(MsgNum)[5]);
                        }
                }
 
-               if (strlen(uid) > 0) {
-                       wprintf("<response>");
-                               wprintf("<href>");
+               if (!IsEmptyStr(uid)) {
+                       wc_printf("<response>");
+                               wc_printf("<href>");
                                        groupdav_identify_host();
-                                       wprintf("/groupdav/");
-                                       urlescputs(WC->wc_roomname);
+                                       wc_printf("/groupdav/");
+                                       urlescputs(ChrPtr(WCC->CurRoom.name));
                                        euid_escapize(encoded_uid, uid);
-                                       wprintf("/%s", encoded_uid);
-                               wprintf("</href>");
-                               wprintf("<propstat>");
-                                       wprintf("<status>HTTP/1.1 200 OK</status>");
-                                       wprintf("<prop>");
-                                               wprintf("<getetag>\"%ld\"</getetag>", msgs[i]);
-                                       wprintf("</prop>");
-                               wprintf("</propstat>");
-                       wprintf("</response>");
+                                       wc_printf("/%s", encoded_uid);
+                               wc_printf("</href>");
+                               switch(WCC->CurRoom.defview) {
+                               case VIEW_CALENDAR:
+                                       wc_printf("<getcontenttype>text/x-ical</getcontenttype>");
+                                       break;
+                               case VIEW_TASKS:
+                                       wc_printf("<getcontenttype>text/x-ical</getcontenttype>");
+                                       break;
+                               case VIEW_ADDRESSBOOK:
+                                       wc_printf("<getcontenttype>text/x-vcard</getcontenttype>");
+                                       break;
+                               }
+                               wc_printf("<propstat>");
+                                       wc_printf("<status>HTTP/1.1 200 OK</status>");
+                                       wc_printf("<prop>");
+                                               wc_printf("<getetag>\"%ld\"</getetag>", msgs[i]);
+                                       if (now > 0L) {
+                                               http_datestring(datestring, sizeof datestring, now);
+                                               wc_printf("<getlastmodified>");
+                                               escputs(datestring);
+                                               wc_printf("</getlastmodified>");
+                                       }
+                                       wc_printf("</prop>");
+                               wc_printf("</propstat>");
+                       wc_printf("</response>");
                }
        }
+       FreeStrBuf(&MsgNum);
 
-       wprintf("</multistatus>\n");
+       wc_printf("</multistatus>\n");
        end_burst();
 
        if (msgs != NULL) {
                free(msgs);
        }
 }
+
+
+
+int ParseMessageListHeaders_EUID(StrBuf *Line, 
+                                const char **pos, 
+                                message_summary *Msg, 
+                                StrBuf *ConversionBuffer)
+{
+       Msg->euid = NewStrBuf();
+       StrBufExtract_NextToken(Msg->euid,  Line, pos, '|');
+       Msg->date = StrBufExtractNext_long(Line, pos, '|');
+       
+       return StrLength(Msg->euid) > 0;
+}
+
+int DavUIDL_GetParamsGetServerCall(SharedMessageStatus *Stat, 
+                                   void **ViewSpecific, 
+                                   long oper, 
+                                   char *cmd, 
+                                   long len)
+{
+       Stat->defaultsortorder = 0;
+       Stat->sortit = 0;
+       Stat->load_seen = 0;
+       Stat->maxmsgs  = 9999999;
+
+       snprintf(cmd, len, "MSGS ALL|||2");
+       return 200;
+}
+
+int DavUIDL_RenderView_or_Tail(SharedMessageStatus *Stat, 
+                               void **ViewSpecific, 
+                               long oper)
+{
+       
+       DoTemplate(HKEY("msg_listview"),NULL,&NoCtx);
+       
+       return 0;
+}
+
+int DavUIDL_Cleanup(void **ViewSpecific)
+{
+       /* Note: wDumpContent() will output one additional </div> tag. */
+       /* We ought to move this out into template */
+       wDumpContent(1);
+
+       return 0;
+}
+
+
+
+
+void 
+InitModule_PROPFIND
+(void)
+{
+       RegisterReadLoopHandlerset(
+               eReadEUIDS,
+               DavUIDL_GetParamsGetServerCall,
+               NULL, /// TODO: is this right?
+               ParseMessageListHeaders_EUID,
+               NULL, //// ""
+               DavUIDL_RenderView_or_Tail,
+               DavUIDL_Cleanup);
+
+}