1251881Speter/* 2251881Speter * prop_commands.c: Implementation of propset, propget, and proplist. 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 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter 30251881Speter#define APR_WANT_STRFUNC 31251881Speter#include <apr_want.h> 32251881Speter 33251881Speter#include "svn_error.h" 34251881Speter#include "svn_client.h" 35251881Speter#include "client.h" 36251881Speter#include "svn_dirent_uri.h" 37251881Speter#include "svn_path.h" 38251881Speter#include "svn_pools.h" 39251881Speter#include "svn_props.h" 40251881Speter#include "svn_hash.h" 41251881Speter#include "svn_sorts.h" 42251881Speter 43251881Speter#include "svn_private_config.h" 44251881Speter#include "private/svn_wc_private.h" 45251881Speter#include "private/svn_ra_private.h" 46251881Speter#include "private/svn_client_private.h" 47251881Speter 48251881Speter 49251881Speter/*** Code. ***/ 50251881Speter 51251881Speter/* Return an SVN_ERR_CLIENT_PROPERTY_NAME error if NAME is a wcprop, 52251881Speter else return SVN_NO_ERROR. */ 53251881Speterstatic svn_error_t * 54251881Spetererror_if_wcprop_name(const char *name) 55251881Speter{ 56251881Speter if (svn_property_kind2(name) == svn_prop_wc_kind) 57251881Speter { 58251881Speter return svn_error_createf 59251881Speter (SVN_ERR_CLIENT_PROPERTY_NAME, NULL, 60251881Speter _("'%s' is a wcprop, thus not accessible to clients"), 61251881Speter name); 62251881Speter } 63251881Speter 64251881Speter return SVN_NO_ERROR; 65251881Speter} 66251881Speter 67251881Speter 68251881Speterstruct getter_baton 69251881Speter{ 70251881Speter svn_ra_session_t *ra_session; 71251881Speter svn_revnum_t base_revision_for_url; 72251881Speter}; 73251881Speter 74251881Speter 75251881Speterstatic svn_error_t * 76251881Speterget_file_for_validation(const svn_string_t **mime_type, 77251881Speter svn_stream_t *stream, 78251881Speter void *baton, 79251881Speter apr_pool_t *pool) 80251881Speter{ 81251881Speter struct getter_baton *gb = baton; 82251881Speter svn_ra_session_t *ra_session = gb->ra_session; 83251881Speter apr_hash_t *props; 84251881Speter 85251881Speter SVN_ERR(svn_ra_get_file(ra_session, "", gb->base_revision_for_url, 86251881Speter stream, NULL, 87251881Speter (mime_type ? &props : NULL), 88251881Speter pool)); 89251881Speter 90251881Speter if (mime_type) 91251881Speter *mime_type = svn_hash_gets(props, SVN_PROP_MIME_TYPE); 92251881Speter 93251881Speter return SVN_NO_ERROR; 94251881Speter} 95251881Speter 96251881Speter 97251881Speterstatic 98251881Spetersvn_error_t * 99251881Speterdo_url_propset(const char *url, 100251881Speter const char *propname, 101251881Speter const svn_string_t *propval, 102251881Speter const svn_node_kind_t kind, 103251881Speter const svn_revnum_t base_revision_for_url, 104251881Speter const svn_delta_editor_t *editor, 105251881Speter void *edit_baton, 106251881Speter apr_pool_t *pool) 107251881Speter{ 108251881Speter void *root_baton; 109251881Speter 110251881Speter SVN_ERR(editor->open_root(edit_baton, base_revision_for_url, pool, 111251881Speter &root_baton)); 112251881Speter 113251881Speter if (kind == svn_node_file) 114251881Speter { 115251881Speter void *file_baton; 116251881Speter const char *uri_basename = svn_uri_basename(url, pool); 117251881Speter 118251881Speter SVN_ERR(editor->open_file(uri_basename, root_baton, 119251881Speter base_revision_for_url, pool, &file_baton)); 120251881Speter SVN_ERR(editor->change_file_prop(file_baton, propname, propval, pool)); 121251881Speter SVN_ERR(editor->close_file(file_baton, NULL, pool)); 122251881Speter } 123251881Speter else 124251881Speter { 125251881Speter SVN_ERR(editor->change_dir_prop(root_baton, propname, propval, pool)); 126251881Speter } 127251881Speter 128251881Speter return editor->close_directory(root_baton, pool); 129251881Speter} 130251881Speter 131251881Speterstatic svn_error_t * 132251881Speterpropset_on_url(const char *propname, 133251881Speter const svn_string_t *propval, 134251881Speter const char *target, 135251881Speter svn_boolean_t skip_checks, 136251881Speter svn_revnum_t base_revision_for_url, 137251881Speter const apr_hash_t *revprop_table, 138251881Speter svn_commit_callback2_t commit_callback, 139251881Speter void *commit_baton, 140251881Speter svn_client_ctx_t *ctx, 141251881Speter apr_pool_t *pool) 142251881Speter{ 143251881Speter enum svn_prop_kind prop_kind = svn_property_kind2(propname); 144251881Speter svn_ra_session_t *ra_session; 145251881Speter svn_node_kind_t node_kind; 146251881Speter const char *message; 147251881Speter const svn_delta_editor_t *editor; 148251881Speter void *edit_baton; 149251881Speter apr_hash_t *commit_revprops; 150251881Speter svn_error_t *err; 151251881Speter 152251881Speter if (prop_kind != svn_prop_regular_kind) 153251881Speter return svn_error_createf 154251881Speter (SVN_ERR_BAD_PROP_KIND, NULL, 155251881Speter _("Property '%s' is not a regular property"), propname); 156251881Speter 157251881Speter /* Open an RA session for the URL. Note that we don't have a local 158251881Speter directory, nor a place to put temp files. */ 159251881Speter SVN_ERR(svn_client_open_ra_session2(&ra_session, target, NULL, 160251881Speter ctx, pool, pool)); 161251881Speter 162251881Speter SVN_ERR(svn_ra_check_path(ra_session, "", base_revision_for_url, 163251881Speter &node_kind, pool)); 164251881Speter if (node_kind == svn_node_none) 165251881Speter return svn_error_createf 166251881Speter (SVN_ERR_FS_NOT_FOUND, NULL, 167251881Speter _("Path '%s' does not exist in revision %ld"), 168251881Speter target, base_revision_for_url); 169251881Speter 170251881Speter if (node_kind == svn_node_file) 171251881Speter { 172251881Speter /* We need to reparent our session one directory up, since editor 173251881Speter semantics require the root is a directory. 174251881Speter 175251881Speter ### How does this interact with authz? */ 176251881Speter const char *parent_url; 177251881Speter parent_url = svn_uri_dirname(target, pool); 178251881Speter 179251881Speter SVN_ERR(svn_ra_reparent(ra_session, parent_url, pool)); 180251881Speter } 181251881Speter 182251881Speter /* Setting an inappropriate property is not allowed (unless 183251881Speter overridden by 'skip_checks', in some circumstances). Deleting an 184251881Speter inappropriate property is allowed, however, since older clients 185251881Speter allowed (and other clients possibly still allow) setting it in 186251881Speter the first place. */ 187251881Speter if (propval && svn_prop_is_svn_prop(propname)) 188251881Speter { 189251881Speter const svn_string_t *new_value; 190251881Speter struct getter_baton gb; 191251881Speter 192251881Speter gb.ra_session = ra_session; 193251881Speter gb.base_revision_for_url = base_revision_for_url; 194251881Speter SVN_ERR(svn_wc_canonicalize_svn_prop(&new_value, propname, propval, 195251881Speter target, node_kind, skip_checks, 196251881Speter get_file_for_validation, &gb, pool)); 197251881Speter propval = new_value; 198251881Speter } 199251881Speter 200251881Speter /* Create a new commit item and add it to the array. */ 201251881Speter if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) 202251881Speter { 203251881Speter svn_client_commit_item3_t *item; 204251881Speter const char *tmp_file; 205251881Speter apr_array_header_t *commit_items = apr_array_make(pool, 1, sizeof(item)); 206251881Speter 207251881Speter item = svn_client_commit_item3_create(pool); 208251881Speter item->url = target; 209289180Speter item->kind = node_kind; 210251881Speter item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS; 211251881Speter APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; 212251881Speter SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items, 213251881Speter ctx, pool)); 214251881Speter if (! message) 215251881Speter return SVN_NO_ERROR; 216251881Speter } 217251881Speter else 218251881Speter message = ""; 219251881Speter 220251881Speter SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, 221251881Speter message, ctx, pool)); 222251881Speter 223251881Speter /* Fetch RA commit editor. */ 224251881Speter SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session, 225251881Speter svn_client__get_shim_callbacks(ctx->wc_ctx, 226251881Speter NULL, pool))); 227251881Speter SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, 228251881Speter commit_revprops, 229251881Speter commit_callback, 230251881Speter commit_baton, 231251881Speter NULL, TRUE, /* No lock tokens */ 232251881Speter pool)); 233251881Speter 234251881Speter err = do_url_propset(target, propname, propval, node_kind, 235251881Speter base_revision_for_url, editor, edit_baton, pool); 236251881Speter 237251881Speter if (err) 238251881Speter { 239251881Speter /* At least try to abort the edit (and fs txn) before throwing err. */ 240251881Speter svn_error_clear(editor->abort_edit(edit_baton, pool)); 241251881Speter return svn_error_trace(err); 242251881Speter } 243251881Speter 244289180Speter if (ctx->notify_func2) 245289180Speter { 246289180Speter svn_wc_notify_t *notify; 247289180Speter notify = svn_wc_create_notify_url(target, 248289180Speter svn_wc_notify_commit_finalizing, 249289180Speter pool); 250289180Speter ctx->notify_func2(ctx->notify_baton2, notify, pool); 251289180Speter } 252251881Speter /* Close the edit. */ 253251881Speter return editor->close_edit(edit_baton, pool); 254251881Speter} 255251881Speter 256251881Speter/* Check that PROPNAME is a valid name for a versioned property. Return an 257251881Speter * error if it is not valid, specifically if it is: 258251881Speter * - the name of a standard Subversion rev-prop; or 259251881Speter * - in the namespace of WC-props; or 260251881Speter * - not a well-formed property name (except if PROPVAL is NULL: in other 261251881Speter * words we do allow deleting a prop with an ill-formed name). 262251881Speter * 263251881Speter * Since Subversion controls the "svn:" property namespace, we don't honor 264251881Speter * a 'skip_checks' flag here. Checks for unusual property combinations such 265251881Speter * as svn:eol-style with a non-text svn:mime-type might understandably be 266251881Speter * skipped, but things such as using a property name reserved for revprops 267251881Speter * on a local target are never allowed. 268251881Speter */ 269251881Speterstatic svn_error_t * 270251881Spetercheck_prop_name(const char *propname, 271251881Speter const svn_string_t *propval) 272251881Speter{ 273251881Speter if (svn_prop_is_known_svn_rev_prop(propname)) 274251881Speter return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, 275251881Speter _("Revision property '%s' not allowed " 276251881Speter "in this context"), propname); 277251881Speter 278251881Speter SVN_ERR(error_if_wcprop_name(propname)); 279251881Speter 280251881Speter if (propval && ! svn_prop_name_is_valid(propname)) 281251881Speter return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, 282251881Speter _("Bad property name: '%s'"), propname); 283251881Speter 284251881Speter return SVN_NO_ERROR; 285251881Speter} 286251881Speter 287251881Spetersvn_error_t * 288251881Spetersvn_client_propset_local(const char *propname, 289251881Speter const svn_string_t *propval, 290251881Speter const apr_array_header_t *targets, 291251881Speter svn_depth_t depth, 292251881Speter svn_boolean_t skip_checks, 293251881Speter const apr_array_header_t *changelists, 294251881Speter svn_client_ctx_t *ctx, 295251881Speter apr_pool_t *scratch_pool) 296251881Speter{ 297251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 298251881Speter svn_boolean_t targets_are_urls; 299251881Speter int i; 300251881Speter 301251881Speter if (targets->nelts == 0) 302251881Speter return SVN_NO_ERROR; 303251881Speter 304251881Speter /* Check for homogeneity among our targets. */ 305251881Speter targets_are_urls = svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *)); 306251881Speter SVN_ERR(svn_client__assert_homogeneous_target_type(targets)); 307251881Speter 308251881Speter if (targets_are_urls) 309251881Speter return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, 310251881Speter _("Targets must be working copy paths")); 311251881Speter 312251881Speter SVN_ERR(check_prop_name(propname, propval)); 313251881Speter 314251881Speter for (i = 0; i < targets->nelts; i++) 315251881Speter { 316251881Speter svn_node_kind_t kind; 317251881Speter const char *target_abspath; 318251881Speter const char *target = APR_ARRAY_IDX(targets, i, const char *); 319251881Speter 320251881Speter svn_pool_clear(iterpool); 321251881Speter 322251881Speter /* Check for cancellation */ 323251881Speter if (ctx->cancel_func) 324251881Speter SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); 325251881Speter 326251881Speter SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, iterpool)); 327251881Speter 328251881Speter /* Call prop_set for deleted nodes to have special errors */ 329251881Speter SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath, 330251881Speter FALSE, FALSE, iterpool)); 331251881Speter 332251881Speter if (kind == svn_node_unknown || kind == svn_node_none) 333251881Speter { 334251881Speter if (ctx->notify_func2) 335251881Speter { 336251881Speter svn_wc_notify_t *notify = svn_wc_create_notify( 337251881Speter target_abspath, 338251881Speter svn_wc_notify_path_nonexistent, 339251881Speter iterpool); 340251881Speter 341251881Speter ctx->notify_func2(ctx->notify_baton2, notify, iterpool); 342251881Speter } 343251881Speter } 344251881Speter 345251881Speter SVN_WC__CALL_WITH_WRITE_LOCK( 346251881Speter svn_wc_prop_set4(ctx->wc_ctx, target_abspath, propname, 347251881Speter propval, depth, skip_checks, changelists, 348251881Speter ctx->cancel_func, ctx->cancel_baton, 349251881Speter ctx->notify_func2, ctx->notify_baton2, iterpool), 350251881Speter ctx->wc_ctx, target_abspath, FALSE /* lock_anchor */, iterpool); 351251881Speter } 352251881Speter svn_pool_destroy(iterpool); 353251881Speter 354251881Speter return SVN_NO_ERROR; 355251881Speter} 356251881Speter 357251881Spetersvn_error_t * 358251881Spetersvn_client_propset_remote(const char *propname, 359251881Speter const svn_string_t *propval, 360251881Speter const char *url, 361251881Speter svn_boolean_t skip_checks, 362251881Speter svn_revnum_t base_revision_for_url, 363251881Speter const apr_hash_t *revprop_table, 364251881Speter svn_commit_callback2_t commit_callback, 365251881Speter void *commit_baton, 366251881Speter svn_client_ctx_t *ctx, 367251881Speter apr_pool_t *scratch_pool) 368251881Speter{ 369251881Speter if (!svn_path_is_url(url)) 370251881Speter return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL, 371251881Speter _("Targets must be URLs")); 372251881Speter 373251881Speter SVN_ERR(check_prop_name(propname, propval)); 374251881Speter 375251881Speter /* The rationale for requiring the base_revision_for_url 376251881Speter argument is that without it, it's too easy to possibly 377251881Speter overwrite someone else's change without noticing. (See also 378251881Speter tools/examples/svnput.c). */ 379251881Speter if (! SVN_IS_VALID_REVNUM(base_revision_for_url)) 380251881Speter return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, 381251881Speter _("Setting property on non-local targets " 382251881Speter "needs a base revision")); 383251881Speter 384251881Speter /* ### When you set svn:eol-style or svn:keywords on a wc file, 385251881Speter ### Subversion sends a textdelta at commit time to properly 386251881Speter ### normalize the file in the repository. If we want to 387251881Speter ### support editing these properties on URLs, then we should 388251881Speter ### generate the same textdelta; for now, we won't support 389251881Speter ### editing these properties on URLs. (Admittedly, this 390251881Speter ### means that all the machinery with get_file_for_validation 391251881Speter ### is unused.) 392251881Speter */ 393251881Speter if ((strcmp(propname, SVN_PROP_EOL_STYLE) == 0) || 394251881Speter (strcmp(propname, SVN_PROP_KEYWORDS) == 0)) 395251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 396251881Speter _("Setting property '%s' on non-local " 397251881Speter "targets is not supported"), propname); 398251881Speter 399251881Speter SVN_ERR(propset_on_url(propname, propval, url, skip_checks, 400251881Speter base_revision_for_url, revprop_table, 401251881Speter commit_callback, commit_baton, ctx, scratch_pool)); 402251881Speter 403251881Speter return SVN_NO_ERROR; 404251881Speter} 405251881Speter 406251881Speterstatic svn_error_t * 407251881Spetercheck_and_set_revprop(svn_revnum_t *set_rev, 408251881Speter svn_ra_session_t *ra_session, 409251881Speter const char *propname, 410251881Speter const svn_string_t *original_propval, 411251881Speter const svn_string_t *propval, 412251881Speter apr_pool_t *pool) 413251881Speter{ 414251881Speter if (original_propval) 415251881Speter { 416251881Speter /* Ensure old value hasn't changed behind our back. */ 417251881Speter svn_string_t *current; 418251881Speter SVN_ERR(svn_ra_rev_prop(ra_session, *set_rev, propname, ¤t, pool)); 419251881Speter 420251881Speter if (original_propval->data && (! current)) 421251881Speter { 422251881Speter return svn_error_createf( 423251881Speter SVN_ERR_RA_OUT_OF_DATE, NULL, 424251881Speter _("revprop '%s' in r%ld is unexpectedly absent " 425251881Speter "in repository (maybe someone else deleted it?)"), 426251881Speter propname, *set_rev); 427251881Speter } 428251881Speter else if (original_propval->data 429251881Speter && (! svn_string_compare(original_propval, current))) 430251881Speter { 431251881Speter return svn_error_createf( 432251881Speter SVN_ERR_RA_OUT_OF_DATE, NULL, 433251881Speter _("revprop '%s' in r%ld has unexpected value " 434251881Speter "in repository (maybe someone else changed it?)"), 435251881Speter propname, *set_rev); 436251881Speter } 437251881Speter else if ((! original_propval->data) && current) 438251881Speter { 439251881Speter return svn_error_createf( 440251881Speter SVN_ERR_RA_OUT_OF_DATE, NULL, 441251881Speter _("revprop '%s' in r%ld is unexpectedly present " 442251881Speter "in repository (maybe someone else set it?)"), 443251881Speter propname, *set_rev); 444251881Speter } 445251881Speter } 446251881Speter 447251881Speter SVN_ERR(svn_ra_change_rev_prop2(ra_session, *set_rev, propname, 448251881Speter NULL, propval, pool)); 449251881Speter 450251881Speter return SVN_NO_ERROR; 451251881Speter} 452251881Speter 453251881Spetersvn_error_t * 454251881Spetersvn_client_revprop_set2(const char *propname, 455251881Speter const svn_string_t *propval, 456251881Speter const svn_string_t *original_propval, 457251881Speter const char *URL, 458251881Speter const svn_opt_revision_t *revision, 459251881Speter svn_revnum_t *set_rev, 460251881Speter svn_boolean_t force, 461251881Speter svn_client_ctx_t *ctx, 462251881Speter apr_pool_t *pool) 463251881Speter{ 464251881Speter svn_ra_session_t *ra_session; 465251881Speter svn_boolean_t be_atomic; 466251881Speter 467251881Speter if ((strcmp(propname, SVN_PROP_REVISION_AUTHOR) == 0) 468251881Speter && propval 469251881Speter && strchr(propval->data, '\n') != NULL 470251881Speter && (! force)) 471251881Speter return svn_error_create(SVN_ERR_CLIENT_REVISION_AUTHOR_CONTAINS_NEWLINE, 472251881Speter NULL, _("Author name should not contain a newline;" 473251881Speter " value will not be set unless forced")); 474251881Speter 475251881Speter if (propval && ! svn_prop_name_is_valid(propname)) 476251881Speter return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL, 477251881Speter _("Bad property name: '%s'"), propname); 478251881Speter 479251881Speter /* Open an RA session for the URL. */ 480251881Speter SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL, 481251881Speter ctx, pool, pool)); 482251881Speter 483251881Speter /* Resolve the revision into something real, and return that to the 484251881Speter caller as well. */ 485251881Speter SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL, 486251881Speter ra_session, revision, pool)); 487251881Speter 488251881Speter SVN_ERR(svn_ra_has_capability(ra_session, &be_atomic, 489251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool)); 490251881Speter if (be_atomic) 491251881Speter { 492251881Speter /* Convert ORIGINAL_PROPVAL to an OLD_VALUE_P. */ 493251881Speter const svn_string_t *const *old_value_p; 494251881Speter const svn_string_t *unset = NULL; 495251881Speter 496251881Speter if (original_propval == NULL) 497289180Speter old_value_p = NULL; 498251881Speter else if (original_propval->data == NULL) 499289180Speter old_value_p = &unset; 500251881Speter else 501289180Speter old_value_p = &original_propval; 502251881Speter 503251881Speter /* The actual RA call. */ 504251881Speter SVN_ERR(svn_ra_change_rev_prop2(ra_session, *set_rev, propname, 505251881Speter old_value_p, propval, pool)); 506251881Speter } 507251881Speter else 508251881Speter { 509251881Speter /* The actual RA call. */ 510251881Speter SVN_ERR(check_and_set_revprop(set_rev, ra_session, propname, 511251881Speter original_propval, propval, pool)); 512251881Speter } 513251881Speter 514251881Speter if (ctx->notify_func2) 515251881Speter { 516251881Speter svn_wc_notify_t *notify = svn_wc_create_notify_url(URL, 517251881Speter propval == NULL 518251881Speter ? svn_wc_notify_revprop_deleted 519251881Speter : svn_wc_notify_revprop_set, 520251881Speter pool); 521251881Speter notify->prop_name = propname; 522251881Speter notify->revision = *set_rev; 523251881Speter 524289180Speter ctx->notify_func2(ctx->notify_baton2, notify, pool); 525251881Speter } 526251881Speter 527251881Speter return SVN_NO_ERROR; 528251881Speter} 529251881Speter 530289180Spetersvn_error_t * 531289180Spetersvn_client__remote_propget(apr_hash_t *props, 532289180Speter apr_array_header_t **inherited_props, 533289180Speter const char *propname, 534289180Speter const char *target_prefix, 535289180Speter const char *target_relative, 536289180Speter svn_node_kind_t kind, 537289180Speter svn_revnum_t revnum, 538289180Speter svn_ra_session_t *ra_session, 539289180Speter svn_depth_t depth, 540289180Speter apr_pool_t *result_pool, 541289180Speter apr_pool_t *scratch_pool) 542251881Speter{ 543251881Speter apr_hash_t *dirents; 544251881Speter apr_hash_t *prop_hash = NULL; 545251881Speter const svn_string_t *val; 546251881Speter const char *target_full_url = 547251881Speter svn_path_url_add_component2(target_prefix, target_relative, 548251881Speter scratch_pool); 549251881Speter 550251881Speter if (kind == svn_node_dir) 551251881Speter { 552251881Speter SVN_ERR(svn_ra_get_dir2(ra_session, 553251881Speter (depth >= svn_depth_files ? &dirents : NULL), 554251881Speter NULL, &prop_hash, target_relative, revnum, 555251881Speter SVN_DIRENT_KIND, scratch_pool)); 556251881Speter } 557251881Speter else if (kind == svn_node_file) 558251881Speter { 559251881Speter SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum, 560251881Speter NULL, NULL, &prop_hash, scratch_pool)); 561251881Speter } 562251881Speter else if (kind == svn_node_none) 563251881Speter { 564251881Speter return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 565251881Speter _("'%s' does not exist in revision %ld"), 566251881Speter target_full_url, revnum); 567251881Speter } 568251881Speter else 569251881Speter { 570251881Speter return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL, 571251881Speter _("Unknown node kind for '%s'"), 572251881Speter target_full_url); 573251881Speter } 574251881Speter 575251881Speter if (inherited_props) 576251881Speter { 577251881Speter const char *repos_root_url; 578289180Speter int i; 579289180Speter apr_array_header_t *final_iprops = 580289180Speter apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *)); 581251881Speter 582251881Speter /* We will filter out all but PROPNAME later, making a final copy 583251881Speter in RESULT_POOL, so pass SCRATCH_POOL for all pools. */ 584251881Speter SVN_ERR(svn_ra_get_inherited_props(ra_session, inherited_props, 585251881Speter target_relative, revnum, 586251881Speter scratch_pool, scratch_pool)); 587251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, 588251881Speter scratch_pool)); 589251881Speter SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props, 590251881Speter repos_root_url, 591251881Speter scratch_pool, 592251881Speter scratch_pool)); 593251881Speter 594289180Speter /* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */ 595251881Speter for (i = 0; i < (*inherited_props)->nelts; i++) 596251881Speter { 597251881Speter svn_prop_inherited_item_t *iprop = 598251881Speter APR_ARRAY_IDX((*inherited_props), i, svn_prop_inherited_item_t *); 599251881Speter svn_string_t *iprop_val = svn_hash_gets(iprop->prop_hash, propname); 600251881Speter 601251881Speter if (iprop_val) 602251881Speter { 603251881Speter svn_prop_inherited_item_t *new_iprop = 604251881Speter apr_palloc(result_pool, sizeof(*new_iprop)); 605251881Speter new_iprop->path_or_url = 606251881Speter apr_pstrdup(result_pool, iprop->path_or_url); 607251881Speter new_iprop->prop_hash = apr_hash_make(result_pool); 608251881Speter svn_hash_sets(new_iprop->prop_hash, 609251881Speter apr_pstrdup(result_pool, propname), 610251881Speter svn_string_dup(iprop_val, result_pool)); 611251881Speter APR_ARRAY_PUSH(final_iprops, svn_prop_inherited_item_t *) = 612251881Speter new_iprop; 613251881Speter } 614251881Speter } 615251881Speter *inherited_props = final_iprops; 616251881Speter } 617251881Speter 618251881Speter if (prop_hash 619251881Speter && (val = svn_hash_gets(prop_hash, propname))) 620251881Speter { 621251881Speter svn_hash_sets(props, 622251881Speter apr_pstrdup(result_pool, target_full_url), 623251881Speter svn_string_dup(val, result_pool)); 624251881Speter } 625251881Speter 626251881Speter if (depth >= svn_depth_files 627251881Speter && kind == svn_node_dir 628251881Speter && apr_hash_count(dirents) > 0) 629251881Speter { 630251881Speter apr_hash_index_t *hi; 631251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 632251881Speter 633251881Speter for (hi = apr_hash_first(scratch_pool, dirents); 634251881Speter hi; 635251881Speter hi = apr_hash_next(hi)) 636251881Speter { 637289180Speter const char *this_name = apr_hash_this_key(hi); 638289180Speter svn_dirent_t *this_ent = apr_hash_this_val(hi); 639251881Speter const char *new_target_relative; 640251881Speter svn_depth_t depth_below_here = depth; 641251881Speter 642251881Speter svn_pool_clear(iterpool); 643251881Speter 644251881Speter if (depth == svn_depth_files && this_ent->kind == svn_node_dir) 645251881Speter continue; 646251881Speter 647251881Speter if (depth == svn_depth_files || depth == svn_depth_immediates) 648251881Speter depth_below_here = svn_depth_empty; 649251881Speter 650251881Speter new_target_relative = svn_relpath_join(target_relative, this_name, 651251881Speter iterpool); 652251881Speter 653289180Speter SVN_ERR(svn_client__remote_propget(props, NULL, 654289180Speter propname, 655289180Speter target_prefix, 656289180Speter new_target_relative, 657289180Speter this_ent->kind, 658289180Speter revnum, 659289180Speter ra_session, 660289180Speter depth_below_here, 661289180Speter result_pool, iterpool)); 662251881Speter } 663251881Speter 664251881Speter svn_pool_destroy(iterpool); 665251881Speter } 666251881Speter 667251881Speter return SVN_NO_ERROR; 668251881Speter} 669251881Speter 670251881Speter/* Baton for recursive_propget_receiver(). */ 671251881Speterstruct recursive_propget_receiver_baton 672251881Speter{ 673251881Speter apr_hash_t *props; /* Hash to collect props. */ 674251881Speter apr_pool_t *pool; /* Pool to allocate additions to PROPS. */ 675251881Speter svn_wc_context_t *wc_ctx; /* Working copy context. */ 676251881Speter}; 677251881Speter 678251881Speter/* An implementation of svn_wc__proplist_receiver_t. */ 679251881Speterstatic svn_error_t * 680251881Speterrecursive_propget_receiver(void *baton, 681251881Speter const char *local_abspath, 682251881Speter apr_hash_t *props, 683251881Speter apr_pool_t *scratch_pool) 684251881Speter{ 685251881Speter struct recursive_propget_receiver_baton *b = baton; 686251881Speter 687251881Speter if (apr_hash_count(props)) 688251881Speter { 689251881Speter apr_hash_index_t *hi = apr_hash_first(scratch_pool, props); 690251881Speter svn_hash_sets(b->props, apr_pstrdup(b->pool, local_abspath), 691289180Speter svn_string_dup(apr_hash_this_val(hi), b->pool)); 692251881Speter } 693251881Speter 694251881Speter return SVN_NO_ERROR; 695251881Speter} 696251881Speter 697251881Speter/* Return the property value for any PROPNAME set on TARGET in *PROPS, 698251881Speter with WC paths of char * for keys and property values of 699251881Speter svn_string_t * for values. Assumes that PROPS is non-NULL. Additions 700251881Speter to *PROPS are allocated in RESULT_POOL, temporary allocations happen in 701251881Speter SCRATCH_POOL. 702251881Speter 703251881Speter CHANGELISTS is an array of const char * changelist names, used as a 704251881Speter restrictive filter on items whose properties are set; that is, 705251881Speter don't set properties on any item unless it's a member of one of 706251881Speter those changelists. If CHANGELISTS is empty (or altogether NULL), 707251881Speter no changelist filtering occurs. 708251881Speter 709251881Speter Treat DEPTH as in svn_client_propget3(). 710251881Speter*/ 711251881Speterstatic svn_error_t * 712251881Speterget_prop_from_wc(apr_hash_t **props, 713251881Speter const char *propname, 714251881Speter const char *target_abspath, 715251881Speter svn_boolean_t pristine, 716251881Speter svn_node_kind_t kind, 717251881Speter svn_depth_t depth, 718251881Speter const apr_array_header_t *changelists, 719251881Speter svn_client_ctx_t *ctx, 720251881Speter apr_pool_t *result_pool, 721251881Speter apr_pool_t *scratch_pool) 722251881Speter{ 723251881Speter struct recursive_propget_receiver_baton rb; 724251881Speter 725251881Speter /* Technically, svn_depth_unknown just means use whatever depth(s) 726251881Speter we find in the working copy. But this is a walk over extant 727251881Speter working copy paths: if they're there at all, then by definition 728251881Speter the local depth reaches them, so let's just use svn_depth_infinity 729251881Speter to get there. */ 730251881Speter if (depth == svn_depth_unknown) 731251881Speter depth = svn_depth_infinity; 732251881Speter 733251881Speter if (!pristine && depth == svn_depth_infinity 734251881Speter && (!changelists || changelists->nelts == 0)) 735251881Speter { 736251881Speter /* Handle this common svn:mergeinfo case more efficient than the target 737251881Speter list handling in the recursive retrieval. */ 738251881Speter SVN_ERR(svn_wc__prop_retrieve_recursive( 739251881Speter props, ctx->wc_ctx, target_abspath, propname, 740251881Speter result_pool, scratch_pool)); 741251881Speter return SVN_NO_ERROR; 742251881Speter } 743251881Speter 744251881Speter *props = apr_hash_make(result_pool); 745251881Speter rb.props = *props; 746251881Speter rb.pool = result_pool; 747251881Speter rb.wc_ctx = ctx->wc_ctx; 748251881Speter 749251881Speter SVN_ERR(svn_wc__prop_list_recursive(ctx->wc_ctx, target_abspath, 750251881Speter propname, depth, pristine, 751251881Speter changelists, 752251881Speter recursive_propget_receiver, &rb, 753251881Speter ctx->cancel_func, ctx->cancel_baton, 754251881Speter scratch_pool)); 755251881Speter 756251881Speter return SVN_NO_ERROR; 757251881Speter} 758251881Speter 759251881Speter/* Note: this implementation is very similar to svn_client_proplist. */ 760251881Spetersvn_error_t * 761251881Spetersvn_client_propget5(apr_hash_t **props, 762251881Speter apr_array_header_t **inherited_props, 763251881Speter const char *propname, 764251881Speter const char *target, 765251881Speter const svn_opt_revision_t *peg_revision, 766251881Speter const svn_opt_revision_t *revision, 767251881Speter svn_revnum_t *actual_revnum, 768251881Speter svn_depth_t depth, 769251881Speter const apr_array_header_t *changelists, 770251881Speter svn_client_ctx_t *ctx, 771251881Speter apr_pool_t *result_pool, 772251881Speter apr_pool_t *scratch_pool) 773251881Speter{ 774251881Speter svn_revnum_t revnum; 775251881Speter svn_boolean_t local_explicit_props; 776251881Speter svn_boolean_t local_iprops; 777251881Speter 778251881Speter SVN_ERR(error_if_wcprop_name(propname)); 779251881Speter if (!svn_path_is_url(target)) 780251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(target)); 781251881Speter 782251881Speter peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision, 783251881Speter target); 784251881Speter revision = svn_cl__rev_default_to_peg(revision, peg_revision); 785251881Speter 786251881Speter local_explicit_props = 787251881Speter (! svn_path_is_url(target) 788251881Speter && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind) 789251881Speter && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind)); 790251881Speter 791251881Speter local_iprops = 792251881Speter (local_explicit_props 793251881Speter && (peg_revision->kind == svn_opt_revision_working 794251881Speter || peg_revision->kind == svn_opt_revision_unspecified ) 795251881Speter && (revision->kind == svn_opt_revision_working 796251881Speter || revision->kind == svn_opt_revision_unspecified )); 797251881Speter 798251881Speter if (local_explicit_props) 799251881Speter { 800251881Speter svn_node_kind_t kind; 801251881Speter svn_boolean_t pristine; 802251881Speter svn_error_t *err; 803251881Speter 804251881Speter /* If FALSE, we want the working revision. */ 805251881Speter pristine = (revision->kind == svn_opt_revision_committed 806251881Speter || revision->kind == svn_opt_revision_base); 807251881Speter 808251881Speter SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target, 809251881Speter pristine, FALSE, 810251881Speter scratch_pool)); 811251881Speter 812251881Speter if (kind == svn_node_unknown || kind == svn_node_none) 813251881Speter { 814251881Speter /* svn uses SVN_ERR_UNVERSIONED_RESOURCE as warning only 815251881Speter for this function. */ 816251881Speter return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL, 817251881Speter _("'%s' is not under version control"), 818251881Speter svn_dirent_local_style(target, 819251881Speter scratch_pool)); 820251881Speter } 821251881Speter 822251881Speter err = svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx, 823251881Speter target, NULL, revision, 824251881Speter scratch_pool); 825251881Speter if (err && err->apr_err == SVN_ERR_CLIENT_BAD_REVISION) 826251881Speter { 827251881Speter svn_error_clear(err); 828251881Speter revnum = SVN_INVALID_REVNUM; 829251881Speter } 830251881Speter else if (err) 831251881Speter return svn_error_trace(err); 832251881Speter 833251881Speter if (inherited_props && local_iprops) 834251881Speter { 835251881Speter const char *repos_root_url; 836251881Speter 837251881Speter SVN_ERR(svn_wc__get_iprops(inherited_props, ctx->wc_ctx, 838251881Speter target, propname, 839251881Speter result_pool, scratch_pool)); 840251881Speter SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL, 841251881Speter target, ctx, scratch_pool, 842251881Speter scratch_pool)); 843251881Speter SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props, 844251881Speter repos_root_url, 845251881Speter result_pool, 846251881Speter scratch_pool)); 847251881Speter } 848251881Speter 849251881Speter SVN_ERR(get_prop_from_wc(props, propname, target, 850251881Speter pristine, kind, 851251881Speter depth, changelists, ctx, result_pool, 852251881Speter scratch_pool)); 853251881Speter } 854251881Speter 855251881Speter if ((inherited_props && !local_iprops) 856251881Speter || !local_explicit_props) 857251881Speter { 858251881Speter svn_ra_session_t *ra_session; 859251881Speter svn_node_kind_t kind; 860251881Speter svn_opt_revision_t new_operative_rev; 861251881Speter svn_opt_revision_t new_peg_rev; 862251881Speter 863251881Speter /* Peg or operative revisions may be WC specific for 864251881Speter TARGET's explicit props, but still require us to 865251881Speter contact the repository for the inherited properties. */ 866251881Speter if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind) 867251881Speter || SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind)) 868251881Speter { 869251881Speter const char *repos_relpath; 870251881Speter const char *repos_root_url; 871251881Speter const char *local_abspath; 872251881Speter 873266731Speter /* Avoid assertion on the next line when somebody accidentally asks for 874266731Speter a working copy revision on a URL */ 875266731Speter if (svn_path_is_url(target)) 876266731Speter return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED, 877266731Speter NULL, NULL); 878251881Speter 879266731Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(target)); 880266731Speter local_abspath = target; 881266731Speter 882251881Speter if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)) 883251881Speter { 884289180Speter SVN_ERR(svn_wc__node_get_origin(NULL, NULL, 885251881Speter &repos_relpath, 886251881Speter &repos_root_url, 887289180Speter NULL, NULL, NULL, 888251881Speter ctx->wc_ctx, 889251881Speter local_abspath, 890251881Speter FALSE, /* scan_deleted */ 891251881Speter result_pool, 892251881Speter scratch_pool)); 893251881Speter if (repos_relpath) 894251881Speter { 895251881Speter target = svn_path_url_add_component2(repos_root_url, 896251881Speter repos_relpath, 897251881Speter scratch_pool); 898251881Speter if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)) 899251881Speter { 900251881Speter svn_revnum_t resolved_peg_rev; 901251881Speter 902251881Speter SVN_ERR(svn_client__get_revision_number( 903251881Speter &resolved_peg_rev, NULL, ctx->wc_ctx, 904251881Speter local_abspath, NULL, peg_revision, scratch_pool)); 905251881Speter new_peg_rev.kind = svn_opt_revision_number; 906251881Speter new_peg_rev.value.number = resolved_peg_rev; 907251881Speter peg_revision = &new_peg_rev; 908251881Speter } 909251881Speter 910251881Speter if (SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind)) 911251881Speter { 912251881Speter svn_revnum_t resolved_operative_rev; 913251881Speter 914251881Speter SVN_ERR(svn_client__get_revision_number( 915251881Speter &resolved_operative_rev, NULL, ctx->wc_ctx, 916251881Speter local_abspath, NULL, revision, scratch_pool)); 917251881Speter new_operative_rev.kind = svn_opt_revision_number; 918251881Speter new_operative_rev.value.number = resolved_operative_rev; 919251881Speter revision = &new_operative_rev; 920251881Speter } 921251881Speter } 922251881Speter else 923251881Speter { 924251881Speter /* TARGET doesn't exist in the repository, so there are 925251881Speter obviously not inherited props to be found there. */ 926251881Speter local_iprops = TRUE; 927251881Speter *inherited_props = apr_array_make( 928251881Speter result_pool, 0, sizeof(svn_prop_inherited_item_t *)); 929251881Speter } 930251881Speter } 931251881Speter } 932251881Speter 933251881Speter /* Do we still have anything to ask the repository about? */ 934251881Speter if (!local_explicit_props || !local_iprops) 935251881Speter { 936251881Speter svn_client__pathrev_t *loc; 937251881Speter 938251881Speter /* Get an RA plugin for this filesystem object. */ 939251881Speter SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, 940251881Speter target, NULL, 941251881Speter peg_revision, 942251881Speter revision, ctx, 943251881Speter scratch_pool)); 944251881Speter 945251881Speter SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, 946251881Speter scratch_pool)); 947251881Speter 948251881Speter if (!local_explicit_props) 949251881Speter *props = apr_hash_make(result_pool); 950251881Speter 951289180Speter SVN_ERR(svn_client__remote_propget( 952289180Speter !local_explicit_props ? *props : NULL, 953251881Speter !local_iprops ? inherited_props : NULL, 954251881Speter propname, loc->url, "", 955251881Speter kind, loc->rev, ra_session, 956251881Speter depth, result_pool, scratch_pool)); 957251881Speter revnum = loc->rev; 958251881Speter } 959251881Speter } 960251881Speter 961251881Speter if (actual_revnum) 962251881Speter *actual_revnum = revnum; 963251881Speter return SVN_NO_ERROR; 964251881Speter} 965251881Speter 966251881Spetersvn_error_t * 967251881Spetersvn_client_revprop_get(const char *propname, 968251881Speter svn_string_t **propval, 969251881Speter const char *URL, 970251881Speter const svn_opt_revision_t *revision, 971251881Speter svn_revnum_t *set_rev, 972251881Speter svn_client_ctx_t *ctx, 973251881Speter apr_pool_t *pool) 974251881Speter{ 975251881Speter svn_ra_session_t *ra_session; 976251881Speter apr_pool_t *subpool = svn_pool_create(pool); 977251881Speter svn_error_t *err; 978251881Speter 979251881Speter /* Open an RA session for the URL. Note that we don't have a local 980251881Speter directory, nor a place to put temp files. */ 981251881Speter SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL, 982251881Speter ctx, subpool, subpool)); 983251881Speter 984251881Speter /* Resolve the revision into something real, and return that to the 985251881Speter caller as well. */ 986251881Speter SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL, 987251881Speter ra_session, revision, subpool)); 988251881Speter 989251881Speter /* The actual RA call. */ 990251881Speter err = svn_ra_rev_prop(ra_session, *set_rev, propname, propval, pool); 991251881Speter 992251881Speter /* Close RA session */ 993251881Speter svn_pool_destroy(subpool); 994251881Speter return svn_error_trace(err); 995251881Speter} 996251881Speter 997251881Speter 998251881Speter/* Call RECEIVER for the given PATH and its PROP_HASH and/or 999251881Speter * INHERITED_PROPERTIES. 1000251881Speter * 1001251881Speter * If PROP_HASH is null or has zero count or INHERITED_PROPERTIES is null, 1002251881Speter * then do nothing. 1003251881Speter */ 1004251881Speterstatic svn_error_t* 1005251881Spetercall_receiver(const char *path, 1006251881Speter apr_hash_t *prop_hash, 1007251881Speter apr_array_header_t *inherited_properties, 1008251881Speter svn_proplist_receiver2_t receiver, 1009251881Speter void *receiver_baton, 1010251881Speter apr_pool_t *scratch_pool) 1011251881Speter{ 1012251881Speter if ((prop_hash && apr_hash_count(prop_hash)) 1013251881Speter || inherited_properties) 1014251881Speter SVN_ERR(receiver(receiver_baton, path, prop_hash, inherited_properties, 1015251881Speter scratch_pool)); 1016251881Speter 1017251881Speter return SVN_NO_ERROR; 1018251881Speter} 1019251881Speter 1020251881Speter 1021251881Speter/* Helper for the remote case of svn_client_proplist. 1022251881Speter * 1023251881Speter * If GET_EXPLICIT_PROPS is true, then call RECEIVER for paths at or under 1024251881Speter * "TARGET_PREFIX/TARGET_RELATIVE@REVNUM" (obtained using RA_SESSION) which 1025251881Speter * have regular properties. If GET_TARGET_INHERITED_PROPS is true, then send 1026251881Speter * the target's inherited properties to the callback. 1027251881Speter * 1028251881Speter * The 'path' and keys for 'prop_hash' and 'inherited_prop' arguments to 1029251881Speter * RECEIVER are all URLs. 1030251881Speter * 1031251881Speter * RESULT_POOL is used to allocated the 'path', 'prop_hash', and 1032251881Speter * 'inherited_prop' arguments to RECEIVER. SCRATCH_POOL is used for all 1033251881Speter * other (temporary) allocations. 1034251881Speter * 1035251881Speter * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE". 1036251881Speter * 1037251881Speter * If the target is a directory, only fetch properties for the files 1038251881Speter * and directories at depth DEPTH. DEPTH has not effect on inherited 1039251881Speter * properties. 1040251881Speter */ 1041251881Speterstatic svn_error_t * 1042251881Speterremote_proplist(const char *target_prefix, 1043251881Speter const char *target_relative, 1044251881Speter svn_node_kind_t kind, 1045251881Speter svn_revnum_t revnum, 1046251881Speter svn_ra_session_t *ra_session, 1047251881Speter svn_boolean_t get_explicit_props, 1048251881Speter svn_boolean_t get_target_inherited_props, 1049251881Speter svn_depth_t depth, 1050251881Speter svn_proplist_receiver2_t receiver, 1051251881Speter void *receiver_baton, 1052251881Speter svn_cancel_func_t cancel_func, 1053251881Speter void *cancel_baton, 1054251881Speter apr_pool_t *scratch_pool) 1055251881Speter{ 1056251881Speter apr_hash_t *dirents; 1057251881Speter apr_hash_t *prop_hash = NULL; 1058251881Speter apr_hash_index_t *hi; 1059251881Speter const char *target_full_url = 1060251881Speter svn_path_url_add_component2(target_prefix, target_relative, scratch_pool); 1061251881Speter apr_array_header_t *inherited_props; 1062251881Speter 1063251881Speter /* Note that we pass only the SCRATCH_POOL to svn_ra_get[dir*|file*] because 1064251881Speter we'll be filtering out non-regular properties from PROP_HASH before we 1065251881Speter return. */ 1066251881Speter if (kind == svn_node_dir) 1067251881Speter { 1068251881Speter SVN_ERR(svn_ra_get_dir2(ra_session, 1069251881Speter (depth > svn_depth_empty) ? &dirents : NULL, 1070251881Speter NULL, &prop_hash, target_relative, revnum, 1071251881Speter SVN_DIRENT_KIND, scratch_pool)); 1072251881Speter } 1073251881Speter else if (kind == svn_node_file) 1074251881Speter { 1075251881Speter SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum, 1076251881Speter NULL, NULL, &prop_hash, scratch_pool)); 1077251881Speter } 1078251881Speter else 1079251881Speter { 1080251881Speter return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL, 1081251881Speter _("Unknown node kind for '%s'"), 1082251881Speter target_full_url); 1083251881Speter } 1084251881Speter 1085251881Speter if (get_target_inherited_props) 1086251881Speter { 1087251881Speter const char *repos_root_url; 1088251881Speter 1089251881Speter SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, 1090251881Speter target_relative, revnum, 1091251881Speter scratch_pool, scratch_pool)); 1092251881Speter SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, 1093251881Speter scratch_pool)); 1094251881Speter SVN_ERR(svn_client__iprop_relpaths_to_urls(inherited_props, 1095251881Speter repos_root_url, 1096251881Speter scratch_pool, 1097251881Speter scratch_pool)); 1098251881Speter } 1099251881Speter else 1100251881Speter { 1101251881Speter inherited_props = NULL; 1102251881Speter } 1103251881Speter 1104251881Speter if (!get_explicit_props) 1105251881Speter prop_hash = NULL; 1106251881Speter else 1107251881Speter { 1108251881Speter /* Filter out non-regular properties, since the RA layer returns all 1109251881Speter kinds. Copy regular properties keys/vals from the prop_hash 1110251881Speter allocated in SCRATCH_POOL to the "final" hash allocated in 1111251881Speter RESULT_POOL. */ 1112251881Speter for (hi = apr_hash_first(scratch_pool, prop_hash); 1113251881Speter hi; 1114251881Speter hi = apr_hash_next(hi)) 1115251881Speter { 1116289180Speter const char *name = apr_hash_this_key(hi); 1117289180Speter apr_ssize_t klen = apr_hash_this_key_len(hi); 1118251881Speter svn_prop_kind_t prop_kind; 1119251881Speter 1120251881Speter prop_kind = svn_property_kind2(name); 1121251881Speter 1122251881Speter if (prop_kind != svn_prop_regular_kind) 1123251881Speter { 1124251881Speter apr_hash_set(prop_hash, name, klen, NULL); 1125251881Speter } 1126251881Speter } 1127251881Speter } 1128251881Speter 1129251881Speter SVN_ERR(call_receiver(target_full_url, prop_hash, inherited_props, 1130251881Speter receiver, receiver_baton, scratch_pool)); 1131251881Speter 1132251881Speter if (depth > svn_depth_empty 1133251881Speter && get_explicit_props 1134251881Speter && (kind == svn_node_dir) && (apr_hash_count(dirents) > 0)) 1135251881Speter { 1136251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1137251881Speter 1138251881Speter for (hi = apr_hash_first(scratch_pool, dirents); 1139251881Speter hi; 1140251881Speter hi = apr_hash_next(hi)) 1141251881Speter { 1142289180Speter const char *this_name = apr_hash_this_key(hi); 1143289180Speter svn_dirent_t *this_ent = apr_hash_this_val(hi); 1144251881Speter const char *new_target_relative; 1145251881Speter 1146251881Speter if (cancel_func) 1147251881Speter SVN_ERR(cancel_func(cancel_baton)); 1148251881Speter 1149251881Speter svn_pool_clear(iterpool); 1150251881Speter 1151251881Speter new_target_relative = svn_relpath_join(target_relative, 1152251881Speter this_name, iterpool); 1153251881Speter 1154251881Speter if (this_ent->kind == svn_node_file 1155251881Speter || depth > svn_depth_files) 1156251881Speter { 1157251881Speter svn_depth_t depth_below_here = depth; 1158251881Speter 1159251881Speter if (depth == svn_depth_immediates) 1160251881Speter depth_below_here = svn_depth_empty; 1161251881Speter 1162251881Speter SVN_ERR(remote_proplist(target_prefix, 1163251881Speter new_target_relative, 1164251881Speter this_ent->kind, 1165251881Speter revnum, 1166251881Speter ra_session, 1167251881Speter TRUE /* get_explicit_props */, 1168251881Speter FALSE /* get_target_inherited_props */, 1169251881Speter depth_below_here, 1170251881Speter receiver, receiver_baton, 1171251881Speter cancel_func, cancel_baton, 1172251881Speter iterpool)); 1173251881Speter } 1174251881Speter } 1175251881Speter 1176251881Speter svn_pool_destroy(iterpool); 1177251881Speter } 1178251881Speter 1179251881Speter return SVN_NO_ERROR; 1180251881Speter} 1181251881Speter 1182251881Speter 1183251881Speter/* Baton for recursive_proplist_receiver(). */ 1184251881Speterstruct recursive_proplist_receiver_baton 1185251881Speter{ 1186251881Speter svn_wc_context_t *wc_ctx; /* Working copy context. */ 1187251881Speter svn_proplist_receiver2_t wrapped_receiver; /* Proplist receiver to call. */ 1188251881Speter void *wrapped_receiver_baton; /* Baton for the proplist receiver. */ 1189262250Speter apr_array_header_t *iprops; 1190251881Speter 1191251881Speter /* Anchor, anchor_abspath pair for converting to relative paths */ 1192251881Speter const char *anchor; 1193251881Speter const char *anchor_abspath; 1194251881Speter}; 1195251881Speter 1196251881Speter/* An implementation of svn_wc__proplist_receiver_t. */ 1197251881Speterstatic svn_error_t * 1198251881Speterrecursive_proplist_receiver(void *baton, 1199251881Speter const char *local_abspath, 1200251881Speter apr_hash_t *props, 1201251881Speter apr_pool_t *scratch_pool) 1202251881Speter{ 1203251881Speter struct recursive_proplist_receiver_baton *b = baton; 1204251881Speter const char *path; 1205262250Speter apr_array_header_t *iprops = NULL; 1206251881Speter 1207262250Speter if (b->iprops 1208262250Speter && ! strcmp(local_abspath, b->anchor_abspath)) 1209262250Speter { 1210262250Speter /* Report iprops with the properties for the anchor */ 1211262250Speter iprops = b->iprops; 1212262250Speter b->iprops = NULL; 1213262250Speter } 1214262250Speter else if (b->iprops) 1215262250Speter { 1216262250Speter /* No report for the root? 1217262250Speter Report iprops anyway */ 1218262250Speter 1219262250Speter SVN_ERR(b->wrapped_receiver(b->wrapped_receiver_baton, 1220266731Speter b->anchor ? b->anchor : b->anchor_abspath, 1221262250Speter NULL /* prop_hash */, 1222262250Speter b->iprops, 1223262250Speter scratch_pool)); 1224262250Speter b->iprops = NULL; 1225262250Speter } 1226262250Speter 1227251881Speter /* Attempt to convert absolute paths to relative paths for 1228251881Speter * presentation purposes, if needed. */ 1229251881Speter if (b->anchor && b->anchor_abspath) 1230251881Speter { 1231251881Speter path = svn_dirent_join(b->anchor, 1232251881Speter svn_dirent_skip_ancestor(b->anchor_abspath, 1233251881Speter local_abspath), 1234251881Speter scratch_pool); 1235251881Speter } 1236251881Speter else 1237251881Speter path = local_abspath; 1238251881Speter 1239251881Speter return svn_error_trace(b->wrapped_receiver(b->wrapped_receiver_baton, 1240262250Speter path, props, iprops, 1241251881Speter scratch_pool)); 1242251881Speter} 1243251881Speter 1244251881Speter/* Helper for svn_client_proplist4 when retrieving properties and/or 1245251881Speter inherited properties from the repository. Except as noted below, 1246251881Speter all arguments are as per svn_client_proplist4. 1247251881Speter 1248251881Speter GET_EXPLICIT_PROPS controls if explicit props are retrieved. */ 1249251881Speterstatic svn_error_t * 1250251881Speterget_remote_props(const char *path_or_url, 1251251881Speter const svn_opt_revision_t *peg_revision, 1252251881Speter const svn_opt_revision_t *revision, 1253251881Speter svn_depth_t depth, 1254251881Speter svn_boolean_t get_explicit_props, 1255251881Speter svn_boolean_t get_target_inherited_props, 1256251881Speter svn_proplist_receiver2_t receiver, 1257251881Speter void *receiver_baton, 1258251881Speter svn_client_ctx_t *ctx, 1259251881Speter apr_pool_t *scratch_pool) 1260251881Speter{ 1261251881Speter svn_ra_session_t *ra_session; 1262251881Speter svn_node_kind_t kind; 1263251881Speter svn_opt_revision_t new_operative_rev; 1264251881Speter svn_opt_revision_t new_peg_rev; 1265251881Speter svn_client__pathrev_t *loc; 1266251881Speter 1267251881Speter /* Peg or operative revisions may be WC specific for 1268251881Speter PATH_OR_URL's explicit props, but still require us to 1269251881Speter contact the repository for the inherited properties. */ 1270251881Speter if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind) 1271251881Speter || SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind)) 1272251881Speter { 1273251881Speter const char *repos_relpath; 1274251881Speter const char *repos_root_url; 1275251881Speter const char *local_abspath; 1276251881Speter svn_boolean_t is_copy; 1277251881Speter 1278266731Speter /* Avoid assertion on the next line when somebody accidentally asks for 1279266731Speter a working copy revision on a URL */ 1280266731Speter if (svn_path_is_url(path_or_url)) 1281266731Speter return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED, 1282266731Speter NULL, NULL); 1283266731Speter 1284251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url, 1285251881Speter scratch_pool)); 1286251881Speter 1287251881Speter if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)) 1288251881Speter { 1289251881Speter SVN_ERR(svn_wc__node_get_origin(&is_copy, 1290289180Speter NULL, 1291251881Speter &repos_relpath, 1292251881Speter &repos_root_url, 1293289180Speter NULL, NULL, NULL, 1294251881Speter ctx->wc_ctx, 1295251881Speter local_abspath, 1296251881Speter FALSE, /* scan_deleted */ 1297251881Speter scratch_pool, 1298251881Speter scratch_pool)); 1299251881Speter if (repos_relpath) 1300251881Speter { 1301251881Speter path_or_url = 1302251881Speter svn_path_url_add_component2(repos_root_url, 1303251881Speter repos_relpath, 1304251881Speter scratch_pool); 1305251881Speter if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)) 1306251881Speter { 1307251881Speter svn_revnum_t resolved_peg_rev; 1308251881Speter 1309251881Speter SVN_ERR(svn_client__get_revision_number(&resolved_peg_rev, 1310251881Speter NULL, ctx->wc_ctx, 1311251881Speter local_abspath, NULL, 1312251881Speter peg_revision, 1313251881Speter scratch_pool)); 1314251881Speter new_peg_rev.kind = svn_opt_revision_number; 1315251881Speter new_peg_rev.value.number = resolved_peg_rev; 1316251881Speter peg_revision = &new_peg_rev; 1317251881Speter } 1318251881Speter 1319251881Speter if (SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind)) 1320251881Speter { 1321251881Speter svn_revnum_t resolved_operative_rev; 1322251881Speter 1323251881Speter SVN_ERR(svn_client__get_revision_number( 1324251881Speter &resolved_operative_rev, 1325251881Speter NULL, ctx->wc_ctx, 1326251881Speter local_abspath, NULL, 1327251881Speter revision, 1328251881Speter scratch_pool)); 1329251881Speter new_operative_rev.kind = svn_opt_revision_number; 1330251881Speter new_operative_rev.value.number = resolved_operative_rev; 1331251881Speter revision = &new_operative_rev; 1332251881Speter } 1333251881Speter } 1334251881Speter else 1335251881Speter { 1336251881Speter /* PATH_OR_URL doesn't exist in the repository, so there are 1337251881Speter obviously not inherited props to be found there. If we 1338251881Speter aren't looking for explicit props then we're done. */ 1339251881Speter if (!get_explicit_props) 1340251881Speter return SVN_NO_ERROR; 1341251881Speter } 1342251881Speter } 1343251881Speter } 1344251881Speter 1345251881Speter /* Get an RA session for this URL. */ 1346251881Speter SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc, 1347251881Speter path_or_url, NULL, 1348251881Speter peg_revision, 1349251881Speter revision, ctx, 1350251881Speter scratch_pool)); 1351251881Speter 1352251881Speter SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, 1353251881Speter scratch_pool)); 1354251881Speter 1355251881Speter SVN_ERR(remote_proplist(loc->url, "", kind, loc->rev, ra_session, 1356251881Speter get_explicit_props, 1357251881Speter get_target_inherited_props, 1358251881Speter depth, receiver, receiver_baton, 1359251881Speter ctx->cancel_func, ctx->cancel_baton, 1360251881Speter scratch_pool)); 1361251881Speter return SVN_NO_ERROR; 1362251881Speter} 1363251881Speter 1364251881Speter/* Helper for svn_client_proplist4 when retrieving properties and 1365251881Speter possibly inherited properties from the WC. All arguments are as 1366251881Speter per svn_client_proplist4. */ 1367251881Speterstatic svn_error_t * 1368251881Speterget_local_props(const char *path_or_url, 1369251881Speter const svn_opt_revision_t *revision, 1370251881Speter svn_depth_t depth, 1371251881Speter const apr_array_header_t *changelists, 1372251881Speter svn_boolean_t get_target_inherited_props, 1373251881Speter svn_proplist_receiver2_t receiver, 1374251881Speter void *receiver_baton, 1375251881Speter svn_client_ctx_t *ctx, 1376251881Speter apr_pool_t *scratch_pool) 1377251881Speter{ 1378251881Speter svn_boolean_t pristine; 1379251881Speter svn_node_kind_t kind; 1380251881Speter apr_hash_t *changelist_hash = NULL; 1381251881Speter const char *local_abspath; 1382262250Speter apr_array_header_t *iprops = NULL; 1383251881Speter 1384251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url, 1385251881Speter scratch_pool)); 1386251881Speter 1387251881Speter pristine = ((revision->kind == svn_opt_revision_committed) 1388251881Speter || (revision->kind == svn_opt_revision_base)); 1389251881Speter 1390251881Speter SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath, 1391251881Speter pristine, FALSE, scratch_pool)); 1392251881Speter 1393251881Speter if (kind == svn_node_unknown || kind == svn_node_none) 1394251881Speter { 1395251881Speter /* svn uses SVN_ERR_UNVERSIONED_RESOURCE as warning only 1396251881Speter for this function. */ 1397251881Speter return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL, 1398251881Speter _("'%s' is not under version control"), 1399251881Speter svn_dirent_local_style(local_abspath, 1400251881Speter scratch_pool)); 1401251881Speter } 1402251881Speter 1403251881Speter if (get_target_inherited_props) 1404251881Speter { 1405251881Speter const char *repos_root_url; 1406251881Speter 1407251881Speter SVN_ERR(svn_wc__get_iprops(&iprops, ctx->wc_ctx, local_abspath, 1408251881Speter NULL, scratch_pool, scratch_pool)); 1409251881Speter SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL, local_abspath, 1410251881Speter ctx, scratch_pool, scratch_pool)); 1411251881Speter SVN_ERR(svn_client__iprop_relpaths_to_urls(iprops, repos_root_url, 1412251881Speter scratch_pool, 1413251881Speter scratch_pool)); 1414251881Speter } 1415251881Speter 1416251881Speter if (changelists && changelists->nelts) 1417251881Speter SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, 1418251881Speter changelists, scratch_pool)); 1419251881Speter 1420251881Speter /* Fetch, recursively or not. */ 1421251881Speter if (kind == svn_node_dir) 1422251881Speter { 1423251881Speter struct recursive_proplist_receiver_baton rb; 1424251881Speter 1425251881Speter rb.wc_ctx = ctx->wc_ctx; 1426251881Speter rb.wrapped_receiver = receiver; 1427251881Speter rb.wrapped_receiver_baton = receiver_baton; 1428262250Speter rb.iprops = iprops; 1429262250Speter rb.anchor_abspath = local_abspath; 1430251881Speter 1431251881Speter if (strcmp(path_or_url, local_abspath) != 0) 1432251881Speter { 1433251881Speter rb.anchor = path_or_url; 1434251881Speter } 1435251881Speter else 1436251881Speter { 1437251881Speter rb.anchor = NULL; 1438251881Speter } 1439251881Speter 1440251881Speter SVN_ERR(svn_wc__prop_list_recursive(ctx->wc_ctx, local_abspath, NULL, 1441251881Speter depth, pristine, changelists, 1442251881Speter recursive_proplist_receiver, &rb, 1443251881Speter ctx->cancel_func, ctx->cancel_baton, 1444251881Speter scratch_pool)); 1445262250Speter 1446262250Speter if (rb.iprops) 1447262250Speter { 1448262250Speter /* We didn't report for the root. Report iprops anyway */ 1449262250Speter SVN_ERR(call_receiver(path_or_url, NULL /* props */, rb.iprops, 1450262250Speter receiver, receiver_baton, scratch_pool)); 1451262250Speter } 1452251881Speter } 1453251881Speter else if (svn_wc__changelist_match(ctx->wc_ctx, local_abspath, 1454251881Speter changelist_hash, scratch_pool)) 1455251881Speter { 1456251881Speter apr_hash_t *props; 1457251881Speter 1458251881Speter if (pristine) 1459251881Speter SVN_ERR(svn_wc_get_pristine_props(&props, 1460251881Speter ctx->wc_ctx, local_abspath, 1461251881Speter scratch_pool, scratch_pool)); 1462251881Speter else 1463251881Speter { 1464251881Speter svn_error_t *err; 1465251881Speter 1466251881Speter err = svn_wc_prop_list2(&props, ctx->wc_ctx, local_abspath, 1467251881Speter scratch_pool, scratch_pool); 1468251881Speter 1469251881Speter 1470251881Speter if (err) 1471251881Speter { 1472251881Speter if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS) 1473251881Speter return svn_error_trace(err); 1474251881Speter /* As svn_wc_prop_list2() doesn't return NULL for locally-deleted 1475251881Speter let's do that here. */ 1476251881Speter svn_error_clear(err); 1477251881Speter props = apr_hash_make(scratch_pool); 1478251881Speter } 1479251881Speter } 1480251881Speter 1481262250Speter SVN_ERR(call_receiver(path_or_url, props, iprops, 1482251881Speter receiver, receiver_baton, scratch_pool)); 1483251881Speter 1484251881Speter } 1485251881Speter return SVN_NO_ERROR; 1486251881Speter} 1487251881Speter 1488251881Spetersvn_error_t * 1489251881Spetersvn_client_proplist4(const char *path_or_url, 1490251881Speter const svn_opt_revision_t *peg_revision, 1491251881Speter const svn_opt_revision_t *revision, 1492251881Speter svn_depth_t depth, 1493251881Speter const apr_array_header_t *changelists, 1494251881Speter svn_boolean_t get_target_inherited_props, 1495251881Speter svn_proplist_receiver2_t receiver, 1496251881Speter void *receiver_baton, 1497251881Speter svn_client_ctx_t *ctx, 1498251881Speter apr_pool_t *scratch_pool) 1499251881Speter{ 1500251881Speter svn_boolean_t local_explicit_props; 1501251881Speter svn_boolean_t local_iprops; 1502251881Speter 1503251881Speter peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision, 1504251881Speter path_or_url); 1505251881Speter revision = svn_cl__rev_default_to_peg(revision, peg_revision); 1506251881Speter 1507251881Speter if (depth == svn_depth_unknown) 1508251881Speter depth = svn_depth_empty; 1509251881Speter 1510251881Speter /* Are explicit props available locally? */ 1511251881Speter local_explicit_props = 1512251881Speter (! svn_path_is_url(path_or_url) 1513251881Speter && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind) 1514251881Speter && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind)); 1515251881Speter 1516251881Speter /* If we want iprops are they available locally? */ 1517251881Speter local_iprops = 1518251881Speter (get_target_inherited_props /* We want iprops */ 1519251881Speter && local_explicit_props /* No local explicit props means no local iprops. */ 1520251881Speter && (peg_revision->kind == svn_opt_revision_working 1521251881Speter || peg_revision->kind == svn_opt_revision_unspecified ) 1522251881Speter && (revision->kind == svn_opt_revision_working 1523251881Speter || revision->kind == svn_opt_revision_unspecified )); 1524251881Speter 1525251881Speter if ((get_target_inherited_props && !local_iprops) 1526251881Speter || !local_explicit_props) 1527251881Speter { 1528251881Speter SVN_ERR(get_remote_props(path_or_url, peg_revision, revision, depth, 1529251881Speter !local_explicit_props, 1530251881Speter (get_target_inherited_props && !local_iprops), 1531251881Speter receiver, receiver_baton, ctx, scratch_pool)); 1532251881Speter } 1533251881Speter 1534251881Speter if (local_explicit_props) 1535251881Speter { 1536251881Speter SVN_ERR(get_local_props(path_or_url, revision, depth, changelists, 1537251881Speter local_iprops, receiver, receiver_baton, ctx, 1538251881Speter scratch_pool)); 1539251881Speter } 1540251881Speter 1541251881Speter return SVN_NO_ERROR; 1542251881Speter} 1543251881Speter 1544251881Spetersvn_error_t * 1545251881Spetersvn_client_revprop_list(apr_hash_t **props, 1546251881Speter const char *URL, 1547251881Speter const svn_opt_revision_t *revision, 1548251881Speter svn_revnum_t *set_rev, 1549251881Speter svn_client_ctx_t *ctx, 1550251881Speter apr_pool_t *pool) 1551251881Speter{ 1552251881Speter svn_ra_session_t *ra_session; 1553251881Speter apr_hash_t *proplist; 1554251881Speter apr_pool_t *subpool = svn_pool_create(pool); 1555251881Speter svn_error_t *err; 1556251881Speter 1557251881Speter /* Open an RA session for the URL. Note that we don't have a local 1558251881Speter directory, nor a place to put temp files. */ 1559251881Speter SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL, 1560251881Speter ctx, subpool, subpool)); 1561251881Speter 1562251881Speter /* Resolve the revision into something real, and return that to the 1563251881Speter caller as well. */ 1564251881Speter SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL, 1565251881Speter ra_session, revision, subpool)); 1566251881Speter 1567251881Speter /* The actual RA call. */ 1568251881Speter err = svn_ra_rev_proplist(ra_session, *set_rev, &proplist, pool); 1569251881Speter 1570251881Speter *props = proplist; 1571251881Speter svn_pool_destroy(subpool); /* Close RA session */ 1572251881Speter return svn_error_trace(err); 1573251881Speter} 1574