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