1251881Speter/*
2251881Speter * properties.c:  stuff related to Subversion properties
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <apr_pools.h>
27251881Speter#include <apr_hash.h>
28251881Speter#include <apr_tables.h>
29251881Speter#include <string.h>       /* for strncmp() */
30251881Speter#include "svn_hash.h"
31251881Speter#include "svn_string.h"
32251881Speter#include "svn_props.h"
33251881Speter#include "svn_error.h"
34251881Speter#include "svn_ctype.h"
35251881Speter#include "private/svn_subr_private.h"
36251881Speter
37251881Speter
38251881Speter/* All Subversion-specific versioned node properties
39251881Speter * known to this client, that are applicable to both a file and a dir.
40251881Speter */
41251881Speter#define SVN_PROP__NODE_COMMON_PROPS SVN_PROP_MERGEINFO, \
42251881Speter                                    SVN_PROP_TEXT_TIME, \
43251881Speter                                    SVN_PROP_OWNER, \
44251881Speter                                    SVN_PROP_GROUP, \
45251881Speter                                    SVN_PROP_UNIX_MODE,
46251881Speter
47251881Speter/* All Subversion-specific versioned node properties
48251881Speter * known to this client, that are applicable to a dir only.
49251881Speter */
50251881Speter#define SVN_PROP__NODE_DIR_ONLY_PROPS SVN_PROP_IGNORE, \
51251881Speter                                      SVN_PROP_INHERITABLE_IGNORES, \
52251881Speter                                      SVN_PROP_INHERITABLE_AUTO_PROPS, \
53251881Speter                                      SVN_PROP_EXTERNALS,
54251881Speter
55251881Speter/* All Subversion-specific versioned node properties
56251881Speter * known to this client, that are applicable to a file only.
57251881Speter */
58251881Speter#define SVN_PROP__NODE_FILE_ONLY_PROPS SVN_PROP_MIME_TYPE, \
59251881Speter                                       SVN_PROP_EOL_STYLE, \
60251881Speter                                       SVN_PROP_KEYWORDS, \
61251881Speter                                       SVN_PROP_EXECUTABLE, \
62251881Speter                                       SVN_PROP_NEEDS_LOCK, \
63251881Speter                                       SVN_PROP_SPECIAL,
64251881Speter
65251881Speterstatic const char *const known_rev_props[]
66251881Speter = { SVN_PROP_REVISION_ALL_PROPS
67251881Speter     NULL };
68251881Speter
69251881Speterstatic const char *const known_node_props[]
70251881Speter = { SVN_PROP__NODE_COMMON_PROPS
71251881Speter     SVN_PROP__NODE_DIR_ONLY_PROPS
72251881Speter     SVN_PROP__NODE_FILE_ONLY_PROPS
73251881Speter     NULL };
74251881Speter
75251881Speterstatic const char *const known_dir_props[]
76251881Speter = { SVN_PROP__NODE_COMMON_PROPS
77251881Speter     SVN_PROP__NODE_DIR_ONLY_PROPS
78251881Speter     NULL };
79251881Speter
80251881Speterstatic const char *const known_file_props[]
81251881Speter = { SVN_PROP__NODE_COMMON_PROPS
82251881Speter     SVN_PROP__NODE_FILE_ONLY_PROPS
83251881Speter     NULL };
84251881Speter
85251881Speterstatic svn_boolean_t
86251881Speteris_known_prop(const char *prop_name,
87251881Speter              const char *const *known_props)
88251881Speter{
89251881Speter  while (*known_props)
90251881Speter    {
91251881Speter      if (strcmp(prop_name, *known_props++) == 0)
92251881Speter        return TRUE;
93251881Speter    }
94251881Speter  return FALSE;
95251881Speter}
96251881Speter
97251881Spetersvn_boolean_t
98251881Spetersvn_prop_is_known_svn_rev_prop(const char *prop_name)
99251881Speter{
100251881Speter  return is_known_prop(prop_name, known_rev_props);
101251881Speter}
102251881Speter
103251881Spetersvn_boolean_t
104251881Spetersvn_prop_is_known_svn_node_prop(const char *prop_name)
105251881Speter{
106251881Speter  return is_known_prop(prop_name, known_node_props);
107251881Speter}
108251881Speter
109251881Spetersvn_boolean_t
110251881Spetersvn_prop_is_known_svn_file_prop(const char *prop_name)
111251881Speter{
112251881Speter  return is_known_prop(prop_name, known_file_props);
113251881Speter}
114251881Speter
115251881Spetersvn_boolean_t
116251881Spetersvn_prop_is_known_svn_dir_prop(const char *prop_name)
117251881Speter{
118251881Speter  return is_known_prop(prop_name, known_dir_props);
119251881Speter}
120251881Speter
121251881Speter
122251881Spetersvn_boolean_t
123251881Spetersvn_prop_is_svn_prop(const char *prop_name)
124251881Speter{
125251881Speter  return strncmp(prop_name, SVN_PROP_PREFIX, (sizeof(SVN_PROP_PREFIX) - 1))
126251881Speter         == 0;
127251881Speter}
128251881Speter
129251881Speter
130251881Spetersvn_boolean_t
131251881Spetersvn_prop_has_svn_prop(const apr_hash_t *props, apr_pool_t *pool)
132251881Speter{
133251881Speter  apr_hash_index_t *hi;
134251881Speter  const void *prop_name;
135251881Speter
136251881Speter  if (! props)
137251881Speter    return FALSE;
138251881Speter
139251881Speter  for (hi = apr_hash_first(pool, (apr_hash_t *)props); hi;
140251881Speter       hi = apr_hash_next(hi))
141251881Speter    {
142251881Speter      apr_hash_this(hi, &prop_name, NULL, NULL);
143251881Speter      if (svn_prop_is_svn_prop((const char *) prop_name))
144251881Speter        return TRUE;
145251881Speter    }
146251881Speter
147251881Speter  return FALSE;
148251881Speter}
149251881Speter
150251881Speter
151251881Speter#define SIZEOF_WC_PREFIX (sizeof(SVN_PROP_WC_PREFIX) - 1)
152251881Speter#define SIZEOF_ENTRY_PREFIX (sizeof(SVN_PROP_ENTRY_PREFIX) - 1)
153251881Speter
154251881Spetersvn_prop_kind_t
155251881Spetersvn_property_kind2(const char *prop_name)
156251881Speter{
157251881Speter
158251881Speter  if (strncmp(prop_name, SVN_PROP_WC_PREFIX, SIZEOF_WC_PREFIX) == 0)
159251881Speter    return svn_prop_wc_kind;
160251881Speter
161251881Speter  if (strncmp(prop_name, SVN_PROP_ENTRY_PREFIX, SIZEOF_ENTRY_PREFIX) == 0)
162251881Speter    return svn_prop_entry_kind;
163251881Speter
164251881Speter  return svn_prop_regular_kind;
165251881Speter}
166251881Speter
167251881Speter
168251881Speter/* NOTE: this function is deprecated, but we cannot move it to deprecated.c
169251881Speter   because we need the SIZEOF_*_PREFIX constant symbols defined above.  */
170251881Spetersvn_prop_kind_t
171251881Spetersvn_property_kind(int *prefix_len,
172251881Speter                  const char *prop_name)
173251881Speter{
174251881Speter  svn_prop_kind_t kind = svn_property_kind2(prop_name);
175251881Speter
176251881Speter  if (prefix_len)
177251881Speter    {
178251881Speter      if (kind == svn_prop_wc_kind)
179251881Speter        *prefix_len = SIZEOF_WC_PREFIX;
180251881Speter      else if (kind == svn_prop_entry_kind)
181251881Speter        *prefix_len = SIZEOF_ENTRY_PREFIX;
182251881Speter      else
183251881Speter        *prefix_len = 0;
184251881Speter    }
185251881Speter
186251881Speter  return kind;
187251881Speter}
188251881Speter
189251881Speter
190251881Spetersvn_error_t *
191251881Spetersvn_categorize_props(const apr_array_header_t *proplist,
192251881Speter                     apr_array_header_t **entry_props,
193251881Speter                     apr_array_header_t **wc_props,
194251881Speter                     apr_array_header_t **regular_props,
195251881Speter                     apr_pool_t *pool)
196251881Speter{
197251881Speter  int i;
198251881Speter  if (entry_props)
199251881Speter    *entry_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
200251881Speter  if (wc_props)
201251881Speter    *wc_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
202251881Speter  if (regular_props)
203251881Speter    *regular_props = apr_array_make(pool, 1, sizeof(svn_prop_t));
204251881Speter
205251881Speter  for (i = 0; i < proplist->nelts; i++)
206251881Speter    {
207251881Speter      svn_prop_t *prop, *newprop;
208251881Speter      enum svn_prop_kind kind;
209251881Speter
210251881Speter      prop = &APR_ARRAY_IDX(proplist, i, svn_prop_t);
211251881Speter      kind = svn_property_kind2(prop->name);
212251881Speter      newprop = NULL;
213251881Speter
214251881Speter      if (kind == svn_prop_regular_kind)
215251881Speter        {
216251881Speter          if (regular_props)
217251881Speter            newprop = apr_array_push(*regular_props);
218251881Speter        }
219251881Speter      else if (kind == svn_prop_wc_kind)
220251881Speter        {
221251881Speter          if (wc_props)
222251881Speter            newprop = apr_array_push(*wc_props);
223251881Speter        }
224251881Speter      else if (kind == svn_prop_entry_kind)
225251881Speter        {
226251881Speter          if (entry_props)
227251881Speter            newprop = apr_array_push(*entry_props);
228251881Speter        }
229251881Speter      else
230251881Speter        /* Technically this can't happen, but might as well have the
231251881Speter           code ready in case that ever changes. */
232251881Speter        return svn_error_createf(SVN_ERR_BAD_PROP_KIND, NULL,
233251881Speter                                 "Bad property kind for property '%s'",
234251881Speter                                 prop->name);
235251881Speter
236251881Speter      if (newprop)
237251881Speter        {
238251881Speter          newprop->name = prop->name;
239251881Speter          newprop->value = prop->value;
240251881Speter        }
241251881Speter    }
242251881Speter
243251881Speter  return SVN_NO_ERROR;
244251881Speter}
245251881Speter
246251881Speter
247251881Spetersvn_error_t *
248251881Spetersvn_prop_diffs(apr_array_header_t **propdiffs,
249251881Speter               const apr_hash_t *target_props,
250251881Speter               const apr_hash_t *source_props,
251251881Speter               apr_pool_t *pool)
252251881Speter{
253251881Speter  apr_hash_index_t *hi;
254251881Speter  apr_array_header_t *ary = apr_array_make(pool, 1, sizeof(svn_prop_t));
255251881Speter
256251881Speter  /* Note: we will be storing the pointers to the keys (from the hashes)
257251881Speter     into the propdiffs array.  It is acceptable for us to
258251881Speter     reference the same memory as the base/target_props hash. */
259251881Speter
260251881Speter  /* Loop over SOURCE_PROPS and examine each key.  This will allow us to
261251881Speter     detect any `deletion' events or `set-modification' events.  */
262251881Speter  for (hi = apr_hash_first(pool, (apr_hash_t *)source_props); hi;
263251881Speter       hi = apr_hash_next(hi))
264251881Speter    {
265251881Speter      const void *key;
266251881Speter      apr_ssize_t klen;
267251881Speter      void *val;
268251881Speter      const svn_string_t *propval1, *propval2;
269251881Speter
270251881Speter      /* Get next property */
271251881Speter      apr_hash_this(hi, &key, &klen, &val);
272251881Speter      propval1 = val;
273251881Speter
274251881Speter      /* Does property name exist in TARGET_PROPS? */
275251881Speter      propval2 = apr_hash_get((apr_hash_t *)target_props, key, klen);
276251881Speter
277251881Speter      if (propval2 == NULL)
278251881Speter        {
279251881Speter          /* Add a delete event to the array */
280251881Speter          svn_prop_t *p = apr_array_push(ary);
281251881Speter          p->name = key;
282251881Speter          p->value = NULL;
283251881Speter        }
284251881Speter      else if (! svn_string_compare(propval1, propval2))
285251881Speter        {
286251881Speter          /* Add a set (modification) event to the array */
287251881Speter          svn_prop_t *p = apr_array_push(ary);
288251881Speter          p->name = key;
289251881Speter          p->value = svn_string_dup(propval2, pool);
290251881Speter        }
291251881Speter    }
292251881Speter
293251881Speter  /* Loop over TARGET_PROPS and examine each key.  This allows us to
294251881Speter     detect `set-creation' events */
295251881Speter  for (hi = apr_hash_first(pool, (apr_hash_t *)target_props); hi;
296251881Speter       hi = apr_hash_next(hi))
297251881Speter    {
298251881Speter      const void *key;
299251881Speter      apr_ssize_t klen;
300251881Speter      void *val;
301251881Speter      const svn_string_t *propval;
302251881Speter
303251881Speter      /* Get next property */
304251881Speter      apr_hash_this(hi, &key, &klen, &val);
305251881Speter      propval = val;
306251881Speter
307251881Speter      /* Does property name exist in SOURCE_PROPS? */
308251881Speter      if (NULL == apr_hash_get((apr_hash_t *)source_props, key, klen))
309251881Speter        {
310251881Speter          /* Add a set (creation) event to the array */
311251881Speter          svn_prop_t *p = apr_array_push(ary);
312251881Speter          p->name = key;
313251881Speter          p->value = svn_string_dup(propval, pool);
314251881Speter        }
315251881Speter    }
316251881Speter
317251881Speter  /* Done building our array of user events. */
318251881Speter  *propdiffs = ary;
319251881Speter
320251881Speter  return SVN_NO_ERROR;
321251881Speter}
322251881Speter
323251881Speterapr_hash_t *
324251881Spetersvn_prop__patch(const apr_hash_t *original_props,
325251881Speter                const apr_array_header_t *prop_changes,
326251881Speter                apr_pool_t *pool)
327251881Speter{
328251881Speter  apr_hash_t *props = apr_hash_copy(pool, original_props);
329251881Speter  int i;
330251881Speter
331251881Speter  for (i = 0; i < prop_changes->nelts; i++)
332251881Speter    {
333251881Speter      const svn_prop_t *p = &APR_ARRAY_IDX(prop_changes, i, svn_prop_t);
334251881Speter
335251881Speter      svn_hash_sets(props, p->name, p->value);
336251881Speter    }
337251881Speter  return props;
338251881Speter}
339251881Speter
340251881Speter/**
341251881Speter * Reallocate the members of PROP using POOL.
342251881Speter */
343251881Speterstatic void
344251881Spetersvn_prop__members_dup(svn_prop_t *prop, apr_pool_t *pool)
345251881Speter{
346251881Speter  if (prop->name)
347251881Speter    prop->name = apr_pstrdup(pool, prop->name);
348251881Speter  if (prop->value)
349251881Speter    prop->value = svn_string_dup(prop->value, pool);
350251881Speter}
351251881Speter
352251881Spetersvn_prop_t *
353251881Spetersvn_prop_dup(const svn_prop_t *prop, apr_pool_t *pool)
354251881Speter{
355251881Speter  svn_prop_t *new_prop = apr_palloc(pool, sizeof(*new_prop));
356251881Speter
357251881Speter  *new_prop = *prop;
358251881Speter
359251881Speter  svn_prop__members_dup(new_prop, pool);
360251881Speter
361251881Speter  return new_prop;
362251881Speter}
363251881Speter
364251881Speterapr_array_header_t *
365251881Spetersvn_prop_array_dup(const apr_array_header_t *array, apr_pool_t *pool)
366251881Speter{
367251881Speter  int i;
368251881Speter  apr_array_header_t *new_array = apr_array_copy(pool, array);
369251881Speter  for (i = 0; i < new_array->nelts; ++i)
370251881Speter    {
371251881Speter      svn_prop_t *elt = &APR_ARRAY_IDX(new_array, i, svn_prop_t);
372251881Speter      svn_prop__members_dup(elt, pool);
373251881Speter    }
374251881Speter  return new_array;
375251881Speter}
376251881Speter
377251881Speterapr_array_header_t *
378251881Spetersvn_prop_hash_to_array(const apr_hash_t *hash,
379251881Speter                       apr_pool_t *pool)
380251881Speter{
381251881Speter  apr_hash_index_t *hi;
382251881Speter  apr_array_header_t *array = apr_array_make(pool,
383251881Speter                                             apr_hash_count((apr_hash_t *)hash),
384251881Speter                                             sizeof(svn_prop_t));
385251881Speter
386251881Speter  for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi;
387251881Speter       hi = apr_hash_next(hi))
388251881Speter    {
389251881Speter      const void *key;
390251881Speter      void *val;
391251881Speter      svn_prop_t prop;
392251881Speter
393251881Speter      apr_hash_this(hi, &key, NULL, &val);
394251881Speter      prop.name = key;
395251881Speter      prop.value = val;
396251881Speter      APR_ARRAY_PUSH(array, svn_prop_t) = prop;
397251881Speter    }
398251881Speter
399251881Speter  return array;
400251881Speter}
401251881Speter
402251881Speterapr_hash_t *
403251881Spetersvn_prop_hash_dup(const apr_hash_t *hash,
404251881Speter                  apr_pool_t *pool)
405251881Speter{
406251881Speter  apr_hash_index_t *hi;
407251881Speter  apr_hash_t *new_hash = apr_hash_make(pool);
408251881Speter
409251881Speter  for (hi = apr_hash_first(pool, (apr_hash_t *)hash); hi;
410251881Speter       hi = apr_hash_next(hi))
411251881Speter    {
412251881Speter      const void *key;
413251881Speter      apr_ssize_t klen;
414251881Speter      void *prop;
415251881Speter
416251881Speter      apr_hash_this(hi, &key, &klen, &prop);
417251881Speter      apr_hash_set(new_hash, apr_pstrmemdup(pool, key, klen), klen,
418251881Speter                   svn_string_dup(prop, pool));
419251881Speter    }
420251881Speter  return new_hash;
421251881Speter}
422251881Speter
423251881Speterapr_hash_t *
424251881Spetersvn_prop_array_to_hash(const apr_array_header_t *properties,
425251881Speter                       apr_pool_t *pool)
426251881Speter{
427251881Speter  int i;
428251881Speter  apr_hash_t *prop_hash = apr_hash_make(pool);
429251881Speter
430251881Speter  for (i = 0; i < properties->nelts; i++)
431251881Speter    {
432251881Speter      const svn_prop_t *prop = &APR_ARRAY_IDX(properties, i, svn_prop_t);
433251881Speter      svn_hash_sets(prop_hash, prop->name, prop->value);
434251881Speter    }
435251881Speter
436251881Speter  return prop_hash;
437251881Speter}
438251881Speter
439251881Spetersvn_boolean_t
440251881Spetersvn_prop_is_boolean(const char *prop_name)
441251881Speter{
442251881Speter  /* If we end up with more than 3 of these, we should probably put
443251881Speter     them in a table and use bsearch.  With only three, it doesn't
444251881Speter     make any speed difference.  */
445251881Speter  if (strcmp(prop_name, SVN_PROP_EXECUTABLE) == 0
446251881Speter      || strcmp(prop_name, SVN_PROP_NEEDS_LOCK) == 0
447251881Speter      || strcmp(prop_name, SVN_PROP_SPECIAL) == 0)
448251881Speter    return TRUE;
449251881Speter  return FALSE;
450251881Speter}
451251881Speter
452251881Speter
453251881Spetersvn_boolean_t
454251881Spetersvn_prop_needs_translation(const char *propname)
455251881Speter{
456251881Speter  /* ### Someday, we may want to be picky and choosy about which
457251881Speter     properties require UTF8 and EOL conversion.  For now, all "svn:"
458251881Speter     props need it.  */
459251881Speter
460251881Speter  return svn_prop_is_svn_prop(propname);
461251881Speter}
462251881Speter
463251881Speter
464251881Spetersvn_boolean_t
465251881Spetersvn_prop_name_is_valid(const char *prop_name)
466251881Speter{
467251881Speter  const char *p = prop_name;
468251881Speter
469251881Speter  /* The characters we allow use identical representations in UTF8
470251881Speter     and ASCII, so we can just test for the appropriate ASCII codes.
471251881Speter     But we can't use standard C character notation ('A', 'B', etc)
472251881Speter     because there's no guarantee that this C environment is using
473251881Speter     ASCII. */
474251881Speter
475251881Speter  if (!(svn_ctype_isalpha(*p)
476251881Speter        || *p == SVN_CTYPE_ASCII_COLON
477251881Speter        || *p == SVN_CTYPE_ASCII_UNDERSCORE))
478251881Speter    return FALSE;
479251881Speter  p++;
480251881Speter  for (; *p; p++)
481251881Speter    {
482251881Speter      if (!(svn_ctype_isalnum(*p)
483251881Speter            || *p == SVN_CTYPE_ASCII_MINUS
484251881Speter            || *p == SVN_CTYPE_ASCII_DOT
485251881Speter            || *p == SVN_CTYPE_ASCII_COLON
486251881Speter            || *p == SVN_CTYPE_ASCII_UNDERSCORE))
487251881Speter        return FALSE;
488251881Speter    }
489251881Speter  return TRUE;
490251881Speter}
491251881Speter
492251881Speterconst char *
493251881Spetersvn_prop_get_value(const apr_hash_t *props,
494251881Speter                   const char *prop_name)
495251881Speter{
496251881Speter  svn_string_t *str;
497251881Speter
498251881Speter  if (!props)
499251881Speter    return NULL;
500251881Speter
501251881Speter  str = svn_hash_gets((apr_hash_t *)props, prop_name);
502251881Speter
503251881Speter  if (str)
504251881Speter    return str->data;
505251881Speter
506251881Speter  return NULL;
507251881Speter}
508