1 /*
2    WebDAV 207 multi-status response handling
3    Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk>
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9 
10    This library 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 GNU
13    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
18    MA 02111-1307, USA
19 
20 */
21 
22 /* Generic handling for WebDAV 207 Multi-Status responses. */
23 
24 #include "config.h"
25 
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29 
30 #include "ne_alloc.h"
31 #include "ne_utils.h"
32 #include "ne_xml.h"
33 #include "ne_207.h"
34 #include "ne_uri.h"
35 #include "ne_basic.h"
36 
37 #include "ne_i18n.h"
38 
39 struct ne_207_parser_s {
40     ne_207_start_response *start_response;
41     ne_207_end_response *end_response;
42     ne_207_start_propstat *start_propstat;
43     ne_207_end_propstat *end_propstat;
44     ne_xml_parser *parser;
45     void *userdata;
46 
47     ne_buffer *cdata;
48 
49     /* remember whether we are in a response: the validation
50      * doesn't encapsulate this since we only count as being
51      * "in a response" when we've seen the href element. */
52     int in_response;
53 
54     /* current position */
55     void *response, *propstat;
56     /* caching */
57     ne_status status;
58     char *description, *href;
59 };
60 
61 #define ELM_multistatus 1
62 #define ELM_response 2
63 #define ELM_responsedescription 3
64 #define ELM_href 4
65 #define ELM_prop (NE_207_STATE_PROP)
66 #define ELM_status 6
67 #define ELM_propstat 7
68 
69 static const struct ne_xml_idmap map207[] = {
70     { "DAV:", "multistatus", ELM_multistatus },
71     { "DAV:", "response", ELM_response },
72     { "DAV:", "responsedescription", ELM_responsedescription },
73     { "DAV:", "href", ELM_href },
74     { "DAV:", "propstat", ELM_propstat },
75     { "DAV:", "prop",  ELM_prop },
76     { "DAV:", "status", ELM_status }
77 };
78 
79 /* Set the callbacks for the parser */
ne_207_set_response_handlers(ne_207_parser * p,ne_207_start_response * start,ne_207_end_response * end)80 void ne_207_set_response_handlers(ne_207_parser *p,
81                                   ne_207_start_response *start,
82                                   ne_207_end_response *end)
83 {
84     p->start_response = start;
85     p->end_response = end;
86 }
87 
ne_207_set_propstat_handlers(ne_207_parser * p,ne_207_start_propstat * start,ne_207_end_propstat * end)88 void ne_207_set_propstat_handlers(ne_207_parser *p,
89 				   ne_207_start_propstat *start,
90 				   ne_207_end_propstat *end)
91 {
92     p->start_propstat = start;
93     p->end_propstat = end;
94 }
95 
ne_207_get_current_response(ne_207_parser * p)96 void *ne_207_get_current_response(ne_207_parser *p)
97 {
98     return p->response;
99 }
100 
ne_207_get_current_propstat(ne_207_parser * p)101 void *ne_207_get_current_propstat(ne_207_parser *p)
102 {
103     return p->propstat;
104 }
105 
106 /* return non-zero if (child, parent) is an interesting element */
can_handle(int parent,int child)107 static int can_handle(int parent, int child)
108 {
109     return (parent == 0 && child == ELM_multistatus) ||
110         (parent == ELM_multistatus && child == ELM_response) ||
111         (parent == ELM_response &&
112          (child == ELM_href || child == ELM_status ||
113           child == ELM_propstat || child == ELM_responsedescription)) ||
114         (parent == ELM_propstat &&
115          (child == ELM_prop || child == ELM_status ||
116           child == ELM_responsedescription));
117 }
118 
cdata_207(void * userdata,int state,const char * buf,size_t len)119 static int cdata_207(void *userdata, int state, const char *buf, size_t len)
120 {
121     ne_207_parser *p = userdata;
122 
123     if ((state == ELM_href || state == ELM_responsedescription ||
124          state == ELM_status) && p->cdata->used + len < 2048)
125         ne_buffer_append(p->cdata, buf, len);
126 
127     return 0;
128 }
129 
start_element(void * userdata,int parent,const char * nspace,const char * name,const char ** atts)130 static int start_element(void *userdata, int parent,
131                          const char *nspace, const char *name,
132                          const char **atts)
133 {
134     ne_207_parser *p = userdata;
135     int state = ne_xml_mapid(map207, NE_XML_MAPLEN(map207), nspace, name);
136 
137     if (!can_handle(parent, state))
138         return NE_XML_DECLINE;
139 
140     /* if not in a response, ignore everything. */
141     if (!p->in_response && state != ELM_response && state != ELM_multistatus &&
142         state != ELM_href)
143         return NE_XML_DECLINE;
144 
145     if (state == ELM_propstat && p->start_propstat)
146         p->propstat = p->start_propstat(p->userdata, p->response);
147 
148     ne_buffer_clear(p->cdata);
149 
150     return state;
151 }
152 
153 #define GIVE_STATUS(p) ((p)->status.reason_phrase?&(p)->status:NULL)
154 
155 #define HAVE_CDATA(p) ((p)->cdata->used > 1)
156 
157 static int
end_element(void * userdata,int state,const char * nspace,const char * name)158 end_element(void *userdata, int state, const char *nspace, const char *name)
159 {
160     ne_207_parser *p = userdata;
161     const char *cdata = p->cdata->data;
162 
163     switch (state) {
164     case ELM_responsedescription:
165 	if (HAVE_CDATA(p)) {
166 	    NE_FREE(p->description);
167 	    p->description = ne_strdup(cdata);
168 	}
169 	break;
170     case ELM_href:
171 	/* Now we have the href, begin the response */
172 	if (p->start_response && HAVE_CDATA(p)) {
173 	    p->response = p->start_response(p->userdata, cdata);
174 	    p->in_response = 1;
175 	}
176 	break;
177     case ELM_status:
178 	if (HAVE_CDATA(p)) {
179 	    NE_FREE(p->status.reason_phrase);
180 	    if (ne_parse_statusline(cdata, &p->status)) {
181 		char buf[500];
182 		NE_DEBUG(NE_DBG_HTTP, "Status line: %s\n", cdata);
183 		ne_snprintf(buf, 500,
184 			    _("Invalid HTTP status line in status element "
185                               "at line %d of response:\nStatus line was: %s"),
186 			    ne_xml_currentline(p->parser), cdata);
187 		ne_xml_set_error(p->parser, buf);
188 		return -1;
189 	    } else {
190 		NE_DEBUG(NE_DBG_XML, "Decoded status line: %s\n", cdata);
191 	    }
192 	}
193 	break;
194     case ELM_propstat:
195 	if (p->end_propstat)
196 	    p->end_propstat(p->userdata, p->propstat, GIVE_STATUS(p),
197 			    p->description);
198 	p->propstat = NULL;
199 	NE_FREE(p->description);
200 	NE_FREE(p->status.reason_phrase);
201 	break;
202     case ELM_response:
203         if (!p->in_response) break;
204 	if (p->end_response)
205 	    p->end_response(p->userdata, p->response, GIVE_STATUS(p),
206 			    p->description);
207 	p->response = NULL;
208 	p->in_response = 0;
209 	NE_FREE(p->status.reason_phrase);
210 	NE_FREE(p->description);
211 	break;
212     }
213     return 0;
214 }
215 
ne_207_create(ne_xml_parser * parser,void * userdata)216 ne_207_parser *ne_207_create(ne_xml_parser *parser, void *userdata)
217 {
218     ne_207_parser *p = ne_calloc(sizeof *p);
219 
220     p->parser = parser;
221     p->userdata = userdata;
222     p->cdata = ne_buffer_create();
223 
224     /* Add handler for the standard 207 elements */
225     ne_xml_push_handler(parser, start_element, cdata_207, end_element, p);
226 
227     return p;
228 }
229 
ne_207_destroy(ne_207_parser * p)230 void ne_207_destroy(ne_207_parser *p)
231 {
232     if (p->status.reason_phrase) ne_free(p->status.reason_phrase);
233     ne_buffer_destroy(p->cdata);
234     ne_free(p);
235 }
236 
ne_accept_207(void * userdata,ne_request * req,const ne_status * status)237 int ne_accept_207(void *userdata, ne_request *req, const ne_status *status)
238 {
239     return (status->code == 207);
240 }
241 
242 /* Handling of 207 errors: we keep a string buffer, and append
243  * messages to it as they come down.
244  *
245  * Note, 424 means it would have worked but something else went wrong.
246  * We will have had the error for "something else", so we display
247  * that, and skip 424 errors. */
248 
249 /* This is passed as userdata to the 207 code. */
250 struct context {
251     char *href;
252     ne_buffer *buf;
253     unsigned int is_error;
254 };
255 
start_response(void * userdata,const char * href)256 static void *start_response(void *userdata, const char *href)
257 {
258     struct context *ctx = userdata;
259     NE_FREE(ctx->href);
260     ctx->href = ne_strdup(href);
261     return NULL;
262 }
263 
handle_error(struct context * ctx,const ne_status * status,const char * description)264 static void handle_error(struct context *ctx, const ne_status *status,
265 			 const char *description)
266 {
267     if (status && status->klass != 2 && status->code != 424) {
268 	char buf[50];
269 	ctx->is_error = 1;
270 	sprintf(buf, "%d", status->code);
271 	ne_buffer_concat(ctx->buf, ctx->href, ": ",
272 			 buf, " ", status->reason_phrase, "\n", NULL);
273 	if (description != NULL) {
274 	    /* TODO: these can be multi-line. Would be good to
275 	     * word-wrap this at col 80. */
276 	    ne_buffer_concat(ctx->buf, " -> ", description, "\n", NULL);
277 	}
278     }
279 
280 }
281 
end_response(void * userdata,void * response,const ne_status * status,const char * description)282 static void end_response(void *userdata, void *response,
283 			 const ne_status *status, const char *description)
284 {
285     struct context *ctx = userdata;
286     handle_error(ctx, status, description);
287 }
288 
289 static void
end_propstat(void * userdata,void * propstat,const ne_status * status,const char * description)290 end_propstat(void *userdata, void *propstat,
291 	     const ne_status *status, const char *description)
292 {
293     struct context *ctx = userdata;
294     handle_error(ctx, status, description);
295 }
296 
297 /* Dispatch a DAV request and handle a 207 error response appropriately */
298 /* TODO: hook up Content-Type parsing; passing charset to XML parser */
ne_simple_request(ne_session * sess,ne_request * req)299 int ne_simple_request(ne_session *sess, ne_request *req)
300 {
301     int ret;
302     struct context ctx = {0};
303     ne_207_parser *p207;
304     ne_xml_parser *p;
305 
306     p = ne_xml_create();
307     p207 = ne_207_create(p, &ctx);
308     /* The error string is progressively written into the
309      * ne_buffer by the element callbacks */
310     ctx.buf = ne_buffer_create();
311 
312     ne_207_set_response_handlers(p207, start_response, end_response);
313     ne_207_set_propstat_handlers(p207, NULL, end_propstat);
314 
315     ne_add_response_body_reader(req, ne_accept_207, ne_xml_parse_v, p);
316 
317     ret = ne_request_dispatch(req);
318 
319     if (ret == NE_OK) {
320 	if (ne_get_status(req)->code == 207) {
321 	    if (!ne_xml_valid(p)) {
322 		/* The parse was invalid */
323 		ne_set_error(sess, "%s", ne_xml_get_error(p));
324 		ret = NE_ERROR;
325 	    } else if (ctx.is_error) {
326 		/* If we've actually got any error information
327 		 * from the 207, then set that as the error */
328 		ne_set_error(sess, "%s", ctx.buf->data);
329 		ret = NE_ERROR;
330 	    }
331 	} else if (ne_get_status(req)->klass != 2) {
332 	    ret = NE_ERROR;
333 	}
334     }
335 
336     ne_207_destroy(p207);
337     ne_xml_destroy(p);
338     ne_buffer_destroy(ctx.buf);
339     NE_FREE(ctx.href);
340 
341     ne_request_destroy(req);
342 
343     return ret;
344 }
345 
346