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, &current, 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