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
65 static const char *const known_rev_props[]
66 = { SVN_PROP_REVISION_ALL_PROPS
67 NULL };
68
69 static 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
75 static const char *const known_dir_props[]
76 = { SVN_PROP__NODE_COMMON_PROPS
77 SVN_PROP__NODE_DIR_ONLY_PROPS
78 NULL };
79
80 static const char *const known_file_props[]
81 = { SVN_PROP__NODE_COMMON_PROPS
82 SVN_PROP__NODE_FILE_ONLY_PROPS
83 NULL };
84
85 static svn_boolean_t
is_known_prop(const char * prop_name,const char * const * known_props)86 is_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
97 svn_boolean_t
svn_prop_is_known_svn_rev_prop(const char * prop_name)98 svn_prop_is_known_svn_rev_prop(const char *prop_name)
99 {
100 return is_known_prop(prop_name, known_rev_props);
101 }
102
103 svn_boolean_t
svn_prop_is_known_svn_node_prop(const char * prop_name)104 svn_prop_is_known_svn_node_prop(const char *prop_name)
105 {
106 return is_known_prop(prop_name, known_node_props);
107 }
108
109 svn_boolean_t
svn_prop_is_known_svn_file_prop(const char * prop_name)110 svn_prop_is_known_svn_file_prop(const char *prop_name)
111 {
112 return is_known_prop(prop_name, known_file_props);
113 }
114
115 svn_boolean_t
svn_prop_is_known_svn_dir_prop(const char * prop_name)116 svn_prop_is_known_svn_dir_prop(const char *prop_name)
117 {
118 return is_known_prop(prop_name, known_dir_props);
119 }
120
121
122 svn_boolean_t
svn_prop_is_svn_prop(const char * prop_name)123 svn_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
130 svn_boolean_t
svn_prop_has_svn_prop(const apr_hash_t * props,apr_pool_t * pool)131 svn_prop_has_svn_prop(const apr_hash_t *props, apr_pool_t *pool)
132 {
133 apr_hash_index_t *hi;
134
135 if (! props)
136 return FALSE;
137
138 for (hi = apr_hash_first(pool, (apr_hash_t *)props); hi;
139 hi = apr_hash_next(hi))
140 {
141 const char *prop_name = apr_hash_this_key(hi);
142
143 if (svn_prop_is_svn_prop(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
154 svn_prop_kind_t
svn_property_kind2(const char * prop_name)155 svn_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. */
170 svn_prop_kind_t
svn_property_kind(int * prefix_len,const char * prop_name)171 svn_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
190 svn_error_t *
svn_categorize_props(const apr_array_header_t * proplist,apr_array_header_t ** entry_props,apr_array_header_t ** wc_props,apr_array_header_t ** regular_props,apr_pool_t * pool)191 svn_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
247 svn_error_t *
svn_prop_diffs(apr_array_header_t ** propdiffs,const apr_hash_t * target_props,const apr_hash_t * source_props,apr_pool_t * pool)248 svn_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
323 apr_hash_t *
svn_prop__patch(const apr_hash_t * original_props,const apr_array_header_t * prop_changes,apr_pool_t * pool)324 svn_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 */
343 static void
svn_prop__members_dup(svn_prop_t * prop,apr_pool_t * pool)344 svn_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
352 svn_prop_t *
svn_prop_dup(const svn_prop_t * prop,apr_pool_t * pool)353 svn_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
364 apr_array_header_t *
svn_prop_array_dup(const apr_array_header_t * array,apr_pool_t * pool)365 svn_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
377 apr_array_header_t *
svn_prop_hash_to_array(const apr_hash_t * hash,apr_pool_t * pool)378 svn_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
402 apr_hash_t *
svn_prop_hash_dup(const apr_hash_t * hash,apr_pool_t * pool)403 svn_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
423 apr_hash_t *
svn_prop_array_to_hash(const apr_array_header_t * properties,apr_pool_t * pool)424 svn_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
439 svn_boolean_t
svn_prop_is_boolean(const char * prop_name)440 svn_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
453 svn_boolean_t
svn_prop_needs_translation(const char * propname)454 svn_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
464 svn_boolean_t
svn_prop_name_is_valid(const char * prop_name)465 svn_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
492 const char *
svn_prop_get_value(const apr_hash_t * props,const char * prop_name)493 svn_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