1289177Speter/*
2289177Speter * multistatus.c : parse multistatus (error) responses.
3289177Speter *
4289177Speter * ====================================================================
5289177Speter *    Licensed to the Apache Software Foundation (ASF) under one
6289177Speter *    or more contributor license agreements.  See the NOTICE file
7289177Speter *    distributed with this work for additional information
8289177Speter *    regarding copyright ownership.  The ASF licenses this file
9289177Speter *    to you under the Apache License, Version 2.0 (the
10289177Speter *    "License"); you may not use this file except in compliance
11289177Speter *    with the License.  You may obtain a copy of the License at
12289177Speter *
13289177Speter *      http://www.apache.org/licenses/LICENSE-2.0
14289177Speter *
15289177Speter *    Unless required by applicable law or agreed to in writing,
16289177Speter *    software distributed under the License is distributed on an
17289177Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18289177Speter *    KIND, either express or implied.  See the License for the
19289177Speter *    specific language governing permissions and limitations
20289177Speter *    under the License.
21289177Speter * ====================================================================
22289177Speter */
23289177Speter
24289177Speter
25289177Speter
26289177Speter#include <assert.h>
27289177Speter
28289177Speter#include <apr.h>
29289177Speter
30289177Speter#include <serf.h>
31289177Speter#include <serf_bucket_types.h>
32289177Speter
33289177Speter#include "svn_private_config.h"
34289177Speter#include "svn_hash.h"
35289177Speter#include "svn_dirent_uri.h"
36289177Speter#include "svn_path.h"
37289177Speter#include "svn_string.h"
38289177Speter#include "svn_xml.h"
39289177Speter#include "svn_props.h"
40289177Speter#include "svn_dirent_uri.h"
41289177Speter
42289177Speter#include "private/svn_dep_compat.h"
43289177Speter#include "private/svn_fspath.h"
44289177Speter
45289177Speter#include "ra_serf.h"
46289177Speter
47289177Speter/* The current state of our XML parsing. */
48289177Spetertypedef enum iprops_state_e {
49289177Speter  INITIAL = XML_STATE_INITIAL,
50289177Speter  MS_MULTISTATUS,
51289177Speter
52289177Speter  MS_RESPONSE,
53289177Speter  MS_RESPONSE_HREF,
54289177Speter
55289177Speter  MS_PROPSTAT,
56289177Speter  MS_PROPSTAT_PROP,
57289177Speter  MS_PROPSTAT_PROP_NAME,
58289177Speter  MS_PROPSTAT_STATUS,
59289177Speter  MS_PROPSTAT_RESPONSEDESCRIPTION,
60289177Speter  MS_PROPSTAT_ERROR,
61289177Speter  MS_PROPSTAT_ERROR_HUMANREADABLE,
62289177Speter
63289177Speter  MS_RESPONSE_STATUS,
64289177Speter  MS_RESPONSE_RESPONSEDESCRIPTION,
65289177Speter  MS_RESPONSE_ERROR,
66289177Speter  MS_RESPONSE_ERROR_HUMANREADABLE,
67289177Speter
68289177Speter  MS_MULTISTATUS_RESPONSEDESCRIPTION,
69289177Speter
70289177Speter  D_ERROR,
71289177Speter  S_ERROR,
72289177Speter  M_ERROR_HUMANREADABLE
73289177Speter} iprops_state_e;
74289177Speter
75289177Speter/*
76289177Speter  <D:multistatus xmlns:D="DAV:">
77289177Speter    <D:response>
78289177Speter      <D:href>http://something</D:href>
79289177Speter      <!-- Possibly multiple D:href elements -->
80289177Speter      <D:status>HTTP/1.1 500 failed</D:status>
81289177Speter      <D:error>
82289177Speter        <S:human-readable code="12345">
83289177Speter          Some Subversion error
84289177Speter        </S:human-readable>
85289177Speter      </D:error>
86289177Speter      <D:responsedescription>
87289177Speter        Human readable description
88289177Speter      </D:responsedescription>
89289177Speter      <D:location>http://redirected</D:location>
90289177Speter    </D:response>
91289177Speter    ...
92289177Speter  </D:multistatus>
93289177Speter
94289177Speter  Or for property operations:
95289177Speter
96289177Speter  <D:multistatus xmlns:D="DAV:">
97289177Speter    <D:response>
98289177Speter      <D:href>http://somewhere-else</D:href>
99289177Speter      <D:propstat>
100289177Speter        <D:propname><C:myprop /></D:propname>
101289177Speter        <D:status>HTTP/1.1 499 failed</D:status>
102289177Speter        <D:error>
103289177Speter          <S:human-readable code="12345">
104289177Speter            Some Subversion error
105289177Speter          </S:human-readable>
106289177Speter        </D:error>
107289177Speter        <D:responsedescription>
108289177Speter          Human readable description
109289177Speter        </D:responsedescription>
110289177Speter      </D:propstat>
111289177Speter      <D:status>HTTP/1.1 499 failed</D:status>
112289177Speter      <D:error>
113289177Speter        <S:human-readable code="12345">
114289177Speter          Some Subversion error
115289177Speter        </S:human-readable>
116289177Speter      </D:error>
117289177Speter      <D:responsedescription>
118289177Speter        Human readable description
119289177Speter      </D:responsedescription>
120289177Speter      <D:location>http://redirected</D:location>
121289177Speter    <D:responsedescription>
122289177Speter      Global description
123289177Speter    <D:responsedescription>
124289177Speter  </D:multistatus>
125289177Speter
126289177Speter  Or on request failures
127289177Speter  <D:error>
128289177Speter    <X:some-error xmlns="QQ" />
129289177Speter    <D:human-readable code="12345">
130289177Speter          Some Subversion error
131289177Speter    </D:human-readable>
132289177Speter  </D:error>
133289177Speter */
134289177Speter
135289177Speter#define D_ "DAV:"
136289177Speter#define S_ SVN_XML_NAMESPACE
137289177Speter#define M_ "http://apache.org/dav/xmlns"
138289177Speterstatic const svn_ra_serf__xml_transition_t multistatus_ttable[] = {
139289177Speter  { INITIAL, D_, "multistatus", MS_MULTISTATUS,
140289177Speter    FALSE, { NULL }, FALSE },
141289177Speter
142289177Speter  { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION,
143289177Speter    TRUE, { NULL }, TRUE },
144289177Speter
145289177Speter  /* <response> */
146289177Speter  { MS_MULTISTATUS, D_, "response", MS_RESPONSE,
147289177Speter    FALSE, { NULL }, TRUE },
148289177Speter
149289177Speter  { MS_RESPONSE, D_, "href", MS_RESPONSE_HREF,
150289177Speter    TRUE, { NULL }, TRUE },
151289177Speter
152289177Speter  /* <propstat> */
153289177Speter  { MS_RESPONSE, D_, "propstat", MS_PROPSTAT,
154289177Speter    FALSE, { NULL }, TRUE },
155289177Speter
156289177Speter  { MS_PROPSTAT, D_, "prop", MS_PROPSTAT_PROP,
157289177Speter    FALSE, { NULL }, FALSE },
158289177Speter
159289177Speter  { MS_PROPSTAT_PROP, "", "*", MS_PROPSTAT_PROP_NAME,
160289177Speter    FALSE, { NULL }, FALSE },
161289177Speter
162289177Speter  { MS_PROPSTAT, D_, "status", MS_PROPSTAT_STATUS,
163289177Speter    TRUE, { NULL }, TRUE },
164289177Speter
165289177Speter  { MS_PROPSTAT, D_, "responsedescription", MS_PROPSTAT_RESPONSEDESCRIPTION,
166289177Speter    TRUE, { NULL }, TRUE },
167289177Speter
168289177Speter  { MS_PROPSTAT, D_, "error", MS_PROPSTAT_ERROR,
169289177Speter    FALSE, { NULL }, FALSE },
170289177Speter
171289177Speter  { MS_PROPSTAT_ERROR, M_, "human-readable", MS_PROPSTAT_ERROR_HUMANREADABLE,
172289177Speter    TRUE, { "?errcode", NULL }, TRUE },
173289177Speter  /* </propstat> */
174289177Speter
175289177Speter
176289177Speter  { MS_RESPONSE, D_, "status", MS_RESPONSE_STATUS,
177289177Speter    TRUE, { NULL }, TRUE },
178289177Speter
179289177Speter  { MS_RESPONSE, D_, "responsedescription", MS_RESPONSE_RESPONSEDESCRIPTION,
180289177Speter    TRUE, { NULL }, TRUE },
181289177Speter
182289177Speter  { MS_RESPONSE, D_, "error", MS_RESPONSE_ERROR,
183289177Speter    FALSE, { NULL }, TRUE },
184289177Speter
185289177Speter  { MS_RESPONSE_ERROR, M_, "human-readable", MS_RESPONSE_ERROR_HUMANREADABLE,
186289177Speter    TRUE, { "?errcode", NULL }, TRUE },
187289177Speter
188289177Speter  /* </response> */
189289177Speter
190289177Speter  { MS_MULTISTATUS, D_, "responsedescription", MS_MULTISTATUS_RESPONSEDESCRIPTION,
191289177Speter    TRUE, { NULL }, TRUE },
192289177Speter
193289177Speter
194289177Speter  { INITIAL, D_, "error", D_ERROR,
195289177Speter    FALSE, { NULL }, TRUE },
196289177Speter
197289177Speter  { D_ERROR, S_, "error", S_ERROR,
198289177Speter    FALSE, { NULL }, FALSE },
199289177Speter
200289177Speter  { D_ERROR, M_, "human-readable", M_ERROR_HUMANREADABLE,
201289177Speter    TRUE, { "?errcode", NULL }, TRUE },
202289177Speter
203289177Speter  { 0 }
204289177Speter};
205289177Speter
206289177Speter/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric
207289177Speter   status code into *STATUS_CODE_OUT.  Ignores leading whitespace. */
208289177Speterstatic svn_error_t *
209289177Speterparse_status_line(int *status_code_out,
210289177Speter                  const char **reason,
211289177Speter                  const char *status_line,
212289177Speter                  apr_pool_t *result_pool,
213289177Speter                  apr_pool_t *scratch_pool)
214289177Speter{
215289177Speter  svn_error_t *err;
216289177Speter  const char *token;
217289177Speter  char *tok_status;
218289177Speter  svn_stringbuf_t *temp_buf = svn_stringbuf_create(status_line, scratch_pool);
219289177Speter
220289177Speter  svn_stringbuf_strip_whitespace(temp_buf);
221289177Speter  token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
222289177Speter  if (token)
223289177Speter    token = apr_strtok(NULL, " \t\r\n", &tok_status);
224289177Speter  if (!token)
225289177Speter    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
226289177Speter                             _("Malformed DAV:status '%s'"),
227289177Speter                             status_line);
228289177Speter  err = svn_cstring_atoi(status_code_out, token);
229289177Speter  if (err)
230289177Speter    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
231289177Speter                             _("Malformed DAV:status '%s'"),
232289177Speter                             status_line);
233289177Speter
234289177Speter  token = apr_strtok(NULL, " \t\r\n", &tok_status);
235289177Speter
236289177Speter  *reason = apr_pstrdup(result_pool, token);
237289177Speter
238289177Speter  return SVN_NO_ERROR;
239289177Speter}
240289177Speter
241289177Speter
242289177Spetertypedef struct error_item_t
243289177Speter{
244289177Speter  const char *path;
245289177Speter  const char *propname;
246289177Speter
247289177Speter  int http_status;
248289177Speter  const char *http_reason;
249289177Speter  apr_status_t apr_err;
250289177Speter
251289177Speter  const char *message;
252289177Speter} error_item_t;
253289177Speter
254289177Speterstatic svn_error_t *
255289177Spetermultistatus_opened(svn_ra_serf__xml_estate_t *xes,
256289177Speter                   void *baton,
257289177Speter                   int entered_state,
258289177Speter                   const svn_ra_serf__dav_props_t *tag,
259289177Speter                   apr_pool_t *scratch_pool)
260289177Speter{
261289177Speter  /*struct svn_ra_serf__server_error_t *server_error = baton;*/
262289177Speter  const char *propname;
263289177Speter
264289177Speter  switch (entered_state)
265289177Speter    {
266289177Speter      case MS_PROPSTAT_PROP_NAME:
267289177Speter        if (strcmp(tag->xmlns, SVN_DAV_PROP_NS_SVN) == 0)
268289177Speter          propname = apr_pstrcat(scratch_pool, SVN_PROP_PREFIX, tag->name,
269289177Speter                                 SVN_VA_NULL);
270289177Speter        else
271289177Speter          propname = tag->name;
272289177Speter        svn_ra_serf__xml_note(xes, MS_PROPSTAT, "propname", propname);
273289177Speter        break;
274289177Speter      case S_ERROR:
275289177Speter        /* This toggles an has error boolean in libsvn_ra_neon in 1.7 */
276289177Speter        break;
277289177Speter    }
278289177Speter
279289177Speter  return SVN_NO_ERROR;
280289177Speter}
281289177Speter
282289177Speterstatic svn_error_t *
283289177Spetermultistatus_closed(svn_ra_serf__xml_estate_t *xes,
284289177Speter                   void *baton,
285289177Speter                   int leaving_state,
286289177Speter                   const svn_string_t *cdata,
287289177Speter                   apr_hash_t *attrs,
288289177Speter                   apr_pool_t *scratch_pool)
289289177Speter{
290289177Speter  struct svn_ra_serf__server_error_t *server_error = baton;
291289177Speter  const char *errcode;
292289177Speter  const char *status;
293289177Speter
294289177Speter  switch (leaving_state)
295289177Speter    {
296289177Speter      case MS_RESPONSE_HREF:
297289177Speter        {
298289177Speter          apr_status_t result;
299289177Speter          apr_uri_t uri;
300289177Speter
301289177Speter          result = apr_uri_parse(scratch_pool, cdata->data, &uri);
302289177Speter          if (result)
303289177Speter            return svn_ra_serf__wrap_err(result, NULL);
304289177Speter          svn_ra_serf__xml_note(xes, MS_RESPONSE, "path",
305289177Speter                                svn_urlpath__canonicalize(uri.path, scratch_pool));
306289177Speter        }
307289177Speter        break;
308289177Speter      case MS_RESPONSE_STATUS:
309289177Speter        svn_ra_serf__xml_note(xes, MS_RESPONSE, "status", cdata->data);
310289177Speter        break;
311289177Speter      case MS_RESPONSE_ERROR_HUMANREADABLE:
312289177Speter        svn_ra_serf__xml_note(xes, MS_RESPONSE, "human-readable", cdata->data);
313289177Speter        errcode = svn_hash_gets(attrs, "errcode");
314289177Speter        if (errcode)
315289177Speter          svn_ra_serf__xml_note(xes, MS_RESPONSE, "errcode", errcode);
316289177Speter        break;
317289177Speter      case MS_RESPONSE:
318299742Sdim        if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL)
319289177Speter          {
320289177Speter            error_item_t *item;
321289177Speter
322289177Speter            item = apr_pcalloc(server_error->pool, sizeof(*item));
323289177Speter
324289177Speter            item->path = apr_pstrdup(server_error->pool,
325289177Speter                                     svn_hash_gets(attrs, "path"));
326289177Speter
327289177Speter            SVN_ERR(parse_status_line(&item->http_status,
328289177Speter                                      &item->http_reason,
329289177Speter                                      status,
330289177Speter                                      server_error->pool,
331289177Speter                                      scratch_pool));
332289177Speter
333289177Speter            /* Do we have a mod_dav specific message? */
334289177Speter            item->message = svn_hash_gets(attrs, "human-readable");
335289177Speter
336289177Speter            if (item->message)
337289177Speter              {
338289177Speter                if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
339289177Speter                  {
340289177Speter                    apr_int64_t val;
341289177Speter
342289177Speter                    SVN_ERR(svn_cstring_atoi64(&val, errcode));
343289177Speter                    item->apr_err = (apr_status_t)val;
344289177Speter                  }
345289177Speter
346289177Speter                item->message = apr_pstrdup(server_error->pool, item->message);
347289177Speter              }
348289177Speter            else
349289177Speter              item->message = apr_pstrdup(server_error->pool,
350289177Speter                                          svn_hash_gets(attrs, "description"));
351289177Speter
352289177Speter            APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
353289177Speter          }
354289177Speter        break;
355289177Speter
356289177Speter
357289177Speter      case MS_PROPSTAT_STATUS:
358289177Speter        svn_ra_serf__xml_note(xes, MS_PROPSTAT, "status", cdata->data);
359289177Speter        break;
360289177Speter      case MS_PROPSTAT_ERROR_HUMANREADABLE:
361289177Speter        svn_ra_serf__xml_note(xes, MS_PROPSTAT, "human-readable", cdata->data);
362289177Speter        errcode = svn_hash_gets(attrs, "errcode");
363289177Speter        if (errcode)
364289177Speter          svn_ra_serf__xml_note(xes, MS_PROPSTAT, "errcode", errcode);
365289177Speter        break;
366289177Speter      case MS_PROPSTAT_RESPONSEDESCRIPTION:
367289177Speter        svn_ra_serf__xml_note(xes, MS_PROPSTAT, "description",
368289177Speter                              cdata->data);
369289177Speter        break;
370289177Speter
371289177Speter      case MS_PROPSTAT:
372299742Sdim        if ((status = svn_hash__get_cstring(attrs, "status", NULL)) != NULL)
373289177Speter          {
374289177Speter            apr_hash_t *response_attrs;
375289177Speter            error_item_t *item;
376289177Speter
377289177Speter            response_attrs = svn_ra_serf__xml_gather_since(xes, MS_RESPONSE);
378289177Speter            item = apr_pcalloc(server_error->pool, sizeof(*item));
379289177Speter
380289177Speter            item->path = apr_pstrdup(server_error->pool,
381289177Speter                                     svn_hash_gets(response_attrs, "path"));
382289177Speter            item->propname = apr_pstrdup(server_error->pool,
383289177Speter                                         svn_hash_gets(attrs, "propname"));
384289177Speter
385289177Speter            SVN_ERR(parse_status_line(&item->http_status,
386289177Speter                                      &item->http_reason,
387289177Speter                                      status,
388289177Speter                                      server_error->pool,
389289177Speter                                      scratch_pool));
390289177Speter
391289177Speter            /* Do we have a mod_dav specific message? */
392289177Speter            item->message = svn_hash_gets(attrs, "human-readable");
393289177Speter
394289177Speter            if (item->message)
395289177Speter              {
396289177Speter                if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
397289177Speter                  {
398289177Speter                    apr_int64_t val;
399289177Speter
400289177Speter                    SVN_ERR(svn_cstring_atoi64(&val, errcode));
401289177Speter                    item->apr_err = (apr_status_t)val;
402289177Speter                  }
403289177Speter
404289177Speter                item->message = apr_pstrdup(server_error->pool, item->message);
405289177Speter              }
406289177Speter            else
407289177Speter              item->message = apr_pstrdup(server_error->pool,
408289177Speter                                          svn_hash_gets(attrs, "description"));
409289177Speter
410289177Speter
411289177Speter            APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
412289177Speter          }
413289177Speter        break;
414289177Speter
415289177Speter      case M_ERROR_HUMANREADABLE:
416289177Speter        svn_ra_serf__xml_note(xes, D_ERROR, "human-readable", cdata->data);
417289177Speter        errcode = svn_hash_gets(attrs, "errcode");
418289177Speter        if (errcode)
419289177Speter          svn_ra_serf__xml_note(xes, D_ERROR, "errcode", errcode);
420289177Speter        break;
421289177Speter
422289177Speter      case D_ERROR:
423289177Speter        {
424289177Speter          error_item_t *item;
425289177Speter
426289177Speter          item = apr_pcalloc(server_error->pool, sizeof(*item));
427289177Speter
428289177Speter          item->http_status = server_error->handler->sline.code;
429289177Speter
430289177Speter          /* Do we have a mod_dav specific message? */
431299742Sdim          item->message = svn_hash__get_cstring(attrs, "human-readable",
432299742Sdim                                                NULL);
433289177Speter
434289177Speter          if (item->message)
435289177Speter            {
436289177Speter              if ((errcode = svn_hash_gets(attrs, "errcode")) != NULL)
437289177Speter                {
438289177Speter                  apr_int64_t val;
439289177Speter
440289177Speter                  SVN_ERR(svn_cstring_atoi64(&val, errcode));
441289177Speter                  item->apr_err = (apr_status_t)val;
442289177Speter                }
443289177Speter
444289177Speter              item->message = apr_pstrdup(server_error->pool, item->message);
445289177Speter            }
446289177Speter
447289177Speter
448289177Speter          APR_ARRAY_PUSH(server_error->items, error_item_t *) = item;
449289177Speter        }
450289177Speter    }
451289177Speter  return SVN_NO_ERROR;
452289177Speter}
453289177Speter
454289177Spetersvn_error_t *
455289177Spetersvn_ra_serf__server_error_create(svn_ra_serf__handler_t *handler,
456289177Speter                                 apr_pool_t *scratch_pool)
457289177Speter{
458289177Speter  svn_ra_serf__server_error_t *server_error = handler->server_error;
459289177Speter  svn_error_t *err = NULL;
460289177Speter  int i;
461289177Speter
462289177Speter  for (i = 0; i < server_error->items->nelts; i++)
463289177Speter    {
464289177Speter      const error_item_t *item;
465289177Speter      apr_status_t status;
466289177Speter      const char *message;
467289177Speter      svn_error_t *new_err;
468289177Speter
469289177Speter      item = APR_ARRAY_IDX(server_error->items, i, error_item_t *);
470289177Speter
471289177Speter      if (!item->apr_err && item->http_status == 200)
472289177Speter        {
473289177Speter          continue; /* Success code */
474289177Speter        }
475289177Speter      else if (!item->apr_err && item->http_status == 424 && item->propname)
476289177Speter        {
477289177Speter          continue; /* Failed because other PROPPATCH operations failed */
478289177Speter        }
479289177Speter
480289177Speter      if (item->apr_err)
481289177Speter        status = item->apr_err;
482289177Speter      else
483289177Speter        switch (item->http_status)
484289177Speter          {
485289177Speter            case 0:
486289177Speter              continue; /* Not an error */
487289177Speter            case 301:
488289177Speter            case 302:
489289177Speter            case 303:
490289177Speter            case 307:
491289177Speter            case 308:
492289177Speter              status = SVN_ERR_RA_DAV_RELOCATED;
493289177Speter              break;
494289177Speter            case 403:
495289177Speter              status = SVN_ERR_RA_DAV_FORBIDDEN;
496289177Speter              break;
497289177Speter            case 404:
498289177Speter              status = SVN_ERR_FS_NOT_FOUND;
499289177Speter              break;
500289177Speter            case 409:
501289177Speter              status = SVN_ERR_FS_CONFLICT;
502289177Speter              break;
503289177Speter            case 412:
504289177Speter              status = SVN_ERR_RA_DAV_PRECONDITION_FAILED;
505289177Speter              break;
506289177Speter            case 423:
507289177Speter              status = SVN_ERR_FS_NO_LOCK_TOKEN;
508289177Speter              break;
509289177Speter            case 500:
510289177Speter              status = SVN_ERR_RA_DAV_REQUEST_FAILED;
511289177Speter              break;
512289177Speter            case 501:
513289177Speter              status = SVN_ERR_UNSUPPORTED_FEATURE;
514289177Speter              break;
515289177Speter            default:
516289177Speter              if (err)
517289177Speter                status = err->apr_err; /* Just use previous */
518289177Speter              else
519289177Speter                status = SVN_ERR_RA_DAV_REQUEST_FAILED;
520289177Speter              break;
521289177Speter        }
522289177Speter
523289177Speter      if (item->message && *item->message)
524289177Speter        {
525289177Speter          svn_stringbuf_t *sb = svn_stringbuf_create(item->message,
526289177Speter                                                     scratch_pool);
527289177Speter
528289177Speter          svn_stringbuf_strip_whitespace(sb);
529289177Speter          message = sb->data;
530289177Speter        }
531289177Speter      else if (item->propname)
532289177Speter        {
533289177Speter          message = apr_psprintf(scratch_pool,
534289177Speter                                 _("Property operation on '%s' failed"),
535289177Speter                                 item->propname);
536289177Speter        }
537289177Speter      else
538289177Speter        {
539289177Speter          /* Yuck: Older servers sometimes assume that we get convertable
540289177Speter                   apr error codes, while mod_dav_svn just produces a blank
541289177Speter                   text error, because err->message is NULL. */
542289177Speter          serf_status_line sline;
543289177Speter          svn_error_t *tmp_err;
544289177Speter
545289177Speter          memset(&sline, 0, sizeof(sline));
546289177Speter          sline.code = item->http_status;
547289177Speter          sline.reason = item->http_reason;
548289177Speter
549289177Speter          tmp_err = svn_ra_serf__error_on_status(sline, item->path, NULL);
550289177Speter
551289177Speter          message = (tmp_err && tmp_err->message)
552289177Speter                       ? apr_pstrdup(scratch_pool, tmp_err->message)
553289177Speter                       : _("<blank error>");
554289177Speter          svn_error_clear(tmp_err);
555289177Speter        }
556289177Speter
557289177Speter      SVN_ERR_ASSERT(status > 0);
558289177Speter      new_err = svn_error_create(status, NULL, message);
559289177Speter
560289177Speter      if (item->propname)
561289177Speter        new_err = svn_error_createf(new_err->apr_err, new_err,
562289177Speter                                    _("While handling the '%s' property on '%s':"),
563289177Speter                                    item->propname, item->path);
564289177Speter      else if (item->path)
565289177Speter        new_err = svn_error_createf(new_err->apr_err, new_err,
566289177Speter                                    _("While handling the '%s' path:"),
567289177Speter                                    item->path);
568289177Speter
569289177Speter      err = svn_error_compose_create(
570289177Speter                    err,
571289177Speter                    new_err);
572289177Speter    }
573289177Speter
574289177Speter  /* Theoretically a 207 status can have a 'global' description without a
575289177Speter     global STATUS that summarizes the final result of property/href
576289177Speter     operations.
577289177Speter
578289177Speter     We should wrap that around the existing errors if there is one.
579289177Speter
580289177Speter     But currently I don't see how mod_dav ever sets that value */
581289177Speter
582289177Speter  if (!err)
583289177Speter    {
584289177Speter      /* We should fail.... but why... Who installed us? */
585289177Speter      err = svn_error_trace(svn_ra_serf__unexpected_status(handler));
586289177Speter    }
587289177Speter
588289177Speter  return err;
589289177Speter}
590289177Speter
591289177Speter
592289177Spetersvn_error_t *
593289177Spetersvn_ra_serf__setup_error_parsing(svn_ra_serf__server_error_t **server_err,
594289177Speter                                 svn_ra_serf__handler_t *handler,
595289177Speter                                 svn_boolean_t expect_207_only,
596289177Speter                                 apr_pool_t *result_pool,
597289177Speter                                 apr_pool_t *scratch_pool)
598289177Speter{
599289177Speter  svn_ra_serf__server_error_t *ms_baton;
600289177Speter  svn_ra_serf__handler_t *tmp_handler;
601289177Speter
602289177Speter  int *expected_status = apr_pcalloc(result_pool,
603289177Speter                                     2 * sizeof(expected_status[0]));
604289177Speter
605289177Speter  expected_status[0] = handler->sline.code;
606289177Speter
607289177Speter  ms_baton = apr_pcalloc(result_pool, sizeof(*ms_baton));
608289177Speter  ms_baton->pool = result_pool;
609289177Speter
610289177Speter  ms_baton->items = apr_array_make(result_pool, 4, sizeof(error_item_t *));
611289177Speter  ms_baton->handler = handler;
612289177Speter
613289177Speter  ms_baton->xmlctx = svn_ra_serf__xml_context_create(multistatus_ttable,
614289177Speter                                                     multistatus_opened,
615289177Speter                                                     multistatus_closed,
616289177Speter                                                     NULL,
617289177Speter                                                     ms_baton,
618289177Speter                                                     ms_baton->pool);
619289177Speter
620289177Speter  tmp_handler = svn_ra_serf__create_expat_handler(handler->session,
621289177Speter                                                  ms_baton->xmlctx,
622289177Speter                                                  expected_status,
623289177Speter                                                  result_pool);
624289177Speter
625289177Speter  /* Ugly way to obtain expat_handler() */
626289177Speter  tmp_handler->sline = handler->sline;
627289177Speter  ms_baton->response_handler = tmp_handler->response_handler;
628289177Speter  ms_baton->response_baton = tmp_handler->response_baton;
629289177Speter
630289177Speter  *server_err = ms_baton;
631289177Speter  return SVN_NO_ERROR;
632289177Speter}
633289177Speter
634289177Speter
635289177Speter
636289177Speter/* Implements svn_ra_serf__response_handler_t */
637289177Spetersvn_error_t *
638289177Spetersvn_ra_serf__handle_multistatus_only(serf_request_t *request,
639289177Speter                                     serf_bucket_t *response,
640289177Speter                                     void *baton,
641289177Speter                                     apr_pool_t *scratch_pool)
642289177Speter{
643289177Speter  svn_ra_serf__handler_t *handler = baton;
644289177Speter
645289177Speter  /* This function is just like expect_empty_body() except for the
646289177Speter     XML parsing callbacks. We are looking for very limited pieces of
647289177Speter     the multistatus response.  */
648289177Speter
649289177Speter  /* We should see this just once, in order to initialize SERVER_ERROR.
650289177Speter     At that point, the core error processing will take over. If we choose
651289177Speter     not to parse an error, then we'll never return here (because we
652289177Speter     change the response handler).  */
653289177Speter  SVN_ERR_ASSERT(handler->server_error == NULL);
654289177Speter
655289177Speter    {
656289177Speter      serf_bucket_t *hdrs;
657289177Speter      const char *val;
658289177Speter
659289177Speter      hdrs = serf_bucket_response_get_headers(response);
660289177Speter      val = serf_bucket_headers_get(hdrs, "Content-Type");
661289177Speter      if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
662289177Speter        {
663289177Speter          svn_ra_serf__server_error_t *server_err;
664289177Speter
665289177Speter          SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err,
666289177Speter                                                   handler,
667289177Speter                                                   TRUE,
668289177Speter                                                   handler->handler_pool,
669289177Speter                                                   handler->handler_pool));
670289177Speter
671289177Speter          handler->server_error = server_err;
672289177Speter        }
673289177Speter      else
674289177Speter        {
675289177Speter          /* The body was not text/xml, so we don't know what to do with it.
676289177Speter             Toss anything that arrives.  */
677289177Speter          handler->discard_body = TRUE;
678289177Speter        }
679289177Speter    }
680289177Speter
681289177Speter  /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it
682289177Speter     to call the response handler again. That will start up the XML parsing,
683289177Speter     or it will be dropped on the floor (per the decision above).  */
684289177Speter  return SVN_NO_ERROR;
685289177Speter}
686289177Speter
687289177Spetersvn_error_t *
688289177Spetersvn_ra_serf__handle_server_error(svn_ra_serf__server_error_t *server_error,
689289177Speter                                 svn_ra_serf__handler_t *handler,
690289177Speter                                 serf_request_t *request,
691289177Speter                                 serf_bucket_t *response,
692289177Speter                                 apr_status_t *serf_status,
693289177Speter                                 apr_pool_t *scratch_pool)
694289177Speter{
695289177Speter  svn_error_t *err;
696289177Speter
697289177Speter  err = server_error->response_handler(request, response,
698289177Speter                                       server_error->response_baton,
699289177Speter                                       scratch_pool);
700289177Speter /* If we do not receive an error or it is a non-transient error, return
701289177Speter     immediately.
702289177Speter
703289177Speter     APR_EOF will be returned when parsing is complete.
704289177Speter
705289177Speter     APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through
706289177Speter     parsing and the network has no more data right now.  If we receive that,
707289177Speter     clear the error and return - allowing serf to wait for more data.
708289177Speter     */
709289177Speter  if (!err || SERF_BUCKET_READ_ERROR(err->apr_err))
710289177Speter    return svn_error_trace(err);
711289177Speter
712289177Speter  if (!APR_STATUS_IS_EOF(err->apr_err))
713289177Speter    {
714289177Speter      *serf_status = err->apr_err;
715289177Speter      svn_error_clear(err);
716289177Speter      return SVN_NO_ERROR;
717289177Speter    }
718289177Speter
719289177Speter  /* Clear the EOF. We don't need it as subversion error.  */
720289177Speter  svn_error_clear(err);
721289177Speter  *serf_status = APR_EOF;
722289177Speter
723289177Speter  /* On PROPPATCH we always get status 207, which may or may not imply an
724289177Speter     error status, but let's keep it generic and just do the check for
725289177Speter     any multistatus */
726289177Speter  if (handler->sline.code == 207 /* MULTISTATUS */)
727289177Speter    {
728289177Speter      svn_boolean_t have_error = FALSE;
729289177Speter      int i;
730289177Speter
731289177Speter      for (i = 0; i < server_error->items->nelts; i++)
732289177Speter        {
733289177Speter          const error_item_t *item;
734289177Speter          item = APR_ARRAY_IDX(server_error->items, i, error_item_t *);
735289177Speter
736289177Speter          if (!item->apr_err && item->http_status == 200)
737289177Speter            {
738289177Speter              continue; /* Success code */
739289177Speter            }
740289177Speter
741289177Speter          have_error = TRUE;
742289177Speter          break;
743289177Speter        }
744289177Speter
745289177Speter      if (! have_error)
746289177Speter        handler->server_error = NULL; /* We didn't have a server error */
747289177Speter    }
748289177Speter
749289177Speter  return SVN_NO_ERROR;
750289177Speter}
751