1/*
2 * properties.c:  stuff related to Subversion properties
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#include <apr_pools.h>
27#include <apr_hash.h>
28#include <apr_tables.h>
29#include <string.h>       /* for strncmp() */
30#include "svn_hash.h"
31#include "svn_string.h"
32#include "svn_props.h"
33#include "svn_error.h"
34#include "svn_ctype.h"
35#include "private/svn_subr_private.h"
36
37
38/* All Subversion-specific versioned node properties
39 * known to this client, that are applicable to both a file and a dir.
40 */
41#define SVN_PROP__NODE_COMMON_PROPS SVN_PROP_MERGEINFO, \
42                                    SVN_PROP_TEXT_TIME, \
43                                    SVN_PROP_OWNER, \
44                                    SVN_PROP_GROUP, \
45                                    SVN_PROP_UNIX_MODE,
46
47/* All Subversion-specific versioned node properties
48 * known to this client, that are applicable to a dir only.
49 */
50#define SVN_PROP__NODE_DIR_ONLY_PROPS SVN_PROP_IGNORE, \
51                                      SVN_PROP_INHERITABLE_IGNORES, \
52                                      SVN_PROP_INHERITABLE_AUTO_PROPS, \
53                                      SVN_PROP_EXTERNALS,
54
55/* All Subversion-specific versioned node properties
56 * known to this client, that are applicable to a file only.
57 */
58#define SVN_PROP__NODE_FILE_ONLY_PROPS SVN_PROP_MIME_TYPE, \
59                                       SVN_PROP_EOL_STYLE, \
60                                       SVN_PROP_KEYWORDS, \
61                                       SVN_PROP_EXECUTABLE, \
62                                       SVN_PROP_NEEDS_LOCK, \
63                                       SVN_PROP_SPECIAL,
64
65static const char *const known_rev_props[]
66 = { SVN_PROP_REVISION_ALL_PROPS
67     NULL };
68
69static const char *const known_node_props[]
70 = { SVN_PROP__NODE_COMMON_PROPS
71     SVN_PROP__NODE_DIR_ONLY_PROPS
72     SVN_PROP__NODE_FILE_ONLY_PROPS
73     NULL };
74
75static const char *const known_dir_props[]
76 = { SVN_PROP__NODE_COMMON_PROPS
77     SVN_PROP__NODE_DIR_ONLY_PROPS
78     NULL };
79
80static const char *const known_file_props[]
81 = { SVN_PROP__NODE_COMMON_PROPS
82     SVN_PROP__NODE_FILE_ONLY_PROPS
83     NULL };
84
85static svn_boolean_t
86is_known_prop(const char *prop_name,
87              const char *const *known_props)
88{
89  while (*known_props)
90    {
91      if (strcmp(prop_name, *known_props++) == 0)
92        return TRUE;
93    }
94  return FALSE;
95}
96
97svn_boolean_t
98svn_prop_is_known_svn_rev_prop(const char *prop_name)
99{
100  return is_known_prop(prop_name, known_rev_props);
101}
102
103svn_boolean_t
104svn_prop_is_known_svn_node_prop(const char *prop_name)
105{
106  return is_known_prop(prop_name, known_node_props);
107}
108
109svn_boolean_t
110svn_prop_is_known_svn_file_prop(const char *prop_name)
111{
112  return is_known_prop(prop_name, known_file_props);
113}
114
115svn_boolean_t
116svn_prop_is_known_svn_dir_prop(const char *prop_name)
117{
118  return is_known_prop(prop_name, known_dir_props);
119}
120
121
122svn_boolean_t
123svn_prop_is_svn_prop(const char *prop_name)
124{
125  return strncmp(prop_name, SVN_PROP_PREFIX, (sizeof(SVN_PROP_PREFIX) - 1))
126         == 0;
127}
128
129
130svn_boolean_t
131svn_prop_has_svn_prop(const apr_hash_t *props, apr_pool_t *pool)
132{
133  apr_hash_index_t *hi;
134  const void *prop_name;
135
136  if (! props)
137    return FALSE;
138
139  for (hi = apr_hash_first(pool, (apr_hash_t *)props); hi;
140       hi = apr_hash_next(hi))
141    {
142      apr_hash_this(hi, &prop_name, NULL, NULL);
143      if (svn_prop_is_svn_prop((const char *) prop_name))
144        return TRUE;
145    }
146
147  return FALSE;
148}
149
150
151#define SIZEOF_WC_PREFIX (sizeof(SVN_PROP_WC_PREFIX) - 1)
152#define SIZEOF_ENTRY_PREFIX (sizeof(SVN_PROP_ENTRY_PREFIX) - 1)
153
154svn_prop_kind_t
155svn_property_kind2(const char *prop_name)
156{
157
158  if (strncmp(prop_name, SVN_PROP_WC_PREFIX, SIZEOF_WC_PREFIX) == 0)
159    return svn_prop_wc_kind;
160
161  if (strncmp(prop_name, SVN_PROP_ENTRY_PREFIX, SIZEOF_ENTRY_PREFIX) == 0)
162    return svn_prop_entry_kind;
163
164  return svn_prop_regular_kind;
165}
166
167
168/* NOTE: this function is deprecated, but we cannot move it to deprecated.c
169   because we need the SIZEOF_*_PREFIX constant symbols defined above.  */
170svn_prop_kind_t
171svn_property_kind(int *prefix_len,
172                  const char *prop_name)
173{
174  svn_prop_kind_t kind = svn_property_kind2(prop_name);
175
176  if (prefix_len)
177    {
178      if (kind == svn_prop_wc_kind)
179        *prefix_len = SIZEOF_WC_PREFIX;
180      else if (kind == svn_prop_entry_kind)
181        *prefix_len = SIZEOF_ENTRY_PREFIX;
182      else
183        *prefix_len = 0;
184    }
185
186  return kind;
187}
188
189
190svn_error_t *
191svn_categorize_props(const apr_array_header_t *proplist,
192                     apr_array_header_t **entry_props,
193                     apr_array_header_t **wc_props,
194                     apr_array_header_t **regular_props,
195                     apr_pool_t *pool)
196{
197  int i;
198  if (entry_props)
199    *entry_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
200  if (wc_props)
201    *wc_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
202  if (regular_props)
203    *regular_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
204
205  for (i = 0; i < proplist->nelts; i++)
206    {
207      svn_prop_t *prop, *newprop;
208      enum svn_prop_kind kind;
209
210      prop = &APR_ARRAY_IDX(proplist, i, svn_prop_t);
211      kind = svn_property_kind2(prop->name);
212      newprop = NULL;
213
214      if (kind == svn_prop_regular_kind)
215        {
216          if (regular_props)
217            newprop = apr_array_push(*regular_props);
218        }
219      else if (kind == svn_prop_wc_kind)
220        {
221          if (wc_props)
222            newprop = apr_array_push(*wc_props);
223        }
224      else if (kind == svn_prop_entry_kind)
225        {
226          if (entry_props)
227            newprop = apr_array_push(*entry_props);
228        }
229      else
230        /* Technically this can't happen, but might as well have the
231           code ready in case that ever changes. */
232        return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
233                                 "Bad property kind for property '%s'",
234                                 prop->name);
235
236      if (newprop)
237        {
238          newprop->name = prop->name;
239          newprop->value = prop->value;
240        }
241    }
242
243  return SVN_NO_ERROR;
244}
245
246
247svn_error_t *
248svn_prop_diffs(apr_array_header_t **propdiffs,
249               const apr_hash_t *target_props,
250               const apr_hash_t *source_props,
251               apr_pool_t *pool)
252{
253  apr_hash_index_t *hi;
254  apr_array_header_t *ary = apr_array_make(pool, 1, sizeof(svn_prop_t));
255
256  /* Note: we will be storing the pointers to the keys (from the hashes)
257     into the propdiffs array.  It is acceptable for us to
258     reference the same memory as the base/target_props hash. */
259
260  /* Loop over SOURCE_PROPS and examine each key.  This will allow us to
261     detect any `deletion' events or `set-modification' events.  */
262  for (hi = apr_hash_first(pool, (apr_hash_t *)source_props); hi;
263       hi = apr_hash_next(hi))
264    {
265      const void *key;
266      apr_ssize_t klen;
267      void *val;
268      const svn_string_t *propval1, *propval2;
269
270      /* Get next property */
271      apr_hash_this(hi, &key, &klen, &val);
272      propval1 = val;
273
274      /* Does property name exist in TARGET_PROPS? */
275      propval2 = apr_hash_get((apr_hash_t *)target_props, key, klen);
276
277      if (propval2 == NULL)
278        {
279          /* Add a delete event to the array */
280          svn_prop_t *p = apr_array_push(ary);
281          p->name = key;
282          p->value = NULL;
283        }
284      else if (! svn_string_compare(propval1, propval2))
285        {
286          /* Add a set (modification) event to the array */
287          svn_prop_t *p = apr_array_push(ary);
288          p->name = key;
289          p->value = svn_string_dup(propval2, pool);
290        }
291    }
292
293  /* Loop over TARGET_PROPS and examine each key.  This allows us to
294     detect `set-creation' events */
295  for (hi = apr_hash_first(pool, (apr_hash_t *)target_props); hi;
296       hi = apr_hash_next(hi))
297    {
298      const void *key;
299      apr_ssize_t klen;
300      void *val;
301      const svn_string_t *propval;
302
303      /* Get next property */
304      apr_hash_this(hi, &key, &klen, &val);
305      propval = val;
306
307      /* Does property name exist in SOURCE_PROPS? */
308      if (NULL == apr_hash_get((apr_hash_t *)source_props, key, klen))
309        {
310          /* Add a set (creation) event to the array */
311          svn_prop_t *p = apr_array_push(ary);
312          p->name = key;
313          p->value = svn_string_dup(propval, pool);
314        }
315    }
316
317  /* Done building our array of user events. */
318  *propdiffs = ary;
319
320  return SVN_NO_ERROR;
321}
322
323apr_hash_t *
324svn_prop__patch(const apr_hash_t *original_props,
325                const apr_array_header_t *prop_changes,
326                apr_pool_t *pool)
327{
328  apr_hash_t *props = apr_hash_copy(pool, original_props);
329  int i;
330
331  for (i = 0; i < prop_changes->nelts; i++)
332    {
333      const svn_prop_t *p = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t);
334
335      svn_hash_sets(props, p->name, p->value);
336    }
337  return props;
338}
339
340/**
341 * Reallocate the members of PROP using POOL.
342 */
343static void
344svn_prop__members_dup(svn_prop_t *prop, apr_pool_t *pool)
345{
346  if (prop->name)
347    prop->name = apr_pstrdup(pool, prop->name);
348  if (prop->value)
349    prop->value = svn_string_dup(prop->value, pool);
350}
351
352svn_prop_t *
353svn_prop_dup(const svn_prop_t *prop, apr_pool_t *pool)
354{
355  svn_prop_t *new_prop = apr_palloc(pool, sizeof(*new_prop));
356
357  *new_prop = *prop;
358
359  svn_prop__members_dup(new_prop, pool);
360
361  return new_prop;
362}
363
364apr_array_header_t *
365svn_prop_array_dup(const apr_array_header_t *array, apr_pool_t *pool)
366{
367  int i;
368  apr_array_header_t *new_array = apr_array_copy(pool, array);
369  for (i = 0; i < new_array->nelts; ++i)
370    {
371      svn_prop_t *elt = &APR_ARRAY_IDX(new_array, i, svn_prop_t);
372      svn_prop__members_dup(elt, pool);
373    }
374  return new_array;
375}
376
377apr_array_header_t *
378svn_prop_hash_to_array(const apr_hash_t *hash,
379                       apr_pool_t *pool)
380{
381  apr_hash_index_t *hi;
382  apr_array_header_t *array = apr_array_make(pool,
383                                             apr_hash_count((apr_hash_t *)hash),
384                                             sizeof(svn_prop_t));
385
386  for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi;
387       hi = apr_hash_next(hi))
388    {
389      const void *key;
390      void *val;
391      svn_prop_t prop;
392
393      apr_hash_this(hi, &key, NULL, &val);
394      prop.name = key;
395      prop.value = val;
396      APR_ARRAY_PUSH(array, svn_prop_t) = prop;
397    }
398
399  return array;
400}
401
402apr_hash_t *
403svn_prop_hash_dup(const apr_hash_t *hash,
404                  apr_pool_t *pool)
405{
406  apr_hash_index_t *hi;
407  apr_hash_t *new_hash = apr_hash_make(pool);
408
409  for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi;
410       hi = apr_hash_next(hi))
411    {
412      const void *key;
413      apr_ssize_t klen;
414      void *prop;
415
416      apr_hash_this(hi, &key, &klen, &prop);
417      apr_hash_set(new_hash, apr_pstrmemdup(pool, key, klen), klen,
418                   svn_string_dup(prop, pool));
419    }
420  return new_hash;
421}
422
423apr_hash_t *
424svn_prop_array_to_hash(const apr_array_header_t *properties,
425                       apr_pool_t *pool)
426{
427  int i;
428  apr_hash_t *prop_hash = apr_hash_make(pool);
429
430  for (i = 0; i < properties->nelts; i++)
431    {
432      const svn_prop_t *prop = &APR_ARRAY_IDX(properties, i, svn_prop_t);
433      svn_hash_sets(prop_hash, prop->name, prop->value);
434    }
435
436  return prop_hash;
437}
438
439svn_boolean_t
440svn_prop_is_boolean(const char *prop_name)
441{
442  /* If we end up with more than 3 of these, we should probably put
443     them in a table and use bsearch.  With only three, it doesn't
444     make any speed difference.  */
445  if (strcmp(prop_name, SVN_PROP_EXECUTABLE) == 0
446      || strcmp(prop_name, SVN_PROP_NEEDS_LOCK) == 0
447      || strcmp(prop_name, SVN_PROP_SPECIAL) == 0)
448    return TRUE;
449  return FALSE;
450}
451
452
453svn_boolean_t
454svn_prop_needs_translation(const char *propname)
455{
456  /* ### Someday, we may want to be picky and choosy about which
457     properties require UTF8 and EOL conversion.  For now, all "svn:"
458     props need it.  */
459
460  return svn_prop_is_svn_prop(propname);
461}
462
463
464svn_boolean_t
465svn_prop_name_is_valid(const char *prop_name)
466{
467  const char *p = prop_name;
468
469  /* The characters we allow use identical representations in UTF8
470     and ASCII, so we can just test for the appropriate ASCII codes.
471     But we can't use standard C character notation ('A', 'B', etc)
472     because there's no guarantee that this C environment is using
473     ASCII. */
474
475  if (!(svn_ctype_isalpha(*p)
476        || *p == SVN_CTYPE_ASCII_COLON
477        || *p == SVN_CTYPE_ASCII_UNDERSCORE))
478    return FALSE;
479  p++;
480  for (; *p; p++)
481    {
482      if (!(svn_ctype_isalnum(*p)
483            || *p == SVN_CTYPE_ASCII_MINUS
484            || *p == SVN_CTYPE_ASCII_DOT
485            || *p == SVN_CTYPE_ASCII_COLON
486            || *p == SVN_CTYPE_ASCII_UNDERSCORE))
487        return FALSE;
488    }
489  return TRUE;
490}
491
492const char *
493svn_prop_get_value(const apr_hash_t *props,
494                   const char *prop_name)
495{
496  svn_string_t *str;
497
498  if (!props)
499    return NULL;
500
501  str = svn_hash_gets((apr_hash_t *)props, prop_name);
502
503  if (str)
504    return str->data;
505
506  return NULL;
507}
508