inherited_props.c revision 299742
1/* 2 * inherited_props.c : ra_serf implementation of svn_ra_get_inherited_props 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25#include <apr_tables.h> 26#include <apr_xml.h> 27 28#include "svn_hash.h" 29#include "svn_path.h" 30#include "svn_ra.h" 31#include "svn_sorts.h" 32#include "svn_string.h" 33#include "svn_xml.h" 34#include "svn_props.h" 35#include "svn_base64.h" 36 37#include "private/svn_dav_protocol.h" 38#include "private/svn_sorts_private.h" 39#include "../libsvn_ra/ra_loader.h" 40#include "svn_private_config.h" 41#include "ra_serf.h" 42 43 44/* The current state of our XML parsing. */ 45typedef enum iprops_state_e { 46 INITIAL = XML_STATE_INITIAL, 47 IPROPS_REPORT, 48 IPROPS_ITEM, 49 IPROPS_PATH, 50 IPROPS_PROPNAME, 51 IPROPS_PROPVAL 52} iprops_state_e; 53 54/* Struct for accumulating inherited props. */ 55typedef struct iprops_context_t { 56 /* The depth-first ordered array of svn_prop_inherited_item_t * 57 structures we are building. */ 58 apr_array_header_t *iprops; 59 60 /* Pool in which to allocate elements of IPROPS. */ 61 apr_pool_t *pool; 62 63 /* The repository's root URL. */ 64 const char *repos_root_url; 65 66 /* Current property name */ 67 svn_stringbuf_t *curr_propname; 68 69 /* Current element in IPROPS. */ 70 svn_prop_inherited_item_t *curr_iprop; 71 72 /* Path we are finding inherited properties for. This is relative to 73 the RA session passed to svn_ra_serf__get_inherited_props. */ 74 const char *path; 75 /* The revision of PATH*/ 76 svn_revnum_t revision; 77} iprops_context_t; 78 79#define S_ SVN_XML_NAMESPACE 80static const svn_ra_serf__xml_transition_t iprops_table[] = { 81 { INITIAL, S_, SVN_DAV__INHERITED_PROPS_REPORT, IPROPS_REPORT, 82 FALSE, { NULL }, FALSE }, 83 84 { IPROPS_REPORT, S_, SVN_DAV__IPROP_ITEM, IPROPS_ITEM, 85 FALSE, { NULL }, TRUE }, 86 87 { IPROPS_ITEM, S_, SVN_DAV__IPROP_PATH, IPROPS_PATH, 88 TRUE, { NULL }, TRUE }, 89 90 { IPROPS_ITEM, S_, SVN_DAV__IPROP_PROPNAME, IPROPS_PROPNAME, 91 TRUE, { NULL }, TRUE }, 92 93 { IPROPS_ITEM, S_, SVN_DAV__IPROP_PROPVAL, IPROPS_PROPVAL, 94 TRUE, { "?V:encoding", NULL }, TRUE }, 95 96 { 0 } 97}; 98 99/* Conforms to svn_ra_serf__xml_opened_t */ 100static svn_error_t * 101iprops_opened(svn_ra_serf__xml_estate_t *xes, 102 void *baton, 103 int entered_state, 104 const svn_ra_serf__dav_props_t *tag, 105 apr_pool_t *scratch_pool) 106{ 107 iprops_context_t *iprops_ctx = baton; 108 109 if (entered_state == IPROPS_ITEM) 110 { 111 svn_stringbuf_setempty(iprops_ctx->curr_propname); 112 113 iprops_ctx->curr_iprop = apr_pcalloc(iprops_ctx->pool, 114 sizeof(*iprops_ctx->curr_iprop)); 115 116 iprops_ctx->curr_iprop->prop_hash = apr_hash_make(iprops_ctx->pool); 117 } 118 return SVN_NO_ERROR; 119} 120 121/* Conforms to svn_ra_serf__xml_closed_t */ 122static svn_error_t * 123iprops_closed(svn_ra_serf__xml_estate_t *xes, 124 void *baton, 125 int leaving_state, 126 const svn_string_t *cdata, 127 apr_hash_t *attrs, 128 apr_pool_t *scratch_pool) 129{ 130 iprops_context_t *iprops_ctx = baton; 131 132 if (leaving_state == IPROPS_ITEM) 133 { 134 APR_ARRAY_PUSH(iprops_ctx->iprops, svn_prop_inherited_item_t *) = 135 iprops_ctx->curr_iprop; 136 137 iprops_ctx->curr_iprop = NULL; 138 } 139 else if (leaving_state == IPROPS_PATH) 140 { 141 /* Every <iprop-item> has a single <iprop-path> */ 142 if (iprops_ctx->curr_iprop->path_or_url) 143 return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); 144 145 iprops_ctx->curr_iprop->path_or_url = 146 apr_pstrdup(iprops_ctx->pool, cdata->data); 147 } 148 else if (leaving_state == IPROPS_PROPNAME) 149 { 150 if (iprops_ctx->curr_propname->len) 151 return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); 152 153 /* Store propname for value */ 154 svn_stringbuf_set(iprops_ctx->curr_propname, cdata->data); 155 } 156 else if (leaving_state == IPROPS_PROPVAL) 157 { 158 const char *encoding; 159 const svn_string_t *val_str; 160 161 if (! iprops_ctx->curr_propname->len) 162 return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, NULL); 163 164 encoding = svn_hash_gets(attrs, "V:encoding"); 165 166 if (encoding) 167 { 168 if (strcmp(encoding, "base64") != 0) 169 return svn_error_createf(SVN_ERR_XML_MALFORMED, 170 NULL, 171 _("Got unrecognized encoding '%s'"), 172 encoding); 173 174 /* Decode into the right pool. */ 175 val_str = svn_base64_decode_string(cdata, iprops_ctx->pool); 176 } 177 else 178 { 179 /* Copy into the right pool. */ 180 val_str = svn_string_dup(cdata, iprops_ctx->pool); 181 } 182 183 svn_hash_sets(iprops_ctx->curr_iprop->prop_hash, 184 apr_pstrdup(iprops_ctx->pool, 185 iprops_ctx->curr_propname->data), 186 val_str); 187 /* Clear current propname. */ 188 svn_stringbuf_setempty(iprops_ctx->curr_propname); 189 } 190 else 191 SVN_ERR_MALFUNCTION(); /* Invalid transition table */ 192 193 return SVN_NO_ERROR; 194} 195 196static svn_error_t * 197create_iprops_body(serf_bucket_t **bkt, 198 void *baton, 199 serf_bucket_alloc_t *alloc, 200 apr_pool_t *pool /* request pool */, 201 apr_pool_t *scratch_pool) 202{ 203 iprops_context_t *iprops_ctx = baton; 204 serf_bucket_t *body_bkt; 205 206 body_bkt = serf_bucket_aggregate_create(alloc); 207 208 svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, 209 "S:" SVN_DAV__INHERITED_PROPS_REPORT, 210 "xmlns:S", SVN_XML_NAMESPACE, 211 SVN_VA_NULL); 212 svn_ra_serf__add_tag_buckets(body_bkt, 213 "S:" SVN_DAV__REVISION, 214 apr_ltoa(pool, iprops_ctx->revision), 215 alloc); 216 svn_ra_serf__add_tag_buckets(body_bkt, "S:" SVN_DAV__PATH, 217 iprops_ctx->path, alloc); 218 svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, 219 "S:" SVN_DAV__INHERITED_PROPS_REPORT); 220 *bkt = body_bkt; 221 return SVN_NO_ERROR; 222} 223 224/* Per request information for get_iprops_via_more_requests */ 225typedef struct iprop_rq_info_t 226{ 227 const char *relpath; 228 const char *urlpath; 229 apr_hash_t *props; 230 svn_ra_serf__handler_t *handler; 231} iprop_rq_info_t; 232 233 234/* Assumes session reparented to the repository root. The old session 235 root is passed as session_url */ 236static svn_error_t * 237get_iprops_via_more_requests(svn_ra_session_t *ra_session, 238 apr_array_header_t **iprops, 239 const char *session_url, 240 const char *path, 241 svn_revnum_t revision, 242 apr_pool_t *result_pool, 243 apr_pool_t *scratch_pool) 244{ 245 svn_ra_serf__session_t *session = ra_session->priv; 246 const char *url; 247 const char *relpath; 248 apr_array_header_t *rq_info; 249 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 250 apr_interval_time_t waittime_left = session->timeout; 251 const svn_revnum_t rev_marker = SVN_INVALID_REVNUM; 252 int i; 253 254 rq_info = apr_array_make(scratch_pool, 16, sizeof(iprop_rq_info_t *)); 255 256 if (!svn_path_is_empty(path)) 257 url = svn_path_url_add_component2(session_url, path, scratch_pool); 258 else 259 url = session_url; 260 261 relpath = svn_uri_skip_ancestor(session->repos_root_str, url, scratch_pool); 262 263 /* Create all requests */ 264 while (relpath[0] != '\0') 265 { 266 iprop_rq_info_t *rq = apr_pcalloc(scratch_pool, sizeof(*rq)); 267 268 relpath = svn_relpath_dirname(relpath, scratch_pool); 269 270 rq->relpath = relpath; 271 rq->props = apr_hash_make(scratch_pool); 272 273 SVN_ERR(svn_ra_serf__get_stable_url(&rq->urlpath, NULL, session, 274 svn_path_url_add_component2( 275 session->repos_root.path, 276 relpath, scratch_pool), 277 revision, 278 scratch_pool, scratch_pool)); 279 280 SVN_ERR(svn_ra_serf__create_propfind_handler( 281 &rq->handler, session, 282 rq->urlpath, 283 rev_marker, "0", all_props, 284 svn_ra_serf__deliver_svn_props, 285 rq->props, 286 scratch_pool)); 287 288 /* Allow ignoring authz problems */ 289 rq->handler->no_fail_on_http_failure_status = TRUE; 290 291 svn_ra_serf__request_create(rq->handler); 292 293 APR_ARRAY_PUSH(rq_info, iprop_rq_info_t *) = rq; 294 } 295 296 while (TRUE) 297 { 298 svn_pool_clear(iterpool); 299 300 SVN_ERR(svn_ra_serf__context_run(session, &waittime_left, iterpool)); 301 302 for (i = 0; i < rq_info->nelts; i++) 303 { 304 iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *); 305 306 if (!rq->handler->done) 307 break; 308 } 309 310 if (i >= rq_info->nelts) 311 break; /* All requests done */ 312 } 313 314 *iprops = apr_array_make(result_pool, rq_info->nelts, 315 sizeof(svn_prop_inherited_item_t *)); 316 317 /* And now create the result set */ 318 for (i = 0; i < rq_info->nelts; i++) 319 { 320 iprop_rq_info_t *rq = APR_ARRAY_IDX(rq_info, i, iprop_rq_info_t *); 321 apr_hash_t *node_props; 322 svn_prop_inherited_item_t *new_iprop; 323 324 if (rq->handler->sline.code != 207 && rq->handler->sline.code != 403) 325 { 326 if (rq->handler->server_error) 327 SVN_ERR(svn_ra_serf__server_error_create(rq->handler, 328 scratch_pool)); 329 330 return svn_error_trace(svn_ra_serf__unexpected_status(rq->handler)); 331 } 332 333 node_props = rq->props; 334 335 svn_ra_serf__keep_only_regular_props(node_props, scratch_pool); 336 337 if (!apr_hash_count(node_props)) 338 continue; 339 340 new_iprop = apr_palloc(result_pool, sizeof(*new_iprop)); 341 new_iprop->path_or_url = apr_pstrdup(result_pool, rq->relpath); 342 new_iprop->prop_hash = svn_prop_hash_dup(node_props, result_pool); 343 svn_sort__array_insert(*iprops, &new_iprop, 0); 344 } 345 346 return SVN_NO_ERROR; 347} 348 349/* Request a inherited-props-report from the URL attached to RA_SESSION, 350 and fill the IPROPS array hash with the results. */ 351svn_error_t * 352svn_ra_serf__get_inherited_props(svn_ra_session_t *ra_session, 353 apr_array_header_t **iprops, 354 const char *path, 355 svn_revnum_t revision, 356 apr_pool_t *result_pool, 357 apr_pool_t *scratch_pool) 358{ 359 iprops_context_t *iprops_ctx; 360 svn_ra_serf__session_t *session = ra_session->priv; 361 svn_ra_serf__handler_t *handler; 362 svn_ra_serf__xml_context_t *xmlctx; 363 const char *req_url; 364 svn_boolean_t iprop_capable; 365 366 SVN_ERR(svn_ra_serf__has_capability(ra_session, &iprop_capable, 367 SVN_RA_CAPABILITY_INHERITED_PROPS, 368 scratch_pool)); 369 370 if (!iprop_capable) 371 { 372 svn_error_t *err; 373 const char *reparent_uri = NULL; 374 const char *session_uri; 375 const char *repos_root_url; 376 377 SVN_ERR(svn_ra_serf__get_repos_root(ra_session, &repos_root_url, 378 scratch_pool)); 379 380 session_uri = apr_pstrdup(scratch_pool, session->session_url_str); 381 if (strcmp(repos_root_url, session->session_url_str) != 0) 382 { 383 reparent_uri = session_uri; 384 SVN_ERR(svn_ra_serf__reparent(ra_session, repos_root_url, 385 scratch_pool)); 386 } 387 388 err = get_iprops_via_more_requests(ra_session, iprops, session_uri, path, 389 revision, result_pool, scratch_pool); 390 391 if (reparent_uri) 392 err = svn_error_compose_create(err, 393 svn_ra_serf__reparent(ra_session, 394 reparent_uri , 395 scratch_pool)); 396 397 return svn_error_trace(err); 398 } 399 400 SVN_ERR(svn_ra_serf__get_stable_url(&req_url, 401 NULL /* latest_revnum */, 402 session, 403 NULL /* url */, 404 revision, 405 scratch_pool, scratch_pool)); 406 407 SVN_ERR_ASSERT(session->repos_root_str); 408 409 iprops_ctx = apr_pcalloc(scratch_pool, sizeof(*iprops_ctx)); 410 iprops_ctx->repos_root_url = session->repos_root_str; 411 iprops_ctx->pool = result_pool; 412 iprops_ctx->curr_propname = svn_stringbuf_create_empty(scratch_pool); 413 iprops_ctx->curr_iprop = NULL; 414 iprops_ctx->iprops = apr_array_make(result_pool, 1, 415 sizeof(svn_prop_inherited_item_t *)); 416 iprops_ctx->path = path; 417 iprops_ctx->revision = revision; 418 419 xmlctx = svn_ra_serf__xml_context_create(iprops_table, 420 iprops_opened, iprops_closed, 421 NULL, 422 iprops_ctx, 423 scratch_pool); 424 handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, 425 scratch_pool); 426 427 handler->method = "REPORT"; 428 handler->path = req_url; 429 430 handler->body_delegate = create_iprops_body; 431 handler->body_delegate_baton = iprops_ctx; 432 handler->body_type = "text/xml"; 433 434 SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); 435 436 if (handler->sline.code != 200) 437 return svn_error_trace(svn_ra_serf__unexpected_status(handler)); 438 439 *iprops = iprops_ctx->iprops; 440 441 return SVN_NO_ERROR; 442} 443