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