Back to index

webcit  8.12-dfsg
dav_propfind.c
Go to the documentation of this file.
00001 /*
00002  * Handles GroupDAV and CalDAV PROPFIND requests.
00003  *
00004  * A few notes about our XML output:
00005  *
00006  * --> Yes, we are spewing tags directly instead of using an XML library.
00007  *     Whining about it will be summarily ignored.
00008  *
00009  * --> XML is deliberately output with no whitespace/newlines between tags.
00010  *     This makes it difficult to read, but we have discovered clients which
00011  *     crash when you try to pretty it up.
00012  *
00013  * References:
00014  * http://www.ietf.org/rfc/rfc4791.txt
00015  * http://blogs.nologin.es/rickyepoderi/index.php?/archives/14-Introducing-CalDAV-Part-I.html
00016 
00017 Sample query:
00018 
00019 PROPFIND /groupdav/calendar/ HTTP/1.1
00020 Content-type: text/xml; charset=utf-8
00021 Content-length: 166
00022 
00023 <?xml version="1.0" encoding="UTF-8"?>
00024 <D:propfind xmlns:D="DAV:">
00025   <D:prop>
00026     <D:getcontenttype/>
00027     <D:resourcetype/>
00028     <D:getetag/>
00029   </D:prop>
00030 </D:propfind>
00031 
00032  *
00033  * Copyright (c) 2005-2012 by the citadel.org team
00034  *
00035  * This program is open source software; you can redistribute it and/or
00036  * modify it under the terms of the GNU General Public License version 3.
00037  *
00038  * This program is distributed in the hope that it will be useful,
00039  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00040  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00041  * GNU General Public License for more details.
00042  */
00043 
00044 #include "webcit.h"
00045 #include "webserver.h"
00046 #include "dav.h"
00047 
00048 /*
00049  * Given an encoded UID, translate that to an unencoded Citadel EUID and
00050  * then search for it in the current room.  Return a message number or -1
00051  * if not found.
00052  *
00053  */
00054 long locate_message_by_uid(const char *uid) {
00055        char buf[256];
00056        char decoded_uid[1024];
00057        long retval = (-1L);
00058 
00059        /* decode the UID */
00060        euid_unescapize(decoded_uid, uid);
00061 
00062        /* ask Citadel if we have this one */
00063        serv_printf("EUID %s", decoded_uid);
00064        serv_getln(buf, sizeof buf);
00065        if (buf[0] == '2') {
00066               retval = atol(&buf[4]);
00067        }
00068 
00069        return(retval);
00070 }
00071 
00072 
00073 /*
00074  * IgnoreFloor: set to 0 or 1 _nothing else_
00075  * Subfolders: direct child floors will be put here.
00076  */
00077 const folder *GetRESTFolder(int IgnoreFloor, HashList *Subfolders)
00078 {
00079        wcsession  *WCC = WC;
00080        void *vFolder;
00081        const folder *ThisFolder = NULL;
00082        const folder *FoundFolder = NULL;
00083        const folder *BestGuess = NULL;
00084        int nBestGuess = 0;
00085        HashPos    *itd, *itfl;
00086        StrBuf     * Dir;
00087        void       *vDir;
00088        long        len;
00089         const char *Key;
00090        int iRoom, jURL, urlp;
00091        int delta;
00092 
00093 /*
00094  * Guess room: if the full URL matches a room, list thats it. We also need to remember direct sub rooms.
00095  * if the URL is longer, we need to find the "best guess" so we can find the room we're in, and the rest
00096  * of the URL will be uids and so on.
00097  */
00098        itfl = GetNewHashPos(WCC->Floors, 0);
00099        urlp = GetCount(WCC->Directory);
00100 
00101        while (GetNextHashPos(WCC->Floors, itfl, &len, &Key, &vFolder) && 
00102               (ThisFolder == NULL))
00103        {
00104               ThisFolder = vFolder;
00105               if (!IgnoreFloor && /* so we can handle legacy URLS... */
00106                   (ThisFolder->Floor != WCC->CurrentFloor))
00107                      continue;
00108 
00109               if (ThisFolder->nRoomNameParts > 1) 
00110               {
00111                      /*TODO: is that number all right? */
00112 //                   if (urlp - ThisFolder->nRoomNameParts != 2) {
00113 //                          if (BestGuess != NULL)
00114 //                                 continue;
00115 //ThisFolder->name
00116 //                          itd  = GetNewHashPos(WCC->Directory, 0);
00117 //                          GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir); //TODO: how many to fast forward?
00118 //                   }
00119                      itd  = GetNewHashPos(WCC->Directory, 0);
00120                      GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir); //TODO: how many to fast forward?
00121        
00122                      for (iRoom = 0, /* Fast forward the floorname as we checked it above: */ jURL = IgnoreFloor; 
00123 
00124                           (iRoom <= ThisFolder->nRoomNameParts) && (jURL <= urlp); 
00125 
00126                           iRoom++, jURL++, GetNextHashPos(WCC->Directory, itd, &len, &Key, &vDir))
00127                      {
00128                             Dir = (StrBuf*)vDir;
00129                             if (strcmp(ChrPtr(ThisFolder->RoomNameParts[iRoom]), 
00130                                       ChrPtr(Dir)) != 0)
00131                             {
00132                                    DeleteHashPos(&itd);
00133                                    continue;
00134                             }
00135                      }
00136                      DeleteHashPos(&itd);
00137                      /* Gotcha? */
00138                      if ((iRoom == ThisFolder->nRoomNameParts) && (jURL == urlp))
00139                      {
00140                             FoundFolder = ThisFolder;
00141                      }
00142                      /* URL got more parts then this room, so we remember it for the best guess*/
00143                      else if ((jURL <= urlp) &&
00144                              (ThisFolder->nRoomNameParts <= nBestGuess))
00145                      {
00146                             BestGuess = ThisFolder;
00147                             nBestGuess = jURL - 1;
00148                      }
00149                      /* Room has more parts than the URL, it might be a sub-room? */
00150                      else if (iRoom <ThisFolder->nRoomNameParts) 
00151                      {
00152                             Put(Subfolders, SKEY(ThisFolder->name), 
00153                                 /* Cast away const, its a reference. */
00154                                 (void*)ThisFolder, reference_free_handler);
00155                      }
00156               }
00157               else {
00158                      delta = GetCount(WCC->Directory) - ThisFolder->nRoomNameParts;
00159                      if ((delta != 2) && (nBestGuess > 1))
00160                          continue;
00161                      
00162                      itd  = GetNewHashPos(WCC->Directory, 0);
00163                                           
00164                      if (!GetNextHashPos(WCC->Directory, 
00165                                        itd, &len, &Key, &vDir) ||
00166                          (vDir == NULL))
00167                      {
00168                             DeleteHashPos(&itd);
00169                             
00170                             syslog(0, "5\n");
00171                             continue;
00172                      }
00173                      DeleteHashPos(&itd);
00174                      Dir = (StrBuf*) vDir;
00175                      if (strcmp(ChrPtr(ThisFolder->name), 
00176                                           ChrPtr(Dir))
00177                          != 0)
00178                      {
00179                             DeleteHashPos(&itd);
00180                             
00181                             syslog(0, "5\n");
00182                             continue;
00183                      }
00184                      DeleteHashPos(&itfl);
00185                      DeleteHashPos(&itd);
00186                      if (delta != 2) {
00187                             nBestGuess = 1;
00188                             BestGuess = ThisFolder;
00189                      }
00190                      else 
00191                             FoundFolder = ThisFolder;
00192               }
00193        }
00194 
00195 /* TODO: Subfolders: remove patterns not matching the best guess or thisfolder */
00196        DeleteHashPos(&itfl);
00197        if (FoundFolder != NULL)
00198               return FoundFolder;
00199        else
00200               return BestGuess;
00201 }
00202 
00203 
00204 
00205 
00206 long GotoRestRoom(HashList *SubRooms)
00207 {
00208        int IgnoreFloor = 0; /* deprecated... */
00209        wcsession *WCC = WC;
00210        long Count;
00211        long State;
00212        const folder *ThisFolder;
00213 
00214        State = REST_TOPLEVEL;
00215 
00216        if (WCC->Hdr->HR.Handler != NULL) 
00217               State |= REST_IN_NAMESPACE;
00218 
00219        Count = GetCount(WCC->Directory);
00220        
00221        if (Count == 0) return State;
00222 
00223        if (Count >= 1) State |=REST_IN_FLOOR;
00224        if (Count == 1) return State;
00225        
00226        /* 
00227         * More than 3 params and no floor found? 
00228         * -> fall back to old non-floored notation
00229         */
00230        if ((Count >= 3) && (WCC->CurrentFloor == NULL))
00231               IgnoreFloor = 1;
00232        if (Count >= 3)
00233        {
00234               IgnoreFloor = 0;
00235               State |= REST_IN_FLOOR;
00236 
00237               ThisFolder = GetRESTFolder(IgnoreFloor, SubRooms);
00238               if (ThisFolder != NULL)
00239               {
00240                      if (WCC->ThisRoom != NULL)
00241                             if (CompareRooms(WCC->ThisRoom, ThisFolder) != 0)
00242                                    gotoroom(ThisFolder->name);
00243                      State |= REST_IN_ROOM;
00244                      
00245               }
00246               if (GetCount(SubRooms) > 0)
00247                      State |= REST_HAVE_SUB_ROOMS;
00248        }
00249        if ((WCC->ThisRoom != NULL) && 
00250            (Count + IgnoreFloor > 3))
00251        {
00252               if (WCC->Hdr->HR.Handler->RID(ExistsID, IgnoreFloor))
00253               {
00254                      State |= REST_GOT_LOCAL_PART;
00255               }
00256               else {
00258                      State |= REST_NONEXIST;
00259               }
00260 
00261 
00262        }
00263        return State;
00264 }
00265 
00266 
00267 
00268 /*
00269  * List rooms (or "collections" in DAV terminology) which contain
00270  * interesting groupware objects.
00271  */
00272 void dav_collection_list(void)
00273 {
00274        wcsession *WCC = WC;
00275        char buf[256];
00276        char roomname[256];
00277        int view;
00278        char datestring[256];
00279        time_t now;
00280        time_t mtime;
00281        int is_groupware_collection = 0;
00282        int starting_point = 1;            
00284        if (WCC->Hdr->HR.Handler == NULL) {
00285               starting_point = 0;
00286        }
00287        else if (StrLength(WCC->Hdr->HR.ReqLine) == 0) {
00288               starting_point = 1;
00289        }
00290        else {
00291               starting_point = 2;
00292        }
00293 
00294        now = time(NULL);
00295        http_datestring(datestring, sizeof datestring, now);
00296 
00297        /*
00298         * Be rude.  Completely ignore the XML request and simply send them
00299         * everything we know about.  Let the client sort it out.
00300         */
00301        hprintf("HTTP/1.0 207 Multi-Status\r\n");
00302        dav_common_headers();
00303        hprintf("Date: %s\r\n", datestring);
00304        hprintf("Content-type: text/xml\r\n");
00305        if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))      
00306               hprintf("Content-encoding: identity\r\n");
00307 
00308        begin_burst();
00309 
00310        wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00311               "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
00312        );
00313 
00314        /*
00315         * If the client is requesting the root, show a root node.
00316         */
00317        if (starting_point == 0) {
00318               wc_printf("<response>");
00319                      wc_printf("<href>");
00320                             dav_identify_host();
00321                             wc_printf("/");
00322                      wc_printf("</href>");
00323                      wc_printf("<propstat>");
00324                             wc_printf("<status>HTTP/1.1 200 OK</status>");
00325                             wc_printf("<prop>");
00326                                    wc_printf("<displayname>/</displayname>");
00327                                    wc_printf("<resourcetype><collection/></resourcetype>");
00328                                    wc_printf("<getlastmodified>");
00329                                           escputs(datestring);
00330                                    wc_printf("</getlastmodified>");
00331                             wc_printf("</prop>");
00332                      wc_printf("</propstat>");
00333               wc_printf("</response>");
00334        }
00335 
00336        /*
00337         * If the client is requesting "/groupdav", show a /groupdav subdirectory.
00338         */
00339        if ((starting_point + WCC->Hdr->HR.dav_depth) >= 1) {
00340               wc_printf("<response>");
00341                      wc_printf("<href>");
00342                             dav_identify_host();
00343                             wc_printf("/groupdav");
00344                      wc_printf("</href>");
00345                      wc_printf("<propstat>");
00346                             wc_printf("<status>HTTP/1.1 200 OK</status>");
00347                             wc_printf("<prop>");
00348                                    wc_printf("<displayname>GroupDAV</displayname>");
00349                                    wc_printf("<resourcetype><collection/></resourcetype>");
00350                                    wc_printf("<getlastmodified>");
00351                                           escputs(datestring);
00352                                    wc_printf("</getlastmodified>");
00353                             wc_printf("</prop>");
00354                      wc_printf("</propstat>");
00355               wc_printf("</response>");
00356        }
00357 
00358        /*
00359         * Now go through the list and make it look like a DAV collection
00360         */
00361        serv_puts("LKRA");
00362        serv_getln(buf, sizeof buf);
00363        if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
00364 
00365               extract_token(roomname, buf, 0, '|', sizeof roomname);
00366               view = extract_int(buf, 7);
00367               mtime = extract_long(buf, 8);
00368               http_datestring(datestring, sizeof datestring, mtime);
00369 
00370               /*
00371                * For now, only list rooms that we know a GroupDAV client
00372                * might be interested in.  In the future we may add
00373                * the rest.
00374                *
00375                * We determine the type of objects which are stored in each
00376                * room by looking at the *default* view for the room.  This
00377                * allows, for example, a Calendar room to appear as a
00378                * GroupDAV calendar even if the user has switched it to a
00379                * Calendar List view.
00380                */
00381               if (   (view == VIEW_CALENDAR) || 
00382                      (view == VIEW_TASKS) || 
00383                      (view == VIEW_ADDRESSBOOK) ||
00384                      (view == VIEW_NOTES) ||
00385                      (view == VIEW_JOURNAL) ||
00386                      (view == VIEW_WIKI)
00387               ) {
00388                      is_groupware_collection = 1;
00389               }
00390               else {
00391                      is_groupware_collection = 0;
00392               }
00393 
00394               if ( (is_groupware_collection) && ((starting_point + WCC->Hdr->HR.dav_depth) >= 2) ) {
00395                      wc_printf("<response>");
00396 
00397                      wc_printf("<href>");
00398                      dav_identify_host();
00399                      wc_printf("/groupdav/");
00400                      urlescputs(roomname);
00401                      wc_printf("/</href>");
00402 
00403                      wc_printf("<propstat>");
00404                      wc_printf("<status>HTTP/1.1 200 OK</status>");
00405                      wc_printf("<prop>");
00406                      wc_printf("<displayname>");
00407                      escputs(roomname);
00408                      wc_printf("</displayname>");
00409                      wc_printf("<resourcetype><collection/>");
00410 
00411                      switch(view) {
00412                      case VIEW_CALENDAR:
00413                             wc_printf("<G:vevent-collection />");
00414                             break;
00415                      case VIEW_TASKS:
00416                             wc_printf("<G:vtodo-collection />");
00417                             break;
00418                      case VIEW_ADDRESSBOOK:
00419                             wc_printf("<G:vcard-collection />");
00420                             break;
00421                      case VIEW_NOTES:
00422                             wc_printf("<G:vnotes-collection />");
00423                             break;
00424                      case VIEW_JOURNAL:
00425                             wc_printf("<G:vjournal-collection />");
00426                             break;
00427                      case VIEW_WIKI:
00428                             wc_printf("<G:wiki-collection />");
00429                             break;
00430                      }
00431 
00432                      wc_printf("</resourcetype>");
00433                      wc_printf("<getlastmodified>");
00434                             escputs(datestring);
00435                      wc_printf("</getlastmodified>");
00436                      wc_printf("</prop>");
00437                      wc_printf("</propstat>");
00438                      wc_printf("</response>");
00439               }
00440        }
00441        wc_printf("</multistatus>\n");
00442 
00443        end_burst();
00444 }
00445 
00446 
00447 void propfind_xml_start(void *data, const char *supplied_el, const char **attr) {
00448        // syslog(LOG_DEBUG, "<%s>", supplied_el);
00449 }
00450 
00451 void propfind_xml_end(void *data, const char *supplied_el) {
00452        // syslog(LOG_DEBUG, "</%s>", supplied_el);
00453 }
00454 
00455 
00456 
00457 /*
00458  * The pathname is always going to be /groupdav/room_name/msg_num
00459  */
00460 void dav_propfind(void) 
00461 {
00462        wcsession *WCC = WC;
00463        StrBuf *dav_roomname;
00464        StrBuf *dav_uid;
00465        StrBuf *MsgNum;
00466        long BufLen;
00467        long dav_msgnum = (-1);
00468        char uid[256];
00469        char encoded_uid[256];
00470        long *msgs = NULL;
00471        int num_msgs = 0;
00472        int i;
00473        char datestring[256];
00474        time_t now;
00475 
00476        now = time(NULL);
00477        http_datestring(datestring, sizeof datestring, now);
00478 
00479        int parse_success = 0;
00480        XML_Parser xp = XML_ParserCreateNS(NULL, '|');
00481        if (xp) {
00482               // XML_SetUserData(xp, XXX);
00483               XML_SetElementHandler(xp, propfind_xml_start, propfind_xml_end);
00484               // XML_SetCharacterDataHandler(xp, xrds_xml_chardata);
00485 
00486               const char *req = ChrPtr(WCC->upload);
00487               if (req) {
00488                      req = strchr(req, '<');                   /* hunt for the first tag */
00489               }
00490               if (!req) {
00491                      req = "ERROR";                            /* force it to barf */
00492               }
00493 
00494               i = XML_Parse(xp, req, strlen(req), 1);
00495               if (!i) {
00496                      syslog(LOG_DEBUG, "XML_Parse() failed: %s", XML_ErrorString(XML_GetErrorCode(xp)));
00497                      XML_ParserFree(xp);
00498                      parse_success = 0;
00499               }
00500               else {
00501                      parse_success = 1;
00502               }
00503        }
00504 
00505        if (!parse_success) {
00506               hprintf("HTTP/1.1 500 Internal Server Error\r\n");
00507               dav_common_headers();
00508               hprintf("Date: %s\r\n", datestring);
00509               hprintf("Content-Type: text/plain\r\n");
00510               wc_printf("An internal error has occurred at %s:%d.\r\n", __FILE__ , __LINE__ );
00511               end_burst();
00512               return;
00513        }
00514 
00515        dav_roomname = NewStrBuf();
00516        dav_uid = NewStrBuf();
00517        StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
00518        StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');
00519 
00520        syslog(LOG_DEBUG, "PROPFIND requested for '%s' at depth %d",
00521               ChrPtr(dav_roomname), WCC->Hdr->HR.dav_depth
00522        );
00523 
00524        /*
00525         * If the room name is blank, the client is requesting a folder list.
00526         */
00527        if (StrLength(dav_roomname) == 0) {
00528               dav_collection_list();
00529               FreeStrBuf(&dav_roomname);
00530               FreeStrBuf(&dav_uid);
00531               return;
00532        }
00533 
00534        /* Go to the correct room. */
00535        if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
00536               gotoroom(dav_roomname);
00537        }
00538        if (strcasecmp(ChrPtr(WCC->CurRoom.name), ChrPtr(dav_roomname))) {
00539               hprintf("HTTP/1.1 404 not found\r\n");
00540               dav_common_headers();
00541               hprintf("Date: %s\r\n", datestring);
00542               hprintf("Content-Type: text/plain\r\n");
00543               wc_printf("There is no folder called \"%s\" on this server.\r\n", ChrPtr(dav_roomname));
00544               end_burst();
00545               FreeStrBuf(&dav_roomname);
00546               FreeStrBuf(&dav_uid);
00547               return;
00548        }
00549 
00550        /* If dav_uid is non-empty, client is requesting a PROPFIND on
00551         * a specific item in the room.  This is not valid GroupDAV, but
00552         * it is valid WebDAV (and probably CalDAV too).
00553         */
00554        if (StrLength(dav_uid) != 0) {
00555 
00556               dav_msgnum = locate_message_by_uid(ChrPtr(dav_uid));
00557               if (dav_msgnum < 0) {
00558                      hprintf("HTTP/1.1 404 not found\r\n");
00559                      dav_common_headers();
00560                      hprintf("Content-Type: text/plain\r\n");
00561                      wc_printf("Object \"%s\" was not found in the \"%s\" folder.\r\n",
00562                             ChrPtr(dav_uid),
00563                             ChrPtr(dav_roomname)
00564                      );
00565                      end_burst();
00566                      FreeStrBuf(&dav_roomname);
00567                      FreeStrBuf(&dav_uid);
00568                      return;
00569               }
00570 
00571               /* Be rude.  Completely ignore the XML request and simply send them
00572                * everything we know about (which is going to simply be the ETag and
00573                * nothing else).  Let the client-side parser sort it out.
00574                */
00575               hprintf("HTTP/1.0 207 Multi-Status\r\n");
00576               dav_common_headers();
00577               hprintf("Date: %s\r\n", datestring);
00578               hprintf("Content-type: text/xml\r\n");
00579               if (DisableGzip || (!WCC->Hdr->HR.gzip_ok))      
00580                      hprintf("Content-encoding: identity\r\n");
00581        
00582               begin_burst();
00583        
00584               wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00585                      "<multistatus xmlns=\"DAV:\">"
00586               );
00587 
00588               wc_printf("<response>");
00589               
00590               wc_printf("<href>");
00591               dav_identify_host();
00592               wc_printf("/groupdav/");
00593               urlescputs(ChrPtr(WCC->CurRoom.name));
00594               euid_escapize(encoded_uid, ChrPtr(dav_uid));
00595               wc_printf("/%s", encoded_uid);
00596               wc_printf("</href>");
00597               wc_printf("<propstat>");
00598               wc_printf("<status>HTTP/1.1 200 OK</status>");
00599               wc_printf("<prop>");
00600               wc_printf("<getetag>\"%ld\"</getetag>", dav_msgnum);
00601               wc_printf("<getlastmodified>");
00602               escputs(datestring);
00603               wc_printf("</getlastmodified>");
00604               wc_printf("</prop>");
00605               wc_printf("</propstat>");
00606 
00607               wc_printf("</response>\n");
00608               wc_printf("</multistatus>\n");
00609               end_burst();
00610               FreeStrBuf(&dav_roomname);
00611               FreeStrBuf(&dav_uid);
00612               return;
00613        }
00614        FreeStrBuf(&dav_roomname);
00615        FreeStrBuf(&dav_uid);
00616 
00617 
00618        /*
00619         * If we get to this point the client is performing a PROPFIND on the room itself.
00620         *
00621         * We call it a room; DAV calls it a "collection."  We have to give it some properties
00622         * of the room itself and then offer a list of all items contained therein.
00623         *
00624         * Be rude.  Completely ignore the XML request and simply send them
00625         * everything we know about (which is going to simply be the ETag and
00626         * nothing else).  Let the client-side parser sort it out.
00627         */
00628        //syslog(LOG_DEBUG, "BE RUDE AND IGNORE: \033[31m%s\033[0m", ChrPtr(WC->upload) );
00629        hprintf("HTTP/1.0 207 Multi-Status\r\n");
00630        dav_common_headers();
00631        hprintf("Date: %s\r\n", datestring);
00632        hprintf("Content-type: text/xml\r\n");
00633        if (DisableGzip || (!WCC->Hdr->HR.gzip_ok)) {
00634               hprintf("Content-encoding: identity\r\n");
00635        }
00636        begin_burst();
00637 
00638        wc_printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00639               "<D:multistatus "
00640                      "xmlns:D=\"DAV:\" "
00641                      "xmlns:G=\"http://groupdav.org/\" "
00642                      "xmlns:C=\"urn:ietf:params:xml:ns:caldav\""
00643               ">"
00644        );
00645 
00646        /* Transmit the collection resource */
00647        wc_printf("<D:response>");
00648 
00649        wc_printf("<D:href>");
00650        dav_identify_host();
00651        wc_printf("/groupdav/");
00652        urlescputs(ChrPtr(WCC->CurRoom.name));
00653        wc_printf("</D:href>");
00654 
00655        wc_printf("<D:propstat>");
00656        wc_printf("<D:status>HTTP/1.1 200 OK</D:status>");
00657        wc_printf("<D:prop>");
00658        wc_printf("<D:displayname>");
00659        escputs(ChrPtr(WCC->CurRoom.name));
00660        wc_printf("</D:displayname>");
00661 
00662        wc_printf("<D:owner/>");           /* empty owner ought to be legal; see rfc3744 section 5.1 */
00663 
00664        wc_printf("<D:resourcetype><D:collection/>");
00665        switch(WCC->CurRoom.defview) {
00666               case VIEW_CALENDAR:
00667                      wc_printf("<G:vevent-collection />");
00668                      wc_printf("<C:calendar />");
00669                      break;
00670               case VIEW_TASKS:
00671                      wc_printf("<G:vtodo-collection />");
00672                      break;
00673               case VIEW_ADDRESSBOOK:
00674                      wc_printf("<G:vcard-collection />");
00675                      break;
00676        }
00677        wc_printf("</D:resourcetype>");
00678 
00679        /* FIXME get the mtime
00680        wc_printf("<D:getlastmodified>");
00681               escputs(datestring);
00682        wc_printf("</D:getlastmodified>");
00683        */
00684        wc_printf("</D:prop>");
00685        wc_printf("</D:propstat>");
00686        wc_printf("</D:response>");
00687 
00688        /* If a depth greater than zero was specified, transmit the collection listing */
00689 
00690        if (WCC->Hdr->HR.dav_depth > 0) {
00691               MsgNum = NewStrBuf();
00692               serv_puts("MSGS ALL");
00693        
00694               StrBuf_ServGetln(MsgNum);
00695               if (GetServerStatus(MsgNum, NULL) == 1)
00696                      while (BufLen = StrBuf_ServGetln(MsgNum), 
00697                             ((BufLen >= 0) && 
00698                             ((BufLen != 3) || strcmp(ChrPtr(MsgNum), "000"))  ))
00699                      {
00700                             msgs = realloc(msgs, ++num_msgs * sizeof(long));
00701                             msgs[num_msgs-1] = StrTol(MsgNum);
00702                      }
00703        
00704               if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
00705        
00706                      syslog(LOG_DEBUG, "PROPFIND enumerating message # %ld", msgs[i]);
00707                      strcpy(uid, "");
00708                      now = (-1);
00709                      serv_printf("MSG0 %ld|3", msgs[i]);
00710                      StrBuf_ServGetln(MsgNum);
00711                      if (GetServerStatus(MsgNum, NULL) == 1)
00712                             while (BufLen = StrBuf_ServGetln(MsgNum), 
00713                                    ((BufLen >= 0) && 
00714                                    ((BufLen != 3) || strcmp(ChrPtr(MsgNum), "000")) ))
00715                             {
00716                                    if (!strncasecmp(ChrPtr(MsgNum), "exti=", 5)) {
00717                                           strcpy(uid, &ChrPtr(MsgNum)[5]);
00718                                    }
00719                                    else if (!strncasecmp(ChrPtr(MsgNum), "time=", 5)) {
00720                                           now = atol(&ChrPtr(MsgNum)[5]);
00721                             }
00722                      }
00723        
00724                      if (!IsEmptyStr(uid)) {
00725                             wc_printf("<D:response>");
00726                                    wc_printf("<D:href>");
00727                                           dav_identify_host();
00728                                           wc_printf("/groupdav/");
00729                                           urlescputs(ChrPtr(WCC->CurRoom.name));
00730                                           euid_escapize(encoded_uid, uid);
00731                                           wc_printf("/%s", encoded_uid);
00732                                    wc_printf("</D:href>");
00733                                    switch(WCC->CurRoom.defview) {
00734                                    case VIEW_CALENDAR:
00735                                           wc_printf("<D:getcontenttype>text/x-ical</D:getcontenttype>");
00736                                           break;
00737                                    case VIEW_TASKS:
00738                                           wc_printf("<D:getcontenttype>text/x-ical</D:getcontenttype>");
00739                                           break;
00740                                    case VIEW_ADDRESSBOOK:
00741                                           wc_printf("<D:getcontenttype>text/x-vcard</D:getcontenttype>");
00742                                           break;
00743                                    }
00744                                    wc_printf("<D:propstat>");
00745                                           wc_printf("<D:status>HTTP/1.1 200 OK</D:status>");
00746                                           wc_printf("<D:prop>");
00747                                                  wc_printf("<D:getetag>\"%ld\"</D:getetag>", msgs[i]);
00748                                           if (now > 0L) {
00749                                                  http_datestring(datestring, sizeof datestring, now);
00750                                                  wc_printf("<D:getlastmodified>");
00751                                                  escputs(datestring);
00752                                                  wc_printf("</D:getlastmodified>");
00753                                           }
00754                                           wc_printf("</D:prop>");
00755                                    wc_printf("</D:propstat>");
00756                             wc_printf("</D:response>");
00757                      }
00758               }
00759               FreeStrBuf(&MsgNum);
00760        }
00761 
00762        wc_printf("</D:multistatus>\n");
00763        end_burst();
00764 
00765        if (msgs != NULL) {
00766               free(msgs);
00767        }
00768 }
00769 
00770 
00771 
00772 int ParseMessageListHeaders_EUID(StrBuf *Line, 
00773                              const char **pos, 
00774                              message_summary *Msg, 
00775                              StrBuf *ConversionBuffer)
00776 {
00777        Msg->euid = NewStrBuf();
00778        StrBufExtract_NextToken(Msg->euid,  Line, pos, '|');
00779        Msg->date = StrBufExtractNext_long(Line, pos, '|');
00780        
00781        return StrLength(Msg->euid) > 0;
00782 }
00783 
00784 int DavUIDL_GetParamsGetServerCall(SharedMessageStatus *Stat, 
00785                                void **ViewSpecific, 
00786                                long oper, 
00787                                char *cmd, 
00788                                long len,
00789                                char *filter,
00790                                long flen)
00791 {
00792        Stat->defaultsortorder = 0;
00793        Stat->sortit = 0;
00794        Stat->load_seen = 0;
00795        Stat->maxmsgs  = 9999999;
00796 
00797        snprintf(cmd, len, "MSGS ALL|||2");
00798        return 200;
00799 }
00800 
00801 int DavUIDL_RenderView_or_Tail(SharedMessageStatus *Stat, 
00802                             void **ViewSpecific, 
00803                             long oper)
00804 {
00805        
00806        DoTemplate(HKEY("msg_listview"),NULL,&NoCtx);
00807        
00808        return 0;
00809 }
00810 
00811 int DavUIDL_Cleanup(void **ViewSpecific)
00812 {
00813        /* Note: wDumpContent() will output one additional </div> tag. */
00814        /* We ought to move this out into template */
00815        wDumpContent(1);
00816 
00817        return 0;
00818 }
00819 
00820 
00821 
00822 
00823 void 
00824 InitModule_PROPFIND
00825 (void)
00826 {
00827        RegisterReadLoopHandlerset(
00828               eReadEUIDS,
00829               DavUIDL_GetParamsGetServerCall,
00830               NULL,
00831               NULL, 
00832               ParseMessageListHeaders_EUID,
00833               NULL, 
00834               DavUIDL_RenderView_or_Tail,
00835               DavUIDL_Cleanup);
00836 
00837 }