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