1 /*
2  * config.c :  reading configuration information
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 <assert.h>
27 
28 #define APR_WANT_STRFUNC
29 #define APR_WANT_MEMFUNC
30 #include <apr_want.h>
31 
32 #include <apr_general.h>
33 #include <apr_lib.h>
34 #include "svn_hash.h"
35 #include "svn_error.h"
36 #include "svn_pools.h"
37 #include "config_impl.h"
38 
39 #include "svn_private_config.h"
40 #include "private/svn_dep_compat.h"
41 #include "private/svn_subr_private.h"
42 
43 
44 
45 
46 /* Section table entries. */
47 typedef struct cfg_section_t cfg_section_t;
48 struct cfg_section_t
49 {
50   /* The section name. */
51   const char *name;
52 
53   /* Table of cfg_option_t's. */
54   apr_hash_t *options;
55 };
56 
57 
58 /* Option table entries. */
59 typedef struct cfg_option_t cfg_option_t;
60 struct cfg_option_t
61 {
62   /* The option name. */
63   const char *name;
64 
65   /* The option name, converted into a hash key. */
66   const char *hash_key;
67 
68   /* The unexpanded option value. */
69   const char *value;
70 
71   /* The expanded option value. */
72   const char *x_value;
73 
74   /* Expansion flag. If this is TRUE, this value has already been expanded.
75      In this case, if x_value is NULL, no expansions were necessary,
76      and value should be used directly. */
77   svn_boolean_t expanded;
78 };
79 
80 
81 
82 svn_error_t *
svn_config_create2(svn_config_t ** cfgp,svn_boolean_t section_names_case_sensitive,svn_boolean_t option_names_case_sensitive,apr_pool_t * result_pool)83 svn_config_create2(svn_config_t **cfgp,
84                    svn_boolean_t section_names_case_sensitive,
85                    svn_boolean_t option_names_case_sensitive,
86                    apr_pool_t *result_pool)
87 {
88   svn_config_t *cfg = apr_palloc(result_pool, sizeof(*cfg));
89 
90   cfg->sections = apr_hash_make(result_pool);
91   cfg->pool = result_pool;
92   cfg->x_pool = svn_pool_create(result_pool);
93   cfg->x_values = FALSE;
94   cfg->tmp_key = svn_stringbuf_create_empty(result_pool);
95   cfg->tmp_value = svn_stringbuf_create_empty(result_pool);
96   cfg->section_names_case_sensitive = section_names_case_sensitive;
97   cfg->option_names_case_sensitive = option_names_case_sensitive;
98   cfg->read_only = FALSE;
99 
100   *cfgp = cfg;
101   return SVN_NO_ERROR;
102 }
103 
104 svn_error_t *
svn_config_read3(svn_config_t ** cfgp,const char * file,svn_boolean_t must_exist,svn_boolean_t section_names_case_sensitive,svn_boolean_t option_names_case_sensitive,apr_pool_t * result_pool)105 svn_config_read3(svn_config_t **cfgp, const char *file,
106                  svn_boolean_t must_exist,
107                  svn_boolean_t section_names_case_sensitive,
108                  svn_boolean_t option_names_case_sensitive,
109                  apr_pool_t *result_pool)
110 {
111   svn_config_t *cfg;
112   svn_error_t *err;
113 
114   SVN_ERR(svn_config_create2(&cfg,
115                              section_names_case_sensitive,
116                              option_names_case_sensitive,
117                              result_pool));
118 
119   /* Yes, this is platform-specific code in Subversion, but there's no
120      practical way to migrate it into APR, as it's simultaneously
121      Subversion-specific and Windows-specific.  Even if we eventually
122      want to have APR offer a generic config-reading interface, it
123      makes sense to test it here first and migrate it later. */
124 #ifdef WIN32
125   if (0 == strncmp(file, SVN_REGISTRY_PREFIX, SVN_REGISTRY_PREFIX_LEN))
126     err = svn_config__parse_registry(cfg, file + SVN_REGISTRY_PREFIX_LEN,
127                                      must_exist, result_pool);
128   else
129 #endif /* WIN32 */
130     err = svn_config__parse_file(cfg, file, must_exist, result_pool);
131 
132   if (err != SVN_NO_ERROR)
133     return err;
134   else
135     *cfgp = cfg;
136 
137   return SVN_NO_ERROR;
138 }
139 
140 svn_error_t *
svn_config_parse(svn_config_t ** cfgp,svn_stream_t * stream,svn_boolean_t section_names_case_sensitive,svn_boolean_t option_names_case_sensitive,apr_pool_t * result_pool)141 svn_config_parse(svn_config_t **cfgp, svn_stream_t *stream,
142                  svn_boolean_t section_names_case_sensitive,
143                  svn_boolean_t option_names_case_sensitive,
144                  apr_pool_t *result_pool)
145 {
146   svn_config_t *cfg;
147   svn_error_t *err;
148   apr_pool_t *scratch_pool = svn_pool_create(result_pool);
149 
150   err = svn_config_create2(&cfg,
151                            section_names_case_sensitive,
152                            option_names_case_sensitive,
153                            result_pool);
154 
155   if (err == SVN_NO_ERROR)
156     err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
157 
158   if (err == SVN_NO_ERROR)
159     *cfgp = cfg;
160 
161   svn_pool_destroy(scratch_pool);
162 
163   return err;
164 }
165 
166 /* Read various configuration sources into *CFGP, in this order, with
167  * later reads overriding the results of earlier ones:
168  *
169  *    1. SYS_REGISTRY_PATH   (only on Win32, but ignored if NULL)
170  *
171  *    2. SYS_FILE_PATH       (everywhere, but ignored if NULL)
172  *
173  *    3. USR_REGISTRY_PATH   (only on Win32, but ignored if NULL)
174  *
175  *    4. USR_FILE_PATH       (everywhere, but ignored if NULL)
176  *
177  * Allocate *CFGP in POOL.  Even if no configurations are read,
178  * allocate an empty *CFGP.
179  */
180 static svn_error_t *
read_all(svn_config_t ** cfgp,const char * sys_registry_path,const char * usr_registry_path,const char * sys_file_path,const char * usr_file_path,apr_pool_t * pool)181 read_all(svn_config_t **cfgp,
182          const char *sys_registry_path,
183          const char *usr_registry_path,
184          const char *sys_file_path,
185          const char *usr_file_path,
186          apr_pool_t *pool)
187 {
188   svn_boolean_t red_config = FALSE;  /* "red" is the past tense of "read" */
189 
190   /*** Read system-wide configurations first... ***/
191 
192 #ifdef WIN32
193   if (sys_registry_path)
194     {
195       SVN_ERR(svn_config_read3(cfgp, sys_registry_path, FALSE, FALSE, FALSE,
196                                pool));
197       red_config = TRUE;
198     }
199 #endif /* WIN32 */
200 
201   if (sys_file_path)
202     {
203       if (red_config)
204         SVN_ERR(svn_config_merge(*cfgp, sys_file_path, FALSE));
205       else
206         {
207           SVN_ERR(svn_config_read3(cfgp, sys_file_path,
208                                    FALSE, FALSE, FALSE, pool));
209           red_config = TRUE;
210         }
211     }
212 
213   /*** ...followed by per-user configurations. ***/
214 
215 #ifdef WIN32
216   if (usr_registry_path)
217     {
218       if (red_config)
219         SVN_ERR(svn_config_merge(*cfgp, usr_registry_path, FALSE));
220       else
221         {
222           SVN_ERR(svn_config_read3(cfgp, usr_registry_path,
223                                    FALSE, FALSE, FALSE, pool));
224           red_config = TRUE;
225         }
226     }
227 #endif /* WIN32 */
228 
229   if (usr_file_path)
230     {
231       if (red_config)
232         SVN_ERR(svn_config_merge(*cfgp, usr_file_path, FALSE));
233       else
234         {
235           SVN_ERR(svn_config_read3(cfgp, usr_file_path,
236                                    FALSE, FALSE, FALSE, pool));
237           red_config = TRUE;
238         }
239     }
240 
241   if (! red_config)
242     SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
243 
244   return SVN_NO_ERROR;
245 }
246 
247 
248 /* CONFIG_DIR provides an override for the default behavior of reading
249    the default set of overlay files described by read_all()'s doc
250    string.  Returns non-NULL *CFG or an error. */
251 static svn_error_t *
get_category_config(svn_config_t ** cfg,const char * config_dir,const char * category,apr_pool_t * pool)252 get_category_config(svn_config_t **cfg,
253                     const char *config_dir,
254                     const char *category,
255                     apr_pool_t *pool)
256 {
257   const char *usr_reg_path = NULL, *sys_reg_path = NULL;
258   const char *usr_cfg_path, *sys_cfg_path;
259   svn_error_t *err = NULL;
260 
261   *cfg = NULL;
262 
263   if (! config_dir)
264     {
265 #ifdef WIN32
266       sys_reg_path = apr_pstrcat(pool, SVN_REGISTRY_SYS_CONFIG_PATH,
267                                  category, SVN_VA_NULL);
268       usr_reg_path = apr_pstrcat(pool, SVN_REGISTRY_USR_CONFIG_PATH,
269                                  category, SVN_VA_NULL);
270 #endif /* WIN32 */
271 
272       err = svn_config__sys_config_path(&sys_cfg_path, category, pool);
273       if ((err) && (err->apr_err == SVN_ERR_BAD_FILENAME))
274         {
275           sys_cfg_path = NULL;
276           svn_error_clear(err);
277         }
278       else if (err)
279         return err;
280     }
281   else
282     sys_cfg_path = NULL;
283 
284   SVN_ERR(svn_config_get_user_config_path(&usr_cfg_path, config_dir, category,
285                                           pool));
286   return read_all(cfg, sys_reg_path, usr_reg_path,
287                   sys_cfg_path, usr_cfg_path, pool);
288 }
289 
290 
291 svn_error_t *
svn_config_get_config(apr_hash_t ** cfg_hash,const char * config_dir,apr_pool_t * pool)292 svn_config_get_config(apr_hash_t **cfg_hash,
293                       const char *config_dir,
294                       apr_pool_t *pool)
295 {
296   svn_config_t *cfg;
297   *cfg_hash = apr_hash_make(pool);
298 
299   SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_SERVERS,
300                               pool));
301   svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, cfg);
302 
303   SVN_ERR(get_category_config(&cfg, config_dir, SVN_CONFIG_CATEGORY_CONFIG,
304                               pool));
305   svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, cfg);
306 
307   return SVN_NO_ERROR;
308 }
309 
310 svn_error_t *
svn_config__get_default_config(apr_hash_t ** cfg_hash,apr_pool_t * pool)311 svn_config__get_default_config(apr_hash_t **cfg_hash,
312                                apr_pool_t *pool)
313 {
314   svn_config_t *empty_cfg;
315   *cfg_hash = apr_hash_make(pool);
316 
317   SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool));
318   svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_CONFIG, empty_cfg);
319 
320   SVN_ERR(svn_config_create2(&empty_cfg, FALSE, FALSE, pool));
321   svn_hash_sets(*cfg_hash, SVN_CONFIG_CATEGORY_SERVERS, empty_cfg);
322 
323   return SVN_NO_ERROR;
324 }
325 
326 
327 
328 /* Iterate through CFG, passing BATON to CALLBACK for every (SECTION, OPTION)
329    pair.  Stop if CALLBACK returns TRUE.  Allocate from POOL. */
330 static void
for_each_option(svn_config_t * cfg,void * baton,apr_pool_t * pool,svn_boolean_t callback (void * same_baton,cfg_section_t * section,cfg_option_t * option))331 for_each_option(svn_config_t *cfg, void *baton, apr_pool_t *pool,
332                 svn_boolean_t callback(void *same_baton,
333                                        cfg_section_t *section,
334                                        cfg_option_t *option))
335 {
336   apr_hash_index_t *sec_ndx;
337   for (sec_ndx = apr_hash_first(pool, cfg->sections);
338        sec_ndx != NULL;
339        sec_ndx = apr_hash_next(sec_ndx))
340     {
341       void *sec_ptr;
342       cfg_section_t *sec;
343       apr_hash_index_t *opt_ndx;
344 
345       apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
346       sec = sec_ptr;
347 
348       for (opt_ndx = apr_hash_first(pool, sec->options);
349            opt_ndx != NULL;
350            opt_ndx = apr_hash_next(opt_ndx))
351         {
352           void *opt_ptr;
353           cfg_option_t *opt;
354 
355           apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
356           opt = opt_ptr;
357 
358           if (callback(baton, sec, opt))
359             return;
360         }
361     }
362 }
363 
364 
365 
366 static svn_boolean_t
merge_callback(void * baton,cfg_section_t * section,cfg_option_t * option)367 merge_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
368 {
369   svn_config_set(baton, section->name, option->name, option->value);
370   return FALSE;
371 }
372 
373 svn_error_t *
svn_config_merge(svn_config_t * cfg,const char * file,svn_boolean_t must_exist)374 svn_config_merge(svn_config_t *cfg, const char *file,
375                  svn_boolean_t must_exist)
376 {
377   /* The original config hash shouldn't change if there's an error
378      while reading the confguration, so read into a temporary table.
379      ### We could use a tmp subpool for this, since merge_cfg is going
380      to be tossed afterwards.  Premature optimization, though? */
381   svn_config_t *merge_cfg;
382   SVN_ERR(svn_config_read3(&merge_cfg, file, must_exist,
383                            cfg->section_names_case_sensitive,
384                            cfg->option_names_case_sensitive,
385                            cfg->pool));
386 
387   /* Now copy the new options into the original table. */
388   for_each_option(merge_cfg, cfg, merge_cfg->pool, merge_callback);
389   return SVN_NO_ERROR;
390 }
391 
392 
393 
394 /* Remove variable expansions from CFG.  Walk through the options tree,
395    killing all expanded values, then clear the expanded value pool. */
396 static svn_boolean_t
rmex_callback(void * baton,cfg_section_t * section,cfg_option_t * option)397 rmex_callback(void *baton, cfg_section_t *section, cfg_option_t *option)
398 {
399   /* Only clear the `expanded' flag if the value actually contains
400      variable expansions. */
401   if (option->expanded && option->x_value != NULL)
402     {
403       option->x_value = NULL;
404       option->expanded = FALSE;
405     }
406 
407   return FALSE;
408 }
409 
410 static void
remove_expansions(svn_config_t * cfg)411 remove_expansions(svn_config_t *cfg)
412 {
413   if (!cfg->x_values)
414     return;
415 
416   for_each_option(cfg, NULL, cfg->x_pool, rmex_callback);
417   svn_pool_clear(cfg->x_pool);
418   cfg->x_values = FALSE;
419 }
420 
421 
422 
423 /* Canonicalize a string for hashing.  Modifies KEY in place. */
424 static APR_INLINE char *
make_hash_key(char * key)425 make_hash_key(char *key)
426 {
427   register char *p;
428   for (p = key; *p != 0; ++p)
429     *p = (char)apr_tolower(*p);
430   return key;
431 }
432 
433 /* Return the value for KEY in HASH.  If CASE_SENSITIVE is FALSE,
434    BUFFER will be used to construct the normalized hash key. */
435 static void *
get_hash_value(apr_hash_t * hash,svn_stringbuf_t * buffer,const char * key,svn_boolean_t case_sensitive)436 get_hash_value(apr_hash_t *hash,
437                svn_stringbuf_t *buffer,
438                const char *key,
439                svn_boolean_t case_sensitive)
440 {
441   apr_size_t i;
442   apr_size_t len = strlen(key);
443 
444   if (case_sensitive)
445     return apr_hash_get(hash, key, len);
446 
447   svn_stringbuf_ensure(buffer, len);
448   for (i = 0; i < len; ++i)
449     buffer->data[i] = (char)apr_tolower(key[i]);
450 
451   return apr_hash_get(hash, buffer->data, len);
452 }
453 
454 /* Return a pointer to an option in CFG, or NULL if it doesn't exist.
455    if SECTIONP is non-null, return a pointer to the option's section.
456    OPTION may be NULL. */
457 static cfg_option_t *
find_option(svn_config_t * cfg,const char * section,const char * option,cfg_section_t ** sectionp)458 find_option(svn_config_t *cfg, const char *section, const char *option,
459             cfg_section_t **sectionp)
460 {
461   void *sec_ptr = get_hash_value(cfg->sections, cfg->tmp_key, section,
462                                  cfg->section_names_case_sensitive);
463   if (sectionp != NULL)
464     *sectionp = sec_ptr;
465 
466   if (sec_ptr != NULL && option != NULL)
467     {
468       cfg_section_t *sec = sec_ptr;
469       cfg_option_t *opt = get_hash_value(sec->options, cfg->tmp_key, option,
470                                          cfg->option_names_case_sensitive);
471       /* NOTE: ConfigParser's sections are case sensitive. */
472       if (opt == NULL
473           && apr_strnatcasecmp(section, SVN_CONFIG__DEFAULT_SECTION) != 0)
474         /* Options which aren't found in the requested section are
475            also sought after in the default section. */
476         opt = find_option(cfg, SVN_CONFIG__DEFAULT_SECTION, option, &sec);
477       return opt;
478     }
479 
480   return NULL;
481 }
482 
483 
484 /* Has a bi-directional dependency with make_string_from_option(). */
485 static void
486 expand_option_value(svn_config_t *cfg, cfg_section_t *section,
487                     const char *opt_value, const char **opt_x_valuep,
488                     apr_pool_t *x_pool);
489 
490 
491 /* Set *VALUEP according to the OPT's value.  A value for X_POOL must
492    only ever be passed into this function by expand_option_value(). */
493 static void
make_string_from_option(const char ** valuep,svn_config_t * cfg,cfg_section_t * section,cfg_option_t * opt,apr_pool_t * x_pool)494 make_string_from_option(const char **valuep, svn_config_t *cfg,
495                         cfg_section_t *section, cfg_option_t *opt,
496                         apr_pool_t* x_pool)
497 {
498   /* Expand the option value if necessary. */
499   if (!opt->expanded)
500     {
501       /* before attempting to expand an option, check for the placeholder.
502        * If none is there, there is no point in calling expand_option_value.
503        */
504       if (opt->value && strchr(opt->value, '%'))
505         {
506           apr_pool_t *tmp_pool;
507 
508           /* setting read-only mode should have expanded all values
509            * automatically. */
510           assert(!cfg->read_only);
511 
512           tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
513 
514           expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool);
515           opt->expanded = TRUE;
516 
517           if (x_pool != cfg->x_pool)
518             {
519               /* Grab the fully expanded value from tmp_pool before its
520                  disappearing act. */
521               if (opt->x_value)
522                 opt->x_value = apr_pstrmemdup(cfg->x_pool, opt->x_value,
523                                               strlen(opt->x_value));
524               if (!x_pool)
525                 svn_pool_destroy(tmp_pool);
526             }
527         }
528       else
529         {
530           opt->expanded = TRUE;
531         }
532     }
533 
534   if (opt->x_value)
535     *valuep = opt->x_value;
536   else
537     *valuep = opt->value;
538 }
539 
540 
541 /* Start of variable-replacement placeholder */
542 #define FMT_START     "%("
543 #define FMT_START_LEN (sizeof(FMT_START) - 1)
544 
545 /* End of variable-replacement placeholder */
546 #define FMT_END       ")s"
547 #define FMT_END_LEN   (sizeof(FMT_END) - 1)
548 
549 
550 /* Expand OPT_VALUE (which may be NULL) in SECTION into *OPT_X_VALUEP.
551    If no variable replacements are done, set *OPT_X_VALUEP to
552    NULL. Allocate from X_POOL. */
553 static void
expand_option_value(svn_config_t * cfg,cfg_section_t * section,const char * opt_value,const char ** opt_x_valuep,apr_pool_t * x_pool)554 expand_option_value(svn_config_t *cfg, cfg_section_t *section,
555                     const char *opt_value, const char **opt_x_valuep,
556                     apr_pool_t *x_pool)
557 {
558   svn_stringbuf_t *buf = NULL;
559   const char *parse_from = opt_value;
560   const char *copy_from = parse_from;
561   const char *name_start, *name_end;
562 
563   while (parse_from != NULL
564          && *parse_from != '\0'
565          && (name_start = strstr(parse_from, FMT_START)) != NULL)
566     {
567       name_start += FMT_START_LEN;
568       if (*name_start == '\0')
569         /* FMT_START at end of opt_value. */
570         break;
571 
572       name_end = strstr(name_start, FMT_END);
573       if (name_end != NULL)
574         {
575           cfg_option_t *x_opt;
576           apr_size_t len = name_end - name_start;
577           char *name = apr_pstrmemdup(x_pool, name_start, len);
578 
579           x_opt = find_option(cfg, section->name, name, NULL);
580 
581           if (x_opt != NULL)
582             {
583               const char *cstring;
584 
585               /* Pass back the sub-pool originally provided by
586                  make_string_from_option() as an indication of when it
587                  should terminate. */
588               make_string_from_option(&cstring, cfg, section, x_opt, x_pool);
589 
590               /* Append the plain text preceding the expansion. */
591               len = name_start - FMT_START_LEN - copy_from;
592               if (buf == NULL)
593                 {
594                   buf = svn_stringbuf_ncreate(copy_from, len, x_pool);
595                   cfg->x_values = TRUE;
596                 }
597               else
598                 svn_stringbuf_appendbytes(buf, copy_from, len);
599 
600               /* Append the expansion and adjust parse pointers. */
601               svn_stringbuf_appendcstr(buf, cstring);
602               parse_from = name_end + FMT_END_LEN;
603               copy_from = parse_from;
604             }
605           else
606             /* Though ConfigParser considers the failure to resolve
607                the requested expansion an exception condition, we
608                consider it to be plain text, and look for the start of
609                the next one. */
610             parse_from = name_end + FMT_END_LEN;
611         }
612       else
613         /* Though ConfigParser treats unterminated format specifiers
614            as an exception condition, we consider them to be plain
615            text.  The fact that there are no more format specifier
616            endings means we're done parsing. */
617         parse_from = NULL;
618     }
619 
620   if (buf != NULL)
621     {
622       /* Copy the remainder of the plain text. */
623       svn_stringbuf_appendcstr(buf, copy_from);
624       *opt_x_valuep = buf->data;
625     }
626   else
627     *opt_x_valuep = NULL;
628 }
629 
630 static cfg_section_t *
svn_config_addsection(svn_config_t * cfg,const char * section)631 svn_config_addsection(svn_config_t *cfg,
632                       const char *section)
633 {
634   cfg_section_t *s;
635   const char *hash_key;
636 
637   s = apr_palloc(cfg->pool, sizeof(cfg_section_t));
638   s->name = apr_pstrdup(cfg->pool, section);
639   if(cfg->section_names_case_sensitive)
640     hash_key = s->name;
641   else
642     hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
643   s->options = apr_hash_make(cfg->pool);
644   svn_hash_sets(cfg->sections, hash_key, s);
645 
646   return s;
647 }
648 
649 static void
svn_config_create_option(cfg_option_t ** opt,const char * option,const char * value,svn_boolean_t option_names_case_sensitive,apr_pool_t * pool)650 svn_config_create_option(cfg_option_t **opt,
651                          const char *option,
652                          const char *value,
653                          svn_boolean_t option_names_case_sensitive,
654                          apr_pool_t *pool)
655 {
656   cfg_option_t *o;
657 
658   o = apr_palloc(pool, sizeof(cfg_option_t));
659   o->name = apr_pstrdup(pool, option);
660   if(option_names_case_sensitive)
661     o->hash_key = o->name;
662   else
663     o->hash_key = make_hash_key(apr_pstrdup(pool, option));
664 
665   o->value = apr_pstrdup(pool, value);
666   o->x_value = NULL;
667   o->expanded = FALSE;
668 
669   *opt = o;
670 }
671 
672 svn_boolean_t
svn_config__is_expanded(svn_config_t * cfg,const char * section,const char * option)673 svn_config__is_expanded(svn_config_t *cfg,
674                         const char *section,
675                         const char *option)
676 {
677   cfg_option_t *opt;
678 
679   if (cfg == NULL)
680     return FALSE;
681 
682   /* does the option even exist? */
683   opt = find_option(cfg, section, option, NULL);
684   if (opt == NULL)
685     return FALSE;
686 
687   /* already expanded? */
688   if (opt->expanded)
689     return TRUE;
690 
691   /* needs expansion? */
692   if (opt->value && strchr(opt->value, '%'))
693     return FALSE;
694 
695   /* no expansion necessary */
696   return TRUE;
697 }
698 
699 
700 void
svn_config_get(svn_config_t * cfg,const char ** valuep,const char * section,const char * option,const char * default_value)701 svn_config_get(svn_config_t *cfg, const char **valuep,
702                const char *section, const char *option,
703                const char *default_value)
704 {
705   *valuep = default_value;
706   if (cfg)
707     {
708       cfg_section_t *sec;
709       cfg_option_t *opt = find_option(cfg, section, option, &sec);
710       if (opt != NULL)
711         {
712           make_string_from_option(valuep, cfg, sec, opt, NULL);
713         }
714       else
715         /* before attempting to expand an option, check for the placeholder.
716          * If there is none, there is no point in calling expand_option_value.
717          */
718         if (default_value && strchr(default_value, '%'))
719           {
720             apr_pool_t *tmp_pool = svn_pool_create(cfg->pool);
721             const char *x_default;
722             expand_option_value(cfg, sec, default_value, &x_default, tmp_pool);
723             if (x_default)
724               {
725                 svn_stringbuf_set(cfg->tmp_value, x_default);
726                 *valuep = cfg->tmp_value->data;
727               }
728             svn_pool_destroy(tmp_pool);
729           }
730     }
731 }
732 
733 
734 
735 void
svn_config_set(svn_config_t * cfg,const char * section,const char * option,const char * value)736 svn_config_set(svn_config_t *cfg,
737                const char *section, const char *option,
738                const char *value)
739 {
740   cfg_section_t *sec;
741   cfg_option_t *opt;
742 
743   /* Ignore write attempts to r/o configurations.
744    *
745    * Since we should never try to modify r/o data, trigger an assertion
746    * in debug mode.
747    */
748 #ifdef SVN_DEBUG
749   SVN_ERR_ASSERT_NO_RETURN(!cfg->read_only);
750 #endif
751   if (cfg->read_only)
752     return;
753 
754   remove_expansions(cfg);
755 
756   opt = find_option(cfg, section, option, &sec);
757   if (opt != NULL)
758     {
759       /* Replace the option's value. */
760       opt->value = apr_pstrdup(cfg->pool, value);
761       opt->expanded = FALSE;
762       return;
763     }
764 
765   /* Create a new option */
766   svn_config_create_option(&opt, option, value,
767                            cfg->option_names_case_sensitive,
768                            cfg->pool);
769 
770   if (sec == NULL)
771     {
772       /* Even the section doesn't exist. Create it. */
773       sec = svn_config_addsection(cfg, section);
774     }
775 
776   svn_hash_sets(sec->options, opt->hash_key, opt);
777 }
778 
779 
780 
781 /* Set *BOOLP to true or false depending (case-insensitively) on INPUT.
782    If INPUT is null, set *BOOLP to DEFAULT_VALUE.
783 
784    INPUT is a string indicating truth or falsehood in any of the usual
785    ways: "true"/"yes"/"on"/etc, "false"/"no"/"off"/etc.
786 
787    If INPUT is neither NULL nor a recognized string, return an error
788    with code SVN_ERR_BAD_CONFIG_VALUE; use SECTION and OPTION in
789    constructing the error string. */
790 static svn_error_t *
get_bool(svn_boolean_t * boolp,const char * input,svn_boolean_t default_value,const char * section,const char * option)791 get_bool(svn_boolean_t *boolp, const char *input, svn_boolean_t default_value,
792          const char *section, const char *option)
793 {
794   svn_tristate_t value = svn_tristate__from_word(input);
795 
796   if (value == svn_tristate_true)
797     *boolp = TRUE;
798   else if (value == svn_tristate_false)
799     *boolp = FALSE;
800   else if (input == NULL) /* no value provided */
801     *boolp = default_value;
802 
803   else if (section) /* unrecognized value */
804     return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
805                              _("Config error: invalid boolean "
806                                "value '%s' for '[%s] %s'"),
807                              input, section, option);
808   else
809     return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
810                              _("Config error: invalid boolean "
811                                "value '%s' for '%s'"),
812                              input, option);
813 
814   return SVN_NO_ERROR;
815 }
816 
817 
818 svn_error_t *
svn_config_get_bool(svn_config_t * cfg,svn_boolean_t * valuep,const char * section,const char * option,svn_boolean_t default_value)819 svn_config_get_bool(svn_config_t *cfg, svn_boolean_t *valuep,
820                     const char *section, const char *option,
821                     svn_boolean_t default_value)
822 {
823   const char *tmp_value;
824   svn_config_get(cfg, &tmp_value, section, option, NULL);
825   return get_bool(valuep, tmp_value, default_value, section, option);
826 }
827 
828 
829 
830 void
svn_config_set_bool(svn_config_t * cfg,const char * section,const char * option,svn_boolean_t value)831 svn_config_set_bool(svn_config_t *cfg,
832                     const char *section, const char *option,
833                     svn_boolean_t value)
834 {
835   svn_config_set(cfg, section, option,
836                  (value ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE));
837 }
838 
839 svn_error_t *
svn_config_get_int64(svn_config_t * cfg,apr_int64_t * valuep,const char * section,const char * option,apr_int64_t default_value)840 svn_config_get_int64(svn_config_t *cfg,
841                      apr_int64_t *valuep,
842                      const char *section,
843                      const char *option,
844                      apr_int64_t default_value)
845 {
846   const char *tmp_value;
847   svn_config_get(cfg, &tmp_value, section, option, NULL);
848   if (tmp_value)
849     return svn_cstring_strtoi64(valuep, tmp_value,
850                                 APR_INT64_MIN, APR_INT64_MAX, 10);
851 
852   *valuep = default_value;
853   return SVN_NO_ERROR;
854 }
855 
856 void
svn_config_set_int64(svn_config_t * cfg,const char * section,const char * option,apr_int64_t value)857 svn_config_set_int64(svn_config_t *cfg,
858                      const char *section,
859                      const char *option,
860                      apr_int64_t value)
861 {
862   svn_config_set(cfg, section, option,
863                  apr_psprintf(cfg->pool, "%" APR_INT64_T_FMT, value));
864 }
865 
866 svn_error_t *
svn_config_get_yes_no_ask(svn_config_t * cfg,const char ** valuep,const char * section,const char * option,const char * default_value)867 svn_config_get_yes_no_ask(svn_config_t *cfg, const char **valuep,
868                           const char *section, const char *option,
869                           const char* default_value)
870 {
871   const char *tmp_value;
872 
873   svn_config_get(cfg, &tmp_value, section, option, NULL);
874 
875   if (! tmp_value)
876     tmp_value = default_value;
877 
878   if (tmp_value && (0 == svn_cstring_casecmp(tmp_value, SVN_CONFIG_ASK)))
879     {
880       *valuep = SVN_CONFIG_ASK;
881     }
882   else
883     {
884       svn_boolean_t bool_val;
885       /* We already incorporated default_value into tmp_value if
886          necessary, so the FALSE below will be ignored unless the
887          caller is doing something it shouldn't be doing. */
888       SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
889       *valuep = bool_val ? SVN_CONFIG_TRUE : SVN_CONFIG_FALSE;
890     }
891 
892   return SVN_NO_ERROR;
893 }
894 
895 svn_error_t *
svn_config_get_tristate(svn_config_t * cfg,svn_tristate_t * valuep,const char * section,const char * option,const char * unknown_value,svn_tristate_t default_value)896 svn_config_get_tristate(svn_config_t *cfg, svn_tristate_t *valuep,
897                         const char *section, const char *option,
898                         const char *unknown_value,
899                         svn_tristate_t default_value)
900 {
901   const char *tmp_value;
902 
903   svn_config_get(cfg, &tmp_value, section, option, NULL);
904 
905   if (! tmp_value)
906     {
907       *valuep = default_value;
908     }
909   else if (0 == svn_cstring_casecmp(tmp_value, unknown_value))
910     {
911       *valuep = svn_tristate_unknown;
912     }
913   else
914     {
915       svn_boolean_t bool_val;
916       /* We already incorporated default_value into tmp_value if
917          necessary, so the FALSE below will be ignored unless the
918          caller is doing something it shouldn't be doing. */
919       SVN_ERR(get_bool(&bool_val, tmp_value, FALSE, section, option));
920       *valuep = bool_val ? svn_tristate_true : svn_tristate_false;
921     }
922 
923   return SVN_NO_ERROR;
924 }
925 
926 int
svn_config_enumerate_sections(svn_config_t * cfg,svn_config_section_enumerator_t callback,void * baton)927 svn_config_enumerate_sections(svn_config_t *cfg,
928                               svn_config_section_enumerator_t callback,
929                               void *baton)
930 {
931   apr_hash_index_t *sec_ndx;
932   int count = 0;
933   apr_pool_t *subpool = svn_pool_create(cfg->x_pool);
934 
935   for (sec_ndx = apr_hash_first(subpool, cfg->sections);
936        sec_ndx != NULL;
937        sec_ndx = apr_hash_next(sec_ndx))
938     {
939       void *sec_ptr;
940       cfg_section_t *sec;
941 
942       apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
943       sec = sec_ptr;
944       ++count;
945       if (!callback(sec->name, baton))
946         break;
947     }
948 
949   svn_pool_destroy(subpool);
950   return count;
951 }
952 
953 
954 int
svn_config_enumerate_sections2(svn_config_t * cfg,svn_config_section_enumerator2_t callback,void * baton,apr_pool_t * pool)955 svn_config_enumerate_sections2(svn_config_t *cfg,
956                                svn_config_section_enumerator2_t callback,
957                                void *baton, apr_pool_t *pool)
958 {
959   apr_hash_index_t *sec_ndx;
960   apr_pool_t *iteration_pool;
961   int count = 0;
962 
963   iteration_pool = svn_pool_create(pool);
964   for (sec_ndx = apr_hash_first(pool, cfg->sections);
965        sec_ndx != NULL;
966        sec_ndx = apr_hash_next(sec_ndx))
967     {
968       void *sec_ptr;
969       cfg_section_t *sec;
970 
971       apr_hash_this(sec_ndx, NULL, NULL, &sec_ptr);
972       sec = sec_ptr;
973       ++count;
974       svn_pool_clear(iteration_pool);
975       if (!callback(sec->name, baton, iteration_pool))
976         break;
977     }
978   svn_pool_destroy(iteration_pool);
979 
980   return count;
981 }
982 
983 
984 
985 int
svn_config_enumerate(svn_config_t * cfg,const char * section,svn_config_enumerator_t callback,void * baton)986 svn_config_enumerate(svn_config_t *cfg, const char *section,
987                      svn_config_enumerator_t callback, void *baton)
988 {
989   cfg_section_t *sec;
990   apr_hash_index_t *opt_ndx;
991   int count;
992   apr_pool_t *subpool;
993 
994   find_option(cfg, section, NULL, &sec);
995   if (sec == NULL)
996     return 0;
997 
998   subpool = svn_pool_create(cfg->pool);
999   count = 0;
1000   for (opt_ndx = apr_hash_first(subpool, sec->options);
1001        opt_ndx != NULL;
1002        opt_ndx = apr_hash_next(opt_ndx))
1003     {
1004       void *opt_ptr;
1005       cfg_option_t *opt;
1006       const char *temp_value;
1007 
1008       apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
1009       opt = opt_ptr;
1010 
1011       ++count;
1012       make_string_from_option(&temp_value, cfg, sec, opt, NULL);
1013       if (!callback(opt->name, temp_value, baton))
1014         break;
1015     }
1016 
1017   svn_pool_destroy(subpool);
1018   return count;
1019 }
1020 
1021 
1022 int
svn_config_enumerate2(svn_config_t * cfg,const char * section,svn_config_enumerator2_t callback,void * baton,apr_pool_t * pool)1023 svn_config_enumerate2(svn_config_t *cfg, const char *section,
1024                       svn_config_enumerator2_t callback, void *baton,
1025                       apr_pool_t *pool)
1026 {
1027   cfg_section_t *sec;
1028   apr_hash_index_t *opt_ndx;
1029   apr_pool_t *iteration_pool;
1030   int count;
1031 
1032   find_option(cfg, section, NULL, &sec);
1033   if (sec == NULL)
1034     return 0;
1035 
1036   iteration_pool = svn_pool_create(pool);
1037   count = 0;
1038   for (opt_ndx = apr_hash_first(pool, sec->options);
1039        opt_ndx != NULL;
1040        opt_ndx = apr_hash_next(opt_ndx))
1041     {
1042       void *opt_ptr;
1043       cfg_option_t *opt;
1044       const char *temp_value;
1045 
1046       apr_hash_this(opt_ndx, NULL, NULL, &opt_ptr);
1047       opt = opt_ptr;
1048 
1049       ++count;
1050       make_string_from_option(&temp_value, cfg, sec, opt, NULL);
1051       svn_pool_clear(iteration_pool);
1052       if (!callback(opt->name, temp_value, baton, iteration_pool))
1053         break;
1054     }
1055   svn_pool_destroy(iteration_pool);
1056 
1057   return count;
1058 }
1059 
1060 
1061 
1062 /* Baton for search_groups() */
1063 struct search_groups_baton
1064 {
1065   const char *key;          /* Provided by caller of svn_config_find_group */
1066   const char *match;        /* Filled in by search_groups */
1067   apr_pool_t *pool;
1068 };
1069 
1070 
1071 /* This is an `svn_config_enumerator_t' function, and BATON is a
1072  * `struct search_groups_baton *'.
1073  */
search_groups(const char * name,const char * value,void * baton,apr_pool_t * pool)1074 static svn_boolean_t search_groups(const char *name,
1075                                    const char *value,
1076                                    void *baton,
1077                                    apr_pool_t *pool)
1078 {
1079   struct search_groups_baton *b = baton;
1080   apr_array_header_t *list;
1081 
1082   list = svn_cstring_split(value, ",", TRUE, pool);
1083   if (svn_cstring_match_glob_list(b->key, list))
1084     {
1085       /* Fill in the match and return false, to stop enumerating. */
1086       b->match = apr_pstrdup(b->pool, name);
1087       return FALSE;
1088     }
1089   else
1090     return TRUE;
1091 }
1092 
1093 
svn_config_find_group(svn_config_t * cfg,const char * key,const char * master_section,apr_pool_t * pool)1094 const char *svn_config_find_group(svn_config_t *cfg, const char *key,
1095                                   const char *master_section,
1096                                   apr_pool_t *pool)
1097 {
1098   struct search_groups_baton gb;
1099 
1100   gb.key = key;
1101   gb.match = NULL;
1102   gb.pool = pool;
1103   (void) svn_config_enumerate2(cfg, master_section, search_groups, &gb, pool);
1104   return gb.match;
1105 }
1106 
1107 
1108 const char*
svn_config_get_server_setting(svn_config_t * cfg,const char * server_group,const char * option_name,const char * default_value)1109 svn_config_get_server_setting(svn_config_t *cfg,
1110                               const char* server_group,
1111                               const char* option_name,
1112                               const char* default_value)
1113 {
1114   const char *retval;
1115   svn_config_get(cfg, &retval, SVN_CONFIG_SECTION_GLOBAL,
1116                  option_name, default_value);
1117   if (server_group)
1118     {
1119       svn_config_get(cfg, &retval, server_group, option_name, retval);
1120     }
1121   return retval;
1122 }
1123 
1124 
1125 svn_error_t *
svn_config_dup(svn_config_t ** cfgp,const svn_config_t * src,apr_pool_t * pool)1126 svn_config_dup(svn_config_t **cfgp,
1127                const svn_config_t *src,
1128                apr_pool_t *pool)
1129 {
1130   apr_hash_index_t *sectidx;
1131   apr_hash_index_t *optidx;
1132 
1133   *cfgp = 0;
1134   SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
1135 
1136   (*cfgp)->x_values = src->x_values;
1137   (*cfgp)->section_names_case_sensitive = src->section_names_case_sensitive;
1138   (*cfgp)->option_names_case_sensitive = src->option_names_case_sensitive;
1139 
1140   for (sectidx = apr_hash_first(pool, src->sections);
1141        sectidx != NULL;
1142        sectidx = apr_hash_next(sectidx))
1143   {
1144     const void *sectkey;
1145     void *sectval;
1146     apr_ssize_t sectkeyLength;
1147     cfg_section_t * srcsect;
1148     cfg_section_t * destsec;
1149 
1150     apr_hash_this(sectidx, &sectkey, &sectkeyLength, &sectval);
1151     srcsect = sectval;
1152 
1153     destsec = svn_config_addsection(*cfgp, srcsect->name);
1154 
1155     for (optidx = apr_hash_first(pool, srcsect->options);
1156          optidx != NULL;
1157          optidx = apr_hash_next(optidx))
1158     {
1159       const void *optkey;
1160       void *optval;
1161       apr_ssize_t optkeyLength;
1162       cfg_option_t *srcopt;
1163       cfg_option_t *destopt;
1164 
1165       apr_hash_this(optidx, &optkey, &optkeyLength, &optval);
1166       srcopt = optval;
1167 
1168       svn_config_create_option(&destopt, srcopt->name, srcopt->value,
1169                                (*cfgp)->option_names_case_sensitive,
1170                                pool);
1171 
1172       destopt->value = apr_pstrdup(pool, srcopt->value);
1173       destopt->x_value = apr_pstrdup(pool, srcopt->x_value);
1174       destopt->expanded = srcopt->expanded;
1175       apr_hash_set(destsec->options,
1176                    apr_pstrdup(pool, (const char*)optkey),
1177                    optkeyLength, destopt);
1178     }
1179   }
1180 
1181   return SVN_NO_ERROR;
1182 }
1183 
1184 svn_error_t *
svn_config_copy_config(apr_hash_t ** cfg_hash,apr_hash_t * src_hash,apr_pool_t * pool)1185 svn_config_copy_config(apr_hash_t **cfg_hash,
1186                        apr_hash_t *src_hash,
1187                        apr_pool_t *pool)
1188 {
1189   apr_hash_index_t *cidx;
1190 
1191   *cfg_hash = apr_hash_make(pool);
1192   for (cidx = apr_hash_first(pool, src_hash);
1193        cidx != NULL;
1194        cidx = apr_hash_next(cidx))
1195   {
1196     const void *ckey;
1197     void *cval;
1198     apr_ssize_t ckeyLength;
1199     svn_config_t * srcconfig;
1200     svn_config_t * destconfig;
1201 
1202     apr_hash_this(cidx, &ckey, &ckeyLength, &cval);
1203     srcconfig = cval;
1204 
1205     SVN_ERR(svn_config_dup(&destconfig, srcconfig, pool));
1206 
1207     apr_hash_set(*cfg_hash,
1208                  apr_pstrdup(pool, (const char*)ckey),
1209                  ckeyLength, destconfig);
1210   }
1211 
1212   return SVN_NO_ERROR;
1213 }
1214 
1215 svn_error_t*
svn_config_get_server_setting_int(svn_config_t * cfg,const char * server_group,const char * option_name,apr_int64_t default_value,apr_int64_t * result_value,apr_pool_t * pool)1216 svn_config_get_server_setting_int(svn_config_t *cfg,
1217                                   const char *server_group,
1218                                   const char *option_name,
1219                                   apr_int64_t default_value,
1220                                   apr_int64_t *result_value,
1221                                   apr_pool_t *pool)
1222 {
1223   const char* tmp_value;
1224   char *end_pos;
1225 
1226   tmp_value = svn_config_get_server_setting(cfg, server_group,
1227                                             option_name, NULL);
1228   if (tmp_value == NULL)
1229     *result_value = default_value;
1230   else
1231     {
1232       /* read tmp_value as an int now */
1233       *result_value = apr_strtoi64(tmp_value, &end_pos, 0);
1234 
1235       if (*end_pos != 0)
1236         {
1237           return svn_error_createf
1238             (SVN_ERR_BAD_CONFIG_VALUE, NULL,
1239              _("Config error: invalid integer value '%s'"),
1240              tmp_value);
1241         }
1242     }
1243 
1244   return SVN_NO_ERROR;
1245 }
1246 
1247 svn_error_t *
svn_config_get_server_setting_bool(svn_config_t * cfg,svn_boolean_t * valuep,const char * server_group,const char * option_name,svn_boolean_t default_value)1248 svn_config_get_server_setting_bool(svn_config_t *cfg,
1249                                    svn_boolean_t *valuep,
1250                                    const char *server_group,
1251                                    const char *option_name,
1252                                    svn_boolean_t default_value)
1253 {
1254   const char* tmp_value;
1255   tmp_value = svn_config_get_server_setting(cfg, server_group,
1256                                             option_name, NULL);
1257   return get_bool(valuep, tmp_value, default_value,
1258                   server_group, option_name);
1259 }
1260 
1261 
1262 svn_boolean_t
svn_config_has_section(svn_config_t * cfg,const char * section)1263 svn_config_has_section(svn_config_t *cfg, const char *section)
1264 {
1265   return NULL != get_hash_value(cfg->sections, cfg->tmp_key, section,
1266                                 cfg->section_names_case_sensitive);
1267 }
1268