Readloop remove special cases
[citadel.git] / webcit / dav_propfind.c
index e6d0abb5e5b3f1cafb5260e15a848d64c415481e..66b2063e9139dfc3f1081b4ee9263035d00705ae 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Handles GroupDAV PROPFIND requests.
+ * Handles GroupDAV and CalDAV 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-2011 by the citadel.org team
+ * References:
+ * http://www.ietf.org/rfc/rfc4791.txt
+ * http://blogs.nologin.es/rickyepoderi/index.php?/archives/14-Introducing-CalDAV-Part-I.html
+
+Sample query:
+
+PROPFIND /groupdav/calendar/ HTTP/1.1
+Content-type: text/xml; charset=utf-8
+Content-length: 166
+
+<?xml version="1.0" encoding="UTF-8"?>
+<D:propfind xmlns:D="DAV:">
+  <D:prop>
+    <D:getcontenttype/>
+    <D:resourcetype/>
+    <D:getetag/>
+  </D:prop>
+</D:propfind>
+
+ *
+ * Copyright (c) 2005-2012 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 version 3.
  * 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.
- *
  */
 
 #include "webcit.h"
 #include "webserver.h"
 #include "dav.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
@@ -75,7 +92,8 @@ const folder *GetRESTFolder(int IgnoreFloor, HashList *Subfolders)
 
 /*
  * 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.
+ * 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);
@@ -88,7 +106,6 @@ const folder *GetRESTFolder(int IgnoreFloor, HashList *Subfolders)
                    (ThisFolder->Floor != WCC->CurrentFloor))
                        continue;
 
-
                if (ThisFolder->nRoomNameParts > 1) 
                {
                        /*TODO: is that number all right? */
@@ -427,6 +444,15 @@ void dav_collection_list(void)
 }
 
 
+void propfind_xml_start(void *data, const char *supplied_el, const char **attr) {
+       // syslog(LOG_DEBUG, "<%s>", supplied_el);
+}
+
+void propfind_xml_end(void *data, const char *supplied_el) {
+       // syslog(LOG_DEBUG, "</%s>", supplied_el);
+}
+
+
 
 /*
  * The pathname is always going to be /groupdav/room_name/msg_num
@@ -447,16 +473,54 @@ void dav_propfind(void)
        char datestring[256];
        time_t now;
 
-       syslog(LOG_DEBUG, "PROPFIND\n\033[31m%s\033[0m", ChrPtr(WCC->upload));
-
        now = time(NULL);
        http_datestring(datestring, sizeof datestring, now);
 
+       int parse_success = 0;
+       XML_Parser xp = XML_ParserCreateNS(NULL, '|');
+       if (xp) {
+               // XML_SetUserData(xp, XXX);
+               XML_SetElementHandler(xp, propfind_xml_start, propfind_xml_end);
+               // XML_SetCharacterDataHandler(xp, xrds_xml_chardata);
+
+               const char *req = ChrPtr(WCC->upload);
+               if (req) {
+                       req = strchr(req, '<');                 /* hunt for the first tag */
+               }
+               if (!req) {
+                       req = "ERROR";                          /* force it to barf */
+               }
+
+               i = XML_Parse(xp, req, strlen(req), 1);
+               if (!i) {
+                       syslog(LOG_DEBUG, "XML_Parse() failed: %s", XML_ErrorString(XML_GetErrorCode(xp)));
+                       XML_ParserFree(xp);
+                       parse_success = 0;
+               }
+               else {
+                       parse_success = 1;
+               }
+       }
+
+       if (!parse_success) {
+               hprintf("HTTP/1.1 500 Internal Server Error\r\n");
+               dav_common_headers();
+               hprintf("Date: %s\r\n", datestring);
+               hprintf("Content-Type: text/plain\r\n");
+               wc_printf("An internal error has occurred at %s:%d.\r\n", __FILE__ , __LINE__ );
+               end_burst();
+               return;
+       }
+
        dav_roomname = NewStrBuf();
        dav_uid = NewStrBuf();
        StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
        StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');
 
+       syslog(LOG_DEBUG, "PROPFIND requested for '%s' at depth %d",
+               ChrPtr(dav_roomname), WCC->Hdr->HR.dav_depth
+       );
+
        /*
         * If the room name is blank, the client is requesting a folder list.
         */
@@ -552,13 +616,16 @@ void dav_propfind(void)
 
 
        /*
-        * We got to this point, which means that the client is requesting
-        * a 'collection' (i.e. a list of all items in the room).
+        * If we get to this point the client is performing a PROPFIND on the room itself.
+        *
+        * We call it a room; DAV calls it a "collection."  We have to give it some properties
+        * of the room itself and then offer a list of all items contained therein.
         *
         * Be rude.  Completely ignore the XML request and simply send them
         * everything we know about (which is going to simply be the ETag and
         * nothing else).  Let the client-side parser sort it out.
         */
+       //syslog(LOG_DEBUG, "BE RUDE AND IGNORE: \033[31m%s\033[0m", ChrPtr(WC->upload) );
        hprintf("HTTP/1.0 207 Multi-Status\r\n");
        dav_common_headers();
        hprintf("Date: %s\r\n", datestring);
@@ -569,30 +636,36 @@ void dav_propfind(void)
        begin_burst();
 
        wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
-               "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
+               "<D:multistatus "
+                       "xmlns:D=\"DAV:\" "
+                       "xmlns:G=\"http://groupdav.org/\" "
+                       "xmlns:C=\"urn:ietf:params:xml:ns:caldav\""
+               ">"
        );
 
+       /* Transmit the collection resource */
+       wc_printf("<D:response>");
 
-       /* Transmit the collection resource (FIXME check depth and starting point) */
-       wc_printf("<response>");
-
-       wc_printf("<href>");
+       wc_printf("<D:href>");
        dav_identify_host();
        wc_printf("/groupdav/");
        urlescputs(ChrPtr(WCC->CurRoom.name));
-       wc_printf("</href>");
+       wc_printf("</D:href>");
 
-       wc_printf("<propstat>");
-       wc_printf("<status>HTTP/1.1 200 OK</status>");
-       wc_printf("<prop>");
-       wc_printf("<displayname>");
+       wc_printf("<D:propstat>");
+       wc_printf("<D:status>HTTP/1.1 200 OK</D:status>");
+       wc_printf("<D:prop>");
+       wc_printf("<D:displayname>");
        escputs(ChrPtr(WCC->CurRoom.name));
-       wc_printf("</displayname>");
-       wc_printf("<resourcetype><collection/>");
+       wc_printf("</D:displayname>");
 
+       wc_printf("<D:owner/>");                /* empty owner ought to be legal; see rfc3744 section 5.1 */
+
+       wc_printf("<D:resourcetype><D:collection/>");
        switch(WCC->CurRoom.defview) {
                case VIEW_CALENDAR:
                        wc_printf("<G:vevent-collection />");
+                       wc_printf("<C:calendar />");
                        break;
                case VIEW_TASKS:
                        wc_printf("<G:vtodo-collection />");
@@ -601,89 +674,92 @@ void dav_propfind(void)
                        wc_printf("<G:vcard-collection />");
                        break;
        }
+       wc_printf("</D:resourcetype>");
 
-       wc_printf("</resourcetype>");
        /* FIXME get the mtime
-       wc_printf("<getlastmodified>");
+       wc_printf("<D:getlastmodified>");
                escputs(datestring);
-       wc_printf("</getlastmodified>");
+       wc_printf("</D:getlastmodified>");
        */
-       wc_printf("</prop>");
-       wc_printf("</propstat>");
-       wc_printf("</response>");
-
-       /* Transmit the collection listing (FIXME check depth and starting point) */
+       wc_printf("</D:prop>");
+       wc_printf("</D:propstat>");
+       wc_printf("</D:response>");
 
-       MsgNum = NewStrBuf();
-       serv_puts("MSGS ALL");
+       /* If a depth greater than zero was specified, transmit the collection listing */
 
-       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]);
+       if (WCC->Hdr->HR.dav_depth > 0) {
+               MsgNum = NewStrBuf();
+               serv_puts("MSGS ALL");
+       
                StrBuf_ServGetln(MsgNum);
                if (GetServerStatus(MsgNum, NULL) == 1)
                        while (BufLen = StrBuf_ServGetln(MsgNum), 
-                              ((BufLen >= 0) && 
-                               ((BufLen != 3) || strcmp(ChrPtr(MsgNum), "000")) ))
+                       ((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]);
+                               msgs = realloc(msgs, ++num_msgs * sizeof(long));
+                               msgs[num_msgs-1] = StrTol(MsgNum);
                        }
-               }
-
-               if (!IsEmptyStr(uid)) {
-                       wc_printf("<response>");
-                               wc_printf("<href>");
-                                       dav_identify_host();
-                                       wc_printf("/groupdav/");
-                                       urlescputs(ChrPtr(WCC->CurRoom.name));
-                                       euid_escapize(encoded_uid, uid);
-                                       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;
+       
+               if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
+       
+                       syslog(LOG_DEBUG, "PROPFIND enumerating message # %ld", msgs[i]);
+                       strcpy(uid, "");
+                       now = (-1);
+                       serv_printf("MSG0 %ld|3", msgs[i]);
+                       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]);
                                }
-                               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>");
+                       }
+       
+                       if (!IsEmptyStr(uid)) {
+                               wc_printf("<D:response>");
+                                       wc_printf("<D:href>");
+                                               dav_identify_host();
+                                               wc_printf("/groupdav/");
+                                               urlescputs(ChrPtr(WCC->CurRoom.name));
+                                               euid_escapize(encoded_uid, uid);
+                                               wc_printf("/%s", encoded_uid);
+                                       wc_printf("</D:href>");
+                                       switch(WCC->CurRoom.defview) {
+                                       case VIEW_CALENDAR:
+                                               wc_printf("<D:getcontenttype>text/x-ical</D:getcontenttype>");
+                                               break;
+                                       case VIEW_TASKS:
+                                               wc_printf("<D:getcontenttype>text/x-ical</D:getcontenttype>");
+                                               break;
+                                       case VIEW_ADDRESSBOOK:
+                                               wc_printf("<D:getcontenttype>text/x-vcard</D:getcontenttype>");
+                                               break;
                                        }
-                                       wc_printf("</prop>");
-                               wc_printf("</propstat>");
-                       wc_printf("</response>");
+                                       wc_printf("<D:propstat>");
+                                               wc_printf("<D:status>HTTP/1.1 200 OK</D:status>");
+                                               wc_printf("<D:prop>");
+                                                       wc_printf("<D:getetag>\"%ld\"</D:getetag>", msgs[i]);
+                                               if (now > 0L) {
+                                                       http_datestring(datestring, sizeof datestring, now);
+                                                       wc_printf("<D:getlastmodified>");
+                                                       escputs(datestring);
+                                                       wc_printf("</D:getlastmodified>");
+                                               }
+                                               wc_printf("</D:prop>");
+                                       wc_printf("</D:propstat>");
+                               wc_printf("</D:response>");
+                       }
                }
+               FreeStrBuf(&MsgNum);
        }
-       FreeStrBuf(&MsgNum);
 
-       wc_printf("</multistatus>\n");
+       wc_printf("</D:multistatus>\n");
        end_burst();
 
        if (msgs != NULL) {
@@ -706,10 +782,12 @@ int ParseMessageListHeaders_EUID(StrBuf *Line,
 }
 
 int DavUIDL_GetParamsGetServerCall(SharedMessageStatus *Stat, 
-                                   void **ViewSpecific, 
-                                   long oper, 
-                                   char *cmd, 
-                                   long len)
+                                  void **ViewSpecific, 
+                                  long oper, 
+                                  char *cmd, 
+                                  long len,
+                                  char *filter,
+                                  long flen)
 {
        Stat->defaultsortorder = 0;
        Stat->sortit = 0;
@@ -749,6 +827,7 @@ InitModule_PROPFIND
        RegisterReadLoopHandlerset(
                eReadEUIDS,
                DavUIDL_GetParamsGetServerCall,
+               NULL,
                NULL, /// TODO: is this right?
                ParseMessageListHeaders_EUID,
                NULL, //// ""