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