1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2011 Olaf Wintermann. All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include <stdio.h> |
|
30 #include <stdlib.h> |
|
31 #include <string.h> |
|
32 |
|
33 #include "webdav.h" |
|
34 #include "sstring.h" |
|
35 #include "pool.h" |
|
36 #include "pblock.h" |
|
37 |
|
38 #include "davparser.h" |
|
39 |
|
40 int webdav_service(pblock *pb, Session *sn, Request *rq) { |
|
41 /* TODO: |
|
42 * Dies ist die Implementierung für PROPFIND. Es sollte für jede webdav- |
|
43 * Methode eine eigene Service-Funktion geben. Solange die anderen |
|
44 * Methoden nicht implementiert werden, behandelt webdav_service nur |
|
45 * PROPFIND. |
|
46 */ |
|
47 |
|
48 /* TODO: clean up if errors occurs */ |
|
49 |
|
50 /* Get request body which contains the webdav XML request */ |
|
51 char *xml_body; |
|
52 size_t xml_len = 0; |
|
53 |
|
54 char *ctlen = pblock_findkeyval(pb_key_content_length, rq->headers); |
|
55 if(ctlen) { |
|
56 xml_len = atoi(ctlen); |
|
57 } else { |
|
58 /* invalid request */ |
|
59 return REQ_ABORTED; |
|
60 } |
|
61 |
|
62 xml_body = pool_malloc(sn->pool, xml_len + 1); |
|
63 xml_body[xml_len] = 0; |
|
64 if(!xml_body) { |
|
65 /* server error */ |
|
66 return REQ_ABORTED; |
|
67 } |
|
68 |
|
69 /* TODO: bug with multi reads */ |
|
70 int r = 0; |
|
71 char *xb = xml_body; |
|
72 size_t xl = xml_len; |
|
73 while((r = netbuf_getbytes(sn->inbuf, xb, xl)) != NETBUF_EOF) { |
|
74 xb += r; |
|
75 xl -= xml_len; |
|
76 } |
|
77 |
|
78 PropfindRequest *davrq = dav_parse_propfind(sn, rq, xml_body, xml_len); |
|
79 davrq->sn = sn; |
|
80 davrq->rq = rq; |
|
81 davrq->propertyBackend = create_property_backend(); |
|
82 davrq->notFoundProps = NULL; |
|
83 davrq->forbiddenProps = NULL; |
|
84 davrq->out = sbuf_new(512); |
|
85 |
|
86 /* write header */ |
|
87 sbuf_puts(davrq->out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"); |
|
88 sbuf_puts(davrq->out, "<D:multistatus xmlns:D=\"DAV:\">\n"); |
|
89 |
|
90 /* get stat of file */ |
|
91 char *ppath = pblock_findkeyval(pb_key_ppath, rq->vars); |
|
92 char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb); |
|
93 |
|
94 struct stat st; |
|
95 if(stat(ppath, &st) != 0) { |
|
96 perror("webdav_service: stat"); |
|
97 return REQ_ABORTED; |
|
98 } |
|
99 /* TODO: check for more modes */ |
|
100 if(S_ISDIR(st.st_mode)) { |
|
101 DIR *dir = opendir(ppath); |
|
102 if(dir == NULL) { |
|
103 protocol_status(sn, rq, 500, NULL); |
|
104 printf("webdav_service: DIR is null\n"); |
|
105 return REQ_ABORTED; |
|
106 } |
|
107 |
|
108 struct dirent *f; |
|
109 while((f = readdir(dir)) != NULL) { |
|
110 if(strcmp(f->d_name, ".") == 0 || strcmp(f->d_name, "..") == 0) { |
|
111 continue; |
|
112 } |
|
113 |
|
114 sstr_t filename = sstr(f->d_name); |
|
115 sstr_t _path = sstr(ppath); |
|
116 sstr_t _uri = sstr(uri); |
|
117 |
|
118 sstr_t newuri; |
|
119 newuri.length = filename.length + _uri.length; |
|
120 newuri.ptr = alloca(newuri.length + 1); |
|
121 newuri = sstrncat(2, newuri, _uri, filename); |
|
122 |
|
123 sstr_t newpath; |
|
124 newpath.length = _path.length + filename.length; |
|
125 newpath.ptr = alloca(newpath.length + 1); |
|
126 newpath = sstrncat(2, newpath, _path, filename); |
|
127 |
|
128 davrq->path = newpath.ptr; |
|
129 davrq->uri = newuri.ptr; |
|
130 dav_create_response(davrq); |
|
131 } |
|
132 } |
|
133 davrq->path = ppath; |
|
134 davrq->uri = uri; |
|
135 dav_create_response(davrq); |
|
136 |
|
137 sbuf_puts(davrq->out, "</D:multistatus>\n"); |
|
138 |
|
139 /* send buffer to client */ |
|
140 pblock_removekey(pb_key_content_type, rq->srvhdrs); |
|
141 pblock_nvinsert("content-type", "text/xml", rq->srvhdrs); |
|
142 pblock_nninsert("content-length", davrq->out->length, rq->srvhdrs); |
|
143 |
|
144 protocol_status(sn, rq, 207, NULL); |
|
145 http_start_response(sn, rq); |
|
146 |
|
147 net_write(sn->csd, davrq->out->ptr, davrq->out->length); |
|
148 |
|
149 return REQ_PROCEED; |
|
150 } |
|
151 |
|
152 int dav_foreach_reqprop(UcxDlist *list, PropfindRequest *davrq) { |
|
153 DavProperty *prop = list->data; |
|
154 int error = 0; |
|
155 |
|
156 char *str = davrq->propertyBackend->get_property( |
|
157 davrq->propertyBackend, |
|
158 davrq, |
|
159 davrq->path, |
|
160 prop->xmlns, |
|
161 prop->name, |
|
162 &error); |
|
163 if(str == NULL) { |
|
164 UcxDlist **dl = NULL; |
|
165 if(error == 404) { |
|
166 dl = &davrq->notFoundProps; |
|
167 } else { |
|
168 dl = &davrq->forbiddenProps; |
|
169 } |
|
170 *dl = ucx_dlist_append(*dl, prop); |
|
171 } else { |
|
172 //printf("dav property: {%s|%s::%s\n", prop->xmlns, prop->name, str); |
|
173 sbuf_puts(davrq->out, "<D:"); |
|
174 sbuf_puts(davrq->out, prop->name); |
|
175 sbuf_puts(davrq->out, ">"); |
|
176 sbuf_puts(davrq->out, str); |
|
177 sbuf_puts(davrq->out, "</D:"); |
|
178 sbuf_puts(davrq->out, prop->name); |
|
179 sbuf_puts(davrq->out, ">\n"); |
|
180 } |
|
181 return 0; |
|
182 } |
|
183 |
|
184 void dav_create_response(PropfindRequest *davrq) { |
|
185 sbuf_puts(davrq->out, "<D:response>\n"); |
|
186 |
|
187 sbuf_puts(davrq->out, "<D:href>"); |
|
188 sbuf_puts(davrq->out, davrq->uri); |
|
189 sbuf_puts(davrq->out, "</D:href>\n"); |
|
190 |
|
191 sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); |
|
192 |
|
193 ucx_dlist_foreach( |
|
194 davrq->properties, |
|
195 (ucx_callback)dav_foreach_reqprop, |
|
196 davrq); |
|
197 |
|
198 sbuf_puts(davrq->out, "</D:prop>\n<D:status>HTTP/1.1 200 OK</D:status>\n"); |
|
199 sbuf_puts(davrq->out, "</D:propstat>\n"); |
|
200 |
|
201 /* 404 props */ |
|
202 sbuf_puts(davrq->out, "<D:propstat>\n<D:prop>\n"); |
|
203 UcxDlist *dl = davrq->notFoundProps; |
|
204 while(dl != NULL) { |
|
205 DavProperty *nfp = dl->data; |
|
206 sbuf_puts(davrq->out, "<D:"); |
|
207 sbuf_puts(davrq->out, nfp->name); |
|
208 sbuf_puts(davrq->out, " />\n"); |
|
209 dl = dl->next; |
|
210 } |
|
211 sbuf_puts(davrq->out, "</D:prop>\n"); |
|
212 sbuf_puts(davrq->out, "<D:status>HTTP/1.1 404 Not Found</D:status>\n"); |
|
213 sbuf_puts(davrq->out, "</D:propstat>\n"); |
|
214 |
|
215 /* end */ |
|
216 sbuf_puts(davrq->out, "</D:response>\n"); |
|
217 |
|
218 } |
|
219 |
|
220 char* dav_get_property( |
|
221 DAVPropertyBackend *b, |
|
222 PropfindRequest *davrq, |
|
223 char *path, |
|
224 char *xmlns, |
|
225 char *name, |
|
226 int *error) |
|
227 { |
|
228 DAVDefaultBackend *be = (DAVDefaultBackend*)b; |
|
229 *error = 200; |
|
230 |
|
231 if(strcmp(name, "getcontentlength") == 0) { |
|
232 struct stat s; |
|
233 if(stat(davrq->path, &s) != 0) { |
|
234 *error = 403; /* really? */ |
|
235 return NULL; |
|
236 } |
|
237 if(S_ISDIR(s.st_mode)) { |
|
238 *error = 404; |
|
239 return NULL; |
|
240 } |
|
241 char *buf = pool_malloc(davrq->sn->pool, 24); |
|
242 sprintf(buf, "%d", s.st_size); |
|
243 return buf; |
|
244 } |
|
245 |
|
246 *error = 404; |
|
247 return NULL; |
|
248 } |
|
249 |
|
250 DAVPropertyBackend* create_property_backend() { |
|
251 DAVDefaultBackend *backend = malloc(sizeof(DAVDefaultBackend)); |
|
252 backend->backend.get_property = dav_get_property; |
|
253 backend->path = NULL; |
|
254 backend->s = 0; |
|
255 return (DAVPropertyBackend*)backend; |
|
256 } |
|