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