* migrate more of the upload handling to strbuf
[citadel.git] / webcit / groupdav_put.c
1 /*
2  * $Id$
3  *
4  * Handles GroupDAV PUT requests.
5  *
6  */
7
8 #include "webcit.h"
9 #include "webserver.h"
10 #include "groupdav.h"
11
12
13 /*
14  * This function is for uploading an ENTIRE calendar, not just one
15  * component.  This would be for webcal:// 'publish' operations, not
16  * for GroupDAV.
17  */
18 void groupdav_put_bigics(void)
19 {
20         wcsession *WCC = WC;
21         char buf[1024];
22
23         /*
24          * Tell the server that when we save a calendar event, we
25          * do *not* want the server to generate invitations. 
26          */
27         serv_puts("ICAL sgi|0");
28         serv_getln(buf, sizeof buf);
29
30         serv_puts("ICAL putics");
31         serv_getln(buf, sizeof buf);
32         if (buf[0] != '4') {
33                 hprintf("HTTP/1.1 502 Bad Gateway\r\n");
34                 groupdav_common_headers();
35                 hprintf("Content-type: text/plain\r\n");
36                 wprintf("%s\r\n", &buf[4]);
37                 end_burst();
38                 return;
39         }
40
41         serv_putbuf(WCC->upload);
42         serv_printf("\n000");
43
44         /* Report success and not much else. */
45         hprintf("HTTP/1.1 204 No Content\r\n");
46         lprintf(9, "HTTP/1.1 204 No Content\r\n");
47         groupdav_common_headers();
48         begin_burst();
49         end_burst();
50 }
51
52
53
54 /*
55  * The pathname is always going to take one of two formats:
56  * [/groupdav/]room_name/euid   (GroupDAV)
57  * [/groupdav/]room_name                (webcal)
58  */
59 void groupdav_put(void) 
60 {
61         wcsession *WCC = WC;
62         StrBuf *dav_roomname;
63         StrBuf *dav_uid;
64         long new_msgnum = (-2L);
65         long old_msgnum = (-1L);
66         char buf[SIZ];
67         int n = 0;
68
69         if (StrBufNum_tokens(WCC->Hdr->HR.ReqLine, '/') < 2) {
70                 hprintf("HTTP/1.1 404 not found\r\n");
71                 groupdav_common_headers();
72                 hprintf("Content-Type: text/plain\r\n");
73                 wprintf("The object you requested was not found.\r\n");
74                 end_burst();
75                 return;
76         }
77
78         dav_roomname = NewStrBuf();;
79         dav_uid = NewStrBuf();;
80         StrBufExtract_token(dav_roomname, WCC->Hdr->HR.ReqLine, 0, '/');
81         StrBufExtract_token(dav_uid, WCC->Hdr->HR.ReqLine, 1, '/');
82         if ((!strcasecmp(ChrPtr(dav_uid), "ics")) || 
83             (!strcasecmp(ChrPtr(dav_uid), "calendar.ics"))) {
84                 FlushStrBuf(dav_uid);
85         }
86
87         /* Go to the correct room. */
88         if (strcasecmp(ChrPtr(WC->wc_roomname), ChrPtr(dav_roomname))) {
89                 gotoroom(dav_roomname);
90         }
91         if (strcasecmp(ChrPtr(WC->wc_roomname), ChrPtr(dav_roomname))) {
92                 hprintf("HTTP/1.1 404 not found\r\n");
93                 groupdav_common_headers();
94                 hprintf("Content-Type: text/plain\r\n");
95                 wprintf("There is no folder called \"%s\" on this server.\r\n",
96                         ChrPtr(dav_roomname));
97                 end_burst();
98                 FreeStrBuf(&dav_roomname);
99                 FreeStrBuf(&dav_uid);           
100                 return;
101         }
102
103         /*
104          * If an HTTP If-Match: header is present, the client is attempting
105          * to replace an existing item.  We have to check to see if the
106          * message number associated with the supplied uid matches what the
107          * client is expecting.  If not, the server probably contains a newer
108          * version, so we fail...
109          */
110         if (StrLength(WCC->Hdr->HR.dav_ifmatch) > 0) {
111                 lprintf(9, "dav_ifmatch: %s\n", WCC->Hdr->HR.dav_ifmatch);
112                 old_msgnum = locate_message_by_uid(ChrPtr(dav_uid));
113                 lprintf(9, "old_msgnum:  %ld\n", old_msgnum);
114                 if (StrTol(WCC->Hdr->HR.dav_ifmatch) != old_msgnum) {
115                         hprintf("HTTP/1.1 412 Precondition Failed\r\n");
116                         lprintf(9, "HTTP/1.1 412 Precondition Failed (ifmatch=%ld, old_msgnum=%ld)\r\n",
117                                 StrTol(WCC->Hdr->HR.dav_ifmatch), old_msgnum);
118                         groupdav_common_headers();
119                         hprintf("Content-Length: 0\r\n");
120                         end_burst();
121                         FreeStrBuf(&dav_roomname);
122                         FreeStrBuf(&dav_uid);
123                         return;
124                 }
125         }
126
127         /** PUT on the collection itself uploads an ICS of the entire collection.
128          */
129         if (StrLength(dav_uid) == 0) {
130                 groupdav_put_bigics();
131                 FreeStrBuf(&dav_roomname);
132                 FreeStrBuf(&dav_uid);
133                 return;
134         }
135
136         /*
137          * We are cleared for upload!  We use the new calling syntax for ENT0
138          * which allows a confirmation to be sent back to us.  That's how we
139          * extract the message ID.
140          */
141         serv_puts("ENT0 1|||4|||1|");
142         serv_getln(buf, sizeof buf);
143         if (buf[0] != '8') {
144                 hprintf("HTTP/1.1 502 Bad Gateway\r\n");
145                 groupdav_common_headers();
146                 hprintf("Content-type: text/plain\r\n");
147
148                 wprintf("%s\r\n", &buf[4]);
149                 end_burst();
150                 return;
151         }
152
153         /* Send the content to the Citadel server */
154         serv_printf("Content-type: %s\n\n", WCC->upload_content_type);
155         serv_putbuf(WCC->upload);
156         serv_puts("\n000");
157
158         /* Fetch the reply from the Citadel server */
159         n = 0;
160         FlushStrBuf(dav_uid);
161         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
162                 switch(n++) {
163                 case 0: 
164                         new_msgnum = atol(buf);
165                         break;
166                 case 1: 
167                         lprintf(9, "new_msgnum=%ld (%s)\n", new_msgnum, buf);
168                         break;
169                 case 2: 
170                         StrBufAppendBufPlain(dav_uid, buf, -1, 0);
171                         break;
172                 default:
173                         break;
174                 }
175         }
176
177         /* Tell the client what happened. */
178
179         /* Citadel failed in some way? */
180         if (new_msgnum < 0L) {
181                 hprintf("HTTP/1.1 502 Bad Gateway\r\n");
182                 groupdav_common_headers();
183                 hprintf("Content-type: text/plain\r\n");
184                 wprintf("new_msgnum is %ld\r\n"
185                         "\r\n", new_msgnum);
186                 end_burst();
187                 FreeStrBuf(&dav_roomname);
188                 FreeStrBuf(&dav_uid);
189                 return;
190         }
191
192         /* We created this item for the first time. */
193         if (old_msgnum < 0L) {
194                 char escaped_uid[1024];
195                 hprintf("HTTP/1.1 201 Created\r\n");
196                 lprintf(9, "HTTP/1.1 201 Created\r\n");
197                 groupdav_common_headers();
198                 hprintf("etag: \"%ld\"\r\n", new_msgnum);
199                 hprintf("Location: ");
200                 groupdav_identify_host();
201                 hprintf("/groupdav/");/* TODO */
202                 hurlescputs(ChrPtr(dav_roomname));
203                 euid_escapize(escaped_uid, ChrPtr(dav_uid));
204                 hprintf("/%s\r\n", escaped_uid);
205                 end_burst();
206                 FreeStrBuf(&dav_roomname);
207                 FreeStrBuf(&dav_uid);
208                 return;
209         }
210
211         /* We modified an existing item. */
212         hprintf("HTTP/1.1 204 No Content\r\n");
213         lprintf(9, "HTTP/1.1 204 No Content\r\n");
214         groupdav_common_headers();
215         hprintf("Etag: \"%ld\"\r\n", new_msgnum);
216         /* The item we replaced has probably already been deleted by
217          * the Citadel server, but we'll do this anyway, just in case.
218          */
219         serv_printf("DELE %ld", old_msgnum);
220         serv_getln(buf, sizeof buf);
221         begin_burst();
222         end_burst();
223         FreeStrBuf(&dav_roomname);
224         FreeStrBuf(&dav_uid);
225         return;
226 }