]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_client/prop_commands.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_client / prop_commands.c
1 /*
2  * prop_commands.c:  Implementation of propset, propget, and proplist.
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29
30 #define APR_WANT_STRFUNC
31 #include <apr_want.h>
32
33 #include "svn_error.h"
34 #include "svn_client.h"
35 #include "client.h"
36 #include "svn_dirent_uri.h"
37 #include "svn_path.h"
38 #include "svn_pools.h"
39 #include "svn_props.h"
40 #include "svn_hash.h"
41 #include "svn_sorts.h"
42
43 #include "svn_private_config.h"
44 #include "private/svn_wc_private.h"
45 #include "private/svn_ra_private.h"
46 #include "private/svn_client_private.h"
47
48 \f
49 /*** Code. ***/
50
51 /* Return an SVN_ERR_CLIENT_PROPERTY_NAME error if NAME is a wcprop,
52    else return SVN_NO_ERROR. */
53 static svn_error_t *
54 error_if_wcprop_name(const char *name)
55 {
56   if (svn_property_kind2(name) == svn_prop_wc_kind)
57     {
58       return svn_error_createf
59         (SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
60          _("'%s' is a wcprop, thus not accessible to clients"),
61          name);
62     }
63
64   return SVN_NO_ERROR;
65 }
66
67
68 struct getter_baton
69 {
70   svn_ra_session_t *ra_session;
71   svn_revnum_t base_revision_for_url;
72 };
73
74
75 static svn_error_t *
76 get_file_for_validation(const svn_string_t **mime_type,
77                         svn_stream_t *stream,
78                         void *baton,
79                         apr_pool_t *pool)
80 {
81   struct getter_baton *gb = baton;
82   svn_ra_session_t *ra_session = gb->ra_session;
83   apr_hash_t *props;
84
85   SVN_ERR(svn_ra_get_file(ra_session, "", gb->base_revision_for_url,
86                           stream, NULL,
87                           (mime_type ? &props : NULL),
88                           pool));
89
90   if (mime_type)
91     *mime_type = svn_hash_gets(props, SVN_PROP_MIME_TYPE);
92
93   return SVN_NO_ERROR;
94 }
95
96
97 static
98 svn_error_t *
99 do_url_propset(const char *url,
100                const char *propname,
101                const svn_string_t *propval,
102                const svn_node_kind_t kind,
103                const svn_revnum_t base_revision_for_url,
104                const svn_delta_editor_t *editor,
105                void *edit_baton,
106                apr_pool_t *pool)
107 {
108   void *root_baton;
109
110   SVN_ERR(editor->open_root(edit_baton, base_revision_for_url, pool,
111                             &root_baton));
112
113   if (kind == svn_node_file)
114     {
115       void *file_baton;
116       const char *uri_basename = svn_uri_basename(url, pool);
117
118       SVN_ERR(editor->open_file(uri_basename, root_baton,
119                                 base_revision_for_url, pool, &file_baton));
120       SVN_ERR(editor->change_file_prop(file_baton, propname, propval, pool));
121       SVN_ERR(editor->close_file(file_baton, NULL, pool));
122     }
123   else
124     {
125       SVN_ERR(editor->change_dir_prop(root_baton, propname, propval, pool));
126     }
127
128   return editor->close_directory(root_baton, pool);
129 }
130
131 static svn_error_t *
132 propset_on_url(const char *propname,
133                const svn_string_t *propval,
134                const char *target,
135                svn_boolean_t skip_checks,
136                svn_revnum_t base_revision_for_url,
137                const apr_hash_t *revprop_table,
138                svn_commit_callback2_t commit_callback,
139                void *commit_baton,
140                svn_client_ctx_t *ctx,
141                apr_pool_t *pool)
142 {
143   enum svn_prop_kind prop_kind = svn_property_kind2(propname);
144   svn_ra_session_t *ra_session;
145   svn_node_kind_t node_kind;
146   const char *message;
147   const svn_delta_editor_t *editor;
148   void *edit_baton;
149   apr_hash_t *commit_revprops;
150   svn_error_t *err;
151
152   if (prop_kind != svn_prop_regular_kind)
153     return svn_error_createf
154       (SVN_ERR_BAD_PROP_KIND, NULL,
155        _("Property '%s' is not a regular property"), propname);
156
157   /* Open an RA session for the URL. Note that we don't have a local
158      directory, nor a place to put temp files. */
159   SVN_ERR(svn_client_open_ra_session2(&ra_session, target, NULL,
160                                       ctx, pool, pool));
161
162   SVN_ERR(svn_ra_check_path(ra_session, "", base_revision_for_url,
163                             &node_kind, pool));
164   if (node_kind == svn_node_none)
165     return svn_error_createf
166       (SVN_ERR_FS_NOT_FOUND, NULL,
167        _("Path '%s' does not exist in revision %ld"),
168        target, base_revision_for_url);
169
170   if (node_kind == svn_node_file)
171     {
172       /* We need to reparent our session one directory up, since editor
173          semantics require the root is a directory.
174
175          ### How does this interact with authz? */
176       const char *parent_url;
177       parent_url = svn_uri_dirname(target, pool);
178
179       SVN_ERR(svn_ra_reparent(ra_session, parent_url, pool));
180     }
181
182   /* Setting an inappropriate property is not allowed (unless
183      overridden by 'skip_checks', in some circumstances).  Deleting an
184      inappropriate property is allowed, however, since older clients
185      allowed (and other clients possibly still allow) setting it in
186      the first place. */
187   if (propval && svn_prop_is_svn_prop(propname))
188     {
189       const svn_string_t *new_value;
190       struct getter_baton gb;
191
192       gb.ra_session = ra_session;
193       gb.base_revision_for_url = base_revision_for_url;
194       SVN_ERR(svn_wc_canonicalize_svn_prop(&new_value, propname, propval,
195                                            target, node_kind, skip_checks,
196                                            get_file_for_validation, &gb, pool));
197       propval = new_value;
198     }
199
200   /* Create a new commit item and add it to the array. */
201   if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
202     {
203       svn_client_commit_item3_t *item;
204       const char *tmp_file;
205       apr_array_header_t *commit_items = apr_array_make(pool, 1, sizeof(item));
206
207       item = svn_client_commit_item3_create(pool);
208       item->url = target;
209       item->kind = node_kind;
210       item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
211       APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
212       SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
213                                       ctx, pool));
214       if (! message)
215         return SVN_NO_ERROR;
216     }
217   else
218     message = "";
219
220   SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
221                                            message, ctx, pool));
222
223   /* Fetch RA commit editor. */
224   SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
225                         svn_client__get_shim_callbacks(ctx->wc_ctx,
226                                                        NULL, pool)));
227   SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
228                                     commit_revprops,
229                                     commit_callback,
230                                     commit_baton,
231                                     NULL, TRUE, /* No lock tokens */
232                                     pool));
233
234   err = do_url_propset(target, propname, propval, node_kind,
235                        base_revision_for_url, editor, edit_baton, pool);
236
237   if (err)
238     {
239       /* At least try to abort the edit (and fs txn) before throwing err. */
240       svn_error_clear(editor->abort_edit(edit_baton, pool));
241       return svn_error_trace(err);
242     }
243
244   if (ctx->notify_func2)
245     {
246       svn_wc_notify_t *notify;
247       notify = svn_wc_create_notify_url(target,
248                                         svn_wc_notify_commit_finalizing,
249                                         pool);
250       ctx->notify_func2(ctx->notify_baton2, notify, pool);
251     }
252   /* Close the edit. */
253   return editor->close_edit(edit_baton, pool);
254 }
255
256 /* Check that PROPNAME is a valid name for a versioned property.  Return an
257  * error if it is not valid, specifically if it is:
258  *   - the name of a standard Subversion rev-prop; or
259  *   - in the namespace of WC-props; or
260  *   - not a well-formed property name (except if PROPVAL is NULL: in other
261  *     words we do allow deleting a prop with an ill-formed name).
262  *
263  * Since Subversion controls the "svn:" property namespace, we don't honor
264  * a 'skip_checks' flag here.  Checks for unusual property combinations such
265  * as svn:eol-style with a non-text svn:mime-type might understandably be
266  * skipped, but things such as using a property name reserved for revprops
267  * on a local target are never allowed.
268  */
269 static svn_error_t *
270 check_prop_name(const char *propname,
271                 const svn_string_t *propval)
272 {
273   if (svn_prop_is_known_svn_rev_prop(propname))
274     return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
275                              _("Revision property '%s' not allowed "
276                                "in this context"), propname);
277
278   SVN_ERR(error_if_wcprop_name(propname));
279
280   if (propval && ! svn_prop_name_is_valid(propname))
281     return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
282                              _("Bad property name: '%s'"), propname);
283
284   return SVN_NO_ERROR;
285 }
286
287 svn_error_t *
288 svn_client_propset_local(const char *propname,
289                          const svn_string_t *propval,
290                          const apr_array_header_t *targets,
291                          svn_depth_t depth,
292                          svn_boolean_t skip_checks,
293                          const apr_array_header_t *changelists,
294                          svn_client_ctx_t *ctx,
295                          apr_pool_t *scratch_pool)
296 {
297   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
298   svn_boolean_t targets_are_urls;
299   int i;
300
301   if (targets->nelts == 0)
302     return SVN_NO_ERROR;
303
304   /* Check for homogeneity among our targets. */
305   targets_are_urls = svn_path_is_url(APR_ARRAY_IDX(targets, 0, const char *));
306   SVN_ERR(svn_client__assert_homogeneous_target_type(targets));
307
308   if (targets_are_urls)
309     return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
310                             _("Targets must be working copy paths"));
311
312   SVN_ERR(check_prop_name(propname, propval));
313
314   for (i = 0; i < targets->nelts; i++)
315     {
316       svn_node_kind_t kind;
317       const char *target_abspath;
318       const char *target = APR_ARRAY_IDX(targets, i, const char *);
319
320       svn_pool_clear(iterpool);
321
322       /* Check for cancellation */
323       if (ctx->cancel_func)
324         SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
325
326       SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, iterpool));
327
328       /* Call prop_set for deleted nodes to have special errors */
329       SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath,
330                                 FALSE, FALSE, iterpool));
331
332       if (kind == svn_node_unknown || kind == svn_node_none)
333         {
334           if (ctx->notify_func2)
335             {
336               svn_wc_notify_t *notify = svn_wc_create_notify(
337                                           target_abspath,
338                                           svn_wc_notify_path_nonexistent,
339                                           iterpool);
340
341               ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
342             }
343         }
344
345       SVN_WC__CALL_WITH_WRITE_LOCK(
346         svn_wc_prop_set4(ctx->wc_ctx, target_abspath, propname,
347                          propval, depth, skip_checks, changelists,
348                          ctx->cancel_func, ctx->cancel_baton,
349                          ctx->notify_func2, ctx->notify_baton2, iterpool),
350         ctx->wc_ctx, target_abspath, FALSE /* lock_anchor */, iterpool);
351     }
352   svn_pool_destroy(iterpool);
353
354   return SVN_NO_ERROR;
355 }
356
357 svn_error_t *
358 svn_client_propset_remote(const char *propname,
359                           const svn_string_t *propval,
360                           const char *url,
361                           svn_boolean_t skip_checks,
362                           svn_revnum_t base_revision_for_url,
363                           const apr_hash_t *revprop_table,
364                           svn_commit_callback2_t commit_callback,
365                           void *commit_baton,
366                           svn_client_ctx_t *ctx,
367                           apr_pool_t *scratch_pool)
368 {
369   if (!svn_path_is_url(url))
370     return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
371                             _("Targets must be URLs"));
372
373   SVN_ERR(check_prop_name(propname, propval));
374
375   /* The rationale for requiring the base_revision_for_url
376      argument is that without it, it's too easy to possibly
377      overwrite someone else's change without noticing.  (See also
378      tools/examples/svnput.c). */
379   if (! SVN_IS_VALID_REVNUM(base_revision_for_url))
380     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
381                             _("Setting property on non-local targets "
382                               "needs a base revision"));
383
384   /* ### When you set svn:eol-style or svn:keywords on a wc file,
385      ### Subversion sends a textdelta at commit time to properly
386      ### normalize the file in the repository.  If we want to
387      ### support editing these properties on URLs, then we should
388      ### generate the same textdelta; for now, we won't support
389      ### editing these properties on URLs.  (Admittedly, this
390      ### means that all the machinery with get_file_for_validation
391      ### is unused.)
392    */
393   if ((strcmp(propname, SVN_PROP_EOL_STYLE) == 0) ||
394       (strcmp(propname, SVN_PROP_KEYWORDS) == 0))
395     return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
396                              _("Setting property '%s' on non-local "
397                                "targets is not supported"), propname);
398
399   SVN_ERR(propset_on_url(propname, propval, url, skip_checks,
400                          base_revision_for_url, revprop_table,
401                          commit_callback, commit_baton, ctx, scratch_pool));
402
403   return SVN_NO_ERROR;
404 }
405
406 static svn_error_t *
407 check_and_set_revprop(svn_revnum_t *set_rev,
408                       svn_ra_session_t *ra_session,
409                       const char *propname,
410                       const svn_string_t *original_propval,
411                       const svn_string_t *propval,
412                       apr_pool_t *pool)
413 {
414   if (original_propval)
415     {
416       /* Ensure old value hasn't changed behind our back. */
417       svn_string_t *current;
418       SVN_ERR(svn_ra_rev_prop(ra_session, *set_rev, propname, &current, pool));
419
420       if (original_propval->data && (! current))
421         {
422           return svn_error_createf(
423                   SVN_ERR_RA_OUT_OF_DATE, NULL,
424                   _("revprop '%s' in r%ld is unexpectedly absent "
425                     "in repository (maybe someone else deleted it?)"),
426                   propname, *set_rev);
427         }
428       else if (original_propval->data
429                && (! svn_string_compare(original_propval, current)))
430         {
431           return svn_error_createf(
432                   SVN_ERR_RA_OUT_OF_DATE, NULL,
433                   _("revprop '%s' in r%ld has unexpected value "
434                     "in repository (maybe someone else changed it?)"),
435                   propname, *set_rev);
436         }
437       else if ((! original_propval->data) && current)
438         {
439           return svn_error_createf(
440                   SVN_ERR_RA_OUT_OF_DATE, NULL,
441                   _("revprop '%s' in r%ld is unexpectedly present "
442                     "in repository (maybe someone else set it?)"),
443                   propname, *set_rev);
444         }
445     }
446
447   SVN_ERR(svn_ra_change_rev_prop2(ra_session, *set_rev, propname,
448                                   NULL, propval, pool));
449
450   return SVN_NO_ERROR;
451 }
452
453 svn_error_t *
454 svn_client_revprop_set2(const char *propname,
455                         const svn_string_t *propval,
456                         const svn_string_t *original_propval,
457                         const char *URL,
458                         const svn_opt_revision_t *revision,
459                         svn_revnum_t *set_rev,
460                         svn_boolean_t force,
461                         svn_client_ctx_t *ctx,
462                         apr_pool_t *pool)
463 {
464   svn_ra_session_t *ra_session;
465   svn_boolean_t be_atomic;
466
467   if ((strcmp(propname, SVN_PROP_REVISION_AUTHOR) == 0)
468       && propval
469       && strchr(propval->data, '\n') != NULL
470       && (! force))
471     return svn_error_create(SVN_ERR_CLIENT_REVISION_AUTHOR_CONTAINS_NEWLINE,
472                             NULL, _("Author name should not contain a newline;"
473                                     " value will not be set unless forced"));
474
475   if (propval && ! svn_prop_name_is_valid(propname))
476     return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
477                              _("Bad property name: '%s'"), propname);
478
479   /* Open an RA session for the URL. */
480   SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL,
481                                       ctx, pool, pool));
482
483   /* Resolve the revision into something real, and return that to the
484      caller as well. */
485   SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL,
486                                           ra_session, revision, pool));
487
488   SVN_ERR(svn_ra_has_capability(ra_session, &be_atomic,
489                                 SVN_RA_CAPABILITY_ATOMIC_REVPROPS, pool));
490   if (be_atomic)
491     {
492       /* Convert ORIGINAL_PROPVAL to an OLD_VALUE_P. */
493       const svn_string_t *const *old_value_p;
494       const svn_string_t *unset = NULL;
495
496       if (original_propval == NULL)
497         old_value_p = NULL;
498       else if (original_propval->data == NULL)
499         old_value_p = &unset;
500       else
501         old_value_p = &original_propval;
502
503       /* The actual RA call. */
504       SVN_ERR(svn_ra_change_rev_prop2(ra_session, *set_rev, propname,
505                                       old_value_p, propval, pool));
506     }
507   else
508     {
509       /* The actual RA call. */
510       SVN_ERR(check_and_set_revprop(set_rev, ra_session, propname,
511                                     original_propval, propval, pool));
512     }
513
514   if (ctx->notify_func2)
515     {
516       svn_wc_notify_t *notify = svn_wc_create_notify_url(URL,
517                                              propval == NULL
518                                                ? svn_wc_notify_revprop_deleted
519                                                : svn_wc_notify_revprop_set,
520                                              pool);
521       notify->prop_name = propname;
522       notify->revision = *set_rev;
523
524       ctx->notify_func2(ctx->notify_baton2, notify, pool);
525     }
526
527   return SVN_NO_ERROR;
528 }
529
530 svn_error_t *
531 svn_client__remote_propget(apr_hash_t *props,
532                            apr_array_header_t **inherited_props,
533                            const char *propname,
534                            const char *target_prefix,
535                            const char *target_relative,
536                            svn_node_kind_t kind,
537                            svn_revnum_t revnum,
538                            svn_ra_session_t *ra_session,
539                            svn_depth_t depth,
540                            apr_pool_t *result_pool,
541                            apr_pool_t *scratch_pool)
542 {
543   apr_hash_t *dirents;
544   apr_hash_t *prop_hash = NULL;
545   const svn_string_t *val;
546   const char *target_full_url =
547     svn_path_url_add_component2(target_prefix, target_relative,
548                                 scratch_pool);
549
550   if (kind == svn_node_dir)
551     {
552       SVN_ERR(svn_ra_get_dir2(ra_session,
553                               (depth >= svn_depth_files ? &dirents : NULL),
554                               NULL, &prop_hash, target_relative, revnum,
555                               SVN_DIRENT_KIND, scratch_pool));
556     }
557   else if (kind == svn_node_file)
558     {
559       SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum,
560                               NULL, NULL, &prop_hash, scratch_pool));
561     }
562   else if (kind == svn_node_none)
563     {
564       return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
565                                _("'%s' does not exist in revision %ld"),
566                                target_full_url, revnum);
567     }
568   else
569     {
570       return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
571                                _("Unknown node kind for '%s'"),
572                                target_full_url);
573     }
574
575   if (inherited_props)
576     {
577       const char *repos_root_url;
578       int i;
579       apr_array_header_t *final_iprops =
580         apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *));
581
582       /* We will filter out all but PROPNAME later, making a final copy
583          in RESULT_POOL, so pass SCRATCH_POOL for all pools. */
584       SVN_ERR(svn_ra_get_inherited_props(ra_session, inherited_props,
585                                          target_relative, revnum,
586                                          scratch_pool, scratch_pool));
587       SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
588                                      scratch_pool));
589       SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props,
590                                                  repos_root_url,
591                                                  scratch_pool,
592                                                  scratch_pool));
593
594       /* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */
595       for (i = 0; i < (*inherited_props)->nelts; i++)
596         {
597           svn_prop_inherited_item_t *iprop =
598             APR_ARRAY_IDX((*inherited_props), i, svn_prop_inherited_item_t *);
599           svn_string_t *iprop_val = svn_hash_gets(iprop->prop_hash, propname);
600
601           if (iprop_val)
602             {
603               svn_prop_inherited_item_t *new_iprop =
604                 apr_palloc(result_pool, sizeof(*new_iprop));
605               new_iprop->path_or_url =
606                 apr_pstrdup(result_pool, iprop->path_or_url);
607               new_iprop->prop_hash = apr_hash_make(result_pool);
608               svn_hash_sets(new_iprop->prop_hash,
609                             apr_pstrdup(result_pool, propname),
610                             svn_string_dup(iprop_val, result_pool));
611               APR_ARRAY_PUSH(final_iprops, svn_prop_inherited_item_t *) =
612                 new_iprop;
613             }
614         }
615       *inherited_props = final_iprops;
616     }
617
618   if (prop_hash
619       && (val = svn_hash_gets(prop_hash, propname)))
620     {
621       svn_hash_sets(props,
622                     apr_pstrdup(result_pool, target_full_url),
623                     svn_string_dup(val, result_pool));
624     }
625
626   if (depth >= svn_depth_files
627       && kind == svn_node_dir
628       && apr_hash_count(dirents) > 0)
629     {
630       apr_hash_index_t *hi;
631       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
632
633       for (hi = apr_hash_first(scratch_pool, dirents);
634            hi;
635            hi = apr_hash_next(hi))
636         {
637           const char *this_name = apr_hash_this_key(hi);
638           svn_dirent_t *this_ent = apr_hash_this_val(hi);
639           const char *new_target_relative;
640           svn_depth_t depth_below_here = depth;
641
642           svn_pool_clear(iterpool);
643
644           if (depth == svn_depth_files && this_ent->kind == svn_node_dir)
645             continue;
646
647           if (depth == svn_depth_files || depth == svn_depth_immediates)
648             depth_below_here = svn_depth_empty;
649
650           new_target_relative = svn_relpath_join(target_relative, this_name,
651                                                  iterpool);
652
653           SVN_ERR(svn_client__remote_propget(props, NULL,
654                                              propname,
655                                              target_prefix,
656                                              new_target_relative,
657                                              this_ent->kind,
658                                              revnum,
659                                              ra_session,
660                                              depth_below_here,
661                                              result_pool, iterpool));
662         }
663
664       svn_pool_destroy(iterpool);
665     }
666
667   return SVN_NO_ERROR;
668 }
669
670 /* Baton for recursive_propget_receiver(). */
671 struct recursive_propget_receiver_baton
672 {
673   apr_hash_t *props; /* Hash to collect props. */
674   apr_pool_t *pool; /* Pool to allocate additions to PROPS. */
675   svn_wc_context_t *wc_ctx;  /* Working copy context. */
676 };
677
678 /* An implementation of svn_wc__proplist_receiver_t. */
679 static svn_error_t *
680 recursive_propget_receiver(void *baton,
681                            const char *local_abspath,
682                            apr_hash_t *props,
683                            apr_pool_t *scratch_pool)
684 {
685   struct recursive_propget_receiver_baton *b = baton;
686
687   if (apr_hash_count(props))
688     {
689       apr_hash_index_t *hi = apr_hash_first(scratch_pool, props);
690       svn_hash_sets(b->props, apr_pstrdup(b->pool, local_abspath),
691                     svn_string_dup(apr_hash_this_val(hi), b->pool));
692     }
693
694   return SVN_NO_ERROR;
695 }
696
697 /* Return the property value for any PROPNAME set on TARGET in *PROPS,
698    with WC paths of char * for keys and property values of
699    svn_string_t * for values.  Assumes that PROPS is non-NULL.  Additions
700    to *PROPS are allocated in RESULT_POOL, temporary allocations happen in
701    SCRATCH_POOL.
702
703    CHANGELISTS is an array of const char * changelist names, used as a
704    restrictive filter on items whose properties are set; that is,
705    don't set properties on any item unless it's a member of one of
706    those changelists.  If CHANGELISTS is empty (or altogether NULL),
707    no changelist filtering occurs.
708
709    Treat DEPTH as in svn_client_propget3().
710 */
711 static svn_error_t *
712 get_prop_from_wc(apr_hash_t **props,
713                  const char *propname,
714                  const char *target_abspath,
715                  svn_boolean_t pristine,
716                  svn_node_kind_t kind,
717                  svn_depth_t depth,
718                  const apr_array_header_t *changelists,
719                  svn_client_ctx_t *ctx,
720                  apr_pool_t *result_pool,
721                  apr_pool_t *scratch_pool)
722 {
723   struct recursive_propget_receiver_baton rb;
724
725   /* Technically, svn_depth_unknown just means use whatever depth(s)
726      we find in the working copy.  But this is a walk over extant
727      working copy paths: if they're there at all, then by definition
728      the local depth reaches them, so let's just use svn_depth_infinity
729      to get there. */
730   if (depth == svn_depth_unknown)
731     depth = svn_depth_infinity;
732
733   if (!pristine && depth == svn_depth_infinity
734       && (!changelists || changelists->nelts == 0))
735     {
736       /* Handle this common svn:mergeinfo case more efficient than the target
737          list handling in the recursive retrieval. */
738       SVN_ERR(svn_wc__prop_retrieve_recursive(
739                             props, ctx->wc_ctx, target_abspath, propname,
740                             result_pool, scratch_pool));
741       return SVN_NO_ERROR;
742     }
743
744   *props = apr_hash_make(result_pool);
745   rb.props = *props;
746   rb.pool = result_pool;
747   rb.wc_ctx = ctx->wc_ctx;
748
749   SVN_ERR(svn_wc__prop_list_recursive(ctx->wc_ctx, target_abspath,
750                                       propname, depth, pristine,
751                                       changelists,
752                                       recursive_propget_receiver, &rb,
753                                       ctx->cancel_func, ctx->cancel_baton,
754                                       scratch_pool));
755
756   return SVN_NO_ERROR;
757 }
758
759 /* Note: this implementation is very similar to svn_client_proplist. */
760 svn_error_t *
761 svn_client_propget5(apr_hash_t **props,
762                     apr_array_header_t **inherited_props,
763                     const char *propname,
764                     const char *target,
765                     const svn_opt_revision_t *peg_revision,
766                     const svn_opt_revision_t *revision,
767                     svn_revnum_t *actual_revnum,
768                     svn_depth_t depth,
769                     const apr_array_header_t *changelists,
770                     svn_client_ctx_t *ctx,
771                     apr_pool_t *result_pool,
772                     apr_pool_t *scratch_pool)
773 {
774   svn_revnum_t revnum;
775   svn_boolean_t local_explicit_props;
776   svn_boolean_t local_iprops;
777
778   SVN_ERR(error_if_wcprop_name(propname));
779   if (!svn_path_is_url(target))
780     SVN_ERR_ASSERT(svn_dirent_is_absolute(target));
781
782   peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision,
783                                                         target);
784   revision = svn_cl__rev_default_to_peg(revision, peg_revision);
785
786   local_explicit_props =
787     (! svn_path_is_url(target)
788      && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind)
789      && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind));
790
791   local_iprops =
792     (local_explicit_props
793      && (peg_revision->kind == svn_opt_revision_working
794          || peg_revision->kind == svn_opt_revision_unspecified )
795      && (revision->kind == svn_opt_revision_working
796          || revision->kind == svn_opt_revision_unspecified ));
797
798   if (local_explicit_props)
799     {
800       svn_node_kind_t kind;
801       svn_boolean_t pristine;
802       svn_error_t *err;
803
804       /* If FALSE, we want the working revision. */
805       pristine = (revision->kind == svn_opt_revision_committed
806                   || revision->kind == svn_opt_revision_base);
807
808       SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target,
809                                 pristine, FALSE,
810                                 scratch_pool));
811
812       if (kind == svn_node_unknown || kind == svn_node_none)
813         {
814           /* svn uses SVN_ERR_UNVERSIONED_RESOURCE as warning only
815              for this function. */
816           return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
817                                    _("'%s' is not under version control"),
818                                    svn_dirent_local_style(target,
819                                                           scratch_pool));
820         }
821
822       err = svn_client__get_revision_number(&revnum, NULL, ctx->wc_ctx,
823                                             target, NULL, revision,
824                                             scratch_pool);
825       if (err && err->apr_err == SVN_ERR_CLIENT_BAD_REVISION)
826         {
827           svn_error_clear(err);
828           revnum = SVN_INVALID_REVNUM;
829         }
830       else if (err)
831         return svn_error_trace(err);
832
833       if (inherited_props && local_iprops)
834         {
835           const char *repos_root_url;
836
837           SVN_ERR(svn_wc__get_iprops(inherited_props, ctx->wc_ctx,
838                                      target, propname,
839                                      result_pool, scratch_pool));
840           SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL,
841                                             target, ctx, scratch_pool,
842                                             scratch_pool));
843           SVN_ERR(svn_client__iprop_relpaths_to_urls(*inherited_props,
844                                                      repos_root_url,
845                                                      result_pool,
846                                                      scratch_pool));
847         }
848
849       SVN_ERR(get_prop_from_wc(props, propname, target,
850                                pristine, kind,
851                                depth, changelists, ctx, result_pool,
852                                scratch_pool));
853     }
854
855   if ((inherited_props && !local_iprops)
856       || !local_explicit_props)
857     {
858       svn_ra_session_t *ra_session;
859       svn_node_kind_t kind;
860       svn_opt_revision_t new_operative_rev;
861       svn_opt_revision_t new_peg_rev;
862
863       /* Peg or operative revisions may be WC specific for
864          TARGET's explicit props, but still require us to
865          contact the repository for the inherited properties. */
866       if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)
867           || SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
868         {
869           const char *repos_relpath;
870           const char *repos_root_url;
871           const char *local_abspath;
872
873           /* Avoid assertion on the next line when somebody accidentally asks for
874              a working copy revision on a URL */
875           if (svn_path_is_url(target))
876             return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED,
877                                     NULL, NULL);
878
879           SVN_ERR_ASSERT(svn_dirent_is_absolute(target));
880           local_abspath = target;
881
882           if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
883             {
884               SVN_ERR(svn_wc__node_get_origin(NULL, NULL,
885                                               &repos_relpath,
886                                               &repos_root_url,
887                                               NULL, NULL, NULL,
888                                               ctx->wc_ctx,
889                                               local_abspath,
890                                               FALSE, /* scan_deleted */
891                                               result_pool,
892                                               scratch_pool));
893               if (repos_relpath)
894                 {
895                   target = svn_path_url_add_component2(repos_root_url,
896                                                        repos_relpath,
897                                                        scratch_pool);
898                   if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
899                     {
900                       svn_revnum_t resolved_peg_rev;
901
902                       SVN_ERR(svn_client__get_revision_number(
903                         &resolved_peg_rev, NULL, ctx->wc_ctx,
904                         local_abspath, NULL, peg_revision, scratch_pool));
905                       new_peg_rev.kind = svn_opt_revision_number;
906                       new_peg_rev.value.number = resolved_peg_rev;
907                       peg_revision = &new_peg_rev;
908                     }
909
910                   if (SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
911                     {
912                       svn_revnum_t resolved_operative_rev;
913
914                       SVN_ERR(svn_client__get_revision_number(
915                         &resolved_operative_rev, NULL, ctx->wc_ctx,
916                         local_abspath, NULL, revision, scratch_pool));
917                       new_operative_rev.kind = svn_opt_revision_number;
918                       new_operative_rev.value.number = resolved_operative_rev;
919                       revision = &new_operative_rev;
920                     }
921                 }
922               else
923                 {
924                   /* TARGET doesn't exist in the repository, so there are
925                      obviously not inherited props to be found there. */
926                   local_iprops = TRUE;
927                   *inherited_props = apr_array_make(
928                     result_pool, 0, sizeof(svn_prop_inherited_item_t *));
929                 }
930             }
931         }
932
933       /* Do we still have anything to ask the repository about? */
934       if (!local_explicit_props || !local_iprops)
935         {
936           svn_client__pathrev_t *loc;
937
938           /* Get an RA plugin for this filesystem object. */
939           SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
940                                                     target, NULL,
941                                                     peg_revision,
942                                                     revision, ctx,
943                                                     scratch_pool));
944
945           SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind,
946                                     scratch_pool));
947
948           if (!local_explicit_props)
949             *props = apr_hash_make(result_pool);
950
951           SVN_ERR(svn_client__remote_propget(
952                                  !local_explicit_props ? *props : NULL,
953                                  !local_iprops ? inherited_props : NULL,
954                                  propname, loc->url, "",
955                                  kind, loc->rev, ra_session,
956                                  depth, result_pool, scratch_pool));
957           revnum = loc->rev;
958         }
959     }
960
961   if (actual_revnum)
962     *actual_revnum = revnum;
963   return SVN_NO_ERROR;
964 }
965
966 svn_error_t *
967 svn_client_revprop_get(const char *propname,
968                        svn_string_t **propval,
969                        const char *URL,
970                        const svn_opt_revision_t *revision,
971                        svn_revnum_t *set_rev,
972                        svn_client_ctx_t *ctx,
973                        apr_pool_t *pool)
974 {
975   svn_ra_session_t *ra_session;
976   apr_pool_t *subpool = svn_pool_create(pool);
977   svn_error_t *err;
978
979   /* Open an RA session for the URL. Note that we don't have a local
980      directory, nor a place to put temp files. */
981   SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL,
982                                       ctx, subpool, subpool));
983
984   /* Resolve the revision into something real, and return that to the
985      caller as well. */
986   SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL,
987                                           ra_session, revision, subpool));
988
989   /* The actual RA call. */
990   err = svn_ra_rev_prop(ra_session, *set_rev, propname, propval, pool);
991
992   /* Close RA session */
993   svn_pool_destroy(subpool);
994   return svn_error_trace(err);
995 }
996
997
998 /* Call RECEIVER for the given PATH and its PROP_HASH and/or
999  * INHERITED_PROPERTIES.
1000  *
1001  * If PROP_HASH is null or has zero count or INHERITED_PROPERTIES is null,
1002  * then do nothing.
1003  */
1004 static svn_error_t*
1005 call_receiver(const char *path,
1006               apr_hash_t *prop_hash,
1007               apr_array_header_t *inherited_properties,
1008               svn_proplist_receiver2_t receiver,
1009               void *receiver_baton,
1010               apr_pool_t *scratch_pool)
1011 {
1012   if ((prop_hash && apr_hash_count(prop_hash))
1013       || inherited_properties)
1014     SVN_ERR(receiver(receiver_baton, path, prop_hash, inherited_properties,
1015                      scratch_pool));
1016
1017   return SVN_NO_ERROR;
1018 }
1019
1020
1021 /* Helper for the remote case of svn_client_proplist.
1022  *
1023  * If GET_EXPLICIT_PROPS is true, then call RECEIVER for paths at or under
1024  * "TARGET_PREFIX/TARGET_RELATIVE@REVNUM" (obtained using RA_SESSION) which
1025  * have regular properties.  If GET_TARGET_INHERITED_PROPS is true, then send
1026  * the target's inherited properties to the callback.
1027  *
1028  * The 'path' and keys for 'prop_hash' and 'inherited_prop' arguments to
1029  * RECEIVER are all URLs.
1030  *
1031  * RESULT_POOL is used to allocated the 'path', 'prop_hash', and
1032  * 'inherited_prop' arguments to RECEIVER.  SCRATCH_POOL is used for all
1033  * other (temporary) allocations.
1034  *
1035  * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
1036  *
1037  * If the target is a directory, only fetch properties for the files
1038  * and directories at depth DEPTH.  DEPTH has not effect on inherited
1039  * properties.
1040  */
1041 static svn_error_t *
1042 remote_proplist(const char *target_prefix,
1043                 const char *target_relative,
1044                 svn_node_kind_t kind,
1045                 svn_revnum_t revnum,
1046                 svn_ra_session_t *ra_session,
1047                 svn_boolean_t get_explicit_props,
1048                 svn_boolean_t get_target_inherited_props,
1049                 svn_depth_t depth,
1050                 svn_proplist_receiver2_t receiver,
1051                 void *receiver_baton,
1052                 svn_cancel_func_t cancel_func,
1053                 void *cancel_baton,
1054                 apr_pool_t *scratch_pool)
1055 {
1056   apr_hash_t *dirents;
1057   apr_hash_t *prop_hash = NULL;
1058   apr_hash_index_t *hi;
1059   const char *target_full_url =
1060     svn_path_url_add_component2(target_prefix, target_relative, scratch_pool);
1061   apr_array_header_t *inherited_props;
1062
1063   /* Note that we pass only the SCRATCH_POOL to svn_ra_get[dir*|file*] because
1064      we'll be filtering out non-regular properties from PROP_HASH before we
1065      return. */
1066   if (kind == svn_node_dir)
1067     {
1068       SVN_ERR(svn_ra_get_dir2(ra_session,
1069                               (depth > svn_depth_empty) ? &dirents : NULL,
1070                               NULL, &prop_hash, target_relative, revnum,
1071                               SVN_DIRENT_KIND, scratch_pool));
1072     }
1073   else if (kind == svn_node_file)
1074     {
1075       SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum,
1076                               NULL, NULL, &prop_hash, scratch_pool));
1077     }
1078   else
1079     {
1080       return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
1081                                _("Unknown node kind for '%s'"),
1082                                target_full_url);
1083     }
1084
1085   if (get_target_inherited_props)
1086     {
1087       const char *repos_root_url;
1088
1089       SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props,
1090                                          target_relative, revnum,
1091                                          scratch_pool, scratch_pool));
1092       SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
1093                                      scratch_pool));
1094       SVN_ERR(svn_client__iprop_relpaths_to_urls(inherited_props,
1095                                                  repos_root_url,
1096                                                  scratch_pool,
1097                                                  scratch_pool));
1098     }
1099   else
1100     {
1101       inherited_props = NULL;
1102     }
1103
1104   if (!get_explicit_props)
1105     prop_hash = NULL;
1106   else
1107     {
1108       /* Filter out non-regular properties, since the RA layer returns all
1109          kinds.  Copy regular properties keys/vals from the prop_hash
1110          allocated in SCRATCH_POOL to the "final" hash allocated in
1111          RESULT_POOL. */
1112       for (hi = apr_hash_first(scratch_pool, prop_hash);
1113            hi;
1114            hi = apr_hash_next(hi))
1115         {
1116           const char *name = apr_hash_this_key(hi);
1117           apr_ssize_t klen = apr_hash_this_key_len(hi);
1118           svn_prop_kind_t prop_kind;
1119
1120           prop_kind = svn_property_kind2(name);
1121
1122           if (prop_kind != svn_prop_regular_kind)
1123             {
1124               apr_hash_set(prop_hash, name, klen, NULL);
1125             }
1126         }
1127     }
1128
1129   SVN_ERR(call_receiver(target_full_url, prop_hash, inherited_props,
1130                         receiver, receiver_baton, scratch_pool));
1131
1132   if (depth > svn_depth_empty
1133       && get_explicit_props
1134       && (kind == svn_node_dir) && (apr_hash_count(dirents) > 0))
1135     {
1136       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1137
1138       for (hi = apr_hash_first(scratch_pool, dirents);
1139            hi;
1140            hi = apr_hash_next(hi))
1141         {
1142           const char *this_name = apr_hash_this_key(hi);
1143           svn_dirent_t *this_ent = apr_hash_this_val(hi);
1144           const char *new_target_relative;
1145
1146           if (cancel_func)
1147             SVN_ERR(cancel_func(cancel_baton));
1148
1149           svn_pool_clear(iterpool);
1150
1151           new_target_relative = svn_relpath_join(target_relative,
1152                                                  this_name, iterpool);
1153
1154           if (this_ent->kind == svn_node_file
1155               || depth > svn_depth_files)
1156             {
1157               svn_depth_t depth_below_here = depth;
1158
1159               if (depth == svn_depth_immediates)
1160                 depth_below_here = svn_depth_empty;
1161
1162               SVN_ERR(remote_proplist(target_prefix,
1163                                       new_target_relative,
1164                                       this_ent->kind,
1165                                       revnum,
1166                                       ra_session,
1167                                       TRUE /* get_explicit_props */,
1168                                       FALSE /* get_target_inherited_props */,
1169                                       depth_below_here,
1170                                       receiver, receiver_baton,
1171                                       cancel_func, cancel_baton,
1172                                       iterpool));
1173             }
1174         }
1175
1176       svn_pool_destroy(iterpool);
1177     }
1178
1179   return SVN_NO_ERROR;
1180 }
1181
1182
1183 /* Baton for recursive_proplist_receiver(). */
1184 struct recursive_proplist_receiver_baton
1185 {
1186   svn_wc_context_t *wc_ctx;  /* Working copy context. */
1187   svn_proplist_receiver2_t wrapped_receiver;  /* Proplist receiver to call. */
1188   void *wrapped_receiver_baton;    /* Baton for the proplist receiver. */
1189   apr_array_header_t *iprops;
1190
1191   /* Anchor, anchor_abspath pair for converting to relative paths */
1192   const char *anchor;
1193   const char *anchor_abspath;
1194 };
1195
1196 /* An implementation of svn_wc__proplist_receiver_t. */
1197 static svn_error_t *
1198 recursive_proplist_receiver(void *baton,
1199                             const char *local_abspath,
1200                             apr_hash_t *props,
1201                             apr_pool_t *scratch_pool)
1202 {
1203   struct recursive_proplist_receiver_baton *b = baton;
1204   const char *path;
1205   apr_array_header_t *iprops = NULL;
1206
1207   if (b->iprops
1208       && ! strcmp(local_abspath, b->anchor_abspath))
1209     {
1210       /* Report iprops with the properties for the anchor */
1211       iprops = b->iprops;
1212       b->iprops = NULL;
1213     }
1214   else if (b->iprops)
1215     {
1216       /* No report for the root?
1217          Report iprops anyway */
1218
1219       SVN_ERR(b->wrapped_receiver(b->wrapped_receiver_baton,
1220                                   b->anchor ? b->anchor : b->anchor_abspath,
1221                                   NULL /* prop_hash */,
1222                                   b->iprops,
1223                                   scratch_pool));
1224       b->iprops = NULL;
1225     }
1226
1227   /* Attempt to convert absolute paths to relative paths for
1228    * presentation purposes, if needed. */
1229   if (b->anchor && b->anchor_abspath)
1230     {
1231       path = svn_dirent_join(b->anchor,
1232                              svn_dirent_skip_ancestor(b->anchor_abspath,
1233                                                       local_abspath),
1234                              scratch_pool);
1235     }
1236   else
1237     path = local_abspath;
1238
1239   return svn_error_trace(b->wrapped_receiver(b->wrapped_receiver_baton,
1240                                              path, props, iprops,
1241                                              scratch_pool));
1242 }
1243
1244 /* Helper for svn_client_proplist4 when retrieving properties and/or
1245    inherited properties from the repository.  Except as noted below,
1246    all arguments are as per svn_client_proplist4.
1247
1248    GET_EXPLICIT_PROPS controls if explicit props are retrieved. */
1249 static svn_error_t *
1250 get_remote_props(const char *path_or_url,
1251                  const svn_opt_revision_t *peg_revision,
1252                  const svn_opt_revision_t *revision,
1253                  svn_depth_t depth,
1254                  svn_boolean_t get_explicit_props,
1255                  svn_boolean_t get_target_inherited_props,
1256                  svn_proplist_receiver2_t receiver,
1257                  void *receiver_baton,
1258                  svn_client_ctx_t *ctx,
1259                  apr_pool_t *scratch_pool)
1260 {
1261   svn_ra_session_t *ra_session;
1262   svn_node_kind_t kind;
1263   svn_opt_revision_t new_operative_rev;
1264   svn_opt_revision_t new_peg_rev;
1265   svn_client__pathrev_t *loc;
1266
1267   /* Peg or operative revisions may be WC specific for
1268      PATH_OR_URL's explicit props, but still require us to
1269      contact the repository for the inherited properties. */
1270   if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)
1271       || SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
1272     {
1273       const char *repos_relpath;
1274       const char *repos_root_url;
1275       const char *local_abspath;
1276       svn_boolean_t is_copy;
1277
1278       /* Avoid assertion on the next line when somebody accidentally asks for
1279          a working copy revision on a URL */
1280       if (svn_path_is_url(path_or_url))
1281         return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED,
1282                                 NULL, NULL);
1283
1284       SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
1285                                       scratch_pool));
1286
1287       if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
1288         {
1289           SVN_ERR(svn_wc__node_get_origin(&is_copy,
1290                                           NULL,
1291                                           &repos_relpath,
1292                                           &repos_root_url,
1293                                           NULL, NULL, NULL,
1294                                           ctx->wc_ctx,
1295                                           local_abspath,
1296                                           FALSE, /* scan_deleted */
1297                                           scratch_pool,
1298                                           scratch_pool));
1299           if (repos_relpath)
1300             {
1301               path_or_url =
1302                 svn_path_url_add_component2(repos_root_url,
1303                                             repos_relpath,
1304                                             scratch_pool);
1305               if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
1306                 {
1307                   svn_revnum_t resolved_peg_rev;
1308
1309                   SVN_ERR(svn_client__get_revision_number(&resolved_peg_rev,
1310                                                           NULL, ctx->wc_ctx,
1311                                                           local_abspath, NULL,
1312                                                           peg_revision,
1313                                                           scratch_pool));
1314                   new_peg_rev.kind = svn_opt_revision_number;
1315                   new_peg_rev.value.number = resolved_peg_rev;
1316                   peg_revision = &new_peg_rev;
1317                 }
1318
1319               if (SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
1320                 {
1321                   svn_revnum_t resolved_operative_rev;
1322
1323                   SVN_ERR(svn_client__get_revision_number(
1324                     &resolved_operative_rev,
1325                     NULL, ctx->wc_ctx,
1326                     local_abspath, NULL,
1327                     revision,
1328                     scratch_pool));
1329                   new_operative_rev.kind = svn_opt_revision_number;
1330                   new_operative_rev.value.number = resolved_operative_rev;
1331                   revision = &new_operative_rev;
1332                 }
1333             }
1334           else
1335             {
1336                   /* PATH_OR_URL doesn't exist in the repository, so there are
1337                      obviously not inherited props to be found there. If we
1338                      aren't looking for explicit props then we're done. */
1339                   if (!get_explicit_props)
1340                     return SVN_NO_ERROR;
1341             }
1342         }
1343     }
1344
1345   /* Get an RA session for this URL. */
1346   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
1347                                             path_or_url, NULL,
1348                                             peg_revision,
1349                                             revision, ctx,
1350                                             scratch_pool));
1351
1352   SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind,
1353                             scratch_pool));
1354
1355   SVN_ERR(remote_proplist(loc->url, "", kind, loc->rev, ra_session,
1356                           get_explicit_props,
1357                           get_target_inherited_props,
1358                           depth, receiver, receiver_baton,
1359                           ctx->cancel_func, ctx->cancel_baton,
1360                           scratch_pool));
1361   return SVN_NO_ERROR;
1362 }
1363
1364 /* Helper for svn_client_proplist4 when retrieving properties and
1365    possibly inherited properties from the WC.  All arguments are as
1366    per svn_client_proplist4. */
1367 static svn_error_t *
1368 get_local_props(const char *path_or_url,
1369                 const svn_opt_revision_t *revision,
1370                 svn_depth_t depth,
1371                 const apr_array_header_t *changelists,
1372                 svn_boolean_t get_target_inherited_props,
1373                 svn_proplist_receiver2_t receiver,
1374                 void *receiver_baton,
1375                 svn_client_ctx_t *ctx,
1376                 apr_pool_t *scratch_pool)
1377 {
1378   svn_boolean_t pristine;
1379   svn_node_kind_t kind;
1380   apr_hash_t *changelist_hash = NULL;
1381   const char *local_abspath;
1382   apr_array_header_t *iprops = NULL;
1383
1384   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
1385                                   scratch_pool));
1386
1387   pristine = ((revision->kind == svn_opt_revision_committed)
1388               || (revision->kind == svn_opt_revision_base));
1389
1390   SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, local_abspath,
1391                             pristine, FALSE, scratch_pool));
1392
1393   if (kind == svn_node_unknown || kind == svn_node_none)
1394     {
1395       /* svn uses SVN_ERR_UNVERSIONED_RESOURCE as warning only
1396          for this function. */
1397       return svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
1398                                _("'%s' is not under version control"),
1399                                svn_dirent_local_style(local_abspath,
1400                                                       scratch_pool));
1401     }
1402
1403   if (get_target_inherited_props)
1404     {
1405       const char *repos_root_url;
1406
1407       SVN_ERR(svn_wc__get_iprops(&iprops, ctx->wc_ctx, local_abspath,
1408                                  NULL, scratch_pool, scratch_pool));
1409       SVN_ERR(svn_client_get_repos_root(&repos_root_url, NULL, local_abspath,
1410                                         ctx, scratch_pool, scratch_pool));
1411       SVN_ERR(svn_client__iprop_relpaths_to_urls(iprops, repos_root_url,
1412                                                  scratch_pool,
1413                                                  scratch_pool));
1414     }
1415
1416   if (changelists && changelists->nelts)
1417     SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash,
1418                                        changelists, scratch_pool));
1419
1420   /* Fetch, recursively or not. */
1421   if (kind == svn_node_dir)
1422     {
1423       struct recursive_proplist_receiver_baton rb;
1424
1425       rb.wc_ctx = ctx->wc_ctx;
1426       rb.wrapped_receiver = receiver;
1427       rb.wrapped_receiver_baton = receiver_baton;
1428       rb.iprops = iprops;
1429       rb.anchor_abspath = local_abspath;
1430
1431       if (strcmp(path_or_url, local_abspath) != 0)
1432         {
1433           rb.anchor = path_or_url;
1434         }
1435       else
1436         {
1437           rb.anchor = NULL;
1438         }
1439
1440       SVN_ERR(svn_wc__prop_list_recursive(ctx->wc_ctx, local_abspath, NULL,
1441                                           depth, pristine, changelists,
1442                                           recursive_proplist_receiver, &rb,
1443                                           ctx->cancel_func, ctx->cancel_baton,
1444                                           scratch_pool));
1445
1446       if (rb.iprops)
1447         {
1448           /* We didn't report for the root. Report iprops anyway */
1449           SVN_ERR(call_receiver(path_or_url, NULL /* props */, rb.iprops,
1450                                 receiver, receiver_baton, scratch_pool));
1451         }
1452     }
1453   else if (svn_wc__changelist_match(ctx->wc_ctx, local_abspath,
1454                                     changelist_hash, scratch_pool))
1455     {
1456       apr_hash_t *props;
1457
1458         if (pristine)
1459           SVN_ERR(svn_wc_get_pristine_props(&props,
1460                                             ctx->wc_ctx, local_abspath,
1461                                             scratch_pool, scratch_pool));
1462         else
1463           {
1464             svn_error_t *err;
1465
1466             err = svn_wc_prop_list2(&props, ctx->wc_ctx, local_abspath,
1467                                     scratch_pool, scratch_pool);
1468
1469
1470             if (err)
1471               {
1472                 if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
1473                   return svn_error_trace(err);
1474                 /* As svn_wc_prop_list2() doesn't return NULL for locally-deleted
1475                    let's do that here.  */
1476                 svn_error_clear(err);
1477                 props = apr_hash_make(scratch_pool);
1478               }
1479           }
1480
1481       SVN_ERR(call_receiver(path_or_url, props, iprops,
1482                             receiver, receiver_baton, scratch_pool));
1483
1484     }
1485   return SVN_NO_ERROR;
1486 }
1487
1488 svn_error_t *
1489 svn_client_proplist4(const char *path_or_url,
1490                      const svn_opt_revision_t *peg_revision,
1491                      const svn_opt_revision_t *revision,
1492                      svn_depth_t depth,
1493                      const apr_array_header_t *changelists,
1494                      svn_boolean_t get_target_inherited_props,
1495                      svn_proplist_receiver2_t receiver,
1496                      void *receiver_baton,
1497                      svn_client_ctx_t *ctx,
1498                      apr_pool_t *scratch_pool)
1499 {
1500   svn_boolean_t local_explicit_props;
1501   svn_boolean_t local_iprops;
1502
1503   peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision,
1504                                                         path_or_url);
1505   revision = svn_cl__rev_default_to_peg(revision, peg_revision);
1506
1507   if (depth == svn_depth_unknown)
1508     depth = svn_depth_empty;
1509
1510   /* Are explicit props available locally? */
1511   local_explicit_props =
1512     (! svn_path_is_url(path_or_url)
1513      && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(peg_revision->kind)
1514      && SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind));
1515
1516   /* If we want iprops are they available locally? */
1517   local_iprops =
1518     (get_target_inherited_props /* We want iprops */
1519      && local_explicit_props /* No local explicit props means no local iprops. */
1520      && (peg_revision->kind == svn_opt_revision_working
1521          || peg_revision->kind == svn_opt_revision_unspecified )
1522      && (revision->kind == svn_opt_revision_working
1523          || revision->kind == svn_opt_revision_unspecified ));
1524
1525   if ((get_target_inherited_props && !local_iprops)
1526       || !local_explicit_props)
1527     {
1528       SVN_ERR(get_remote_props(path_or_url, peg_revision, revision, depth,
1529                                !local_explicit_props,
1530                                (get_target_inherited_props && !local_iprops),
1531                                receiver, receiver_baton, ctx, scratch_pool));
1532     }
1533
1534   if (local_explicit_props)
1535     {
1536       SVN_ERR(get_local_props(path_or_url, revision, depth, changelists,
1537                               local_iprops, receiver, receiver_baton, ctx,
1538                               scratch_pool));
1539     }
1540
1541   return SVN_NO_ERROR;
1542 }
1543
1544 svn_error_t *
1545 svn_client_revprop_list(apr_hash_t **props,
1546                         const char *URL,
1547                         const svn_opt_revision_t *revision,
1548                         svn_revnum_t *set_rev,
1549                         svn_client_ctx_t *ctx,
1550                         apr_pool_t *pool)
1551 {
1552   svn_ra_session_t *ra_session;
1553   apr_hash_t *proplist;
1554   apr_pool_t *subpool = svn_pool_create(pool);
1555   svn_error_t *err;
1556
1557   /* Open an RA session for the URL. Note that we don't have a local
1558      directory, nor a place to put temp files. */
1559   SVN_ERR(svn_client_open_ra_session2(&ra_session, URL, NULL,
1560                                       ctx, subpool, subpool));
1561
1562   /* Resolve the revision into something real, and return that to the
1563      caller as well. */
1564   SVN_ERR(svn_client__get_revision_number(set_rev, NULL, ctx->wc_ctx, NULL,
1565                                           ra_session, revision, subpool));
1566
1567   /* The actual RA call. */
1568   err = svn_ra_rev_proplist(ra_session, *set_rev, &proplist, pool);
1569
1570   *props = proplist;
1571   svn_pool_destroy(subpool); /* Close RA session */
1572   return svn_error_trace(err);
1573 }