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