1251881Speter/* 2251881Speter * merge.c : MERGE response parsing functions for ra_serf 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <apr_uri.h> 27251881Speter 28251881Speter#include <serf.h> 29251881Speter 30251881Speter#include "svn_hash.h" 31251881Speter#include "svn_pools.h" 32251881Speter#include "svn_ra.h" 33251881Speter#include "svn_dav.h" 34251881Speter#include "svn_xml.h" 35251881Speter#include "svn_config.h" 36251881Speter#include "svn_dirent_uri.h" 37251881Speter#include "svn_props.h" 38251881Speter 39251881Speter#include "private/svn_dav_protocol.h" 40251881Speter#include "private/svn_fspath.h" 41251881Speter#include "svn_private_config.h" 42251881Speter 43251881Speter#include "ra_serf.h" 44251881Speter#include "../libsvn_ra/ra_loader.h" 45251881Speter 46251881Speter 47251881Speter/* 48251881Speter * This enum represents the current state of our XML parsing for a MERGE. 49251881Speter */ 50251881Spetertypedef enum merge_state_e { 51251881Speter INITIAL = 0, 52251881Speter MERGE_RESPONSE, 53251881Speter UPDATED_SET, 54251881Speter RESPONSE, 55251881Speter HREF, 56251881Speter PROPSTAT, 57251881Speter PROP, 58251881Speter RESOURCE_TYPE, 59251881Speter BASELINE, 60251881Speter COLLECTION, 61251881Speter SKIP_HREF, 62251881Speter CHECKED_IN, 63251881Speter VERSION_NAME, 64251881Speter DATE, 65251881Speter AUTHOR, 66251881Speter POST_COMMIT_ERR, 67251881Speter 68251881Speter PROP_VAL 69251881Speter} merge_state_e; 70251881Speter 71251881Speter 72251881Speter/* Structure associated with a MERGE request. */ 73251881Spetertypedef struct merge_context_t 74251881Speter{ 75251881Speter apr_pool_t *pool; 76251881Speter 77251881Speter svn_ra_serf__session_t *session; 78251881Speter svn_ra_serf__handler_t *handler; 79251881Speter 80251881Speter apr_hash_t *lock_tokens; 81251881Speter svn_boolean_t keep_locks; 82251881Speter 83251881Speter const char *merge_resource_url; /* URL of resource to be merged. */ 84251881Speter const char *merge_url; /* URL at which the MERGE request is aimed. */ 85251881Speter 86251881Speter svn_commit_info_t *commit_info; 87251881Speter 88251881Speter} merge_context_t; 89251881Speter 90251881Speter 91251881Speter#define D_ "DAV:" 92251881Speter#define S_ SVN_XML_NAMESPACE 93251881Speterstatic const svn_ra_serf__xml_transition_t merge_ttable[] = { 94251881Speter { INITIAL, D_, "merge-response", MERGE_RESPONSE, 95251881Speter FALSE, { NULL }, FALSE }, 96251881Speter 97251881Speter { MERGE_RESPONSE, D_, "updated-set", UPDATED_SET, 98251881Speter FALSE, { NULL }, FALSE }, 99251881Speter 100251881Speter { UPDATED_SET, D_, "response", RESPONSE, 101251881Speter FALSE, { NULL }, TRUE }, 102251881Speter 103251881Speter { RESPONSE, D_, "href", HREF, 104251881Speter TRUE, { NULL }, TRUE }, 105251881Speter 106251881Speter { RESPONSE, D_, "propstat", PROPSTAT, 107251881Speter FALSE, { NULL }, FALSE }, 108251881Speter 109251881Speter#if 0 110251881Speter /* Not needed. */ 111251881Speter { PROPSTAT, D_, "status", STATUS, 112251881Speter FALSE, { NULL }, FALSE }, 113251881Speter#endif 114251881Speter 115251881Speter { PROPSTAT, D_, "prop", PROP, 116251881Speter FALSE, { NULL }, FALSE }, 117251881Speter 118251881Speter { PROP, D_, "resourcetype", RESOURCE_TYPE, 119251881Speter FALSE, { NULL }, FALSE }, 120251881Speter 121251881Speter { RESOURCE_TYPE, D_, "baseline", BASELINE, 122251881Speter FALSE, { NULL }, TRUE }, 123251881Speter 124251881Speter { RESOURCE_TYPE, D_, "collection", COLLECTION, 125251881Speter FALSE, { NULL }, TRUE }, 126251881Speter 127251881Speter { PROP, D_, "checked-in", SKIP_HREF, 128251881Speter FALSE, { NULL }, FALSE }, 129251881Speter 130251881Speter { SKIP_HREF, D_, "href", CHECKED_IN, 131251881Speter TRUE, { NULL }, TRUE }, 132251881Speter 133251881Speter { PROP, D_, SVN_DAV__VERSION_NAME, VERSION_NAME, 134251881Speter TRUE, { NULL }, TRUE }, 135251881Speter 136251881Speter { PROP, D_, SVN_DAV__CREATIONDATE, DATE, 137251881Speter TRUE, { NULL }, TRUE }, 138251881Speter 139251881Speter { PROP, D_, "creator-displayname", AUTHOR, 140251881Speter TRUE, { NULL }, TRUE }, 141251881Speter 142251881Speter { PROP, S_, "post-commit-err", POST_COMMIT_ERR, 143251881Speter TRUE, { NULL }, TRUE }, 144251881Speter 145251881Speter { 0 } 146251881Speter}; 147251881Speter 148251881Speter 149251881Speter/* Conforms to svn_ra_serf__xml_closed_t */ 150251881Speterstatic svn_error_t * 151251881Spetermerge_closed(svn_ra_serf__xml_estate_t *xes, 152251881Speter void *baton, 153251881Speter int leaving_state, 154251881Speter const svn_string_t *cdata, 155251881Speter apr_hash_t *attrs, 156251881Speter apr_pool_t *scratch_pool) 157251881Speter{ 158251881Speter merge_context_t *merge_ctx = baton; 159251881Speter 160251881Speter if (leaving_state == RESPONSE) 161251881Speter { 162251881Speter const char *rtype; 163251881Speter 164251881Speter rtype = svn_hash_gets(attrs, "resourcetype"); 165251881Speter 166251881Speter /* rtype can only be "baseline" or "collection" (or NULL). We can 167251881Speter keep this check simple. */ 168251881Speter if (rtype && *rtype == 'b') 169251881Speter { 170251881Speter const char *rev_str; 171251881Speter 172251881Speter rev_str = svn_hash_gets(attrs, "revision"); 173251881Speter if (rev_str) 174251881Speter merge_ctx->commit_info->revision = SVN_STR_TO_REV(rev_str); 175251881Speter else 176251881Speter merge_ctx->commit_info->revision = SVN_INVALID_REVNUM; 177251881Speter 178251881Speter merge_ctx->commit_info->date = 179251881Speter apr_pstrdup(merge_ctx->pool, 180251881Speter svn_hash_gets(attrs, "date")); 181251881Speter 182251881Speter merge_ctx->commit_info->author = 183251881Speter apr_pstrdup(merge_ctx->pool, 184251881Speter svn_hash_gets(attrs, "author")); 185251881Speter 186251881Speter merge_ctx->commit_info->post_commit_err = 187251881Speter apr_pstrdup(merge_ctx->pool, 188251881Speter svn_hash_gets(attrs, "post-commit-err")); 189251881Speter } 190251881Speter else 191251881Speter { 192251881Speter const char *href; 193251881Speter 194251881Speter href = svn_urlpath__skip_ancestor( 195251881Speter merge_ctx->merge_url, 196251881Speter svn_hash_gets(attrs, "href")); 197251881Speter 198251881Speter if (href == NULL) 199251881Speter return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 200251881Speter _("A MERGE response for '%s' is not " 201251881Speter "a child of the destination ('%s')"), 202251881Speter href, merge_ctx->merge_url); 203251881Speter 204251881Speter /* We now need to dive all the way into the WC to update the 205251881Speter base VCC url. */ 206251881Speter if (!SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(merge_ctx->session) 207251881Speter && merge_ctx->session->wc_callbacks->push_wc_prop) 208251881Speter { 209251881Speter const char *checked_in; 210251881Speter svn_string_t checked_in_str; 211251881Speter 212251881Speter checked_in = svn_hash_gets(attrs, "checked-in"); 213251881Speter checked_in_str.data = checked_in; 214251881Speter checked_in_str.len = strlen(checked_in); 215251881Speter 216251881Speter SVN_ERR(merge_ctx->session->wc_callbacks->push_wc_prop( 217251881Speter merge_ctx->session->wc_callback_baton, 218251881Speter href, 219251881Speter SVN_RA_SERF__WC_CHECKED_IN_URL, 220251881Speter &checked_in_str, 221251881Speter scratch_pool)); 222251881Speter } 223251881Speter } 224251881Speter } 225251881Speter else if (leaving_state == BASELINE) 226251881Speter { 227251881Speter svn_ra_serf__xml_note(xes, RESPONSE, "resourcetype", "baseline"); 228251881Speter } 229251881Speter else if (leaving_state == COLLECTION) 230251881Speter { 231251881Speter svn_ra_serf__xml_note(xes, RESPONSE, "resourcetype", "collection"); 232251881Speter } 233251881Speter else 234251881Speter { 235251881Speter const char *name; 236251881Speter const char *value = cdata->data; 237251881Speter 238251881Speter if (leaving_state == HREF) 239251881Speter { 240251881Speter name = "href"; 241251881Speter value = svn_urlpath__canonicalize(value, scratch_pool); 242251881Speter } 243251881Speter else if (leaving_state == CHECKED_IN) 244251881Speter { 245251881Speter name = "checked-in"; 246251881Speter value = svn_urlpath__canonicalize(value, scratch_pool); 247251881Speter } 248251881Speter else if (leaving_state == VERSION_NAME) 249251881Speter name = "revision"; 250251881Speter else if (leaving_state == DATE) 251251881Speter name = "date"; 252251881Speter else if (leaving_state == AUTHOR) 253251881Speter name = "author"; 254251881Speter else if (leaving_state == POST_COMMIT_ERR) 255251881Speter name = "post-commit-err"; 256251881Speter else 257251881Speter SVN_ERR_MALFUNCTION(); 258251881Speter 259251881Speter svn_ra_serf__xml_note(xes, RESPONSE, name, value); 260251881Speter } 261251881Speter 262251881Speter return SVN_NO_ERROR; 263251881Speter} 264251881Speter 265251881Speter 266251881Speterstatic svn_error_t * 267251881Spetersetup_merge_headers(serf_bucket_t *headers, 268251881Speter void *baton, 269251881Speter apr_pool_t *pool) 270251881Speter{ 271251881Speter merge_context_t *ctx = baton; 272251881Speter 273251881Speter if (!ctx->keep_locks) 274251881Speter { 275251881Speter serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER, 276251881Speter SVN_DAV_OPTION_RELEASE_LOCKS); 277251881Speter } 278251881Speter 279251881Speter return SVN_NO_ERROR; 280251881Speter} 281251881Speter 282251881Spetervoid 283251881Spetersvn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens, 284251881Speter const char *parent, 285251881Speter serf_bucket_t *body, 286251881Speter serf_bucket_alloc_t *alloc, 287251881Speter apr_pool_t *pool) 288251881Speter{ 289251881Speter apr_hash_index_t *hi; 290251881Speter 291251881Speter if (!lock_tokens || apr_hash_count(lock_tokens) == 0) 292251881Speter return; 293251881Speter 294251881Speter svn_ra_serf__add_open_tag_buckets(body, alloc, 295251881Speter "S:lock-token-list", 296251881Speter "xmlns:S", SVN_XML_NAMESPACE, 297251881Speter NULL); 298251881Speter 299251881Speter for (hi = apr_hash_first(pool, lock_tokens); 300251881Speter hi; 301251881Speter hi = apr_hash_next(hi)) 302251881Speter { 303251881Speter const void *key; 304251881Speter apr_ssize_t klen; 305251881Speter void *val; 306251881Speter svn_string_t path; 307251881Speter 308251881Speter apr_hash_this(hi, &key, &klen, &val); 309251881Speter 310251881Speter path.data = key; 311251881Speter path.len = klen; 312251881Speter 313251881Speter if (parent && !svn_relpath_skip_ancestor(parent, key)) 314251881Speter continue; 315251881Speter 316251881Speter svn_ra_serf__add_open_tag_buckets(body, alloc, "S:lock", NULL); 317251881Speter 318251881Speter svn_ra_serf__add_open_tag_buckets(body, alloc, "lock-path", NULL); 319251881Speter svn_ra_serf__add_cdata_len_buckets(body, alloc, path.data, path.len); 320251881Speter svn_ra_serf__add_close_tag_buckets(body, alloc, "lock-path"); 321251881Speter 322251881Speter svn_ra_serf__add_tag_buckets(body, "lock-token", val, alloc); 323251881Speter 324251881Speter svn_ra_serf__add_close_tag_buckets(body, alloc, "S:lock"); 325251881Speter } 326251881Speter 327251881Speter svn_ra_serf__add_close_tag_buckets(body, alloc, "S:lock-token-list"); 328251881Speter} 329251881Speter 330251881Speterstatic svn_error_t* 331251881Spetercreate_merge_body(serf_bucket_t **bkt, 332251881Speter void *baton, 333251881Speter serf_bucket_alloc_t *alloc, 334251881Speter apr_pool_t *pool) 335251881Speter{ 336251881Speter merge_context_t *ctx = baton; 337251881Speter serf_bucket_t *body_bkt; 338251881Speter 339251881Speter body_bkt = serf_bucket_aggregate_create(alloc); 340251881Speter 341251881Speter svn_ra_serf__add_xml_header_buckets(body_bkt, alloc); 342251881Speter svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:merge", 343251881Speter "xmlns:D", "DAV:", 344251881Speter NULL); 345251881Speter svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:source", NULL); 346251881Speter svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", NULL); 347251881Speter 348251881Speter svn_ra_serf__add_cdata_len_buckets(body_bkt, alloc, 349251881Speter ctx->merge_resource_url, 350251881Speter strlen(ctx->merge_resource_url)); 351251881Speter 352251881Speter svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:href"); 353251881Speter svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:source"); 354251881Speter 355251881Speter svn_ra_serf__add_tag_buckets(body_bkt, "D:no-auto-merge", NULL, alloc); 356251881Speter svn_ra_serf__add_tag_buckets(body_bkt, "D:no-checkout", NULL, alloc); 357251881Speter 358251881Speter svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL); 359251881Speter svn_ra_serf__add_tag_buckets(body_bkt, "D:checked-in", NULL, alloc); 360251881Speter svn_ra_serf__add_tag_buckets(body_bkt, "D:" SVN_DAV__VERSION_NAME, NULL, alloc); 361251881Speter svn_ra_serf__add_tag_buckets(body_bkt, "D:resourcetype", NULL, alloc); 362251881Speter svn_ra_serf__add_tag_buckets(body_bkt, "D:" SVN_DAV__CREATIONDATE, NULL, alloc); 363251881Speter svn_ra_serf__add_tag_buckets(body_bkt, "D:creator-displayname", NULL, alloc); 364251881Speter svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop"); 365251881Speter 366251881Speter svn_ra_serf__merge_lock_token_list(ctx->lock_tokens, NULL, body_bkt, alloc, 367251881Speter pool); 368251881Speter 369251881Speter svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:merge"); 370251881Speter 371251881Speter *bkt = body_bkt; 372251881Speter 373251881Speter return SVN_NO_ERROR; 374251881Speter} 375251881Speter 376251881Speter 377251881Spetersvn_error_t * 378251881Spetersvn_ra_serf__run_merge(const svn_commit_info_t **commit_info, 379251881Speter int *response_code, 380251881Speter svn_ra_serf__session_t *session, 381251881Speter svn_ra_serf__connection_t *conn, 382251881Speter const char *merge_resource_url, 383251881Speter apr_hash_t *lock_tokens, 384251881Speter svn_boolean_t keep_locks, 385251881Speter apr_pool_t *result_pool, 386251881Speter apr_pool_t *scratch_pool) 387251881Speter{ 388251881Speter merge_context_t *merge_ctx; 389251881Speter svn_ra_serf__handler_t *handler; 390251881Speter svn_ra_serf__xml_context_t *xmlctx; 391251881Speter 392251881Speter merge_ctx = apr_pcalloc(scratch_pool, sizeof(*merge_ctx)); 393251881Speter 394251881Speter merge_ctx->pool = result_pool; 395251881Speter merge_ctx->session = session; 396251881Speter 397251881Speter merge_ctx->merge_resource_url = merge_resource_url; 398251881Speter 399251881Speter merge_ctx->lock_tokens = lock_tokens; 400251881Speter merge_ctx->keep_locks = keep_locks; 401251881Speter 402251881Speter merge_ctx->commit_info = svn_create_commit_info(result_pool); 403251881Speter 404251881Speter merge_ctx->merge_url = session->session_url.path; 405251881Speter 406251881Speter xmlctx = svn_ra_serf__xml_context_create(merge_ttable, 407251881Speter NULL, merge_closed, NULL, 408251881Speter merge_ctx, 409251881Speter scratch_pool); 410251881Speter handler = svn_ra_serf__create_expat_handler(xmlctx, scratch_pool); 411251881Speter 412251881Speter handler->method = "MERGE"; 413251881Speter handler->path = merge_ctx->merge_url; 414251881Speter handler->body_delegate = create_merge_body; 415251881Speter handler->body_delegate_baton = merge_ctx; 416251881Speter handler->conn = conn; 417251881Speter handler->session = session; 418251881Speter 419251881Speter handler->header_delegate = setup_merge_headers; 420251881Speter handler->header_delegate_baton = merge_ctx; 421251881Speter 422251881Speter merge_ctx->handler = handler; 423251881Speter 424251881Speter SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); 425251881Speter 426251881Speter *commit_info = merge_ctx->commit_info; 427251881Speter *response_code = handler->sline.code; 428251881Speter 429251881Speter return SVN_NO_ERROR; 430251881Speter} 431