| /* |
| * util.c : utility functions for the RA/DAV library |
| * |
| * ==================================================================== |
| * Copyright (c) 2000-2002 CollabNet. All rights reserved. |
| * |
| * This software is licensed as described in the file COPYING, which |
| * you should have received as part of this distribution. The terms |
| * are also available at http://subversion.tigris.org/license-1.html. |
| * If newer versions of this license are posted there, you may use a |
| * newer version instead, at your option. |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals. For exact contribution history, see the revision |
| * history and logs, available at http://subversion.tigris.org/. |
| * ==================================================================== |
| */ |
| |
| #define APR_WANT_STRFUNC |
| #include <apr_want.h> |
| |
| #include <ne_uri.h> |
| |
| #include "svn_string.h" |
| #include "svn_xml.h" |
| |
| #include "ra_dav.h" |
| |
| |
| |
| |
| |
| void svn_ra_dav__copy_href(svn_stringbuf_t *dst, const char *src) |
| { |
| struct uri parsed_url; |
| |
| /* parse the PATH element out of the URL and store it. |
| |
| ### do we want to verify the rest matches the current session? |
| |
| Note: mod_dav does not (currently) use an absolute URL, but simply a |
| server-relative path (i.e. this uri_parse is effectively a no-op). |
| */ |
| (void) uri_parse(src, &parsed_url, NULL); |
| svn_stringbuf_set(dst, parsed_url.path); |
| uri_free(&parsed_url); |
| } |
| |
| svn_error_t *svn_ra_dav__convert_error(ne_session *sess, |
| const char *context, |
| int retcode, |
| apr_pool_t *pool) |
| { |
| int errcode = SVN_ERR_RA_REQUEST_FAILED; |
| const char *msg; |
| |
| /* Convert the return codes. */ |
| switch (retcode) |
| { |
| case NE_AUTH: |
| errcode = SVN_ERR_RA_NOT_AUTHORIZED; |
| msg = "authorization failed"; |
| break; |
| |
| case NE_CONNECT: |
| msg = "could not connect to server"; |
| break; |
| |
| case NE_TIMEOUT: |
| msg = "timed out waiting for server"; |
| break; |
| |
| default: |
| /* Get the error string from neon. */ |
| msg = ne_get_error (sess); |
| break; |
| } |
| |
| return svn_error_createf (errcode, 0, NULL, pool, |
| "%s: %s", context, msg); |
| |
| } |
| |
| |
| /** Error parsing **/ |
| |
| |
| /* Context baton for the error parser. Obviously, it just builds an |
| ERR from POOL. */ |
| typedef struct { |
| |
| svn_error_t *err; |
| apr_pool_t *pool; |
| |
| } parser_cxt_t; |
| |
| |
| /* Custom function of type ne_accept_response. */ |
| static int ra_dav_error_accepter(void *userdata, |
| ne_request *req, |
| ne_status *st) |
| { |
| /* Only accept the body-response if the HTTP status code is *not* 2XX. */ |
| return (st->klass != 2); |
| } |
| |
| |
| static const struct ne_xml_elm error_elements[] = |
| { |
| { "DAV:", "error", ELEM_error, 0 }, |
| { "svn:", "error", ELEM_svn_error, 0 }, |
| { "http://apache.org/dav/xmlns", "human-readable", |
| ELEM_human_readable, NE_XML_CDATA }, |
| |
| /* ### our validator doesn't yet recognize the rich, specific |
| <D:some-condition-failed/> objects as defined by DeltaV.*/ |
| |
| { NULL } |
| }; |
| |
| |
| static int validate_error_elements(void *userdata, |
| ne_xml_elmid parent, |
| ne_xml_elmid child) |
| { |
| switch (parent) |
| { |
| case NE_ELM_root: |
| if (child == ELEM_error) |
| return NE_XML_VALID; |
| else |
| return NE_XML_INVALID; |
| |
| case ELEM_error: |
| if (child == ELEM_svn_error |
| || child == ELEM_human_readable) |
| return NE_XML_VALID; |
| else |
| return NE_XML_DECLINE; /* ignore if something else was in there */ |
| |
| default: |
| return NE_XML_DECLINE; |
| } |
| |
| /* NOTREACHED */ |
| } |
| |
| |
| static int start_err_element(void *userdata, const struct ne_xml_elm *elm, |
| const char **atts) |
| { |
| parser_cxt_t *pc = (parser_cxt_t *) userdata; |
| |
| switch (elm->id) |
| { |
| case ELEM_svn_error: |
| { |
| /* allocate the svn_error_t. Hopefully the value will be |
| overwritten by the <human-readable> tag, or even someday by |
| a <D:failed-precondition/> tag. */ |
| pc->err = svn_error_create(APR_EGENERAL, 0, NULL, pc->pool, |
| "General svn error from server"); |
| break; |
| } |
| case ELEM_human_readable: |
| { |
| /* get the errorcode attribute if present */ |
| const char *errcode_str = |
| svn_xml_get_attr_value("errcode", /* ### make constant in |
| some mod_dav header? */ |
| atts); |
| |
| if (errcode_str && pc->err) |
| pc->err->apr_err = atoi(errcode_str); |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int end_err_element(void *userdata, const struct ne_xml_elm *elm, |
| const char *cdata) |
| { |
| parser_cxt_t *pc = (parser_cxt_t *) userdata; |
| |
| switch (elm->id) |
| { |
| case ELEM_human_readable: |
| { |
| if (cdata && pc->err) |
| pc->err->message = apr_pstrdup(pc->err->pool, cdata); |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| |
| svn_error_t *svn_ra_dav__parsed_request(svn_ra_session_t *ras, |
| const char *method, |
| const char *url, |
| const char *body, |
| int fd, |
| const struct ne_xml_elm *elements, |
| ne_xml_validate_cb validate_cb, |
| ne_xml_startelm_cb startelm_cb, |
| ne_xml_endelm_cb endelm_cb, |
| void *baton, |
| apr_pool_t *pool) |
| { |
| ne_request *req; |
| ne_xml_parser *success_parser; |
| ne_xml_parser *error_parser; |
| int rv; |
| int code; |
| const char *msg; |
| svn_error_t *err; |
| parser_cxt_t *pc = apr_pcalloc (pool, sizeof(*pc)); |
| |
| pc->pool = pool; |
| |
| /* create/prep the request */ |
| req = ne_request_create(ras->sess, method, url); |
| |
| if (body != NULL) |
| ne_set_request_body_buffer(req, body, strlen(body)); |
| else |
| ne_set_request_body_fd(req, fd); |
| |
| /* ### use a symbolic name somewhere for this MIME type? */ |
| ne_add_request_header(req, "Content-Type", "text/xml"); |
| |
| /* create a parser to read the normal response body */ |
| success_parser = ne_xml_create(); |
| ne_xml_push_handler(success_parser, elements, |
| validate_cb, startelm_cb, endelm_cb, baton); |
| |
| /* create a parser to read the <D:error> response body */ |
| error_parser = ne_xml_create(); |
| ne_xml_push_handler(error_parser, error_elements, validate_error_elements, |
| start_err_element, end_err_element, pc); |
| |
| /* Register the "main" accepter and body-reader with the request -- |
| the one to use when the HTTP status is 2XX */ |
| ne_add_response_body_reader(req, ne_accept_2xx, |
| ne_xml_parse_v, success_parser); |
| |
| /* Register the "error" accepter and body-reader with the request -- |
| the one to use when HTTP status is *not* 2XX */ |
| ne_add_response_body_reader(req, ra_dav_error_accepter, |
| ne_xml_parse_v, error_parser); |
| |
| /* run the request and get the resulting status code. */ |
| rv = ne_request_dispatch(req); |
| code = ne_get_status(req)->code; |
| ne_request_destroy(req); |
| |
| if (rv != NE_OK) |
| { |
| msg = apr_psprintf(pool, "%s of %s", method, url); |
| err = svn_ra_dav__convert_error(ras->sess, msg, rv, pool); |
| goto error; |
| } |
| |
| if (pc->err != NULL) |
| { |
| /* The HTTP status code wasn't 2XX, so the error-parser built an |
| error for us. */ |
| err = pc->err; |
| goto error; |
| } |
| |
| if (code != 200) |
| { |
| /* Bad status, but error-parser didn't build an error. Return a |
| generic error instead.*/ |
| err = svn_error_createf(APR_EGENERAL, 0, NULL, pool, |
| "The %s status was %d, but expected 200.", |
| method, code); |
| goto error; |
| } |
| |
| /* was there an XML parse error somewhere? */ |
| msg = ne_xml_get_error(success_parser); |
| if (msg != NULL && *msg != '\0') |
| { |
| err = svn_error_createf(SVN_ERR_RA_REQUEST_FAILED, 0, NULL, |
| pool, |
| "The %s request returned invalid XML " |
| "in the response: %s. (%s)", |
| method, msg, url); |
| goto error; |
| } |
| /* ### maybe hook this to a pool? */ |
| ne_xml_destroy(success_parser); |
| ne_xml_destroy(error_parser); |
| |
| return NULL; |
| |
| error: |
| ne_xml_destroy(success_parser); |
| ne_xml_destroy(error_parser); |
| return svn_error_createf(err->apr_err, err->src_err, err, NULL, |
| "%s request failed on %s", method, url ); |
| } |
| |
| |
| |
| svn_error_t * |
| svn_ra_dav__maybe_store_auth_info(svn_ra_session_t *ras) |
| { |
| void *a, *auth_baton; |
| svn_ra_simple_password_authenticator_t *authenticator; |
| |
| SVN_ERR (ras->callbacks->get_authenticator (&a, &auth_baton, |
| svn_ra_auth_simple_password, |
| ras->callback_baton, |
| ras->pool)); |
| authenticator = (svn_ra_simple_password_authenticator_t *) a; |
| |
| /* If we have a auth-info storage callback, use it. */ |
| if (authenticator->store_user_and_pass) |
| /* Storage will only happen if AUTH_BATON is already caching auth info. */ |
| SVN_ERR (authenticator->store_user_and_pass (auth_baton)); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| svn_error_t * |
| svn_ra_dav__request_dispatch(int *code, |
| ne_request *request, |
| ne_session *session, |
| const char *method, |
| const char *url, |
| apr_pool_t *pool) |
| { |
| ne_xml_parser *error_parser; |
| int rv; |
| const ne_status *statstruct; |
| const char *code_desc; |
| parser_cxt_t *pc = apr_pcalloc (pool, sizeof(*pc)); |
| |
| pc->pool = pool; |
| |
| /* attach a standard <D:error> body parser to the request */ |
| error_parser = ne_xml_create(); |
| ne_xml_push_handler(error_parser, error_elements, validate_error_elements, |
| start_err_element, end_err_element, pc); |
| ne_add_response_body_reader(request, ra_dav_error_accepter, |
| ne_xml_parse_v, error_parser); |
| |
| /* run the request, see what comes back. */ |
| rv = ne_request_dispatch(request); |
| |
| statstruct = ne_get_status(request); |
| *code = statstruct->code; |
| code_desc = apr_pstrdup(pool, statstruct->reason_phrase); |
| |
| ne_request_destroy(request); |
| ne_xml_destroy(error_parser); |
| |
| /* first, check to see if neon itself got an error */ |
| if (rv != NE_OK) |
| { |
| const char *msg = apr_psprintf(pool, "%s of %s", method, url); |
| return svn_ra_dav__convert_error(session, msg, rv, pool); |
| } |
| |
| /* next, check to see if a <D:error> was discovered */ |
| if (pc->err != NULL) |
| return pc->err; |
| |
| if ((*code < 200) || (*code >= 300)) |
| /* Bad http status, but error-parser didn't build an svn_error_t |
| for some reason. Return a generic error instead.*/ |
| return svn_error_createf(APR_EGENERAL, 0, NULL, pool, |
| "%s of %s returned status code %d (%s)", |
| method, url, *code, code_desc); |
| |
| return SVN_NO_ERROR; |
| } |
| |
| |
| |
| |
| |
| /* |
| * local variables: |
| * eval: (load-file "../../tools/dev/svn-dev.el") |
| * end: |
| */ |