property.c revision 251881
1251881Speter/* 2251881Speter * property.c : property routines 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 <serf.h> 27251881Speter 28251881Speter#include "svn_hash.h" 29251881Speter#include "svn_path.h" 30251881Speter#include "svn_base64.h" 31251881Speter#include "svn_xml.h" 32251881Speter#include "svn_props.h" 33251881Speter#include "svn_dirent_uri.h" 34251881Speter 35251881Speter#include "private/svn_dav_protocol.h" 36251881Speter#include "private/svn_fspath.h" 37251881Speter#include "private/svn_string_private.h" 38251881Speter#include "svn_private_config.h" 39251881Speter 40251881Speter#include "ra_serf.h" 41251881Speter 42251881Speter 43251881Speter/* Our current parsing state we're in for the PROPFIND response. */ 44251881Spetertypedef enum prop_state_e { 45251881Speter INITIAL = 0, 46251881Speter MULTISTATUS, 47251881Speter RESPONSE, 48251881Speter HREF, 49251881Speter PROPSTAT, 50251881Speter STATUS, 51251881Speter PROP, 52251881Speter PROPVAL, 53251881Speter COLLECTION, 54251881Speter HREF_VALUE 55251881Speter} prop_state_e; 56251881Speter 57251881Speter 58251881Speter/* 59251881Speter * This structure represents a pending PROPFIND response. 60251881Speter */ 61251881Spetertypedef struct propfind_context_t { 62251881Speter /* pool to issue allocations from */ 63251881Speter apr_pool_t *pool; 64251881Speter 65251881Speter svn_ra_serf__handler_t *handler; 66251881Speter 67251881Speter /* associated serf session */ 68251881Speter svn_ra_serf__session_t *sess; 69251881Speter svn_ra_serf__connection_t *conn; 70251881Speter 71251881Speter /* the requested path */ 72251881Speter const char *path; 73251881Speter 74251881Speter /* the requested version (number and string form) */ 75251881Speter svn_revnum_t rev; 76251881Speter const char *label; 77251881Speter 78251881Speter /* the request depth */ 79251881Speter const char *depth; 80251881Speter 81251881Speter /* the list of requested properties */ 82251881Speter const svn_ra_serf__dav_props_t *find_props; 83251881Speter 84251881Speter /* hash table that will be updated with the properties 85251881Speter * 86251881Speter * This can be shared between multiple propfind_context_t 87251881Speter * structures 88251881Speter */ 89251881Speter apr_hash_t *ret_props; 90251881Speter 91251881Speter /* hash table containing all the properties associated with the 92251881Speter * "current" <propstat> tag. These will get copied into RET_PROPS 93251881Speter * if the status code similarly associated indicates that they are 94251881Speter * "good"; otherwise, they'll get discarded. 95251881Speter */ 96251881Speter apr_hash_t *ps_props; 97251881Speter 98251881Speter /* If not-NULL, add us to this list when we're done. */ 99251881Speter svn_ra_serf__list_t **done_list; 100251881Speter 101251881Speter svn_ra_serf__list_t done_item; 102251881Speter 103251881Speter} propfind_context_t; 104251881Speter 105251881Speter 106251881Speter#define D_ "DAV:" 107251881Speter#define S_ SVN_XML_NAMESPACE 108251881Speterstatic const svn_ra_serf__xml_transition_t propfind_ttable[] = { 109251881Speter { INITIAL, D_, "multistatus", MULTISTATUS, 110251881Speter FALSE, { NULL }, TRUE }, 111251881Speter 112251881Speter { MULTISTATUS, D_, "response", RESPONSE, 113251881Speter FALSE, { NULL }, FALSE }, 114251881Speter 115251881Speter { RESPONSE, D_, "href", HREF, 116251881Speter TRUE, { NULL }, TRUE }, 117251881Speter 118251881Speter { RESPONSE, D_, "propstat", PROPSTAT, 119251881Speter FALSE, { NULL }, TRUE }, 120251881Speter 121251881Speter { PROPSTAT, D_, "status", STATUS, 122251881Speter TRUE, { NULL }, TRUE }, 123251881Speter 124251881Speter { PROPSTAT, D_, "prop", PROP, 125251881Speter FALSE, { NULL }, FALSE }, 126251881Speter 127251881Speter { PROP, "*", "*", PROPVAL, 128251881Speter TRUE, { "?V:encoding", NULL }, TRUE }, 129251881Speter 130251881Speter { PROPVAL, D_, "collection", COLLECTION, 131251881Speter FALSE, { NULL }, TRUE }, 132251881Speter 133251881Speter { PROPVAL, D_, "href", HREF_VALUE, 134251881Speter TRUE, { NULL }, TRUE }, 135251881Speter 136251881Speter { 0 } 137251881Speter}; 138251881Speter 139251881Speter 140251881Speter/* Return the HTTP status code contained in STATUS_LINE, or 0 if 141251881Speter there's a problem parsing it. */ 142251881Speterstatic int parse_status_code(const char *status_line) 143251881Speter{ 144251881Speter /* STATUS_LINE should be of form: "HTTP/1.1 200 OK" */ 145251881Speter if (status_line[0] == 'H' && 146251881Speter status_line[1] == 'T' && 147251881Speter status_line[2] == 'T' && 148251881Speter status_line[3] == 'P' && 149251881Speter status_line[4] == '/' && 150251881Speter (status_line[5] >= '0' && status_line[5] <= '9') && 151251881Speter status_line[6] == '.' && 152251881Speter (status_line[7] >= '0' && status_line[7] <= '9') && 153251881Speter status_line[8] == ' ') 154251881Speter { 155251881Speter char *reason; 156251881Speter 157251881Speter return apr_strtoi64(status_line + 8, &reason, 10); 158251881Speter } 159251881Speter return 0; 160251881Speter} 161251881Speter 162251881Speter 163251881Speter/* Conforms to svn_ra_serf__path_rev_walker_t */ 164251881Speterstatic svn_error_t * 165251881Spetercopy_into_ret_props(void *baton, 166251881Speter const char *path, apr_ssize_t path_len, 167251881Speter const char *ns, apr_ssize_t ns_len, 168251881Speter const char *name, apr_ssize_t name_len, 169251881Speter const svn_string_t *val, 170251881Speter apr_pool_t *pool) 171251881Speter{ 172251881Speter propfind_context_t *ctx = baton; 173251881Speter 174251881Speter svn_ra_serf__set_ver_prop(ctx->ret_props, path, ctx->rev, ns, name, 175251881Speter val, ctx->pool); 176251881Speter return SVN_NO_ERROR; 177251881Speter} 178251881Speter 179251881Speter 180251881Speter/* Conforms to svn_ra_serf__xml_opened_t */ 181251881Speterstatic svn_error_t * 182251881Speterpropfind_opened(svn_ra_serf__xml_estate_t *xes, 183251881Speter void *baton, 184251881Speter int entered_state, 185251881Speter const svn_ra_serf__dav_props_t *tag, 186251881Speter apr_pool_t *scratch_pool) 187251881Speter{ 188251881Speter propfind_context_t *ctx = baton; 189251881Speter 190251881Speter if (entered_state == PROPVAL) 191251881Speter { 192251881Speter svn_ra_serf__xml_note(xes, PROPVAL, "ns", tag->namespace); 193251881Speter svn_ra_serf__xml_note(xes, PROPVAL, "name", tag->name); 194251881Speter } 195251881Speter else if (entered_state == PROPSTAT) 196251881Speter { 197251881Speter ctx->ps_props = apr_hash_make(ctx->pool); 198251881Speter } 199251881Speter 200251881Speter return SVN_NO_ERROR; 201251881Speter} 202251881Speter 203251881Speter 204251881Speter/* Conforms to svn_ra_serf__xml_closed_t */ 205251881Speterstatic svn_error_t * 206251881Speterpropfind_closed(svn_ra_serf__xml_estate_t *xes, 207251881Speter void *baton, 208251881Speter int leaving_state, 209251881Speter const svn_string_t *cdata, 210251881Speter apr_hash_t *attrs, 211251881Speter apr_pool_t *scratch_pool) 212251881Speter{ 213251881Speter propfind_context_t *ctx = baton; 214251881Speter 215251881Speter if (leaving_state == MULTISTATUS) 216251881Speter { 217251881Speter /* We've gathered all the data from the reponse. Add this item 218251881Speter onto the "done list". External callers will then know this 219251881Speter request has been completed (tho stray response bytes may still 220251881Speter arrive). */ 221251881Speter if (ctx->done_list) 222251881Speter { 223251881Speter ctx->done_item.data = ctx->handler; 224251881Speter ctx->done_item.next = *ctx->done_list; 225251881Speter *ctx->done_list = &ctx->done_item; 226251881Speter } 227251881Speter } 228251881Speter else if (leaving_state == HREF) 229251881Speter { 230251881Speter const char *path; 231251881Speter const svn_string_t *val_str; 232251881Speter 233251881Speter if (strcmp(ctx->depth, "1") == 0) 234251881Speter path = svn_urlpath__canonicalize(cdata->data, scratch_pool); 235251881Speter else 236251881Speter path = ctx->path; 237251881Speter 238251881Speter svn_ra_serf__xml_note(xes, RESPONSE, "path", path); 239251881Speter 240251881Speter /* Copy the value into the right pool, then save the HREF. */ 241251881Speter val_str = svn_string_dup(cdata, ctx->pool); 242251881Speter svn_ra_serf__set_ver_prop(ctx->ret_props, 243251881Speter path, ctx->rev, D_, "href", val_str, 244251881Speter ctx->pool); 245251881Speter } 246251881Speter else if (leaving_state == COLLECTION) 247251881Speter { 248251881Speter svn_ra_serf__xml_note(xes, PROPVAL, "altvalue", "collection"); 249251881Speter } 250251881Speter else if (leaving_state == HREF_VALUE) 251251881Speter { 252251881Speter svn_ra_serf__xml_note(xes, PROPVAL, "altvalue", cdata->data); 253251881Speter } 254251881Speter else if (leaving_state == STATUS) 255251881Speter { 256251881Speter /* Parse the status field, and remember if this is a property 257251881Speter that we wish to ignore. (Typically, if it's not a 200, the 258251881Speter status will be 404 to indicate that a property we 259251881Speter specifically requested from the server doesn't exist.) */ 260251881Speter int status = parse_status_code(cdata->data); 261251881Speter if (status != 200) 262251881Speter svn_ra_serf__xml_note(xes, PROPSTAT, "ignore-prop", "*"); 263251881Speter } 264251881Speter else if (leaving_state == PROPVAL) 265251881Speter { 266251881Speter const char *encoding = svn_hash_gets(attrs, "V:encoding"); 267251881Speter const svn_string_t *val_str; 268251881Speter apr_hash_t *gathered; 269251881Speter const char *path; 270251881Speter const char *ns; 271251881Speter const char *name; 272251881Speter const char *altvalue; 273251881Speter 274251881Speter if (encoding) 275251881Speter { 276251881Speter if (strcmp(encoding, "base64") != 0) 277251881Speter return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, 278251881Speter NULL, 279251881Speter _("Got unrecognized encoding '%s'"), 280251881Speter encoding); 281251881Speter 282251881Speter /* Decode into the right pool. */ 283251881Speter val_str = svn_base64_decode_string(cdata, ctx->pool); 284251881Speter } 285251881Speter else 286251881Speter { 287251881Speter /* Copy into the right pool. */ 288251881Speter val_str = svn_string_dup(cdata, ctx->pool); 289251881Speter } 290251881Speter 291251881Speter /* The current path sits on the RESPONSE state. Gather up all the 292251881Speter state from this PROPVAL to the (grandparent) RESPONSE state, 293251881Speter and grab the path from there. 294251881Speter 295251881Speter Now, it would be nice if we could, at this point, know that 296251881Speter the status code for this property indicated a problem -- then 297251881Speter we could simply bail out here and ignore the property. 298251881Speter Sadly, though, we might get the status code *after* we get 299251881Speter the property value. So we'll carry on with our processing 300251881Speter here, setting the property and value as expected. Once we 301251881Speter know for sure the status code associate with the property, 302251881Speter we'll decide its fate. */ 303251881Speter gathered = svn_ra_serf__xml_gather_since(xes, RESPONSE); 304251881Speter 305251881Speter /* These will be dup'd into CTX->POOL, as necessary. */ 306251881Speter path = svn_hash_gets(gathered, "path"); 307251881Speter if (path == NULL) 308251881Speter path = ctx->path; 309251881Speter 310251881Speter ns = svn_hash_gets(attrs, "ns"); 311251881Speter name = apr_pstrdup(ctx->pool, 312251881Speter svn_hash_gets(attrs, "name")); 313251881Speter 314251881Speter altvalue = svn_hash_gets(attrs, "altvalue"); 315251881Speter if (altvalue != NULL) 316251881Speter val_str = svn_string_create(altvalue, ctx->pool); 317251881Speter 318251881Speter svn_ra_serf__set_ver_prop(ctx->ps_props, 319251881Speter path, ctx->rev, ns, name, val_str, 320251881Speter ctx->pool); 321251881Speter } 322251881Speter else 323251881Speter { 324251881Speter apr_hash_t *gathered; 325251881Speter 326251881Speter SVN_ERR_ASSERT(leaving_state == PROPSTAT); 327251881Speter 328251881Speter gathered = svn_ra_serf__xml_gather_since(xes, PROPSTAT); 329251881Speter 330251881Speter /* If we've squirreled away a note that says we want to ignore 331251881Speter these properties, we'll do so. Otherwise, we need to copy 332251881Speter them from the temporary hash into the ctx->ret_props hash. */ 333251881Speter if (! svn_hash_gets(gathered, "ignore-prop")) 334251881Speter { 335251881Speter SVN_ERR(svn_ra_serf__walk_all_paths(ctx->ps_props, ctx->rev, 336251881Speter copy_into_ret_props, ctx, 337251881Speter scratch_pool)); 338251881Speter } 339251881Speter 340251881Speter ctx->ps_props = NULL; 341251881Speter } 342251881Speter 343251881Speter return SVN_NO_ERROR; 344251881Speter} 345251881Speter 346251881Speter 347251881Speterconst svn_string_t * 348251881Spetersvn_ra_serf__get_ver_prop_string(apr_hash_t *props, 349251881Speter const char *path, 350251881Speter svn_revnum_t rev, 351251881Speter const char *ns, 352251881Speter const char *name) 353251881Speter{ 354251881Speter apr_hash_t *ver_props, *path_props, *ns_props; 355251881Speter void *val = NULL; 356251881Speter 357251881Speter ver_props = apr_hash_get(props, &rev, sizeof(rev)); 358251881Speter if (ver_props) 359251881Speter { 360251881Speter path_props = svn_hash_gets(ver_props, path); 361251881Speter 362251881Speter if (path_props) 363251881Speter { 364251881Speter ns_props = svn_hash_gets(path_props, ns); 365251881Speter if (ns_props) 366251881Speter { 367251881Speter val = svn_hash_gets(ns_props, name); 368251881Speter } 369251881Speter } 370251881Speter } 371251881Speter 372251881Speter return val; 373251881Speter} 374251881Speter 375251881Speterconst char * 376251881Spetersvn_ra_serf__get_ver_prop(apr_hash_t *props, 377251881Speter const char *path, 378251881Speter svn_revnum_t rev, 379251881Speter const char *ns, 380251881Speter const char *name) 381251881Speter{ 382251881Speter const svn_string_t *val; 383251881Speter 384251881Speter val = svn_ra_serf__get_ver_prop_string(props, path, rev, ns, name); 385251881Speter 386251881Speter if (val) 387251881Speter { 388251881Speter return val->data; 389251881Speter } 390251881Speter 391251881Speter return NULL; 392251881Speter} 393251881Speter 394251881Speterconst svn_string_t * 395251881Spetersvn_ra_serf__get_prop_string(apr_hash_t *props, 396251881Speter const char *path, 397251881Speter const char *ns, 398251881Speter const char *name) 399251881Speter{ 400251881Speter return svn_ra_serf__get_ver_prop_string(props, path, SVN_INVALID_REVNUM, 401251881Speter ns, name); 402251881Speter} 403251881Speter 404251881Speterconst char * 405251881Spetersvn_ra_serf__get_prop(apr_hash_t *props, 406251881Speter const char *path, 407251881Speter const char *ns, 408251881Speter const char *name) 409251881Speter{ 410251881Speter return svn_ra_serf__get_ver_prop(props, path, SVN_INVALID_REVNUM, ns, name); 411251881Speter} 412251881Speter 413251881Spetervoid 414251881Spetersvn_ra_serf__set_ver_prop(apr_hash_t *props, 415251881Speter const char *path, svn_revnum_t rev, 416251881Speter const char *ns, const char *name, 417251881Speter const svn_string_t *val, apr_pool_t *pool) 418251881Speter{ 419251881Speter apr_hash_t *ver_props, *path_props, *ns_props; 420251881Speter 421251881Speter ver_props = apr_hash_get(props, &rev, sizeof(rev)); 422251881Speter if (!ver_props) 423251881Speter { 424251881Speter ver_props = apr_hash_make(pool); 425251881Speter apr_hash_set(props, apr_pmemdup(pool, &rev, sizeof(rev)), sizeof(rev), 426251881Speter ver_props); 427251881Speter } 428251881Speter 429251881Speter path_props = svn_hash_gets(ver_props, path); 430251881Speter 431251881Speter if (!path_props) 432251881Speter { 433251881Speter path_props = apr_hash_make(pool); 434251881Speter path = apr_pstrdup(pool, path); 435251881Speter svn_hash_sets(ver_props, path, path_props); 436251881Speter 437251881Speter /* todo: we know that we'll fail the next check, but fall through 438251881Speter * for now for simplicity's sake. 439251881Speter */ 440251881Speter } 441251881Speter 442251881Speter ns_props = svn_hash_gets(path_props, ns); 443251881Speter if (!ns_props) 444251881Speter { 445251881Speter ns_props = apr_hash_make(pool); 446251881Speter ns = apr_pstrdup(pool, ns); 447251881Speter svn_hash_sets(path_props, ns, ns_props); 448251881Speter } 449251881Speter 450251881Speter svn_hash_sets(ns_props, name, val); 451251881Speter} 452251881Speter 453251881Spetervoid 454251881Spetersvn_ra_serf__set_prop(apr_hash_t *props, 455251881Speter const char *path, 456251881Speter const char *ns, const char *name, 457251881Speter const svn_string_t *val, apr_pool_t *pool) 458251881Speter{ 459251881Speter svn_ra_serf__set_ver_prop(props, path, SVN_INVALID_REVNUM, ns, name, 460251881Speter val, pool); 461251881Speter} 462251881Speter 463251881Speter 464251881Speterstatic svn_error_t * 465251881Spetersetup_propfind_headers(serf_bucket_t *headers, 466251881Speter void *setup_baton, 467251881Speter apr_pool_t *pool) 468251881Speter{ 469251881Speter propfind_context_t *ctx = setup_baton; 470251881Speter 471251881Speter serf_bucket_headers_setn(headers, "Depth", ctx->depth); 472251881Speter if (ctx->label) 473251881Speter { 474251881Speter serf_bucket_headers_setn(headers, "Label", ctx->label); 475251881Speter } 476251881Speter 477251881Speter return SVN_NO_ERROR; 478251881Speter} 479251881Speter 480251881Speter#define PROPFIND_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind xmlns=\"DAV:\">" 481251881Speter#define PROPFIND_TRAILER "</propfind>" 482251881Speter 483251881Speterstatic svn_error_t * 484251881Spetercreate_propfind_body(serf_bucket_t **bkt, 485251881Speter void *setup_baton, 486251881Speter serf_bucket_alloc_t *alloc, 487251881Speter apr_pool_t *pool) 488251881Speter{ 489251881Speter propfind_context_t *ctx = setup_baton; 490251881Speter 491251881Speter serf_bucket_t *body_bkt, *tmp; 492251881Speter const svn_ra_serf__dav_props_t *prop; 493251881Speter svn_boolean_t requested_allprop = FALSE; 494251881Speter 495251881Speter body_bkt = serf_bucket_aggregate_create(alloc); 496251881Speter 497251881Speter prop = ctx->find_props; 498251881Speter while (prop && prop->namespace) 499251881Speter { 500251881Speter /* special case the allprop case. */ 501251881Speter if (strcmp(prop->name, "allprop") == 0) 502251881Speter { 503251881Speter requested_allprop = TRUE; 504251881Speter } 505251881Speter 506251881Speter /* <*propname* xmlns="*propns*" /> */ 507251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, alloc); 508251881Speter serf_bucket_aggregate_append(body_bkt, tmp); 509251881Speter 510251881Speter tmp = SERF_BUCKET_SIMPLE_STRING(prop->name, alloc); 511251881Speter serf_bucket_aggregate_append(body_bkt, tmp); 512251881Speter 513251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" xmlns=\"", 514251881Speter sizeof(" xmlns=\"")-1, 515251881Speter alloc); 516251881Speter serf_bucket_aggregate_append(body_bkt, tmp); 517251881Speter 518251881Speter tmp = SERF_BUCKET_SIMPLE_STRING(prop->namespace, alloc); 519251881Speter serf_bucket_aggregate_append(body_bkt, tmp); 520251881Speter 521251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"/>", sizeof("\"/>")-1, 522251881Speter alloc); 523251881Speter serf_bucket_aggregate_append(body_bkt, tmp); 524251881Speter 525251881Speter prop++; 526251881Speter } 527251881Speter 528251881Speter /* If we're not doing an allprop, add <prop> tags. */ 529251881Speter if (!requested_allprop) 530251881Speter { 531251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<prop>", 532251881Speter sizeof("<prop>")-1, 533251881Speter alloc); 534251881Speter serf_bucket_aggregate_prepend(body_bkt, tmp); 535251881Speter } 536251881Speter 537251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN(PROPFIND_HEADER, 538251881Speter sizeof(PROPFIND_HEADER)-1, 539251881Speter alloc); 540251881Speter 541251881Speter serf_bucket_aggregate_prepend(body_bkt, tmp); 542251881Speter 543251881Speter if (!requested_allprop) 544251881Speter { 545251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN("</prop>", 546251881Speter sizeof("</prop>")-1, 547251881Speter alloc); 548251881Speter serf_bucket_aggregate_append(body_bkt, tmp); 549251881Speter } 550251881Speter 551251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN(PROPFIND_TRAILER, 552251881Speter sizeof(PROPFIND_TRAILER)-1, 553251881Speter alloc); 554251881Speter serf_bucket_aggregate_append(body_bkt, tmp); 555251881Speter 556251881Speter *bkt = body_bkt; 557251881Speter return SVN_NO_ERROR; 558251881Speter} 559251881Speter 560251881Speter 561251881Spetersvn_error_t * 562251881Spetersvn_ra_serf__deliver_props(svn_ra_serf__handler_t **propfind_handler, 563251881Speter apr_hash_t *ret_props, 564251881Speter svn_ra_serf__session_t *sess, 565251881Speter svn_ra_serf__connection_t *conn, 566251881Speter const char *path, 567251881Speter svn_revnum_t rev, 568251881Speter const char *depth, 569251881Speter const svn_ra_serf__dav_props_t *find_props, 570251881Speter svn_ra_serf__list_t **done_list, 571251881Speter apr_pool_t *pool) 572251881Speter{ 573251881Speter propfind_context_t *new_prop_ctx; 574251881Speter svn_ra_serf__handler_t *handler; 575251881Speter svn_ra_serf__xml_context_t *xmlctx; 576251881Speter 577251881Speter new_prop_ctx = apr_pcalloc(pool, sizeof(*new_prop_ctx)); 578251881Speter 579251881Speter new_prop_ctx->pool = apr_hash_pool_get(ret_props); 580251881Speter new_prop_ctx->path = path; 581251881Speter new_prop_ctx->find_props = find_props; 582251881Speter new_prop_ctx->ret_props = ret_props; 583251881Speter new_prop_ctx->depth = depth; 584251881Speter new_prop_ctx->sess = sess; 585251881Speter new_prop_ctx->conn = conn; 586251881Speter new_prop_ctx->rev = rev; 587251881Speter new_prop_ctx->done_list = done_list; 588251881Speter 589251881Speter if (SVN_IS_VALID_REVNUM(rev)) 590251881Speter { 591251881Speter new_prop_ctx->label = apr_ltoa(pool, rev); 592251881Speter } 593251881Speter else 594251881Speter { 595251881Speter new_prop_ctx->label = NULL; 596251881Speter } 597251881Speter 598251881Speter xmlctx = svn_ra_serf__xml_context_create(propfind_ttable, 599251881Speter propfind_opened, 600251881Speter propfind_closed, 601251881Speter NULL, 602251881Speter new_prop_ctx, 603251881Speter pool); 604251881Speter handler = svn_ra_serf__create_expat_handler(xmlctx, pool); 605251881Speter 606251881Speter handler->method = "PROPFIND"; 607251881Speter handler->path = path; 608251881Speter handler->body_delegate = create_propfind_body; 609251881Speter handler->body_type = "text/xml"; 610251881Speter handler->body_delegate_baton = new_prop_ctx; 611251881Speter handler->header_delegate = setup_propfind_headers; 612251881Speter handler->header_delegate_baton = new_prop_ctx; 613251881Speter 614251881Speter handler->session = new_prop_ctx->sess; 615251881Speter handler->conn = new_prop_ctx->conn; 616251881Speter 617251881Speter new_prop_ctx->handler = handler; 618251881Speter 619251881Speter *propfind_handler = handler; 620251881Speter 621251881Speter return SVN_NO_ERROR; 622251881Speter} 623251881Speter 624251881Speter 625251881Speter/* 626251881Speter * This helper function will block until the PROP_CTX indicates that is done 627251881Speter * or another error is returned. 628251881Speter */ 629251881Spetersvn_error_t * 630251881Spetersvn_ra_serf__wait_for_props(svn_ra_serf__handler_t *handler, 631251881Speter apr_pool_t *scratch_pool) 632251881Speter{ 633251881Speter svn_error_t *err; 634251881Speter svn_error_t *err2; 635251881Speter 636251881Speter err = svn_ra_serf__context_run_one(handler, scratch_pool); 637251881Speter 638251881Speter err2 = svn_ra_serf__error_on_status(handler->sline.code, 639251881Speter handler->path, 640251881Speter handler->location); 641251881Speter 642251881Speter return svn_error_compose_create(err2, err); 643251881Speter} 644251881Speter 645251881Speter/* 646251881Speter * This is a blocking version of deliver_props. 647251881Speter */ 648251881Spetersvn_error_t * 649251881Spetersvn_ra_serf__retrieve_props(apr_hash_t **results, 650251881Speter svn_ra_serf__session_t *sess, 651251881Speter svn_ra_serf__connection_t *conn, 652251881Speter const char *url, 653251881Speter svn_revnum_t rev, 654251881Speter const char *depth, 655251881Speter const svn_ra_serf__dav_props_t *props, 656251881Speter apr_pool_t *result_pool, 657251881Speter apr_pool_t *scratch_pool) 658251881Speter{ 659251881Speter svn_ra_serf__handler_t *handler; 660251881Speter 661251881Speter *results = apr_hash_make(result_pool); 662251881Speter 663251881Speter SVN_ERR(svn_ra_serf__deliver_props(&handler, *results, sess, conn, url, 664251881Speter rev, depth, props, NULL, result_pool)); 665251881Speter SVN_ERR(svn_ra_serf__wait_for_props(handler, scratch_pool)); 666251881Speter 667251881Speter return SVN_NO_ERROR; 668251881Speter} 669251881Speter 670251881Speter 671251881Spetersvn_error_t * 672251881Spetersvn_ra_serf__fetch_node_props(apr_hash_t **results, 673251881Speter svn_ra_serf__connection_t *conn, 674251881Speter const char *url, 675251881Speter svn_revnum_t revision, 676251881Speter const svn_ra_serf__dav_props_t *which_props, 677251881Speter apr_pool_t *result_pool, 678251881Speter apr_pool_t *scratch_pool) 679251881Speter{ 680251881Speter apr_hash_t *multiprops; 681251881Speter apr_hash_t *ver_props; 682251881Speter 683251881Speter /* Note: a couple extra hash tables and whatnot get into RESULT_POOL. 684251881Speter Not a big deal at this point. Theoretically, we could fetch all 685251881Speter props into SCRATCH_POOL, then copy just the REVISION/URL props 686251881Speter into RESULT_POOL. Too much work for too little gain... */ 687251881Speter SVN_ERR(svn_ra_serf__retrieve_props(&multiprops, conn->session, conn, 688251881Speter url, revision, "0", which_props, 689251881Speter result_pool, scratch_pool)); 690251881Speter 691251881Speter ver_props = apr_hash_get(multiprops, &revision, sizeof(revision)); 692251881Speter if (ver_props != NULL) 693251881Speter { 694251881Speter *results = svn_hash_gets(ver_props, url); 695251881Speter if (*results != NULL) 696251881Speter return SVN_NO_ERROR; 697251881Speter } 698251881Speter 699251881Speter return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, 700251881Speter _("The PROPFIND response did not include " 701251881Speter "the requested properties")); 702251881Speter} 703251881Speter 704251881Speter 705251881Spetersvn_error_t * 706251881Spetersvn_ra_serf__walk_node_props(apr_hash_t *props, 707251881Speter svn_ra_serf__walker_visitor_t walker, 708251881Speter void *baton, 709251881Speter apr_pool_t *scratch_pool) 710251881Speter{ 711251881Speter apr_pool_t *iterpool; 712251881Speter apr_hash_index_t *ns_hi; 713251881Speter 714251881Speter iterpool = svn_pool_create(scratch_pool); 715251881Speter for (ns_hi = apr_hash_first(scratch_pool, props); ns_hi; 716251881Speter ns_hi = apr_hash_next(ns_hi)) 717251881Speter { 718251881Speter void *ns_val; 719251881Speter const void *ns_name; 720251881Speter apr_hash_index_t *name_hi; 721251881Speter 722251881Speter /* NOTE: We do not clear ITERPOOL in this loop. Generally, there are 723251881Speter very few namespaces, so this loop will not have many iterations. 724251881Speter Instead, ITERPOOL is used for the inner loop. */ 725251881Speter 726251881Speter apr_hash_this(ns_hi, &ns_name, NULL, &ns_val); 727251881Speter 728251881Speter for (name_hi = apr_hash_first(scratch_pool, ns_val); name_hi; 729251881Speter name_hi = apr_hash_next(name_hi)) 730251881Speter { 731251881Speter void *prop_val; 732251881Speter const void *prop_name; 733251881Speter 734251881Speter /* See note above, regarding clearing of this pool. */ 735251881Speter svn_pool_clear(iterpool); 736251881Speter 737251881Speter apr_hash_this(name_hi, &prop_name, NULL, &prop_val); 738251881Speter 739251881Speter SVN_ERR(walker(baton, ns_name, prop_name, prop_val, iterpool)); 740251881Speter } 741251881Speter } 742251881Speter svn_pool_destroy(iterpool); 743251881Speter 744251881Speter return SVN_NO_ERROR; 745251881Speter} 746251881Speter 747251881Speter 748251881Spetersvn_error_t * 749251881Spetersvn_ra_serf__walk_all_props(apr_hash_t *props, 750251881Speter const char *name, 751251881Speter svn_revnum_t rev, 752251881Speter svn_ra_serf__walker_visitor_t walker, 753251881Speter void *baton, 754251881Speter apr_pool_t *scratch_pool) 755251881Speter{ 756251881Speter apr_hash_t *ver_props; 757251881Speter apr_hash_t *path_props; 758251881Speter 759251881Speter ver_props = apr_hash_get(props, &rev, sizeof(rev)); 760251881Speter if (!ver_props) 761251881Speter return SVN_NO_ERROR; 762251881Speter 763251881Speter path_props = svn_hash_gets(ver_props, name); 764251881Speter if (!path_props) 765251881Speter return SVN_NO_ERROR; 766251881Speter 767251881Speter return svn_error_trace(svn_ra_serf__walk_node_props(path_props, 768251881Speter walker, baton, 769251881Speter scratch_pool)); 770251881Speter} 771251881Speter 772251881Speter 773251881Spetersvn_error_t * 774251881Spetersvn_ra_serf__walk_all_paths(apr_hash_t *props, 775251881Speter svn_revnum_t rev, 776251881Speter svn_ra_serf__path_rev_walker_t walker, 777251881Speter void *baton, 778251881Speter apr_pool_t *pool) 779251881Speter{ 780251881Speter apr_hash_index_t *path_hi; 781251881Speter apr_hash_t *ver_props; 782251881Speter 783251881Speter ver_props = apr_hash_get(props, &rev, sizeof(rev)); 784251881Speter 785251881Speter if (!ver_props) 786251881Speter { 787251881Speter return SVN_NO_ERROR; 788251881Speter } 789251881Speter 790251881Speter for (path_hi = apr_hash_first(pool, ver_props); path_hi; 791251881Speter path_hi = apr_hash_next(path_hi)) 792251881Speter { 793251881Speter void *path_props; 794251881Speter const void *path_name; 795251881Speter apr_ssize_t path_len; 796251881Speter apr_hash_index_t *ns_hi; 797251881Speter 798251881Speter apr_hash_this(path_hi, &path_name, &path_len, &path_props); 799251881Speter for (ns_hi = apr_hash_first(pool, path_props); ns_hi; 800251881Speter ns_hi = apr_hash_next(ns_hi)) 801251881Speter { 802251881Speter void *ns_val; 803251881Speter const void *ns_name; 804251881Speter apr_ssize_t ns_len; 805251881Speter apr_hash_index_t *name_hi; 806251881Speter apr_hash_this(ns_hi, &ns_name, &ns_len, &ns_val); 807251881Speter for (name_hi = apr_hash_first(pool, ns_val); name_hi; 808251881Speter name_hi = apr_hash_next(name_hi)) 809251881Speter { 810251881Speter void *prop_val; 811251881Speter const void *prop_name; 812251881Speter apr_ssize_t prop_len; 813251881Speter 814251881Speter apr_hash_this(name_hi, &prop_name, &prop_len, &prop_val); 815251881Speter /* use a subpool? */ 816251881Speter SVN_ERR(walker(baton, path_name, path_len, ns_name, ns_len, 817251881Speter prop_name, prop_len, prop_val, pool)); 818251881Speter } 819251881Speter } 820251881Speter } 821251881Speter 822251881Speter return SVN_NO_ERROR; 823251881Speter} 824251881Speter 825251881Speter 826251881Speterconst char * 827251881Spetersvn_ra_serf__svnname_from_wirename(const char *ns, 828251881Speter const char *name, 829251881Speter apr_pool_t *result_pool) 830251881Speter{ 831251881Speter if (*ns == '\0' || strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0) 832251881Speter return apr_pstrdup(result_pool, name); 833251881Speter 834251881Speter if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) 835251881Speter return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL); 836251881Speter 837251881Speter if (strcmp(ns, SVN_PROP_PREFIX) == 0) 838251881Speter return apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL); 839251881Speter 840251881Speter if (strcmp(name, SVN_DAV__VERSION_NAME) == 0) 841251881Speter return SVN_PROP_ENTRY_COMMITTED_REV; 842251881Speter 843251881Speter if (strcmp(name, SVN_DAV__CREATIONDATE) == 0) 844251881Speter return SVN_PROP_ENTRY_COMMITTED_DATE; 845251881Speter 846251881Speter if (strcmp(name, "creator-displayname") == 0) 847251881Speter return SVN_PROP_ENTRY_LAST_AUTHOR; 848251881Speter 849251881Speter if (strcmp(name, "repository-uuid") == 0) 850251881Speter return SVN_PROP_ENTRY_UUID; 851251881Speter 852251881Speter if (strcmp(name, "lock-token") == 0) 853251881Speter return SVN_PROP_ENTRY_LOCK_TOKEN; 854251881Speter 855251881Speter if (strcmp(name, "checked-in") == 0) 856251881Speter return SVN_RA_SERF__WC_CHECKED_IN_URL; 857251881Speter 858251881Speter if (strcmp(ns, "DAV:") == 0 || strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0) 859251881Speter { 860251881Speter /* Here DAV: properties not yet converted to svn: properties should be 861251881Speter ignored. */ 862251881Speter return NULL; 863251881Speter } 864251881Speter 865251881Speter /* An unknown namespace, must be a custom property. */ 866251881Speter return apr_pstrcat(result_pool, ns, name, (char *)NULL); 867251881Speter} 868251881Speter 869251881Speter 870251881Speter/* Conforms to svn_ra_serf__walker_visitor_t */ 871251881Speterstatic svn_error_t * 872251881Speterset_flat_props(void *baton, 873251881Speter const char *ns, 874251881Speter const char *name, 875251881Speter const svn_string_t *value, 876251881Speter apr_pool_t *pool) 877251881Speter{ 878251881Speter apr_hash_t *props = baton; 879251881Speter apr_pool_t *result_pool = apr_hash_pool_get(props); 880251881Speter const char *prop_name; 881251881Speter 882251881Speter /* ### is VAL in the proper pool? */ 883251881Speter 884251881Speter prop_name = svn_ra_serf__svnname_from_wirename(ns, name, result_pool); 885251881Speter if (prop_name != NULL) 886251881Speter svn_hash_sets(props, prop_name, value); 887251881Speter 888251881Speter return SVN_NO_ERROR; 889251881Speter} 890251881Speter 891251881Speter 892251881Spetersvn_error_t * 893251881Spetersvn_ra_serf__flatten_props(apr_hash_t **flat_props, 894251881Speter apr_hash_t *props, 895251881Speter apr_pool_t *result_pool, 896251881Speter apr_pool_t *scratch_pool) 897251881Speter{ 898251881Speter *flat_props = apr_hash_make(result_pool); 899251881Speter 900251881Speter return svn_error_trace(svn_ra_serf__walk_node_props( 901251881Speter props, 902251881Speter set_flat_props, 903251881Speter *flat_props /* baton */, 904251881Speter scratch_pool)); 905251881Speter} 906251881Speter 907251881Speter 908251881Speterstatic svn_error_t * 909251881Speterselect_revprops(void *baton, 910251881Speter const char *ns, 911251881Speter const char *name, 912251881Speter const svn_string_t *val, 913251881Speter apr_pool_t *scratch_pool) 914251881Speter{ 915251881Speter apr_hash_t *revprops = baton; 916251881Speter apr_pool_t *result_pool = apr_hash_pool_get(revprops); 917251881Speter const char *prop_name; 918251881Speter 919251881Speter /* ### copy NAME into the RESULT_POOL? */ 920251881Speter /* ### copy VAL into the RESULT_POOL? */ 921251881Speter 922251881Speter if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0) 923251881Speter prop_name = name; 924251881Speter else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) 925251881Speter prop_name = apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL); 926251881Speter else if (strcmp(ns, SVN_PROP_PREFIX) == 0) 927251881Speter prop_name = apr_pstrcat(result_pool, SVN_PROP_PREFIX, name, (char *)NULL); 928251881Speter else if (strcmp(ns, "") == 0) 929251881Speter prop_name = name; 930251881Speter else 931251881Speter { 932251881Speter /* do nothing for now? */ 933251881Speter return SVN_NO_ERROR; 934251881Speter } 935251881Speter 936251881Speter svn_hash_sets(revprops, prop_name, val); 937251881Speter 938251881Speter return SVN_NO_ERROR; 939251881Speter} 940251881Speter 941251881Speter 942251881Spetersvn_error_t * 943251881Spetersvn_ra_serf__select_revprops(apr_hash_t **revprops, 944251881Speter const char *name, 945251881Speter svn_revnum_t rev, 946251881Speter apr_hash_t *all_revprops, 947251881Speter apr_pool_t *result_pool, 948251881Speter apr_pool_t *scratch_pool) 949251881Speter{ 950251881Speter *revprops = apr_hash_make(result_pool); 951251881Speter 952251881Speter return svn_error_trace(svn_ra_serf__walk_all_props( 953251881Speter all_revprops, name, rev, 954251881Speter select_revprops, *revprops, 955251881Speter scratch_pool)); 956251881Speter} 957251881Speter 958251881Speter 959251881Speter/* 960251881Speter * Contact the server (using CONN) to calculate baseline 961251881Speter * information for BASELINE_URL at REVISION (which may be 962251881Speter * SVN_INVALID_REVNUM to query the HEAD revision). 963251881Speter * 964251881Speter * If ACTUAL_REVISION is non-NULL, set *ACTUAL_REVISION to revision 965251881Speter * retrieved from the server as part of this process (which should 966251881Speter * match REVISION when REVISION is valid). Set *BASECOLL_URL_P to the 967251881Speter * baseline collection URL. 968251881Speter */ 969251881Speterstatic svn_error_t * 970251881Speterretrieve_baseline_info(svn_revnum_t *actual_revision, 971251881Speter const char **basecoll_url_p, 972251881Speter svn_ra_serf__connection_t *conn, 973251881Speter const char *baseline_url, 974251881Speter svn_revnum_t revision, 975251881Speter apr_pool_t *result_pool, 976251881Speter apr_pool_t *scratch_pool) 977251881Speter{ 978251881Speter apr_hash_t *props; 979251881Speter apr_hash_t *dav_props; 980251881Speter const char *basecoll_url; 981251881Speter 982251881Speter SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn, 983251881Speter baseline_url, revision, 984251881Speter baseline_props, 985251881Speter scratch_pool, scratch_pool)); 986251881Speter dav_props = apr_hash_get(props, "DAV:", 4); 987251881Speter /* If DAV_PROPS is NULL, then svn_prop_get_value() will return NULL. */ 988251881Speter 989251881Speter basecoll_url = svn_prop_get_value(dav_props, "baseline-collection"); 990251881Speter if (!basecoll_url) 991251881Speter { 992251881Speter return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, 993251881Speter _("The PROPFIND response did not include " 994251881Speter "the requested baseline-collection value")); 995251881Speter } 996251881Speter *basecoll_url_p = svn_urlpath__canonicalize(basecoll_url, result_pool); 997251881Speter 998251881Speter if (actual_revision) 999251881Speter { 1000251881Speter const char *version_name; 1001251881Speter 1002251881Speter version_name = svn_prop_get_value(dav_props, SVN_DAV__VERSION_NAME); 1003251881Speter if (!version_name) 1004251881Speter return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, 1005251881Speter _("The PROPFIND response did not include " 1006251881Speter "the requested version-name value")); 1007251881Speter 1008251881Speter *actual_revision = SVN_STR_TO_REV(version_name); 1009251881Speter } 1010251881Speter 1011251881Speter return SVN_NO_ERROR; 1012251881Speter} 1013251881Speter 1014251881Speter 1015251881Speter/* For HTTPv1 servers, do a PROPFIND dance on the VCC to fetch the youngest 1016251881Speter revnum. If BASECOLL_URL is non-NULL, then the corresponding baseline 1017251881Speter collection URL is also returned. 1018251881Speter 1019251881Speter Do the work over CONN. 1020251881Speter 1021251881Speter *BASECOLL_URL (if requested) will be allocated in RESULT_POOL. All 1022251881Speter temporary allocations will be made in SCRATCH_POOL. */ 1023251881Speterstatic svn_error_t * 1024251881Speterv1_get_youngest_revnum(svn_revnum_t *youngest, 1025251881Speter const char **basecoll_url, 1026251881Speter svn_ra_serf__connection_t *conn, 1027251881Speter const char *vcc_url, 1028251881Speter apr_pool_t *result_pool, 1029251881Speter apr_pool_t *scratch_pool) 1030251881Speter{ 1031251881Speter const char *baseline_url; 1032251881Speter const char *bc_url; 1033251881Speter 1034251881Speter /* Fetching DAV:checked-in from the VCC (with no Label: to specify a 1035251881Speter revision) will return the latest Baseline resource's URL. */ 1036251881Speter SVN_ERR(svn_ra_serf__fetch_dav_prop(&baseline_url, conn, vcc_url, 1037251881Speter SVN_INVALID_REVNUM, 1038251881Speter "checked-in", 1039251881Speter scratch_pool, scratch_pool)); 1040251881Speter if (!baseline_url) 1041251881Speter { 1042251881Speter return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, 1043251881Speter _("The OPTIONS response did not include " 1044251881Speter "the requested checked-in value")); 1045251881Speter } 1046251881Speter baseline_url = svn_urlpath__canonicalize(baseline_url, scratch_pool); 1047251881Speter 1048251881Speter /* From the Baseline resource, we can fetch the DAV:baseline-collection 1049251881Speter and DAV:version-name properties. The latter is the revision number, 1050251881Speter which is formally the name used in Label: headers. */ 1051251881Speter 1052251881Speter /* First check baseline information cache. */ 1053251881Speter SVN_ERR(svn_ra_serf__blncache_get_baseline_info(&bc_url, 1054251881Speter youngest, 1055251881Speter conn->session->blncache, 1056251881Speter baseline_url, 1057251881Speter scratch_pool)); 1058251881Speter if (!bc_url) 1059251881Speter { 1060251881Speter SVN_ERR(retrieve_baseline_info(youngest, &bc_url, conn, 1061251881Speter baseline_url, SVN_INVALID_REVNUM, 1062251881Speter scratch_pool, scratch_pool)); 1063251881Speter SVN_ERR(svn_ra_serf__blncache_set(conn->session->blncache, 1064251881Speter baseline_url, *youngest, 1065251881Speter bc_url, scratch_pool)); 1066251881Speter } 1067251881Speter 1068251881Speter if (basecoll_url != NULL) 1069251881Speter *basecoll_url = apr_pstrdup(result_pool, bc_url); 1070251881Speter 1071251881Speter return SVN_NO_ERROR; 1072251881Speter} 1073251881Speter 1074251881Speter 1075251881Spetersvn_error_t * 1076251881Spetersvn_ra_serf__get_youngest_revnum(svn_revnum_t *youngest, 1077251881Speter svn_ra_serf__session_t *session, 1078251881Speter apr_pool_t *scratch_pool) 1079251881Speter{ 1080251881Speter const char *vcc_url; 1081251881Speter 1082251881Speter if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) 1083251881Speter return svn_error_trace(svn_ra_serf__v2_get_youngest_revnum( 1084251881Speter youngest, session->conns[0], scratch_pool)); 1085251881Speter 1086251881Speter SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, scratch_pool)); 1087251881Speter 1088251881Speter return svn_error_trace(v1_get_youngest_revnum(youngest, NULL, 1089251881Speter session->conns[0], vcc_url, 1090251881Speter scratch_pool, scratch_pool)); 1091251881Speter} 1092251881Speter 1093251881Speter 1094251881Speter/* Set *BC_URL to the baseline collection url for REVISION. If REVISION 1095251881Speter is SVN_INVALID_REVNUM, then the youngest revnum ("HEAD") is used. 1096251881Speter 1097251881Speter *REVNUM_USED will be set to the revision used. 1098251881Speter 1099251881Speter Uses the specified CONN, which is part of SESSION. 1100251881Speter 1101251881Speter All allocations (results and temporary) are performed in POOL. */ 1102251881Speterstatic svn_error_t * 1103251881Speterget_baseline_info(const char **bc_url, 1104251881Speter svn_revnum_t *revnum_used, 1105251881Speter svn_ra_serf__session_t *session, 1106251881Speter svn_ra_serf__connection_t *conn, 1107251881Speter svn_revnum_t revision, 1108251881Speter apr_pool_t *pool) 1109251881Speter{ 1110251881Speter /* If we detected HTTP v2 support on the server, we can construct 1111251881Speter the baseline collection URL ourselves, and fetch the latest 1112251881Speter revision (if needed) with an OPTIONS request. */ 1113251881Speter if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) 1114251881Speter { 1115251881Speter if (SVN_IS_VALID_REVNUM(revision)) 1116251881Speter { 1117251881Speter *revnum_used = revision; 1118251881Speter } 1119251881Speter else 1120251881Speter { 1121251881Speter SVN_ERR(svn_ra_serf__v2_get_youngest_revnum( 1122251881Speter revnum_used, conn, pool)); 1123251881Speter if (! SVN_IS_VALID_REVNUM(*revnum_used)) 1124251881Speter return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, 1125251881Speter _("The OPTIONS response did not include " 1126251881Speter "the youngest revision")); 1127251881Speter } 1128251881Speter 1129251881Speter *bc_url = apr_psprintf(pool, "%s/%ld", 1130251881Speter session->rev_root_stub, *revnum_used); 1131251881Speter } 1132251881Speter 1133251881Speter /* Otherwise, we fall back to the old VCC_URL PROPFIND hunt. */ 1134251881Speter else 1135251881Speter { 1136251881Speter const char *vcc_url; 1137251881Speter 1138251881Speter SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, conn, pool)); 1139251881Speter 1140251881Speter if (SVN_IS_VALID_REVNUM(revision)) 1141251881Speter { 1142251881Speter /* First check baseline information cache. */ 1143251881Speter SVN_ERR(svn_ra_serf__blncache_get_bc_url(bc_url, 1144251881Speter session->blncache, 1145251881Speter revision, pool)); 1146251881Speter if (!*bc_url) 1147251881Speter { 1148251881Speter SVN_ERR(retrieve_baseline_info(NULL, bc_url, conn, 1149251881Speter vcc_url, revision, pool, pool)); 1150251881Speter SVN_ERR(svn_ra_serf__blncache_set(session->blncache, NULL, 1151251881Speter revision, *bc_url, pool)); 1152251881Speter } 1153251881Speter 1154251881Speter *revnum_used = revision; 1155251881Speter } 1156251881Speter else 1157251881Speter { 1158251881Speter SVN_ERR(v1_get_youngest_revnum(revnum_used, bc_url, 1159251881Speter conn, vcc_url, 1160251881Speter pool, pool)); 1161251881Speter } 1162251881Speter } 1163251881Speter 1164251881Speter return SVN_NO_ERROR; 1165251881Speter} 1166251881Speter 1167251881Speter 1168251881Spetersvn_error_t * 1169251881Spetersvn_ra_serf__get_stable_url(const char **stable_url, 1170251881Speter svn_revnum_t *latest_revnum, 1171251881Speter svn_ra_serf__session_t *session, 1172251881Speter svn_ra_serf__connection_t *conn, 1173251881Speter const char *url, 1174251881Speter svn_revnum_t revision, 1175251881Speter apr_pool_t *result_pool, 1176251881Speter apr_pool_t *scratch_pool) 1177251881Speter{ 1178251881Speter const char *basecoll_url; 1179251881Speter const char *repos_relpath; 1180251881Speter svn_revnum_t revnum_used; 1181251881Speter 1182251881Speter /* No URL? No sweat. We'll use the session URL. */ 1183251881Speter if (! url) 1184251881Speter url = session->session_url.path; 1185251881Speter 1186251881Speter /* If the caller didn't provide a specific connection for us to use, 1187251881Speter we'll use the default connection. */ 1188251881Speter if (! conn) 1189251881Speter conn = session->conns[0]; 1190251881Speter 1191251881Speter SVN_ERR(get_baseline_info(&basecoll_url, &revnum_used, 1192251881Speter session, conn, revision, scratch_pool)); 1193251881Speter SVN_ERR(svn_ra_serf__get_relative_path(&repos_relpath, url, 1194251881Speter session, conn, scratch_pool)); 1195251881Speter 1196251881Speter *stable_url = svn_path_url_add_component2(basecoll_url, repos_relpath, 1197251881Speter result_pool); 1198251881Speter if (latest_revnum) 1199251881Speter *latest_revnum = revnum_used; 1200251881Speter 1201251881Speter return SVN_NO_ERROR; 1202251881Speter} 1203251881Speter 1204251881Speter 1205251881Spetersvn_error_t * 1206251881Spetersvn_ra_serf__get_resource_type(svn_node_kind_t *kind, 1207251881Speter apr_hash_t *props) 1208251881Speter{ 1209251881Speter apr_hash_t *dav_props; 1210251881Speter const char *res_type; 1211251881Speter 1212251881Speter dav_props = apr_hash_get(props, "DAV:", 4); 1213251881Speter res_type = svn_prop_get_value(dav_props, "resourcetype"); 1214251881Speter if (!res_type) 1215251881Speter { 1216251881Speter /* How did this happen? */ 1217251881Speter return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, 1218251881Speter _("The PROPFIND response did not include the " 1219251881Speter "requested resourcetype value")); 1220251881Speter } 1221251881Speter 1222251881Speter if (strcmp(res_type, "collection") == 0) 1223251881Speter { 1224251881Speter *kind = svn_node_dir; 1225251881Speter } 1226251881Speter else 1227251881Speter { 1228251881Speter *kind = svn_node_file; 1229251881Speter } 1230251881Speter 1231251881Speter return SVN_NO_ERROR; 1232251881Speter} 1233251881Speter 1234251881Speter 1235251881Spetersvn_error_t * 1236251881Spetersvn_ra_serf__fetch_dav_prop(const char **value, 1237251881Speter svn_ra_serf__connection_t *conn, 1238251881Speter const char *url, 1239251881Speter svn_revnum_t revision, 1240251881Speter const char *propname, 1241251881Speter apr_pool_t *result_pool, 1242251881Speter apr_pool_t *scratch_pool) 1243251881Speter{ 1244251881Speter apr_hash_t *props; 1245251881Speter apr_hash_t *dav_props; 1246251881Speter 1247251881Speter SVN_ERR(svn_ra_serf__fetch_node_props(&props, conn, url, revision, 1248251881Speter checked_in_props, 1249251881Speter scratch_pool, scratch_pool)); 1250251881Speter dav_props = apr_hash_get(props, "DAV:", 4); 1251251881Speter if (dav_props == NULL) 1252251881Speter return svn_error_create(SVN_ERR_RA_DAV_PROPS_NOT_FOUND, NULL, 1253251881Speter _("The PROPFIND response did not include " 1254251881Speter "the requested 'DAV:' properties")); 1255251881Speter 1256251881Speter /* We wouldn't get here if the resource was not found (404), so the 1257251881Speter property should be present. 1258251881Speter 1259251881Speter Note: it is okay to call apr_pstrdup() with NULL. */ 1260251881Speter *value = apr_pstrdup(result_pool, svn_prop_get_value(dav_props, propname)); 1261251881Speter 1262251881Speter return SVN_NO_ERROR; 1263251881Speter} 1264