groupdav_propfind.c

00001 /*
00002  * $Id: groupdav_propfind.c 5147 2007-05-08 15:36:22Z ajc $
00003  *
00004  * Handles GroupDAV PROPFIND requests.
00005  *
00006  * A few notes about our XML output:
00007  *
00008  * --> Yes, we are spewing tags directly instead of using an XML library.
00009  *     If you would like to rewrite this using libxml2, code it up and submit
00010  *     a patch.  Whining will be summarily ignored.
00011  *
00012  * --> XML is deliberately output with no whitespace/newlines between tags.
00013  *     This makes it difficult to read, but we have discovered clients which
00014  *     crash when you try to pretty it up.
00015  *
00016  */
00017 
00018 #include "webcit.h"
00019 #include "webserver.h"
00020 #include "groupdav.h"
00021 
00022 /*
00023  * Given an encoded UID, translate that to an unencoded Citadel EUID and
00024  * then search for it in the current room.  Return a message number or -1
00025  * if not found.
00026  *
00027  */
00028 long locate_message_by_uid(char *uid) {
00029         char buf[256];
00030         char decoded_uid[1024];
00031         long retval = (-1L);
00032 
00033         /* Decode the uid */
00034         euid_unescapize(decoded_uid, uid);
00035 
00036 /**************  THE NEW WAY ***********************/
00037         serv_printf("EUID %s", decoded_uid);
00038         serv_getln(buf, sizeof buf);
00039         if (buf[0] == '2') {
00040                 retval = atol(&buf[4]);
00041         }
00042 /***************************************************/
00043 
00044 /**************  THE OLD WAY ***********************
00045         serv_puts("MSGS ALL|0|1");
00046         serv_getln(buf, sizeof buf);
00047         if (buf[0] == '8') {
00048                 serv_printf("exti|%s", decoded_uid);
00049                 serv_puts("000");
00050                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
00051                         retval = atol(buf);
00052                 }
00053         }
00054  ***************************************************/
00055 
00056         return(retval);
00057 }
00058 
00059 
00060 
00061 /*
00062  * List rooms (or "collections" in DAV terminology) which contain
00063  * interesting groupware objects.
00064  */
00065 void groupdav_collection_list(char *dav_pathname, int dav_depth)
00066 {
00067         char buf[256];
00068         char roomname[256];
00069         int view;
00070         char datestring[256];
00071         time_t now;
00072         time_t mtime;
00073         int is_groupware_collection = 0;
00074         int starting_point = 1;         
00076         if (!strcmp(dav_pathname, "/")) {
00077                 starting_point = 0;
00078         }
00079         else if (!strcasecmp(dav_pathname, "/groupdav")) {
00080                 starting_point = 1;
00081         }
00082         else if (!strcasecmp(dav_pathname, "/groupdav/")) {
00083                 starting_point = 1;
00084         }
00085         else if ( (!strncasecmp(dav_pathname, "/groupdav/", 10)) && (strlen(dav_pathname) > 10) ) {
00086                 starting_point = 2;
00087         }
00088 
00089         now = time(NULL);
00090         http_datestring(datestring, sizeof datestring, now);
00091 
00096         wprintf("HTTP/1.0 207 Multi-Status\r\n");
00097         groupdav_common_headers();
00098         wprintf("Date: %s\r\n", datestring);
00099         wprintf("Content-type: text/xml\r\n");
00100         wprintf("Content-encoding: identity\r\n");
00101 
00102         begin_burst();
00103 
00104         wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00105                 "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
00106         );
00107 
00111         if (starting_point == 0) {
00112                 wprintf("<response>");
00113                         wprintf("<href>");
00114                                 groupdav_identify_host();
00115                                 wprintf("/");
00116                         wprintf("</href>");
00117                         wprintf("<propstat>");
00118                                 wprintf("<status>HTTP/1.1 200 OK</status>");
00119                                 wprintf("<prop>");
00120                                         wprintf("<displayname>/</displayname>");
00121                                         wprintf("<resourcetype><collection/></resourcetype>");
00122                                         wprintf("<getlastmodified>");
00123                                                 escputs(datestring);
00124                                         wprintf("</getlastmodified>");
00125                                 wprintf("</prop>");
00126                         wprintf("</propstat>");
00127                 wprintf("</response>");
00128         }
00129 
00133         if ((starting_point + dav_depth) >= 1) {
00134                 wprintf("<response>");
00135                         wprintf("<href>");
00136                                 groupdav_identify_host();
00137                                 wprintf("/groupdav");
00138                         wprintf("</href>");
00139                         wprintf("<propstat>");
00140                                 wprintf("<status>HTTP/1.1 200 OK</status>");
00141                                 wprintf("<prop>");
00142                                         wprintf("<displayname>GroupDAV</displayname>");
00143                                         wprintf("<resourcetype><collection/></resourcetype>");
00144                                         wprintf("<getlastmodified>");
00145                                                 escputs(datestring);
00146                                         wprintf("</getlastmodified>");
00147                                 wprintf("</prop>");
00148                         wprintf("</propstat>");
00149                 wprintf("</response>");
00150         }
00151 
00155         serv_puts("LKRA");
00156         serv_getln(buf, sizeof buf);
00157         if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
00158 
00159                 extract_token(roomname, buf, 0, '|', sizeof roomname);
00160                 view = extract_int(buf, 7);
00161                 mtime = extract_long(buf, 8);
00162                 http_datestring(datestring, sizeof datestring, mtime);
00163 
00164                 /*
00165                  * For now, only list rooms that we know a GroupDAV client
00166                  * might be interested in.  In the future we may add
00167                  * the rest.
00168                  *
00169                  * We determine the type of objects which are stored in each
00170                  * room by looking at the *default* view for the room.  This
00171                  * allows, for example, a Calendar room to appear as a
00172                  * GroupDAV calendar even if the user has switched it to a
00173                  * Calendar List view.
00174                  */
00175                 if ((view == VIEW_CALENDAR) || (view == VIEW_TASKS) || (view == VIEW_ADDRESSBOOK) ) {
00176                         is_groupware_collection = 1;
00177                 }
00178                 else {
00179                         is_groupware_collection = 0;
00180                 }
00181 
00182                 if ( (is_groupware_collection) && ((starting_point + dav_depth) >= 2) ) {
00183                         wprintf("<response>");
00184 
00185                         wprintf("<href>");
00186                         groupdav_identify_host();
00187                         wprintf("/groupdav/");
00188                         urlescputs(roomname);
00189                         wprintf("/</href>");
00190 
00191                         wprintf("<propstat>");
00192                         wprintf("<status>HTTP/1.1 200 OK</status>");
00193                         wprintf("<prop>");
00194                         wprintf("<displayname>");
00195                         escputs(roomname);
00196                         wprintf("</displayname>");
00197                         wprintf("<resourcetype><collection/>");
00198 
00199                         switch(view) {
00200                                 case VIEW_CALENDAR:
00201                                         wprintf("<G:vevent-collection />");
00202                                         break;
00203                                 case VIEW_TASKS:
00204                                         wprintf("<G:vtodo-collection />");
00205                                         break;
00206                                 case VIEW_ADDRESSBOOK:
00207                                         wprintf("<G:vcard-collection />");
00208                                         break;
00209                         }
00210 
00211                         wprintf("</resourcetype>");
00212                         wprintf("<getlastmodified>");
00213                                 escputs(datestring);
00214                         wprintf("</getlastmodified>");
00215                         wprintf("</prop>");
00216                         wprintf("</propstat>");
00217                         wprintf("</response>");
00218                 }
00219         }
00220         wprintf("</multistatus>\n");
00221 
00222         end_burst();
00223 }
00224 
00225 
00226 
00227 /*
00228  * The pathname is always going to be /groupdav/room_name/msg_num
00229  */
00230 void groupdav_propfind(char *dav_pathname, int dav_depth, char *dav_content_type, char *dav_content) {
00231         char dav_roomname[256];
00232         char dav_uid[256];
00233         char msgnum[256];
00234         long dav_msgnum = (-1);
00235         char buf[256];
00236         char uid[256];
00237         char encoded_uid[256];
00238         long *msgs = NULL;
00239         int num_msgs = 0;
00240         int i;
00241         char datestring[256];
00242         time_t now;
00243 
00244         now = time(NULL);
00245         http_datestring(datestring, sizeof datestring, now);
00246 
00247         extract_token(dav_roomname, dav_pathname, 2, '/', sizeof dav_roomname);
00248         extract_token(dav_uid, dav_pathname, 3, '/', sizeof dav_uid);
00249 
00250         /*
00251          * If the room name is blank, the client is requesting a
00252          * folder list.
00253          */
00254         if (strlen(dav_roomname) == 0) {
00255                 groupdav_collection_list(dav_pathname, dav_depth);
00256                 return;
00257         }
00258 
00259         /* Go to the correct room. */
00260         if (strcasecmp(WC->wc_roomname, dav_roomname)) {
00261                 gotoroom(dav_roomname);
00262         }
00263         if (strcasecmp(WC->wc_roomname, dav_roomname)) {
00264                 wprintf("HTTP/1.1 404 not found\r\n");
00265                 groupdav_common_headers();
00266                 wprintf("Date: %s\r\n", datestring);
00267                 wprintf(
00268                         "Content-Type: text/plain\r\n"
00269                         "\r\n"
00270                         "There is no folder called \"%s\" on this server.\r\n",
00271                         dav_roomname
00272                 );
00273                 return;
00274         }
00275 
00276         /* If dav_uid is non-empty, client is requesting a PROPFIND on
00277          * a specific item in the room.  This is not valid GroupDAV, but
00278          * it is valid WebDAV.
00279          */
00280         if (strlen(dav_uid) > 0) {
00281 
00282                 dav_msgnum = locate_message_by_uid(dav_uid);
00283                 if (dav_msgnum < 0) {
00284                         wprintf("HTTP/1.1 404 not found\r\n");
00285                         groupdav_common_headers();
00286                         wprintf(
00287                                 "Content-Type: text/plain\r\n"
00288                                 "\r\n"
00289                                 "Object \"%s\" was not found in the \"%s\" folder.\r\n",
00290                                 dav_uid,
00291                                 dav_roomname
00292                         );
00293                         return;
00294                 }
00295 
00296                 /* Be rude.  Completely ignore the XML request and simply send them
00297                  * everything we know about (which is going to simply be the ETag and
00298                  * nothing else).  Let the client-side parser sort it out.
00299                  */
00300                 wprintf("HTTP/1.0 207 Multi-Status\r\n");
00301                 groupdav_common_headers();
00302                 wprintf("Date: %s\r\n", datestring);
00303                 wprintf("Content-type: text/xml\r\n");
00304                 wprintf("Content-encoding: identity\r\n");
00305         
00306                 begin_burst();
00307         
00308                 wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00309                         "<multistatus xmlns=\"DAV:\">"
00310                 );
00311 
00312                 wprintf("<response>");
00313                 
00314                 wprintf("<href>");
00315                 groupdav_identify_host();
00316                 wprintf("/groupdav/");
00317                 urlescputs(WC->wc_roomname);
00318                 euid_escapize(encoded_uid, dav_uid);
00319                 wprintf("/%s", encoded_uid);
00320                 wprintf("</href>");
00321                 wprintf("<propstat>");
00322                 wprintf("<status>HTTP/1.1 200 OK</status>");
00323                 wprintf("<prop><getetag>\"%ld\"</getetag></prop>", dav_msgnum);
00324                 wprintf("</propstat>");
00325 
00326                 wprintf("</response>\n");
00327                 wprintf("</multistatus>\n");
00328                 end_burst();
00329                 return;
00330         }
00331 
00332 
00333         /*
00334          * We got to this point, which means that the client is requesting
00335          * a 'collection' (i.e. a list of all items in the room).
00336          *
00337          * Be rude.  Completely ignore the XML request and simply send them
00338          * everything we know about (which is going to simply be the ETag and
00339          * nothing else).  Let the client-side parser sort it out.
00340          */
00341         wprintf("HTTP/1.0 207 Multi-Status\r\n");
00342         groupdav_common_headers();
00343         wprintf("Date: %s\r\n", datestring);
00344         wprintf("Content-type: text/xml\r\n");
00345         wprintf("Content-encoding: identity\r\n");
00346 
00347         begin_burst();
00348 
00349         wprintf("<?xml version=\"1.0\" encoding=\"utf-8\"?>"
00350                 "<multistatus xmlns=\"DAV:\" xmlns:G=\"http://groupdav.org/\">"
00351         );
00352 
00353 
00355         wprintf("<response>");
00356 
00357         wprintf("<href>");
00358                 groupdav_identify_host();
00359                 wprintf("/groupdav/");
00360                 urlescputs(WC->wc_roomname);
00361         wprintf("</href>");
00362 
00363         wprintf("<propstat>");
00364         wprintf("<status>HTTP/1.1 200 OK</status>");
00365         wprintf("<prop>");
00366         wprintf("<displayname>");
00367         escputs(WC->wc_roomname);
00368         wprintf("</displayname>");
00369         wprintf("<resourcetype><collection/>");
00370 
00371         switch(WC->wc_default_view) {
00372                 case VIEW_CALENDAR:
00373                         wprintf("<G:vevent-collection />");
00374                         break;
00375                 case VIEW_TASKS:
00376                         wprintf("<G:vtodo-collection />");
00377                         break;
00378                 case VIEW_ADDRESSBOOK:
00379                         wprintf("<G:vcard-collection />");
00380                         break;
00381         }
00382 
00383         wprintf("</resourcetype>");
00384         /* FIXME get the mtime
00385         wprintf("<getlastmodified>");
00386                 escputs(datestring);
00387         wprintf("</getlastmodified>");
00388         */
00389         wprintf("</prop>");
00390         wprintf("</propstat>");
00391         wprintf("</response>");
00392 
00395         serv_puts("MSGS ALL");
00396         serv_getln(buf, sizeof buf);
00397         if (buf[0] == '1') while (serv_getln(msgnum, sizeof msgnum), strcmp(msgnum, "000")) {
00398                 msgs = realloc(msgs, ++num_msgs * sizeof(long));
00399                 msgs[num_msgs-1] = atol(msgnum);
00400         }
00401 
00402         if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
00403 
00404                 strcpy(uid, "");
00405                 serv_printf("MSG0 %ld|3", msgs[i]);
00406                 serv_getln(buf, sizeof buf);
00407                 if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
00408                         if (!strncasecmp(buf, "exti=", 5)) {
00409                                 strcpy(uid, &buf[5]);
00410                         }
00411                 }
00412 
00413                 if (strlen(uid) > 0) {
00414                         wprintf("<response>");
00415                                 wprintf("<href>");
00416                                         groupdav_identify_host();
00417                                         wprintf("/groupdav/");
00418                                         urlescputs(WC->wc_roomname);
00419                                         euid_escapize(encoded_uid, uid);
00420                                         wprintf("/%s", encoded_uid);
00421                                 wprintf("</href>");
00422                                 switch(WC->wc_default_view) {
00423                                 case VIEW_CALENDAR:
00424                                         wprintf("<getcontenttype>text/x-ical</getcontenttype>");
00425                                         break;
00426                                 case VIEW_TASKS:
00427                                         wprintf("<getcontenttype>text/x-ical</getcontenttype>");
00428                                         break;
00429                                 case VIEW_ADDRESSBOOK:
00430                                         wprintf("<getcontenttype>text/x-vcard</getcontenttype>");
00431                                         break;
00432                                 }
00433                                 wprintf("<propstat>");
00434                                         wprintf("<status>HTTP/1.1 200 OK</status>");
00435                                         wprintf("<prop>");
00436                                                 wprintf("<getetag>\"%ld\"</getetag>", msgs[i]);
00437                                         wprintf("</prop>");
00438                                 wprintf("</propstat>");
00439                         wprintf("</response>");
00440                 }
00441         }
00442 
00443         wprintf("</multistatus>\n");
00444         end_burst();
00445 
00446         if (msgs != NULL) {
00447                 free(msgs);
00448         }
00449 }

Generated on Wed Jun 20 23:13:08 2007 for webcit by  doxygen 1.5.2