1251881Speter/* 2251881Speter * inherited_props.c : ra_serf implementation of svn_ra_get_inherited_props 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#include <apr_tables.h> 26251881Speter#include <apr_xml.h> 27251881Speter 28251881Speter#include "svn_hash.h" 29251881Speter#include "svn_path.h" 30251881Speter#include "svn_ra.h" 31299742Sdim#include "svn_sorts.h" 32251881Speter#include "svn_string.h" 33251881Speter#include "svn_xml.h" 34251881Speter#include "svn_props.h" 35251881Speter#include "svn_base64.h" 36251881Speter 37251881Speter#include "private/svn_dav_protocol.h" 38299742Sdim#include "private/svn_sorts_private.h" 39251881Speter#include "../libsvn_ra/ra_loader.h" 40251881Speter#include "svn_private_config.h" 41251881Speter#include "ra_serf.h" 42251881Speter 43251881Speter 44251881Speter/* The current state of our XML parsing. */ 45251881Spetertypedef enum iprops_state_e { 46299742Sdim INITIAL = XML_STATE_INITIAL, 47251881Speter IPROPS_REPORT, 48251881Speter IPROPS_ITEM, 49251881Speter IPROPS_PATH, 50251881Speter IPROPS_PROPNAME, 51251881Speter IPROPS_PROPVAL 52251881Speter} iprops_state_e; 53251881Speter 54251881Speter/* Struct for accumulating inherited props. */ 55251881Spetertypedef struct iprops_context_t { 56251881Speter /* The depth-first ordered array of svn_prop_inherited_item_t * 57251881Speter structures we are building. */ 58251881Speter apr_array_header_t *iprops; 59251881Speter 60251881Speter /* Pool in which to allocate elements of IPROPS. */ 61251881Speter apr_pool_t *pool; 62251881Speter 63251881Speter /* The repository's root URL. */ 64251881Speter const char *repos_root_url; 65251881Speter 66269847Speter /* Current property name */ 67251881Speter svn_stringbuf_t *curr_propname; 68251881Speter 69251881Speter /* Current element in IPROPS. */ 70251881Speter svn_prop_inherited_item_t *curr_iprop; 71251881Speter 72251881Speter /* Path we are finding inherited properties for. This is relative to 73251881Speter the RA session passed to svn_ra_serf__get_inherited_props. */ 74251881Speter const char *path; 75251881Speter /* The revision of PATH*/ 76251881Speter svn_revnum_t revision; 77251881Speter} iprops_context_t; 78251881Speter 79269847Speter#define S_ SVN_XML_NAMESPACE 80269847Speterstatic const svn_ra_serf__xml_transition_t iprops_table[] = { 81269847Speter { INITIAL, S_, SVN_DAV__INHERITED_PROPS_REPORT, IPROPS_REPORT, 82269847Speter FALSE, { NULL }, FALSE }, 83269847Speter 84269847Speter { IPROPS_REPORT, S_, SVN_DAV__IPROP_ITEM, IPROPS_ITEM, 85269847Speter FALSE, { NULL }, TRUE }, 86269847Speter 87269847Speter { IPROPS_ITEM, S_, SVN_DAV__IPROP_PATH, IPROPS_PATH, 88269847Speter TRUE, { NULL }, TRUE }, 89269847Speter 90269847Speter { IPROPS_ITEM, S_, SVN_DAV__IPROP_PROPNAME, IPROPS_PROPNAME, 91269847Speter TRUE, { NULL }, TRUE }, 92269847Speter 93269847Speter { IPROPS_ITEM, S_, SVN_DAV__IPROP_PROPVAL, IPROPS_PROPVAL, 94269847Speter TRUE, { "?V:encoding", NULL }, TRUE }, 95269847Speter 96269847Speter { 0 } 97269847Speter}; 98269847Speter 99269847Speter/* Conforms to svn_ra_serf__xml_opened_t */ 100251881Speterstatic svn_error_t * 101269847Speteriprops_opened(svn_ra_serf__xml_estate_t *xes, 102269847Speter void *baton, 103269847Speter int entered_state, 104269847Speter const svn_ra_serf__dav_props_t *tag, 105251881Speter apr_pool_t *scratch_pool) 106251881Speter{ 107269847Speter iprops_context_t *iprops_ctx = baton; 108251881Speter 109269847Speter if (entered_state == IPROPS_ITEM) 110251881Speter { 111251881Speter svn_stringbuf_setempty(iprops_ctx->curr_propname); 112269847Speter 113269847Speter iprops_ctx->curr_iprop = apr_pcalloc(iprops_ctx->pool, 114269847Speter sizeof(*iprops_ctx->curr_iprop)); 115269847Speter 116269847Speter iprops_ctx->curr_iprop->prop_hash = apr_hash_make(iprops_ctx->pool); 117251881Speter } 118251881Speter return SVN_NO_ERROR; 119251881Speter} 120251881Speter 121269847Speter/* Conforms to svn_ra_serf__xml_closed_t */ 122251881Speterstatic svn_error_t * 123269847Speteriprops_closed(svn_ra_serf__xml_estate_t *xes, 124269847Speter void *baton, 125269847Speter int leaving_state, 126269847Speter const svn_string_t *cdata, 127269847Speter apr_hash_t *attrs, 128269847Speter apr_pool_t *scratch_pool) 129251881Speter{ 130269847Speter iprops_context_t *iprops_ctx = baton; 131251881Speter 132269847Speter if (leaving_state == IPROPS_ITEM) 133269847Speter { 134269847Speter APR_ARRAY_PUSH(iprops_ctx->iprops, svn_prop_inherited_item_t *) = 135269847Speter iprops_ctx->curr_iprop; 136251881Speter 137269847Speter iprops_ctx->curr_iprop = NULL; 138251881Speter } 139269847Speter else if (leaving_state == IPROPS_PATH) 140251881Speter { 141269847Speter /* Every <iprop-item> has a single <iprop-path> */ 142269847Speter if (iprops_ctx->curr_iprop->path_or_url) 143269847Speter return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); 144251881Speter 145251881Speter iprops_ctx->curr_iprop->path_or_url = 146299742Sdim apr_pstrdup(iprops_ctx->pool, cdata->data); 147251881Speter } 148269847Speter else if (leaving_state == IPROPS_PROPNAME) 149251881Speter { 150269847Speter if (iprops_ctx->curr_propname->len) 151269847Speter return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); 152251881Speter 153269847Speter /* Store propname for value */ 154269847Speter svn_stringbuf_set(iprops_ctx->curr_propname, cdata->data); 155269847Speter } 156269847Speter else if (leaving_state == IPROPS_PROPVAL) 157269847Speter { 158269847Speter const char *encoding; 159269847Speter const svn_string_t *val_str; 160269847Speter 161269847Speter if (! iprops_ctx->curr_propname->len) 162269847Speter return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); 163269847Speter 164269847Speter encoding = svn_hash_gets(attrs, "V:encoding"); 165269847Speter 166269847Speter if (encoding) 167251881Speter { 168269847Speter if (strcmp(encoding, "base64") != 0) 169269847Speter return svn_error_createf(SVN_ERR_XML_MALFORMED, 170269847Speter NULL, 171269847Speter _("Got unrecognized encoding '%s'"), 172269847Speter encoding); 173251881Speter 174269847Speter /* Decode into the right pool. */ 175269847Speter val_str = svn_base64_decode_string(cdata, iprops_ctx->pool); 176251881Speter } 177251881Speter else 178251881Speter { 179269847Speter /* Copy into the right pool. */ 180269847Speter val_str = svn_string_dup(cdata, iprops_ctx->pool); 181251881Speter } 182251881Speter 183251881Speter svn_hash_sets(iprops_ctx->curr_iprop->prop_hash, 184251881Speter apr_pstrdup(iprops_ctx->pool, 185251881Speter iprops_ctx->curr_propname->data), 186269847Speter val_str); 187269847Speter /* Clear current propname. */ 188251881Speter svn_stringbuf_setempty(iprops_ctx->curr_propname); 189251881Speter } 190269847Speter else 191269847Speter SVN_ERR_MALFUNCTION(); /* Invalid transition table */ 192251881Speter 193251881Speter return SVN_NO_ERROR; 194251881Speter} 195251881Speter 196251881Speterstatic svn_error_t * 197251881Spetercreate_iprops_body(serf_bucket_t **bkt, 198251881Speter void *baton, 199251881Speter serf_bucket_alloc_t *alloc, 200299742Sdim apr_pool_t *pool /* request pool */, 201299742Sdim apr_pool_t *scratch_pool) 202251881Speter{ 203251881Speter iprops_context_t *iprops_ctx = baton; 204251881Speter serf_bucket_t *body_bkt; 205251881Speter 206251881Speter body_bkt = serf_bucket_aggregate_create(alloc); 207251881Speter 208251881Speter svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, 209251881Speter "S:" SVN_DAV__INHERITED_PROPS_REPORT, 210251881Speter "xmlns:S", SVN_XML_NAMESPACE, 211299742Sdim SVN_VA_NULL); 212251881Speter svn_ra_serf__add_tag_buckets(body_bkt, 213251881Speter "S:" SVN_DAV__REVISION, 214251881Speter apr_ltoa(pool, iprops_ctx->revision), 215251881Speter alloc); 216251881Speter svn_ra_serf__add_tag_buckets(body_bkt, "S:" SVN_DAV__PATH, 217251881Speter iprops_ctx->path, alloc); 218251881Speter svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, 219251881Speter "S:" SVN_DAV__INHERITED_PROPS_REPORT); 220251881Speter *bkt = body_bkt; 221251881Speter return SVN_NO_ERROR; 222251881Speter} 223251881Speter 224299742Sdim/* Per request information for get_iprops_via_more_requests */ 225299742Sdimtypedef struct iprop_rq_info_t 226299742Sdim{ 227299742Sdim const char *relpath; 228299742Sdim const char *urlpath; 229299742Sdim apr_hash_t *props; 230299742Sdim svn_ra_serf__handler_t *handler; 231299742Sdim} iprop_rq_info_t; 232299742Sdim 233299742Sdim 234299742Sdim/* Assumes session reparented to the repository root. The old session 235299742Sdim root is passed as session_url */ 236299742Sdimstatic svn_error_t * 237299742Sdimget_iprops_via_more_requests(svn_ra_session_t *ra_session, 238299742Sdim apr_array_header_t **iprops, 239299742Sdim const char *session_url, 240299742Sdim const char *path, 241299742Sdim svn_revnum_t revision, 242299742Sdim apr_pool_t *result_pool, 243299742Sdim apr_pool_t *scratch_pool) 244299742Sdim{ 245299742Sdim svn_ra_serf__session_t *session = ra_session->priv; 246299742Sdim const char *url; 247299742Sdim const char *relpath; 248299742Sdim apr_array_header_t *rq_info; 249299742Sdim apr_pool_t *iterpool = svn_pool_create(scratch_pool); 250299742Sdim apr_interval_time_t waittime_left = session->timeout; 251299742Sdim const svn_revnum_t rev_marker = SVN_INVALID_REVNUM; 252299742Sdim int i; 253299742Sdim 254299742Sdim rq_info = apr_array_make(scratch_pool, 16, sizeof(iprop_rq_info_t *)); 255299742Sdim 256299742Sdim if (!svn_path_is_empty(path)) 257299742Sdim url = svn_path_url_add_component2(session_url, path, scratch_pool); 258299742Sdim else 259299742Sdim url = session_url; 260299742Sdim 261299742Sdim relpath = svn_uri_skip_ancestor(session->repos_root_str, url, scratch_pool); 262299742Sdim 263299742Sdim /* Create all requests */ 264299742Sdim while (relpath[0] != '\0') 265299742Sdim { 266299742Sdim iprop_rq_info_t *rq = apr_pcalloc(scratch_pool, sizeof(*rq)); 267299742Sdim 268299742Sdim relpath = svn_relpath_dirname(relpath, scratch_pool); 269299742Sdim 270299742Sdim rq->relpath = relpath; 271299742Sdim rq->props = apr_hash_make(scratch_pool); 272299742Sdim 273299742Sdim SVN_ERR(svn_ra_serf__get_stable_url(&rq->urlpath, NULL, session, 274299742Sdim svn_path_url_add_component2( 275299742Sdim session->repos_root.path, 276299742Sdim relpath, scratch_pool), 277299742Sdim revision, 278299742Sdim scratch_pool, scratch_pool)); 279299742Sdim 280299742Sdim SVN_ERR(svn_ra_serf__create_propfind_handler( 281299742Sdim &rq->handler, session, 282299742Sdim rq->urlpath, 283299742Sdim rev_marker, "0", all_props, 284299742Sdim svn_ra_serf__deliver_svn_props, 285299742Sdim rq->props, 286299742Sdim scratch_pool)); 287299742Sdim 288299742Sdim /* Allow ignoring authz problems */ 289299742Sdim rq->handler->no_fail_on_http_failure_status = TRUE; 290299742Sdim 291299742Sdim svn_ra_serf__request_create(rq->handler); 292299742Sdim 293299742Sdim APR_ARRAY_PUSH(rq_info, iprop_rq_info_t *) = rq; 294299742Sdim } 295299742Sdim 296299742Sdim while (TRUE) 297299742Sdim { 298299742Sdim svn_pool_clear(iterpool); 299299742Sdim 300299742Sdim SVN_ERR(svn_ra_serf__context_run(session, &waittime_left, iterpool)); 301299742Sdim 302299742Sdim for (i = 0; i < rq_info->nelts; i++) 303299742Sdim { 304299742Sdim iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *); 305299742Sdim 306299742Sdim if (!rq->handler->done) 307299742Sdim break; 308299742Sdim } 309299742Sdim 310299742Sdim if (i >= rq_info->nelts) 311299742Sdim break; /* All requests done */ 312299742Sdim } 313299742Sdim 314299742Sdim *iprops = apr_array_make(result_pool, rq_info->nelts, 315299742Sdim sizeof(svn_prop_inherited_item_t *)); 316299742Sdim 317299742Sdim /* And now create the result set */ 318299742Sdim for (i = 0; i < rq_info->nelts; i++) 319299742Sdim { 320299742Sdim iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *); 321299742Sdim apr_hash_t *node_props; 322299742Sdim svn_prop_inherited_item_t *new_iprop; 323299742Sdim 324299742Sdim if (rq->handler->sline.code != 207 && rq->handler->sline.code != 403) 325299742Sdim { 326299742Sdim if (rq->handler->server_error) 327299742Sdim SVN_ERR(svn_ra_serf__server_error_create(rq->handler, 328299742Sdim scratch_pool)); 329299742Sdim 330299742Sdim return svn_error_trace(svn_ra_serf__unexpected_status(rq->handler)); 331299742Sdim } 332299742Sdim 333299742Sdim node_props = rq->props; 334299742Sdim 335299742Sdim svn_ra_serf__keep_only_regular_props(node_props, scratch_pool); 336299742Sdim 337299742Sdim if (!apr_hash_count(node_props)) 338299742Sdim continue; 339299742Sdim 340299742Sdim new_iprop = apr_palloc(result_pool, sizeof(*new_iprop)); 341299742Sdim new_iprop->path_or_url = apr_pstrdup(result_pool, rq->relpath); 342299742Sdim new_iprop->prop_hash = svn_prop_hash_dup(node_props, result_pool); 343299742Sdim svn_sort__array_insert(*iprops, &new_iprop, 0); 344299742Sdim } 345299742Sdim 346299742Sdim return SVN_NO_ERROR; 347299742Sdim} 348299742Sdim 349251881Speter/* Request a inherited-props-report from the URL attached to RA_SESSION, 350251881Speter and fill the IPROPS array hash with the results. */ 351251881Spetersvn_error_t * 352251881Spetersvn_ra_serf__get_inherited_props(svn_ra_session_t *ra_session, 353251881Speter apr_array_header_t **iprops, 354251881Speter const char *path, 355251881Speter svn_revnum_t revision, 356251881Speter apr_pool_t *result_pool, 357251881Speter apr_pool_t *scratch_pool) 358251881Speter{ 359251881Speter iprops_context_t *iprops_ctx; 360251881Speter svn_ra_serf__session_t *session = ra_session->priv; 361251881Speter svn_ra_serf__handler_t *handler; 362269847Speter svn_ra_serf__xml_context_t *xmlctx; 363251881Speter const char *req_url; 364299742Sdim svn_boolean_t iprop_capable; 365251881Speter 366299742Sdim SVN_ERR(svn_ra_serf__has_capability(ra_session, &iprop_capable, 367299742Sdim SVN_RA_CAPABILITY_INHERITED_PROPS, 368299742Sdim scratch_pool)); 369299742Sdim 370299742Sdim if (!iprop_capable) 371299742Sdim { 372299742Sdim svn_error_t *err; 373299742Sdim const char *reparent_uri = NULL; 374299742Sdim const char *session_uri; 375299742Sdim const char *repos_root_url; 376299742Sdim 377299742Sdim SVN_ERR(svn_ra_serf__get_repos_root(ra_session, &repos_root_url, 378299742Sdim scratch_pool)); 379299742Sdim 380299742Sdim session_uri = apr_pstrdup(scratch_pool, session->session_url_str); 381299742Sdim if (strcmp(repos_root_url, session->session_url_str) != 0) 382299742Sdim { 383299742Sdim reparent_uri = session_uri; 384299742Sdim SVN_ERR(svn_ra_serf__reparent(ra_session, repos_root_url, 385299742Sdim scratch_pool)); 386299742Sdim } 387299742Sdim 388299742Sdim err = get_iprops_via_more_requests(ra_session, iprops, session_uri, path, 389299742Sdim revision, result_pool, scratch_pool); 390299742Sdim 391299742Sdim if (reparent_uri) 392299742Sdim err = svn_error_compose_create(err, 393299742Sdim svn_ra_serf__reparent(ra_session, 394299742Sdim reparent_uri , 395299742Sdim scratch_pool)); 396299742Sdim 397299742Sdim return svn_error_trace(err); 398299742Sdim } 399299742Sdim 400251881Speter SVN_ERR(svn_ra_serf__get_stable_url(&req_url, 401251881Speter NULL /* latest_revnum */, 402251881Speter session, 403251881Speter NULL /* url */, 404251881Speter revision, 405299742Sdim scratch_pool, scratch_pool)); 406251881Speter 407251881Speter SVN_ERR_ASSERT(session->repos_root_str); 408251881Speter 409251881Speter iprops_ctx = apr_pcalloc(scratch_pool, sizeof(*iprops_ctx)); 410251881Speter iprops_ctx->repos_root_url = session->repos_root_str; 411251881Speter iprops_ctx->pool = result_pool; 412251881Speter iprops_ctx->curr_propname = svn_stringbuf_create_empty(scratch_pool); 413251881Speter iprops_ctx->curr_iprop = NULL; 414251881Speter iprops_ctx->iprops = apr_array_make(result_pool, 1, 415251881Speter sizeof(svn_prop_inherited_item_t *)); 416251881Speter iprops_ctx->path = path; 417251881Speter iprops_ctx->revision = revision; 418251881Speter 419269847Speter xmlctx = svn_ra_serf__xml_context_create(iprops_table, 420299742Sdim iprops_opened, iprops_closed, 421299742Sdim NULL, 422269847Speter iprops_ctx, 423269847Speter scratch_pool); 424299742Sdim handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, 425299742Sdim scratch_pool); 426251881Speter 427251881Speter handler->method = "REPORT"; 428251881Speter handler->path = req_url; 429299742Sdim 430251881Speter handler->body_delegate = create_iprops_body; 431251881Speter handler->body_delegate_baton = iprops_ctx; 432251881Speter handler->body_type = "text/xml"; 433251881Speter 434299742Sdim SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); 435251881Speter 436299742Sdim if (handler->sline.code != 200) 437299742Sdim return svn_error_trace(svn_ra_serf__unexpected_status(handler)); 438299742Sdim 439269847Speter *iprops = iprops_ctx->iprops; 440251881Speter 441251881Speter return SVN_NO_ERROR; 442251881Speter} 443