Remove $Id$ tags from most of webcit
[citadel.git] / webcit / groupdav_propfind.c
index e12051265dd61db7b63b8179bd368724d4b035a6..7c505e0bd7dfbc0594587a2cdd533581f1f0d884 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id$
- *
  * Handles GroupDAV PROPFIND requests.
  *
  * A few notes about our XML output:
  *     This makes it difficult to read, but we have discovered clients which
  *     crash when you try to pretty it up.
  *
+ * Copyright (c) 2005-2010 by the citadel.org team
+ *
+ * This program is free 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"
@@ -43,6 +56,200 @@ long locate_message_by_uid(const char *uid) {
 }
 
 
+/*
+ * 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);
+                               
+                               lprintf(0, "5\n");
+                               continue;
+                       }
+                       DeleteHashPos(&itd);
+                       Dir = (StrBuf*) vDir;
+                       if (strcmp(ChrPtr(ThisFolder->name), 
+                                              ChrPtr(Dir))
+                           != 0)
+                       {
+                               DeleteHashPos(&itd);
+                               
+                               lprintf(0, "5\n");
+                               continue;
+                       }
+                       DeleteHashPos(&itfl);
+                       DeleteHashPos(&itd);
+                       if (delta != 2) {
+                               nBestGuess = 1;
+                               BestGuess = ThisFolder;
+                       }
+                       else 
+                               FoundFolder = ThisFolder;
+               }
+       }
+
+/* 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;
+}
+
+
 
 /*
  * List rooms (or "collections" in DAV terminology) which contain
@@ -228,6 +435,10 @@ void groupdav_collection_list(void)
  */
 void groupdav_propfind(void) 
 {
+#ifdef DEV_RESTDAV
+       HashList *SubRooms = NULL;
+       long State;
+#endif
        wcsession *WCC = WC;
        StrBuf *dav_roomname;
        StrBuf *dav_uid;
@@ -249,6 +460,56 @@ void groupdav_propfind(void)
        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");
+               hprintf("Content-encoding: identity\r\n");
+
+               begin_burst();
+
+
+               /*
+                * If the client is requesting the root, show a root node.
+                */
+               do_template("dav_propfind_top", NULL);
+               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
@@ -262,10 +523,10 @@ void groupdav_propfind(void)
        }
 
        /* Go to the correct room. */
-       if (strcasecmp(ChrPtr(WCC->wc_roomname), ChrPtr(dav_roomname))) {
+       if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
                gotoroom(dav_roomname);
        }
-       if (strcasecmp(ChrPtr(WCC->wc_roomname), ChrPtr(dav_roomname))) {
+       if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
                hprintf("HTTP/1.1 404 not found\r\n");
                groupdav_common_headers();
                hprintf("Date: %s\r\n", datestring);
@@ -321,7 +582,7 @@ void groupdav_propfind(void)
                wc_printf("<href>");
                groupdav_identify_host();
                wc_printf("/groupdav/");
-               urlescputs(ChrPtr(WCC->wc_roomname));
+               urlescputs(ChrPtr(WCC->CurRoom.name));
                euid_escapize(encoded_uid, ChrPtr(dav_uid));
                wc_printf("/%s", encoded_uid);
                wc_printf("</href>");
@@ -373,18 +634,18 @@ void groupdav_propfind(void)
        wc_printf("<href>");
        groupdav_identify_host();
        wc_printf("/groupdav/");
-       urlescputs(ChrPtr(WCC->wc_roomname));
+       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->wc_roomname));
+       escputs(ChrPtr(WCC->CurRoom.name));
        wc_printf("</displayname>");
        wc_printf("<resourcetype><collection/>");
 
-       switch(WCC->wc_default_view) {
+       switch(WCC->CurRoom.defview) {
                case VIEW_CALENDAR:
                        wc_printf("<G:vevent-collection />");
                        break;
@@ -440,11 +701,11 @@ void groupdav_propfind(void)
                                wc_printf("<href>");
                                        groupdav_identify_host();
                                        wc_printf("/groupdav/");
-                                       urlescputs(ChrPtr(WCC->wc_roomname));
+                                       urlescputs(ChrPtr(WCC->CurRoom.name));
                                        euid_escapize(encoded_uid, uid);
                                        wc_printf("/%s", encoded_uid);
                                wc_printf("</href>");
-                               switch(WCC->wc_default_view) {
+                               switch(WCC->CurRoom.defview) {
                                case VIEW_CALENDAR:
                                        wc_printf("<getcontenttype>text/x-ical</getcontenttype>");
                                        break;
@@ -479,3 +740,69 @@ void groupdav_propfind(void)
                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);
+
+}