bc5286fc8610c3b745fd7a91d8151b681d7dbebc
[citadel.git] / webcit / groupdav_main.c
1 /*
2  * $Id$
3  *
4  * Entry point for GroupDAV functions
5  *
6  *
7  * Copyright (c) 2005-2010 by the citadel.org team
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "webcit.h"
25 #include "webserver.h"
26 #include "groupdav.h"
27
28 extern HashList *HandlerHash;
29
30 HashList *DavNamespaces = NULL;
31
32 /*
33  * Output HTTP headers which are common to all requests.
34  *
35  * Please observe that we don't use the usual output_headers()
36  * and wDumpContent() functions in the GroupDAV subsystem, so we
37  * do our own header stuff here.
38  *
39  */
40 void groupdav_common_headers(void) {
41         hprintf(
42                 "Server: %s / %s\r\n"
43                 "Connection: close\r\n",
44                 PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software)
45         );
46 }
47
48
49
50 /*
51  * string conversion function
52  */
53 void euid_escapize(char *target, const char *source) {
54         int i, len;
55         int target_length = 0;
56
57         strcpy(target, "");
58         len = strlen(source);
59         for (i=0; i<len; ++i) {
60                 if ( (isalnum(source[i])) || (source[i]=='-') || (source[i]=='_') ) {
61                         target[target_length] = source[i];
62                         target[++target_length] = 0;
63                 }
64                 else {
65                         sprintf(&target[target_length], "=%02X", (0xFF & source[i]));
66                         target_length += 3;
67                 }
68         }
69 }
70
71 /*
72  * string conversion function
73  */
74 void euid_unescapize(char *target, const char *source) {
75         int a, b, len;
76         char hex[3];
77         int target_length = 0;
78
79         strcpy(target, "");
80
81         len = strlen(source);
82         for (a = 0; a < len; ++a) {
83                 if (source[a] == '=') {
84                         hex[0] = source[a + 1];
85                         hex[1] = source[a + 2];
86                         hex[2] = 0;
87                         b = 0;
88                         b = decode_hex(hex);
89                         target[target_length] = b;
90                         target[++target_length] = 0;
91                         a += 2;
92                 }
93                 else {
94                         target[target_length] = source[a];
95                         target[++target_length] = 0;
96                 }
97         }
98 }
99
100
101
102
103 /*
104  * Main entry point for GroupDAV requests
105  */
106 void groupdav_main(void)
107 {
108         wcsession *WCC = WC;
109         int i, len;
110
111         StrBufUnescape(WCC->Hdr->HR.ReqLine, 0);
112
113         StrBufStripSlashes(WCC->Hdr->HR.ReqLine, 0);
114
115         /*
116          * If there's an If-Match: header, strip out the quotes if present, and
117          * then if all that's left is an asterisk, make it go away entirely.
118          */
119         len = StrLength(WCC->Hdr->HR.dav_ifmatch);
120         if (len > 0) {
121                 StrBufTrim(WCC->Hdr->HR.dav_ifmatch);
122                 if (ChrPtr(WCC->Hdr->HR.dav_ifmatch)[0] == '\"') {
123                         StrBufCutLeft(WCC->Hdr->HR.dav_ifmatch, 1);
124                         len --;
125                         for (i=0; i<len; ++i) {
126                                 if (ChrPtr(WCC->Hdr->HR.dav_ifmatch)[i] == '\"') {
127                                         StrBufCutAt(WCC->Hdr->HR.dav_ifmatch, i, NULL);
128                                         len = StrLength(WCC->Hdr->HR.dav_ifmatch);
129                                 }
130                         }
131                 }
132                 if (!strcmp(ChrPtr(WCC->Hdr->HR.dav_ifmatch), "*")) {
133                         FlushStrBuf(WCC->Hdr->HR.dav_ifmatch);
134                 }
135         }
136
137         switch (WCC->Hdr->HR.eReqType)
138         {
139         /*
140          * The OPTIONS method is not required by GroupDAV.  This is an
141          * experiment to determine what might be involved in supporting
142          * other variants of DAV in the future.
143          */
144         case eOPTIONS:
145                 groupdav_options();
146                 break;
147
148
149         /*
150          * The PROPFIND method is basically used to list all objects in a
151          * room, or to list all relevant rooms on the server.
152          */
153         case ePROPFIND:
154                 groupdav_propfind();
155                 break;
156
157         /*
158          * The GET method is used for fetching individual items.
159          */
160         case eGET:
161                 groupdav_get();
162                 break;
163         
164         /*
165          * The PUT method is used to add or modify items.
166          */
167         case ePUT:
168                 groupdav_put();
169                 break;
170         
171         /*
172          * The DELETE method kills, maims, and destroys.
173          */
174         case eDELETE:
175                 groupdav_delete();
176                 break;
177         default:
178
179         /*
180          * Couldn't find what we were looking for.  Die in a car fire.
181          */
182                 hprintf("HTTP/1.1 501 Method not implemented\r\n");
183                 groupdav_common_headers();
184                 hprintf("Content-Type: text/plain\r\n");
185                 wc_printf("GroupDAV method \"%s\" is not implemented.\r\n",
186                         ReqStrs[WCC->Hdr->HR.eReqType]);
187                 end_burst();
188         }
189 }
190
191
192 /*
193  * Output our host prefix for globally absolute URL's.
194  */  
195 void groupdav_identify_host(void) {
196         wcsession *WCC = WC;
197
198         if (StrLength(WCC->Hdr->HR.http_host)!=0) {
199                 wc_printf("%s://%s",
200                         (is_https ? "https" : "http"),
201                         ChrPtr(WCC->Hdr->HR.http_host));
202         }
203 }
204
205
206 void tmplput_GROUPDAV_HOSTNAME(StrBuf *Target, WCTemplputParams *TP) 
207 {
208         wcsession *WCC = WC;
209
210         if (StrLength(WCC->Hdr->HR.http_host)!=0) {
211                 StrBufAppendPrintf(Target, 
212                                    "%s://%s",
213                                    (is_https ? "https" : "http"),
214                                    ChrPtr(WCC->Hdr->HR.http_host));
215         }
216 }
217
218 /*
219  * Output our host prefix for globally absolute URL's.
220  */  
221 void groupdav_identify_hosthdr(void) {
222         wcsession *WCC = WC;
223
224         if (StrLength(WCC->Hdr->HR.http_host)!=0) {
225                 hprintf("%s://%s",
226                         (is_https ? "https" : "http"),
227                         ChrPtr(WCC->Hdr->HR.http_host));
228         }
229 }
230
231
232 void Header_HandleIfMatch(StrBuf *Line, ParsedHttpHdrs *hdr)
233 {
234         hdr->HR.dav_ifmatch = Line;
235 }
236         
237 void Header_HandleDepth(StrBuf *Line, ParsedHttpHdrs *hdr)
238 {
239         if (!strcasecmp(ChrPtr(Line), "infinity")) {
240                 hdr->HR.dav_depth = 32767;
241         }
242         else if (strcmp(ChrPtr(Line), "0") == 0) {
243                 hdr->HR.dav_depth = 0;
244         }
245         else if (strcmp(ChrPtr(Line), "1") == 0) {
246                 hdr->HR.dav_depth = 1;
247         }
248 }
249 int Conditional_DAV_DEPTH(StrBuf *Target, WCTemplputParams *TP)
250 {
251         return WC->Hdr->HR.dav_depth == GetTemplateTokenNumber(Target, TP, 2, 0);
252 }
253
254
255 void RegisterDAVNamespace(const char * UrlString, 
256                           long UrlSLen, 
257                           const char *DisplayName, 
258                           long dslen, 
259                           WebcitHandlerFunc F, 
260                           WebcitRESTDispatchID RID,
261                           long Flags)
262 {
263         void *vHandler;
264
265         /* first put it in... */
266         WebcitAddUrlHandler(UrlString, UrlSLen, DisplayName, dslen, F, Flags|PARSE_REST_URL);
267         /* get it out again... */
268         GetHash(HandlerHash, UrlString, UrlSLen, &vHandler);
269         ((WebcitHandler*)vHandler)->RID = RID;
270         /* and keep a copy of it, so we can compare it later */
271         Put(DavNamespaces, UrlString, UrlSLen, vHandler, reference_free_handler);
272 }
273
274 int Conditional_DAV_NS(StrBuf *Target, WCTemplputParams *TP)
275 {
276         wcsession *WCC = WC;
277         void *vHandler;
278         const char *NS;
279         long NSLen;
280
281         GetTemplateTokenString(NULL, TP, 2, &NS, &NSLen);
282         GetHash(HandlerHash, NS, NSLen, &vHandler);
283         return WCC->Hdr->HR.Handler == vHandler;
284 }
285
286
287 int Conditional_DAV_NSCURRENT(StrBuf *Target, WCTemplputParams *TP)
288 {
289         wcsession *WCC = WC;
290         void *vHandler;
291
292         vHandler = CTX;
293         return WCC->Hdr->HR.Handler == vHandler;
294 }
295
296 void tmplput_DAV_NAMESPACE(StrBuf *Target, WCTemplputParams *TP)
297 {
298         wcsession *WCC = WC;
299
300         if (TP->Filter.ContextType == CTX_DAVNS) {
301                 WebcitHandler *H;
302                 H = (WebcitHandler*) CTX;
303                 StrBufAppendTemplate(Target, TP, H->Name, 0);
304         }
305         else if (WCC->Hdr->HR.Handler != NULL) {
306                 StrBufAppendTemplate(Target, TP, WCC->Hdr->HR.Handler->Name, 0);
307         }
308 }
309
310 int GroupdavDispatchREST(RESTDispatchID WhichAction, int IgnoreFloor)
311 {
312         wcsession *WCC = WC;
313         void *vDir;
314         
315         switch(WhichAction){
316         case ExistsID:
317                 GetHash(WCC->Directory, IKEY(WCC->ThisRoom->nRoomNameParts + 1), &vDir);
318                 return locate_message_by_uid(ChrPtr((StrBuf*)vDir)) != -1;
319                 /* TODO: remember euid */
320         case PutID:
321         case DeleteID:
322                 break;
323
324
325         }
326         return 0;
327 }
328
329
330 void
331 ServerStartModule_DAV
332 (void)
333 {
334
335         DavNamespaces = NewHash(1, NULL);
336
337 }
338
339 void 
340 ServerShutdownModule_DAV
341 (void)
342 {
343         DeleteHash(&DavNamespaces);
344 }
345
346
347
348
349 void 
350 InitModule_GROUPDAV
351 (void)
352 {
353 //      WebcitAddUrlHandler(HKEY("groupdav"), "", 0, groupdav_main, XHTTP_COMMANDS|COOKIEUNNEEDED|FORCE_SESSIONCLOSE);
354         RegisterDAVNamespace(HKEY("groupdav"), HKEY("GroupDAV"), 
355                              groupdav_main, GroupdavDispatchREST, 
356                              XHTTP_COMMANDS|COOKIEUNNEEDED|FORCE_SESSIONCLOSE);
357
358         RegisterNamespace("DAV:HOSTNAME", 0, 0, tmplput_GROUPDAV_HOSTNAME, NULL, CTX_NONE);
359
360         RegisterConditional(HKEY("COND:DAV:NS"), 0, Conditional_DAV_NS,  CTX_NONE);
361
362         RegisterIterator("DAV:NS", 0, DavNamespaces, NULL, 
363                          NULL, NULL, CTX_DAVNS, CTX_NONE, IT_NOFLAG);
364
365
366         RegisterConditional(HKEY("COND:DAV:NSCURRENT"), 0, Conditional_DAV_NSCURRENT,  CTX_DAVNS);
367         RegisterNamespace("DAV:NAMESPACE", 0, 1, tmplput_DAV_NAMESPACE, NULL, CTX_NONE);
368
369         RegisterHeaderHandler(HKEY("IF-MATCH"), Header_HandleIfMatch);
370         RegisterHeaderHandler(HKEY("DEPTH"), Header_HandleDepth);
371         RegisterConditional(HKEY("COND:DAV:DEPTH"), 1, Conditional_DAV_DEPTH,  CTX_NONE);
372
373 }