* Completed GroupDAV PUT. Untested.
authorArt Cancro <ajc@citadel.org>
Tue, 1 Feb 2005 23:15:50 +0000 (23:15 +0000)
committerArt Cancro <ajc@citadel.org>
Tue, 1 Feb 2005 23:15:50 +0000 (23:15 +0000)
webcit/ChangeLog
webcit/Makefile.in
webcit/groupdav.h
webcit/groupdav_main.c
webcit/groupdav_put.c [new file with mode: 0644]
webcit/summary.c
webcit/webcit.c

index dc6ec3d76de2610c3fb6dcf4096690daa4fb93ac..76fbb627325ae9c874eaa6a6f1b927b43077366d 100644 (file)
@@ -1,4 +1,7 @@
 $Log$
+Revision 528.34  2005/02/01 23:15:50  ajc
+* Completed GroupDAV PUT.  Untested.
+
 Revision 528.33  2005/02/01 19:52:29  ajc
 * Bumped the internal version number to 6.01
 * Set the minimum required Citadel version to 6.40, because we need the
@@ -2284,4 +2287,3 @@ Sun Dec  6 19:50:55 EST 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
 
 1998-12-03 Nathan Bryant <bryant@cs.usm.maine.edu>
        * webserver.c: warning fix
-
index 19418051931c216165d9a2f6c8d1279c80bd440c..6d5bc54792c0b06c4b43721f29ddb42af4b85ef5 100644 (file)
@@ -38,7 +38,7 @@ webserver: webserver.o context_loop.o tools.o ical_dezonify.o \
        calendar.o calendar_tools.o calendar_view.o event.o \
        availability.o iconbar.o crypto.o inetconf.o notes.o \
        groupdav_main.o groupdav_get.o groupdav_propfind.o \
-       groupdav_delete.o \
+       groupdav_delete.o groupdav_put.o \
        $(LIBOBJS)
        $(CC) webserver.o context_loop.o tools.o cookie_conversion.o \
        webcit.o auth.o tcp_sockets.o mainmenu.o serv_func.o who.o listsub.o \
@@ -48,6 +48,7 @@ webserver: webserver.o context_loop.o tools.o ical_dezonify.o \
        summary.o calendar.o calendar_tools.o calendar_view.o event.o \
        availability.o ical_dezonify.o iconbar.o crypto.o inetconf.o notes.o \
        groupdav_main.o groupdav_get.o groupdav_propfind.o groupdav_delete.o \
+       groupdav_put.o \
        $(LIBOBJS) $(LIBS) $(LDFLAGS) -o webserver
 
 .c.o:
index 555f03cb2cb18a3c1fe01db9433ff763aca327e6..bbc035a55906c5657e888b9f72ff61fe1b806213 100644 (file)
@@ -1,8 +1,9 @@
 /* $Id$ */
 
 void groupdav_common_headers(void);
-void groupdav_main(struct httprequest *);
+void groupdav_main(struct httprequest *, char *, int, char *);
 void groupdav_get(char *);
+void groupdav_put(char *, char *, char *, char *);
 void groupdav_delete(char *, char *);
 void groupdav_propfind(char *);
 long locate_message_by_uid(char *);
index 1d71121e7145ba9fdd08fd8e1f7f1fd766c96036..28efe121d58d7bf3a4348ee90076ca9e298aa1e2 100644 (file)
@@ -41,8 +41,11 @@ void groupdav_common_headers(void) {
 /*
  * Main entry point for GroupDAV requests
  */
-void groupdav_main(struct httprequest *req) {
-
+void groupdav_main(struct httprequest *req,
+                       char *dav_content_type,
+                       int dav_content_length,
+                       char *dav_content
+) {
        struct httprequest *rptr;
        char dav_method[SIZ];
        char dav_pathname[SIZ];
@@ -61,7 +64,6 @@ void groupdav_main(struct httprequest *req) {
                         safestrncpy(dav_ifmatch, &rptr->line[10],
                                sizeof dav_ifmatch);
                 }
-
        }
 
        if (!WC->logged_in) {
@@ -96,8 +98,8 @@ void groupdav_main(struct httprequest *req) {
        }
 
        /*
-        * The PROPFIND method is basically used to list all objects in a room,
-        * or to list all relevant rooms on the server.
+        * The PROPFIND method is basically used to list all objects in a
+        * room, or to list all relevant rooms on the server.
         */
        if (!strcasecmp(dav_method, "PROPFIND")) {
                groupdav_propfind(dav_pathname);
@@ -105,13 +107,22 @@ void groupdav_main(struct httprequest *req) {
        }
 
        /*
-        * We like the GET method ... it's nice and simple.
+        * The GET method is used for fetching individual items.
         */
        if (!strcasecmp(dav_method, "GET")) {
                groupdav_get(dav_pathname);
                return;
        }
 
+       /*
+        * The PUT method is used to add or modify items.
+        */
+       if (!strcasecmp(dav_method, "PUT")) {
+               groupdav_put(dav_pathname, dav_ifmatch,
+                               dav_content_type, dav_content);
+               return;
+       }
+
        /*
         * The DELETE method kills, maims, and destroys.
         */
diff --git a/webcit/groupdav_put.c b/webcit/groupdav_put.c
new file mode 100644 (file)
index 0000000..b41f099
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * $Id$
+ *
+ * Handles GroupDAV PUT requests.
+ *
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <limits.h>
+#include <string.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <time.h>
+#include <pthread.h>
+#include "webcit.h"
+#include "webserver.h"
+#include "groupdav.h"
+
+
+/*
+ * The pathname is always going to be /groupdav/room_name/euid
+ */
+void groupdav_put(char *dav_pathname, char *dav_ifmatch,
+               char *dav_content_type, char *dav_content
+) {
+       char dav_roomname[SIZ];
+       char dav_uid[SIZ];
+       long new_msgnum = (-1L);
+       long old_msgnum = (-1L);
+       char buf[SIZ];
+       int n = 0;
+
+       /* First, break off the "/groupdav/" prefix */
+       remove_token(dav_pathname, 0, '/');
+       remove_token(dav_pathname, 0, '/');
+
+       /* Now extract the message euid */
+       n = num_tokens(dav_pathname, '/');
+       extract_token(dav_uid, dav_pathname, n-1, '/');
+       remove_token(dav_pathname, n-1, '/');
+
+       /* What's left is the room name.  Remove trailing slashes. */
+       if (dav_pathname[strlen(dav_pathname)-1] == '/') {
+               dav_pathname[strlen(dav_pathname)-1] = 0;
+       }
+       strcpy(dav_roomname, dav_pathname);
+
+       /* Go to the correct room. */
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               gotoroom(dav_roomname);
+       }
+       if (strcasecmp(WC->wc_roomname, dav_roomname)) {
+               wprintf("HTTP/1.1 404 not found\n");
+               groupdav_common_headers();
+               wprintf(
+                       "Content-Type: text/plain\n"
+                       "\n"
+                       "There is no folder called \"%s\" on this server.\n",
+                       dav_roomname
+               );
+               return;
+       }
+
+       /*
+        * If an HTTP If-Match: header is present, the client is attempting
+        * to replace an existing item.  We have to check to see if the
+        * message number associated with the supplied uid matches what the
+        * client is expecting.  If not, the server probably contains a newer
+        * version, so we fail...
+        */
+       if (strlen(dav_ifmatch) > 0) {
+               old_msgnum = locate_message_by_uid(dav_uid);
+               if (atol(dav_ifmatch) != old_msgnum) {
+                       wprintf("HTTP/1.1 412 Precondition Failed\n");
+                       groupdav_common_headers();
+                       wprintf("Content-Length: 0\n\n");
+                       return;
+               }
+       }
+
+       /*
+        * We are cleared for upload!  We use the new calling syntax for ENT0
+        * which allows a confirmation to be sent back to us.  That's how we
+        * extract the message ID.
+        */
+       serv_puts("ENT0 1|||4|||1|");
+       serv_gets(buf);
+       if (buf[0] != '8') {
+               wprintf("HTTP/1.1 502 Bad Gateway\n");
+               groupdav_common_headers();
+               wprintf("Content-type: text/plain\n"
+                       "\n"
+                       "%s\n", &buf[4]
+               );
+               return;
+       }
+
+       /* Send the content to the Citadel server */
+       serv_printf("Content-type: %s\n\n", dav_content_type);
+       serv_puts(dav_content);
+       serv_puts("\n000\n");
+
+       /* Fetch the reply from the Citadel server */
+       n = 0;
+       strcpy(dav_uid, "");
+       while (serv_gets(buf), strcmp(buf, "000")) {
+               switch(n++) {
+                       case 0:
+                               new_msgnum = atol(buf);
+                               break;
+                       case 2:
+                               strcpy(dav_uid, buf);
+                       default:
+                               break;
+               }
+       }
+
+       /* Tell the client what happened. */
+
+       /* Citadel failed in some way? */
+       if (new_msgnum < 0L) {
+               wprintf("HTTP/1.1 502 Bad Gateway\n");
+               groupdav_common_headers();
+               wprintf("Content-length: 0\n\n");
+               return;
+       }
+
+       /* We created this item for the first time. */
+       if (old_msgnum < 0L) {
+               wprintf("HTTP/1.1 201 Created\n");
+               groupdav_common_headers();
+               wprintf("Content-Length: 0\n");
+               wprintf("Location: ");
+               if (strlen(WC->http_host) > 0) {
+                       wprintf("%s://%s",
+                               (is_https ? "https" : "http"),
+                               WC->http_host);
+               }
+               wprintf("/groupdav/");
+               urlescputs(dav_roomname);
+               wprintf("/%s\n", dav_uid);
+               wprintf("\n");
+               return;
+       }
+
+       /* We modified an existing item. */
+       wprintf("HTTP/1.1 204 No Content\n");
+       groupdav_common_headers();
+       wprintf("Content-Length: 0\n\n");
+
+       /* The item we replaced has probably already been deleted by
+        * the Citadel server, but we'll do this anyway, just in case.
+        */
+       serv_printf("DELE %ld", old_msgnum);
+       serv_gets(buf);
+
+       return;
+}
index b812dd3b518a1ee02cb2272390c530ef43216791..7ed54af4ee68f67ac99977061f80542cf4dbca6d 100644 (file)
@@ -143,6 +143,7 @@ void tasks_section(void) {
                num_msgs = load_msg_ptrs("MSGS ALL");
        }
 
+       wprintf("FIXME start tasks<br>\n");
        if (num_msgs < 1) {
                wprintf("<i>(None)</i><br />\n");
        }
@@ -151,6 +152,7 @@ void tasks_section(void) {
                        display_task(WC->msgarr[i]);
                }
        }
+       wprintf("FIXME end tasks<br>\n");
 
 #else /* WEBCIT_WITH_CALENDAR_SERVICE */
        wprintf("<I>(This server does not support task lists)</I>\n");
@@ -178,6 +180,7 @@ void calendar_section(void) {
        else {
                num_msgs = load_msg_ptrs("MSGS ALL");
        }
+       wprintf("FIXME start calendar<br>\n");
 
        if (num_msgs < 1) {
                wprintf("<i>(Nothing)</i><br />\n");
@@ -188,6 +191,7 @@ void calendar_section(void) {
                }
                calendar_summary_view();
        }
+       wprintf("FIXME end calendar<br>\n");
 
 #else /* WEBCIT_WITH_CALENDAR_SERVICE */
        wprintf("<I>(This server does not support calendars)</I>\n");
index 05d7713a6a5f149a60be3fc9fe3ad8d1c66c20fd..e9c1fac071457d9e10f80d276089fbbd8ce4cb7d 100644 (file)
@@ -828,8 +828,8 @@ void session_loop(struct httprequest *req)
        int ContentLength = 0;
        int BytesRead = 0;
        char ContentType[512];
-       char *content;
-       char *content_end;
+       char *content = NULL;
+       char *content_end = NULL;
        struct httprequest *hptr;
        char browser_host[SIZ];
        char user_agent[SIZ];
@@ -1027,7 +1027,8 @@ void session_loop(struct httprequest *req)
         * our session's authentication.
         */
        if (!strncasecmp(action, "groupdav", 8)) {
-               groupdav_main(req);             /* do GroupDAV stuff */
+                                               /* do GroupDAV stuff */
+               groupdav_main(req, ContentType, ContentLength, content);
                if (!WC->logged_in) {
                        WC->killthis = 1;       /* If not logged in, don't */
                }                               /* keep the session active */