Index: serf.c =================================================================== --- serf.c (revision 22857) +++ serf.c (working copy) @@ -251,15 +251,6 @@ } static svn_error_t * -svn_ra_serf__get_dated_revision(svn_ra_session_t *session, - svn_revnum_t *revision, - apr_time_t tm, - apr_pool_t *pool) -{ - abort(); -} - -static svn_error_t * svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, svn_revnum_t rev, apr_hash_t **ret_props, Index: locks.c =================================================================== --- locks.c (revision 22857) +++ locks.c (working copy) @@ -52,6 +52,7 @@ DEPTH, TIMEOUT, LOCK_TOKEN, + COMMENT, } lock_state_e; typedef struct { @@ -66,18 +67,14 @@ svn_lock_t *lock; - const char *owner; - const char *creation_date; - const char *lock_token; - const char *comment; - svn_boolean_t force; svn_revnum_t revision; svn_boolean_t read_headers; - /* Our HTTP status code. */ + /* Our HTTP status code and reason. */ int status_code; + const char *reason; /* The currently collected value as we build it up */ const char *tmp; @@ -86,6 +83,8 @@ /* are we done? */ svn_boolean_t done; + /* Any errors. */ + svn_error_t *error; } lock_info_t; @@ -102,6 +101,7 @@ case DEPTH: case TIMEOUT: case LOCK_TOKEN: + case COMMENT: parser->state->private = apr_pcalloc(parser->state->pool, sizeof(lock_prop_info_t)); break; @@ -162,6 +162,10 @@ { push_state(parser, ctx, LOCK_TOKEN); } + else if (strcmp(name.name, "owner") == 0) + { + push_state(parser, ctx, COMMENT); + } } else if (state == LOCK_TYPE) { @@ -261,7 +265,18 @@ /* We don't actually need the lock token. */ svn_ra_serf__xml_pop_state(parser); } + else if (state == COMMENT && + strcmp(name.name, "owner") == 0) + { + lock_prop_info_t *info = parser->state->private; + if (info->len) + { + ctx->lock->comment = apr_pstrndup(ctx->pool, info->data, info->len); + } + svn_ra_serf__xml_pop_state(parser); + } + return SVN_NO_ERROR; } @@ -285,6 +300,7 @@ case DEPTH: case TIMEOUT: case LOCK_TOKEN: + case COMMENT: svn_ra_serf__expand_string(&info->data, &info->len, data, len, parser->state->pool); break; @@ -344,17 +360,14 @@ rv = serf_bucket_response_status(response, &sl); ctx->status_code = sl.code; + ctx->reason = sl.reason; /* 423 == Locked */ if (sl.code == 423) { - svn_error_t *err; - - err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, - _("Lock request failed: %d %s"), - sl.code, sl.reason); - xml_ctx->error = err; - return err->apr_err; + ctx->error = svn_ra_serf__handle_server_error(request, response, + pool); + return ctx->error->apr_err; } headers = serf_bucket_response_get_headers(response); @@ -389,6 +402,9 @@ if (APR_STATUS_IS_EOF(status)) { ctx->done = TRUE; + ctx->error = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("Lock request failed: %d %s"), + ctx->status_code, ctx->reason); } } else @@ -437,13 +453,13 @@ svn_ra_serf__add_tag_buckets(buckets, "locktype", "", alloc); - if (ctx->comment) + if (ctx->lock->comment) { svn_stringbuf_t *xml_esc = NULL; svn_string_t val; - val.data = ctx->comment; - val.len = strlen(ctx->comment); + val.data = ctx->lock->comment; + val.len = strlen(ctx->lock->comment); svn_xml_escape_cdata_string(&xml_esc, &val, pool); svn_ra_serf__add_tag_buckets(buckets, "owner", xml_esc->data, alloc); @@ -465,7 +481,6 @@ svn_ra_serf__session_t *session = ra_session->priv; svn_ra_serf__handler_t *handler; svn_ra_serf__xml_parser_t *parser_ctx; - serf_bucket_t *buckets, *tmp; lock_info_t *lock_ctx; const char *req_url; svn_error_t *err; @@ -551,7 +566,6 @@ lock_ctx->path = key; lock_ctx->revision = *((svn_revnum_t*)val); lock_ctx->lock = svn_lock_create(subpool); - lock_ctx->comment = comment; lock_ctx->lock->path = key; lock_ctx->lock->comment = comment; @@ -587,10 +601,12 @@ svn_ra_serf__request_create(handler); error = svn_ra_serf__context_run_wait(&lock_ctx->done, session, subpool); + SVN_ERR(lock_ctx->error); + SVN_ERR(parser_ctx->error); if (error) { - SVN_ERR(parser_ctx->error); - return error; + return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, error, + _("Lock request failed")); } SVN_ERR(lock_func(lock_baton, lock_ctx->path, TRUE, lock_ctx->lock, NULL, @@ -641,7 +657,6 @@ svn_ra_serf__handler_t *handler; svn_ra_serf__simple_request_context_t *ctx; const char *req_url, *path, *token; - lock_info_t *lock_ctx; const void *key; void *val; struct unlock_context_t unlock_ctx; Index: getdate.c =================================================================== --- getdate.c (revision 0) +++ getdate.c (revision 0) @@ -0,0 +1,243 @@ +/* + * getdate.c : entry point for get_dated_revision for ra_serf + * + * ==================================================================== + * Copyright (c) 2006 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/. + * ==================================================================== + */ + + + +#include + +#include + +#include + +#include "svn_pools.h" +#include "svn_ra.h" +#include "svn_dav.h" +#include "svn_xml.h" +#include "../libsvn_ra/ra_loader.h" +#include "svn_config.h" +#include "svn_delta.h" +#include "svn_version.h" +#include "svn_path.h" +#include "svn_time.h" +#include "svn_private_config.h" + +#include "ra_serf.h" + + +/* + * This enum represents the current state of our XML parsing for a REPORT. + */ +typedef enum { + NONE = 0, + VERSION_NAME, +} date_state_e; + +typedef struct { + /* The currently collected value as we build it up */ + const char *tmp; + apr_size_t tmp_len; +} date_info_t; + +typedef struct { + apr_pool_t *pool; + + /* The time asked about. */ + apr_time_t time; + + /* What was the youngest revision at that time? */ + svn_revnum_t *revision; + + /* are we done? */ + svn_boolean_t done; + +} date_context_t; + + +static date_info_t * +push_state(svn_ra_serf__xml_parser_t *parser, + date_context_t *date_ctx, + date_state_e state) +{ + svn_ra_serf__xml_push_state(parser, state); + + if (state == VERSION_NAME) + { + date_info_t *info; + + info = apr_pcalloc(parser->state->pool, sizeof(*info)); + + parser->state->private = info; + } + + return parser->state->private; +} + +static svn_error_t * +start_getdate(svn_ra_serf__xml_parser_t *parser, + void *userData, + svn_ra_serf__dav_props_t name, + const char **attrs) +{ + date_context_t *date_ctx = userData; + date_state_e state; + + state = parser->state->current_state; + + if (state == NONE && + strcmp(name.name, "version-name") == 0) + { + push_state(parser, date_ctx, VERSION_NAME); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +end_getdate(svn_ra_serf__xml_parser_t *parser, + void *userData, + svn_ra_serf__dav_props_t name) +{ + date_context_t *date_ctx = userData; + date_state_e state; + date_info_t *info; + + state = parser->state->current_state; + info = parser->state->private; + + if (state == VERSION_NAME && + strcmp(name.name, "version-name") == 0) + { + *date_ctx->revision = SVN_STR_TO_REV(info->tmp); + svn_ra_serf__xml_pop_state(parser); + } + + return SVN_NO_ERROR; +} + +static svn_error_t * +cdata_getdate(svn_ra_serf__xml_parser_t *parser, + void *userData, + const char *data, + apr_size_t len) +{ + date_context_t *date_ctx = userData; + date_state_e state; + date_info_t *info; + + state = parser->state->current_state; + info = parser->state->private; + + switch (state) + { + case VERSION_NAME: + svn_ra_serf__expand_string(&info->tmp, &info->tmp_len, + data, len, parser->state->pool); + break; + default: + break; + } + + return SVN_NO_ERROR; +} + +#define GETDATE_HEADER "" +#define GETDATE_FOOTER "" + +static serf_bucket_t* +create_getdate_body(void *baton, + serf_bucket_alloc_t *alloc, + apr_pool_t *pool) +{ + serf_bucket_t *buckets, *tmp; + date_context_t *date_ctx = baton; + + buckets = serf_bucket_aggregate_create(alloc); + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN(GETDATE_HEADER, + sizeof(GETDATE_HEADER) - 1, + alloc); + serf_bucket_aggregate_append(buckets, tmp); + + svn_ra_serf__add_tag_buckets(buckets, + "D:creationdate", + svn_time_to_cstring(date_ctx->time, pool), + alloc); + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN(GETDATE_FOOTER, + sizeof(GETDATE_FOOTER)-1, + alloc); + serf_bucket_aggregate_append(buckets, tmp); + + return buckets; +} + +svn_error_t * +svn_ra_serf__get_dated_revision(svn_ra_session_t *ra_session, + svn_revnum_t *revision, + apr_time_t tm, + apr_pool_t *pool) +{ + date_context_t *date_ctx; + svn_ra_serf__session_t *session = ra_session->priv; + svn_ra_serf__handler_t *handler; + svn_ra_serf__xml_parser_t *parser_ctx; + const char *vcc_url; + int status_code; + + date_ctx = apr_pcalloc(pool, sizeof(*date_ctx)); + date_ctx->pool = pool; + date_ctx->time = tm; + date_ctx->revision = revision; + date_ctx->done = FALSE; + + SVN_ERR(svn_ra_serf__discover_root(&vcc_url, NULL, + session, session->conns[0], + session->repos_url.path, pool)); + + handler = apr_pcalloc(pool, sizeof(*handler)); + + handler->method = "REPORT"; + handler->path = vcc_url; + handler->body_type = "text/xml"; + handler->conn = session->conns[0]; + handler->session = session; + + parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx)); + + parser_ctx->pool = pool; + parser_ctx->user_data = date_ctx; + parser_ctx->start = start_getdate; + parser_ctx->end = end_getdate; + parser_ctx->cdata = cdata_getdate; + parser_ctx->done = &date_ctx->done; + parser_ctx->status_code = &status_code; + + handler->body_delegate = create_getdate_body; + handler->body_delegate_baton = date_ctx; + + handler->response_handler = svn_ra_serf__handle_xml_parser; + handler->response_baton = parser_ctx; + + svn_ra_serf__request_create(handler); + + *date_ctx->revision = SVN_INVALID_REVNUM; + + SVN_ERR(svn_ra_serf__context_run_wait(&date_ctx->done, session, pool)); + + return SVN_NO_ERROR; +} Index: util.c =================================================================== --- util.c (revision 22857) +++ util.c (working copy) @@ -41,8 +41,8 @@ serf_bucket_t * svn_ra_serf__conn_setup(apr_socket_t *sock, - void *baton, - apr_pool_t *pool) + void *baton, + apr_pool_t *pool) { serf_bucket_t *bucket; svn_ra_serf__connection_t *conn = baton; @@ -63,9 +63,9 @@ serf_bucket_t* svn_ra_serf__accept_response(serf_request_t *request, - serf_bucket_t *stream, - void *acceptor_baton, - apr_pool_t *pool) + serf_bucket_t *stream, + void *acceptor_baton, + apr_pool_t *pool) { serf_bucket_t *c; serf_bucket_alloc_t *bkt_alloc; @@ -95,9 +95,9 @@ void svn_ra_serf__conn_closed(serf_connection_t *conn, - void *closed_baton, - apr_status_t why, - apr_pool_t *pool) + void *closed_baton, + apr_status_t why, + apr_pool_t *pool) { svn_ra_serf__connection_t *our_conn = closed_baton; @@ -118,6 +118,15 @@ svn_ra_serf__session_t *serf_sess = data; int i; + /* If we are cleaning up due to an error, don't call connection_close + * as we're already on our way out of here and we'll defer to serf's + * cleanups. + */ + if (serf_sess->pending_error) + { + return APR_SUCCESS; + } + for (i = 0; i < serf_sess->num_conns; i++) { if (serf_sess->conns[i]) @@ -309,9 +318,9 @@ apr_status_t svn_ra_serf__handle_discard_body(serf_request_t *request, - serf_bucket_t *response, - void *baton, - apr_pool_t *pool) + serf_bucket_t *response, + void *baton, + apr_pool_t *pool) { apr_status_t status; svn_ra_serf__server_error_t *server_err = baton; @@ -654,7 +663,26 @@ xml_status = XML_Parse(ctx->xmlp, data, len, 0); if (xml_status == XML_STATUS_ERROR && ctx->ignore_errors == FALSE) { - abort(); + XML_ParserFree(ctx->xmlp); + + if (!ctx->status_code) + { + abort(); + } + if (*ctx->done == FALSE) + { + *ctx->done = TRUE; + if (ctx->done_list) + { + ctx->done_item->data = ctx->user_data; + ctx->done_item->next = *ctx->done_list; + *ctx->done_list = ctx->done_item; + } + } + ctx->error = svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + "XML parsing failed: (%d %s)", + sl.code, sl.reason); + return ctx->error->apr_err; } if (ctx->error && ctx->ignore_errors == FALSE) @@ -697,8 +725,38 @@ /* not reached */ } +svn_error_t * +svn_ra_serf__handle_server_error(serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *pool) +{ + svn_ra_serf__server_error_t server_err; + apr_status_t status; + + memset(&server_err, 0, sizeof(server_err)); + status = svn_ra_serf__handle_discard_body(request, response, + &server_err, pool); + + if (APR_STATUS_IS_EOF(status)) + { + status = svn_ra_serf__is_conn_closing(response); + if (status == SERF_ERROR_CLOSING) + { + serf_connection_reset(serf_request_get_conn(request)); + } + } + + SVN_ERR(server_err.error); + + return svn_error_create(APR_EGENERAL, NULL, _("Unspecified error message")); +} + +/* Implements the serf_response_handler_t interface. Wait for HTTP + response status and headers, and invoke CTX->response_handler() to + carry out operation-specific processing. Afterwards, check for + connection close. */ static apr_status_t -handler_default(serf_request_t *request, +handle_response(serf_request_t *request, serf_bucket_t *response, void *baton, apr_pool_t *pool) @@ -707,9 +765,9 @@ serf_status_line sl; apr_status_t status; - /* Uh-oh. Our connection died. Requeue. */ if (!response) { + /* Uh-oh. Our connection died. Requeue. */ if (ctx->response_error) { status = ctx->response_error(request, response, 0, @@ -725,6 +783,12 @@ return APR_SUCCESS; } + status = serf_bucket_response_status(response, &sl); + if (SERF_BUCKET_READ_ERROR(status)) + { + return status; + } + status = serf_bucket_response_wait_for_headers(response); if (status) { @@ -732,11 +796,16 @@ { return status; } - /* If we got an EOF here when we're not a HEAD request, - * something went really wrong: either the server closed on us - * early or we're reading too much. Either way, scream loudly. + + /* Cases where a lack of a response body (via EOF) is okay: + * - A HEAD request + * - 204/304 response + * + * Otherwise, if we get an EOF here, something went really wrong: either + * the server closed on us early or we're reading too much. Either way, + * scream loudly. */ - if (strcmp(ctx->method, "HEAD") != 0) + if (strcmp(ctx->method, "HEAD") != 0 && sl.code != 204 && sl.code != 304) { ctx->session->pending_error = svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, @@ -745,12 +814,6 @@ } } - status = serf_bucket_response_status(response, &sl); - if (status) - { - return status; - } - if (ctx->conn->last_status_code == 401 && sl.code < 400) { svn_auth_save_credentials(ctx->session->auth_state, ctx->session->pool); @@ -768,31 +831,9 @@ } else if (sl.code >= 500) { - svn_ra_serf__server_error_t server_err; - apr_status_t status; - - memset(&server_err, 0, sizeof(server_err)); - status = svn_ra_serf__handle_discard_body(request, response, - &server_err, pool); - - if (APR_STATUS_IS_EOF(status)) - { - status = svn_ra_serf__is_conn_closing(response); - if (status == SERF_ERROR_CLOSING) - { - serf_connection_reset(ctx->conn->conn); - } - } - - if (server_err.error) - { - ctx->session->pending_error = server_err.error; - return server_err.error->apr_err; - } - else - { - return APR_EGENERAL; - } + ctx->session->pending_error = + svn_ra_serf__handle_server_error(request, response, pool); + return ctx->session->pending_error->apr_err; } else { @@ -800,16 +841,23 @@ pool); } - if (APR_STATUS_IS_EOF(status)) { + if (APR_STATUS_IS_EOF(status)) + { status = svn_ra_serf__is_conn_closing(response); - } + } return status; - } +/* Implements the serf_request_setup_t interface (which sets up both a + request and its response handler callback). If the CTX->delegate() + callback is non-NULL, invoke it to carry out the majority of the + serf_request_setup_t implementation. Otherwise, perform default + setup, with special handling for HEAD requests, and finer-grained + callbacks invoked (if non-NULL) to produce the request headers and + body. */ static apr_status_t -setup_default(serf_request_t *request, +setup_request(serf_request_t *request, void *setup_baton, serf_bucket_t **req_bkt, serf_response_acceptor_t *acceptor, @@ -864,17 +912,17 @@ } } - *handler = handler_default; + *handler = handle_response; *handler_baton = ctx; return APR_SUCCESS; } -serf_request_t* +serf_request_t * svn_ra_serf__request_create(svn_ra_serf__handler_t *handler) { return serf_connection_request_create(handler->conn->conn, - setup_default, handler); + setup_request, handler); } svn_error_t * Index: log.c =================================================================== --- log.c (revision 22857) +++ log.c (working copy) @@ -49,6 +49,10 @@ CREATOR, DATE, COMMENT, + ADDED_PATH, + REPLACED_PATH, + DELETED_PATH, + MODIFIED_PATH, } log_state_e; typedef struct { @@ -58,9 +62,11 @@ const char *tmp; apr_size_t tmp_len; + /* Temporary change path - ultimately inserted into changed_paths hash. */ + svn_log_changed_path_t *tmp_path; + /* Hashtable of paths */ - /* TODO implement changed-paths support */ - apr_hash_t *paths; + apr_hash_t *changed_paths; /* Other log fields */ svn_revnum_t version; @@ -105,6 +111,20 @@ parser->state->private = info; } + if (state == ADDED_PATH || state == REPLACED_PATH || + state == DELETED_PATH || state == MODIFIED_PATH) + { + log_info_t *info = parser->state->private; + + if (!info->changed_paths) + { + info->changed_paths = apr_hash_make(info->pool); + } + + info->tmp_path = apr_pcalloc(info->pool, sizeof(*info->tmp_path)); + info->tmp_path->copyfrom_rev = SVN_INVALID_REVNUM; + } + return parser->state->private; } @@ -137,6 +157,8 @@ } else if (state == ITEM) { + log_info_t *info; + if (strcmp(name.name, "version-name") == 0) { push_state(parser, log_ctx, VERSION); @@ -153,6 +175,60 @@ { push_state(parser, log_ctx, COMMENT); } + else if (strcmp(name.name, "added-path") == 0) + { + const char *copy_path, *copy_rev_str; + + info = push_state(parser, log_ctx, ADDED_PATH); + info->tmp_path->action = 'A'; + + copy_path = svn_ra_serf__find_attr(attrs, "copyfrom-path"); + copy_rev_str = svn_ra_serf__find_attr(attrs, "copyfrom-rev"); + if (copy_path && copy_rev_str) + { + svn_revnum_t copy_rev; + + copy_rev = SVN_STR_TO_REV(copy_rev_str); + if (SVN_IS_VALID_REVNUM(copy_rev)) + { + info->tmp_path->copyfrom_path = apr_pstrdup(info->pool, + copy_path); + info->tmp_path->copyfrom_rev = copy_rev; + } + } + } + else if (strcmp(name.name, "replaced-path") == 0) + { + const char *copy_path, *copy_rev_str; + + info = push_state(parser, log_ctx, REPLACED_PATH); + info->tmp_path->action = 'R'; + + copy_path = svn_ra_serf__find_attr(attrs, "copyfrom-path"); + copy_rev_str = svn_ra_serf__find_attr(attrs, "copyfrom-rev"); + if (copy_path && copy_rev_str) + { + svn_revnum_t copy_rev; + + copy_rev = SVN_STR_TO_REV(copy_rev_str); + if (SVN_IS_VALID_REVNUM(copy_rev)) + { + info->tmp_path->copyfrom_path = apr_pstrdup(info->pool, + copy_path); + info->tmp_path->copyfrom_rev = copy_rev; + } + } + } + else if (strcmp(name.name, "deleted-path") == 0) + { + info = push_state(parser, log_ctx, DELETED_PATH); + info->tmp_path->action = 'D'; + } + else if (strcmp(name.name, "modified-path") == 0) + { + info = push_state(parser, log_ctx, MODIFIED_PATH); + info->tmp_path->action = 'M'; + } } return SVN_NO_ERROR; @@ -180,7 +256,7 @@ { /* Give the info to the reporter */ SVN_ERR(log_ctx->receiver(log_ctx->receiver_baton, - info->paths, + info->changed_paths, info->version, info->creator, info->date, @@ -217,7 +293,25 @@ info->tmp_len = 0; svn_ra_serf__xml_pop_state(parser); } + else if ((state == ADDED_PATH && + strcmp(name.name, "added-path") == 0) || + (state == DELETED_PATH && + strcmp(name.name, "deleted-path") == 0) || + (state == MODIFIED_PATH && + strcmp(name.name, "modified-path") == 0) || + (state == REPLACED_PATH && + strcmp(name.name, "replaced-path") == 0)) + { + char *path; + path = apr_pstrmemdup(info->pool, info->tmp, info->tmp_len); + info->tmp_len = 0; + + apr_hash_set(info->changed_paths, path, APR_HASH_KEY_STRING, + info->tmp_path); + svn_ra_serf__xml_pop_state(parser); + } + return SVN_NO_ERROR; } @@ -240,6 +334,10 @@ case CREATOR: case DATE: case COMMENT: + case ADDED_PATH: + case REPLACED_PATH: + case DELETED_PATH: + case MODIFIED_PATH: svn_ra_serf__expand_string(&info->tmp, &info->tmp_len, data, len, parser->state->pool); break; Index: update.c =================================================================== --- update.c (revision 22857) +++ update.c (working copy) @@ -384,7 +384,7 @@ apr_pool_create(&new_info->pool, info_parent_pool); new_info->file_baton = NULL; new_info->lock_token = NULL; - new_info->fetch_file = FALSE; + new_info->fetch_file = FALSE; /* Point at our parent's directory state. */ new_info->dir = info->dir; @@ -932,6 +932,9 @@ if (sl.code == 404) { fetch_ctx->done = TRUE; + fetch_ctx->err = svn_error_createf(SVN_ERR_RA_DAV_PATH_NOT_FOUND, NULL, + "'%s' path not found", + fetch_ctx->info->name); return svn_ra_serf__handle_discard_body(request, response, NULL, pool); } @@ -1072,7 +1075,6 @@ static void fetch_file(report_context_t *ctx, report_info_t *info) { - const char *checked_in_url; svn_ra_serf__connection_t *conn; svn_ra_serf__handler_t *handler; @@ -1388,6 +1390,8 @@ abort(); } + info = parser->state->private; + SVN_ERR(open_dir(info->dir)); ctx->update_editor->absent_directory(file_name, @@ -2203,16 +2207,18 @@ (!cur_dir->fetch_props || svn_ra_serf__propfind_is_done(cur_dir->propfind))) { + report_dir_t *parent = cur_dir->parent_dir; + SVN_ERR(close_dir(cur_dir)); - if (cur_dir->parent_dir) + if (parent) { - cur_dir->parent_dir->ref_count--; + parent->ref_count--; } else { closed_root = TRUE; } - cur_dir = cur_dir->parent_dir; + cur_dir = parent; } } report->done_fetches = NULL; @@ -2529,6 +2535,8 @@ stream_ctx->target_stream = stream; stream_ctx->sess = session; stream_ctx->conn = conn; + stream_ctx->info = apr_pcalloc(pool, sizeof(*stream_ctx->info)); + stream_ctx->info->name = fetch_url; handler = apr_pcalloc(pool, sizeof(*handler)); handler->method = "GET"; @@ -2545,6 +2553,7 @@ svn_ra_serf__request_create(handler); SVN_ERR(svn_ra_serf__context_run_wait(&stream_ctx->done, session, pool)); + SVN_ERR(stream_ctx->err); } return SVN_NO_ERROR; Index: property.c =================================================================== --- property.c (revision 22857) +++ property.c (working copy) @@ -96,6 +96,9 @@ /* Are we done issuing the PROPFIND? */ svn_boolean_t done; + /* Context from XML stream */ + svn_ra_serf__xml_parser_t *parser_ctx; + /* If not-NULL, add us to this list when we're done. */ svn_ra_serf__list_t **done_list; @@ -413,7 +416,7 @@ apr_pool_t *pool) { svn_ra_serf__propfind_context_t *ctx = setup_baton; - svn_ra_serf__xml_parser_t *parser_ctx; + svn_ra_serf__xml_parser_t *parser_ctx = ctx->parser_ctx; *req_bkt = svn_ra_serf__bucket_propfind_create(ctx->conn, ctx->path, ctx->label, @@ -433,8 +436,6 @@ } } - parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx)); - parser_ctx->pool = pool; parser_ctx->user_data = ctx; parser_ctx->start = start_propfind; @@ -560,6 +561,9 @@ new_prop_ctx->handler = handler; + new_prop_ctx->parser_ctx = apr_pcalloc(pool, + sizeof(*new_prop_ctx->parser_ctx)); + *prop_ctx = new_prop_ctx; } @@ -590,7 +594,11 @@ svn_ra_serf__session_t *sess, apr_pool_t *pool) { - return svn_ra_serf__context_run_wait(&prop_ctx->done, sess, pool); + svn_error_t *err; + + err = svn_ra_serf__context_run_wait(&prop_ctx->done, sess, pool); + SVN_ERR(prop_ctx->parser_ctx->error); + return err; } /* Index: ra_serf.h =================================================================== --- ra_serf.h (revision 22857) +++ ra_serf.h (working copy) @@ -267,7 +267,7 @@ serf_bucket_alloc_t *alloc, apr_pool_t *pool); -/* Callback for when a request headers are needed. */ +/* Callback for when request headers are needed. */ typedef apr_status_t (*svn_ra_serf__request_header_delegate_t)(serf_bucket_t *headers, void *baton, @@ -545,6 +545,17 @@ apr_pool_t *pool); /* + * Handler that retrieves the embedded XML error response from the + * the @a response body associated with a @a request. + * + * All temporary allocations will be made in a @a pool. + */ +svn_error_t * +svn_ra_serf__handle_server_error(serf_request_t *request, + serf_bucket_t *response, + apr_pool_t *pool); + +/* * This function will feed the RESPONSE body into XMLP. When parsing is * completed (i.e. an EOF is received), *DONE is set to TRUE. * @@ -942,6 +953,12 @@ apr_pool_t *pool); svn_error_t * +svn_ra_serf__get_dated_revision(svn_ra_session_t *session, + svn_revnum_t *revision, + apr_time_t tm, + apr_pool_t *pool); + +svn_error_t * svn_ra_serf__get_commit_editor(svn_ra_session_t *session, const svn_delta_editor_t **editor, void **edit_baton, Index: commit.c =================================================================== --- commit.c (revision 22857) +++ commit.c (working copy) @@ -293,8 +293,6 @@ if (dir->parent_dir) { - SVN_ERR(checkout_dir(dir->parent_dir)); - /* Is our parent a copy? If so, we're already implicitly checked out. */ if (apr_hash_get(dir->commit->copied_entries, dir->parent_dir->name, APR_HASH_KEY_STRING)) @@ -366,7 +364,7 @@ return_response_err(handler, &checkout_ctx->progress), _("Path '%s' not present"), - svn_path_local_style(dir->name, dir->pool)); + dir->name); } return svn_error_createf(SVN_ERR_RA_DAV_PATH_NOT_FOUND, @@ -427,7 +425,7 @@ return_response_err(handler, &file->checkout->progress), _("Path '%s' not present"), - svn_path_local_style(file->name, file->pool)); + file->name); } return svn_error_createf(SVN_ERR_RA_DAV_PATH_NOT_FOUND, @@ -733,9 +731,13 @@ /* We need to flush the file, make it unbuffered (so that it can be * zero-copied via mmap), and reset the position before attempting to * deliver the file. + * + * N.B. If we have APR 1.3+, we can unbuffer the file to let us use mmap + * and zero-copy the PUT body. However, on older APR versions, we can't + * check the buffer status; but serf will fall through and create a file + * bucket for us on the buffered svndiff handle. */ apr_file_flush(ctx->svndiff); - apr_file_buffer_set(ctx->svndiff, NULL, 0); offset = 0; apr_file_seek(ctx->svndiff, APR_SET, &offset); @@ -846,7 +848,6 @@ apr_pool_t *pool) { delete_context_t *ctx = baton; - const char *lock_tokens; serf_bucket_headers_set(headers, SVN_DAV_VERSION_NAME_HEADER, apr_ltoa(pool, ctx->revision)); @@ -1043,7 +1044,6 @@ dir_context_t *dir = parent_baton; delete_context_t *delete_ctx; svn_ra_serf__handler_t *handler; - const char *lock_token; svn_error_t *err; /* Ensure our directory has been checked out */ @@ -1096,7 +1096,7 @@ SVN_ERR(svn_ra_serf__context_run_wait(&delete_ctx->progress.done, dir->commit->session, pool)); } - else + else if (err) { return err; } @@ -1207,7 +1207,11 @@ if (add_dir_ctx->status != 201) { - abort(); + SVN_ERR(add_dir_ctx->server_error.error); + return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, + _("Add directory failed: %s on %s (%d)"), + handler->method, handler->path, + add_dir_ctx->status); } *child_baton = dir; @@ -1755,7 +1759,7 @@ svn_delta_editor_t *editor; commit_context_t *ctx; - ctx = apr_pcalloc(pool, sizeof(commit_context_t)); + ctx = apr_pcalloc(pool, sizeof(*ctx)); ctx->pool = pool; Index: replay.c =================================================================== --- replay.c (revision 22857) +++ replay.c (working copy) @@ -135,9 +135,9 @@ static svn_error_t * start_replay(svn_ra_serf__xml_parser_t *parser, - void *userData, - svn_ra_serf__dav_props_t name, - const char **attrs) + void *userData, + svn_ra_serf__dav_props_t name, + const char **attrs) { replay_context_t *ctx = userData; replay_state_e state; @@ -336,6 +336,10 @@ info = push_state(parser, ctx, APPLY_TEXTDELTA); checksum = svn_ra_serf__find_attr(attrs, "checksum"); + if (checksum) + { + checksum = apr_pstrdup(info->pool, checksum); + } SVN_ERR(ctx->editor->apply_textdelta(info->baton, checksum, info->pool, @@ -350,9 +354,13 @@ strcmp(name.name, "close-file") == 0) { replay_info_t *info = parser->state->private; + const char *checksum; - SVN_ERR(ctx->editor->close_file(info->baton, NULL, parser->state->pool)); + checksum = svn_ra_serf__find_attr(attrs, "checksum"); + SVN_ERR(ctx->editor->close_file(info->baton, checksum, + parser->state->pool)); + svn_ra_serf__xml_pop_state(parser); } else if (((state == OPEN_FILE || state == ADD_FILE) && @@ -362,7 +370,6 @@ { const char *prop_name; prop_info_t *info; - svn_boolean_t del_prop; prop_name = svn_ra_serf__find_attr(attrs, "name"); if (!prop_name) @@ -393,8 +400,8 @@ static svn_error_t * end_replay(svn_ra_serf__xml_parser_t *parser, - void *userData, - svn_ra_serf__dav_props_t name) + void *userData, + svn_ra_serf__dav_props_t name) { replay_context_t *ctx = userData; replay_state_e state; @@ -468,9 +475,9 @@ static svn_error_t * cdata_replay(svn_ra_serf__xml_parser_t *parser, - void *userData, - const char *data, - apr_size_t len) + void *userData, + const char *data, + apr_size_t len) { replay_context_t *replay_ctx = userData; replay_state_e state; @@ -515,8 +522,6 @@ svn_ra_serf__handler_t *handler; svn_ra_serf__xml_parser_t *parser_ctx; serf_bucket_t *buckets, *tmp; - apr_hash_t *props; - svn_revnum_t peg_rev; replay_ctx = apr_pcalloc(pool, sizeof(*replay_ctx)); replay_ctx->pool = pool; @@ -559,8 +564,6 @@ session->bkt_alloc); serf_bucket_aggregate_append(buckets, tmp); - props = apr_hash_make(pool); - handler = apr_pcalloc(pool, sizeof(*handler)); handler->method = "REPORT";