1 /*
2  * config_file.c :  parsing configuration files
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_lib.h>
27 #include <apr_env.h>
28 #include <apr_tables.h>
29 
30 #include "config_impl.h"
31 #include "svn_io.h"
32 #include "svn_types.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_auth.h"
35 #include "svn_hash.h"
36 #include "svn_subst.h"
37 #include "svn_utf.h"
38 #include "svn_pools.h"
39 #include "svn_user.h"
40 #include "svn_ctype.h"
41 
42 #include "private/svn_config_private.h"
43 #include "private/svn_subr_private.h"
44 #include "svn_private_config.h"
45 
46 #ifdef __HAIKU__
47 #  include <FindDirectory.h>
48 #  include <StorageDefs.h>
49 #endif
50 
51 /* Used to terminate lines in large multi-line string literals. */
52 #define NL APR_EOL_STR
53 
54 
55 /* File parsing context */
56 typedef struct parse_context_t
57 {
58   /* The configuration constructor. */
59   svn_config__constructor_t *constructor;
60   void *constructor_baton;
61 
62   /* The stream struct */
63   svn_stream_t *stream;
64 
65   /* The current line in the file */
66   int line;
67 
68   /* Emulate an ungetc */
69   int ungotten_char;
70 
71   /* We're currently parsing a section */
72   svn_boolean_t in_section;
73 
74   /* Temporary strings */
75   svn_stringbuf_t *section;
76   svn_stringbuf_t *option;
77   svn_stringbuf_t *value;
78   svn_stringbuf_t *line_read;
79 
80   /* Parser buffer for getc() to avoid call overhead into several libraries
81      for every character */
82   char parser_buffer[SVN__STREAM_CHUNK_SIZE]; /* Larger than most config files */
83   size_t buffer_pos; /* Current position within parser_buffer */
84   size_t buffer_size; /* parser_buffer contains this many bytes */
85 
86   /* Non-zero if we hit EOF on the stream. */
87   svn_boolean_t hit_stream_eof;
88 } parse_context_t;
89 
90 
91 /* Config representation constructor */
92 struct svn_config__constructor_t
93 {
94   /* Constructor callbacks; see docs for svn_config__constructor_create. */
95   svn_config__open_section_fn open_section;
96   svn_config__close_section_fn close_section;
97   svn_config__add_value_fn add_value;
98 };
99 
100 svn_config__constructor_t *
svn_config__constructor_create(svn_config__open_section_fn open_section_callback,svn_config__close_section_fn close_section_callback,svn_config__add_value_fn add_value_callback,apr_pool_t * result_pool)101 svn_config__constructor_create(
102     svn_config__open_section_fn open_section_callback,
103     svn_config__close_section_fn close_section_callback,
104     svn_config__add_value_fn add_value_callback,
105     apr_pool_t *result_pool)
106 {
107   svn_config__constructor_t *ctor = apr_palloc(result_pool, sizeof(*ctor));
108   ctor->open_section = open_section_callback;
109   ctor->close_section = close_section_callback;
110   ctor->add_value = add_value_callback;
111   return ctor;
112 }
113 
114 /* Called after we've parsed a section name and before we start
115    parsing any options within that section. */
116 static APR_INLINE svn_error_t *
open_section(parse_context_t * ctx,svn_boolean_t * stop)117 open_section(parse_context_t *ctx, svn_boolean_t *stop)
118 {
119   if (ctx->constructor->open_section)
120     {
121       svn_error_t *err = ctx->constructor->open_section(
122           ctx->constructor_baton, ctx->section);
123       if (err)
124         {
125           if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
126             {
127               *stop = TRUE;
128               svn_error_clear(err);
129               return SVN_NO_ERROR;
130             }
131           else
132             return svn_error_trace(err);
133         }
134     }
135 
136   *stop = FALSE;
137   ctx->in_section = TRUE;
138   return SVN_NO_ERROR;
139 }
140 
141 /* Called after we've parsed all options within a section and before
142    we start parsing the next section. */
143 static APR_INLINE svn_error_t *
close_section(parse_context_t * ctx,svn_boolean_t * stop)144 close_section(parse_context_t *ctx, svn_boolean_t *stop)
145 {
146   ctx->in_section = FALSE;
147   if (ctx->constructor->close_section)
148     {
149       svn_error_t *err = ctx->constructor->close_section(
150           ctx->constructor_baton, ctx->section);
151       if (err)
152         {
153           if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
154             {
155               *stop = TRUE;
156               svn_error_clear(err);
157               return SVN_NO_ERROR;
158             }
159           else
160             return svn_error_trace(err);
161         }
162     }
163 
164   *stop = FALSE;
165   return SVN_NO_ERROR;
166 }
167 
168 /* Called every tyme we've parsed a complete (option, value) pair. */
169 static APR_INLINE svn_error_t *
add_value(parse_context_t * ctx,svn_boolean_t * stop)170 add_value(parse_context_t *ctx, svn_boolean_t *stop)
171 {
172   if (ctx->constructor->add_value)
173     {
174       svn_error_t *err =  ctx->constructor->add_value(
175           ctx->constructor_baton, ctx->section,
176           ctx->option, ctx->value);
177       if (err)
178         {
179           if (err->apr_err == SVN_ERR_CEASE_INVOCATION)
180             {
181               *stop = TRUE;
182               svn_error_clear(err);
183               return SVN_NO_ERROR;
184             }
185           else
186             return svn_error_trace(err);
187         }
188     }
189 
190   *stop = FALSE;
191   return SVN_NO_ERROR;
192 }
193 
194 
195 /* Emulate getc() because streams don't support it.
196  *
197  * In order to be able to ungetc(), use the CXT instead of the stream
198  * to be able to store the 'ungotton' character.
199  *
200  */
201 static APR_INLINE svn_error_t *
parser_getc(parse_context_t * ctx,int * c)202 parser_getc(parse_context_t *ctx, int *c)
203 {
204   do
205     {
206       if (ctx->ungotten_char != EOF)
207         {
208           *c = ctx->ungotten_char;
209           ctx->ungotten_char = EOF;
210         }
211       else if (ctx->buffer_pos < ctx->buffer_size)
212         {
213           *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
214           ctx->buffer_pos++;
215         }
216       else
217         {
218           if (!ctx->hit_stream_eof)
219             {
220               ctx->buffer_pos = 0;
221               ctx->buffer_size = sizeof(ctx->parser_buffer);
222 
223               SVN_ERR(svn_stream_read_full(ctx->stream, ctx->parser_buffer,
224                                            &(ctx->buffer_size)));
225               ctx->hit_stream_eof = (ctx->buffer_size != sizeof(ctx->parser_buffer));
226             }
227 
228           if (ctx->buffer_pos < ctx->buffer_size)
229             {
230               *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
231               ctx->buffer_pos++;
232             }
233           else
234             *c = EOF;
235         }
236     }
237   while (*c == '\r');
238 
239   return SVN_NO_ERROR;
240 }
241 
242 /* Simplified version of parser_getc() to be used inside skipping loops.
243  * It will not check for 'ungotton' chars and may or may not ignore '\r'.
244  *
245  * In a 'while(cond) getc();' loop, the first iteration must call
246  * parser_getc to handle all the special cases.  Later iterations should
247  * use parser_getc_plain for maximum performance.
248  */
249 static APR_INLINE svn_error_t *
parser_getc_plain(parse_context_t * ctx,int * c)250 parser_getc_plain(parse_context_t *ctx, int *c)
251 {
252   if (ctx->buffer_pos < ctx->buffer_size)
253     {
254       *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
255       ctx->buffer_pos++;
256 
257       return SVN_NO_ERROR;
258     }
259 
260   return parser_getc(ctx, c);
261 }
262 
263 /* Emulate ungetc() because streams don't support it.
264  *
265  * Use CTX to store the ungotten character C.
266  */
267 static APR_INLINE svn_error_t *
parser_ungetc(parse_context_t * ctx,int c)268 parser_ungetc(parse_context_t *ctx, int c)
269 {
270   ctx->ungotten_char = c;
271 
272   return SVN_NO_ERROR;
273 }
274 
275 /* Read from CTX to the end of the line (or file) and write the data
276    into LINE.  Previous contents of *LINE will be overwritten.
277    Returns the char that ended the line in *C; the char is either
278    EOF or newline. */
279 static svn_error_t *
parser_get_line(parse_context_t * ctx,svn_stringbuf_t * line,int * c)280 parser_get_line(parse_context_t *ctx, svn_stringbuf_t *line, int *c)
281 {
282   int ch;
283   svn_stringbuf_setempty(line);
284 
285   /* Loop until EOF of newline. There is one extra iteration per
286    * parser buffer refill.  The initial parser_getc() also ensures
287    * that the unget buffer is emptied. */
288   SVN_ERR(parser_getc(ctx, &ch));
289   while (ch != '\n' && ch != EOF)
290     {
291       const char *start, *newline;
292 
293       /* Save the char we just read. */
294       svn_stringbuf_appendbyte(line, ch);
295 
296       /* Scan the parser buffer for NL. */
297       start = ctx->parser_buffer + ctx->buffer_pos;
298       newline = memchr(start, '\n', ctx->buffer_size - ctx->buffer_pos);
299       if (newline)
300         {
301           /* NL found before the end of the of the buffer. */
302           svn_stringbuf_appendbytes(line, start, newline - start);
303           ch = '\n';
304           ctx->buffer_pos = newline - ctx->parser_buffer + 1;
305           break;
306         }
307       else
308         {
309           /* Hit the end of the buffer.  This may be either due to
310            * buffer size or EOF.  In any case, all (remaining) current
311            * buffer data is part of the line to read. */
312           const char *end = ctx->parser_buffer + ctx->buffer_size;
313           ctx->buffer_pos = ctx->buffer_size;
314 
315           svn_stringbuf_appendbytes(line, start, end - start);
316         }
317 
318       /* refill buffer, check for EOF */
319       SVN_ERR(parser_getc_plain(ctx, &ch));
320     }
321 
322   *c = ch;
323   return SVN_NO_ERROR;
324 }
325 
326 /* Eat chars from STREAM until encounter non-whitespace, newline, or EOF.
327    Set *PCOUNT to the number of characters eaten, not counting the
328    last one, and return the last char read (the one that caused the
329    break).  */
330 static APR_INLINE svn_error_t *
skip_whitespace(parse_context_t * ctx,int * c,int * pcount)331 skip_whitespace(parse_context_t *ctx, int *c, int *pcount)
332 {
333   int ch = 0;
334   int count = 0;
335 
336   SVN_ERR(parser_getc(ctx, &ch));
337   while (svn_ctype_isspace(ch) && ch != '\n' && ch != EOF)
338     {
339       ++count;
340       SVN_ERR(parser_getc_plain(ctx, &ch));
341     }
342   *pcount = count;
343   *c = ch;
344   return SVN_NO_ERROR;
345 }
346 
347 
348 /* Skip to the end of the line (or file).  Returns the char that ended
349    the line; the char is either EOF or newline. */
350 static APR_INLINE svn_error_t *
skip_to_eoln(parse_context_t * ctx,int * c)351 skip_to_eoln(parse_context_t *ctx, int *c)
352 {
353   int ch;
354 
355   SVN_ERR(parser_getc(ctx, &ch));
356   while (ch != '\n' && ch != EOF)
357     {
358       /* This is much faster than checking individual bytes.
359        * We use this function a lot when skipping comment lines.
360        *
361        * This assumes that the ungetc buffer is empty, but that is a
362        * safe assumption right after reading a character (which would
363        * clear the buffer. */
364       const char *newline = memchr(ctx->parser_buffer + ctx->buffer_pos, '\n',
365                                    ctx->buffer_size - ctx->buffer_pos);
366       if (newline)
367         {
368           ch = '\n';
369           ctx->buffer_pos = newline - ctx->parser_buffer + 1;
370           break;
371         }
372 
373       /* refill buffer, check for EOF */
374       SVN_ERR(parser_getc_plain(ctx, &ch));
375     }
376 
377   *c = ch;
378   return SVN_NO_ERROR;
379 }
380 
381 /* Skip a UTF-8 Byte Order Mark if found. */
382 static APR_INLINE svn_error_t *
skip_bom(parse_context_t * ctx)383 skip_bom(parse_context_t *ctx)
384 {
385   int ch;
386 
387   SVN_ERR(parser_getc(ctx, &ch));
388   if (ch == 0xEF)
389     {
390       const unsigned char *buf = (unsigned char *)ctx->parser_buffer;
391       /* This makes assumptions about the implementation of parser_getc and
392        * the use of skip_bom.  Specifically that parser_getc() will get all
393        * of the BOM characters into the parse_context_t buffer.  This can
394        * safely be assumed as long as we only try to use skip_bom() at the
395        * start of the stream and the buffer is longer than 3 characters. */
396       SVN_ERR_ASSERT(ctx->buffer_size > ctx->buffer_pos + 1 ||
397                      ctx->hit_stream_eof);
398       if (ctx->buffer_size > ctx->buffer_pos + 1 &&
399           buf[ctx->buffer_pos] == 0xBB && buf[ctx->buffer_pos + 1] == 0xBF)
400         ctx->buffer_pos += 2;
401       else
402         SVN_ERR(parser_ungetc(ctx, ch));
403     }
404   else
405     SVN_ERR(parser_ungetc(ctx, ch));
406 
407   return SVN_NO_ERROR;
408 }
409 
410 /* Parse continuation lines of single option value if they exist.  Append
411  * their contents, including *PCH, to CTX->VALUE.  Return the first char
412  * of the next line or EOF in *PCH. */
413 static svn_error_t *
parse_value_continuation_lines(int * pch,parse_context_t * ctx)414 parse_value_continuation_lines(int *pch, parse_context_t *ctx)
415 {
416   svn_boolean_t end_of_val = FALSE;
417   svn_boolean_t stop;
418   int ch = *pch;
419 
420   /* Leading and trailing whitespace is ignored. */
421   svn_stringbuf_strip_whitespace(ctx->value);
422 
423   /* Look for any continuation lines. */
424   for (;;)
425     {
426 
427       if (ch == EOF || end_of_val)
428         {
429           /* At end of file. The value is complete, there can't be
430              any continuation lines. */
431           SVN_ERR(add_value(ctx, &stop));
432           if (stop)
433             return SVN_NO_ERROR;
434           break;
435         }
436       else
437         {
438           int count;
439           ++ctx->line;
440           SVN_ERR(skip_whitespace(ctx, &ch, &count));
441 
442           switch (ch)
443             {
444             case '\n':
445               /* The next line was empty. Ergo, it can't be a
446                  continuation line. */
447               ++ctx->line;
448               end_of_val = TRUE;
449               continue;
450 
451             case EOF:
452               /* This is also an empty line. */
453               end_of_val = TRUE;
454               continue;
455 
456             default:
457               if (count == 0)
458                 {
459                   /* This line starts in the first column.  That means
460                      it's either a section, option or comment.  Put
461                      the char back into the stream, because it doesn't
462                      belong to us. */
463                   SVN_ERR(parser_ungetc(ctx, ch));
464                   end_of_val = TRUE;
465                 }
466               else
467                 {
468                   /* This is a continuation line. Read it. */
469                   SVN_ERR(parser_ungetc(ctx, ch));
470                   SVN_ERR(parser_get_line(ctx, ctx->line_read, &ch));
471 
472                   /* Trailing whitespace is ignored. */
473                   svn_stringbuf_strip_whitespace(ctx->line_read);
474 
475                   svn_stringbuf_appendbyte(ctx->value, ' ');
476                   svn_stringbuf_appendbytes(ctx->value, ctx->line_read->data,
477                                             ctx->line_read->len);
478                 }
479             }
480         }
481     }
482 
483   *pch = ch;
484   return SVN_NO_ERROR;
485 }
486 
487 
488 /* Parse a single option */
489 static svn_error_t *
parse_option(int * pch,parse_context_t * ctx,apr_pool_t * scratch_pool)490 parse_option(int *pch, parse_context_t *ctx, apr_pool_t *scratch_pool)
491 {
492   svn_error_t *err = SVN_NO_ERROR;
493   int ch;
494   char *equals, *separator;
495 
496   /* Read the first line. */
497   parser_ungetc(ctx, *pch); /* Yes, the first char is relevant. */
498   SVN_ERR(parser_get_line(ctx, ctx->line_read, &ch));
499 
500   /* Extract the option name from it. */
501   equals = strchr(ctx->line_read->data, '=');
502   if (equals)
503     {
504       /* Efficiently look for a colon separator prior to the equals sign.
505        * Since there is no strnchr, we limit the search by temporarily
506        * limiting the string. */
507       char *colon;
508       *equals = 0;
509       colon = strchr(ctx->line_read->data, ':');
510       *equals = '=';
511 
512       separator = colon ? colon : equals;
513     }
514   else
515     {
516       /* No '=' separator so far.  Look for colon. */
517       separator = strchr(ctx->line_read->data, ':');
518     }
519 
520   if (!separator)
521     {
522       ch = EOF;
523       err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
524                               _("line %d: Option must end with ':' or '='"),
525                               ctx->line);
526     }
527   else
528     {
529       /* Whitespace around the name separator is ignored. */
530       const char *end = separator;
531       while (svn_ctype_isspace(*--end))
532         ;
533       while (svn_ctype_isspace(*++separator))
534         ;
535 
536       /* Extract the option.  It can't contain whitespace chars. */
537       svn_stringbuf_setempty(ctx->option);
538       svn_stringbuf_appendbytes(ctx->option, ctx->line_read->data,
539                                 end + 1 - ctx->line_read->data);
540 
541       /* Extract the first line of the value. */
542       end = ctx->line_read->data + ctx->line_read->len;
543       svn_stringbuf_setempty(ctx->value);
544       svn_stringbuf_appendbytes(ctx->value, separator, end - separator);
545       err = parse_value_continuation_lines(&ch, ctx);
546     }
547 
548   *pch = ch;
549   return err;
550 }
551 
552 
553 /* Read chars until enounter ']', then skip everything to the end of
554  * the line.  Set *PCH to the character that ended the line (either
555  * newline or EOF), and set CTX->section to the string of characters
556  * seen before ']'.
557  *
558  * This is meant to be called immediately after reading the '[' that
559  * starts a section name.
560  */
561 static svn_error_t *
parse_section_name(int * pch,parse_context_t * ctx,apr_pool_t * scratch_pool)562 parse_section_name(int *pch, parse_context_t *ctx,
563                    apr_pool_t *scratch_pool)
564 {
565   svn_error_t *err = SVN_NO_ERROR;
566   int ch;
567   char *terminal;
568 
569   SVN_ERR(parser_get_line(ctx, ctx->section, &ch));
570   terminal = strchr(ctx->section->data, ']');
571 
572   if (!terminal)
573     {
574       ch = EOF;
575       err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
576                               _("line %d: Section header must end with ']'"),
577                               ctx->line);
578     }
579   else
580     {
581       /* Everything from the ']' to the end of the line is ignored. */
582       *terminal = 0;
583       ctx->section->len = terminal - ctx->section->data;
584     }
585 
586   *pch = ch;
587   return err;
588 }
589 
590 
591 svn_error_t *
svn_config__sys_config_path(const char ** path_p,const char * fname,apr_pool_t * pool)592 svn_config__sys_config_path(const char **path_p,
593                             const char *fname,
594                             apr_pool_t *pool)
595 {
596   *path_p = NULL;
597 
598   /* Note that even if fname is null, svn_dirent_join_many will DTRT. */
599 
600 #ifdef WIN32
601   {
602     const char *folder;
603     SVN_ERR(svn_config__win_config_path(&folder, TRUE, pool, pool));
604     *path_p = svn_dirent_join_many(pool, folder,
605                                    SVN_CONFIG__SUBDIRECTORY, fname,
606                                    SVN_VA_NULL);
607   }
608 
609 #elif defined(__HAIKU__)
610   {
611     char folder[B_PATH_NAME_LENGTH];
612 
613     status_t error = find_directory(B_COMMON_SETTINGS_DIRECTORY, -1, false,
614                                     folder, sizeof(folder));
615     if (error)
616       return SVN_NO_ERROR;
617 
618     *path_p = svn_dirent_join_many(pool, folder,
619                                    SVN_CONFIG__SYS_DIRECTORY, fname,
620                                    SVN_VA_NULL);
621   }
622 #else  /* ! WIN32 && !__HAIKU__ */
623 
624   *path_p = svn_dirent_join_many(pool, SVN_CONFIG__SYS_DIRECTORY, fname,
625                                  SVN_VA_NULL);
626 
627 #endif /* WIN32 */
628 
629   return SVN_NO_ERROR;
630 }
631 
632 /* Callback for svn_config_enumerate2: Continue to next value. */
633 static svn_boolean_t
expand_value(const char * name,const char * value,void * baton,apr_pool_t * pool)634 expand_value(const char *name,
635              const char *value,
636              void *baton,
637              apr_pool_t *pool)
638 {
639   return TRUE;
640 }
641 
642 /* Callback for svn_config_enumerate_sections2:
643  * Enumerate and implicitly expand all values in this section.
644  */
645 static svn_boolean_t
expand_values_in_section(const char * name,void * baton,apr_pool_t * pool)646 expand_values_in_section(const char *name,
647                          void *baton,
648                          apr_pool_t *pool)
649 {
650   svn_config_t *cfg = baton;
651   svn_config_enumerate2(cfg, name, expand_value, NULL, pool);
652 
653   return TRUE;
654 }
655 
656 
657 /*** Exported interfaces. ***/
658 
659 void
svn_config__set_read_only(svn_config_t * cfg,apr_pool_t * scratch_pool)660 svn_config__set_read_only(svn_config_t *cfg,
661                           apr_pool_t *scratch_pool)
662 {
663   /* expand all items such that later calls to getters won't need to
664    * change internal state */
665   svn_config_enumerate_sections2(cfg, expand_values_in_section,
666                                  cfg, scratch_pool);
667 
668   /* now, any modification attempt will be ignored / trigger an assertion
669    * in debug mode */
670   cfg->read_only = TRUE;
671 }
672 
673 svn_boolean_t
svn_config__is_read_only(svn_config_t * cfg)674 svn_config__is_read_only(svn_config_t *cfg)
675 {
676   return cfg->read_only;
677 }
678 
679 svn_config_t *
svn_config__shallow_copy(svn_config_t * src,apr_pool_t * pool)680 svn_config__shallow_copy(svn_config_t *src,
681                          apr_pool_t *pool)
682 {
683   svn_config_t *cfg = apr_palloc(pool, sizeof(*cfg));
684 
685   cfg->sections = src->sections;
686   cfg->pool = pool;
687 
688   /* r/o configs are fully expanded and don't need the x_pool anymore */
689   cfg->x_pool = src->read_only ? NULL : svn_pool_create(pool);
690   cfg->x_values = src->x_values;
691   cfg->tmp_key = svn_stringbuf_create_empty(pool);
692   cfg->tmp_value = svn_stringbuf_create_empty(pool);
693   cfg->section_names_case_sensitive = src->section_names_case_sensitive;
694   cfg->option_names_case_sensitive = src->option_names_case_sensitive;
695   cfg->read_only = src->read_only;
696 
697   return cfg;
698 }
699 
700 void
svn_config__shallow_replace_section(svn_config_t * target,svn_config_t * source,const char * section)701 svn_config__shallow_replace_section(svn_config_t *target,
702                                     svn_config_t *source,
703                                     const char *section)
704 {
705   if (target->read_only)
706     target->sections = apr_hash_copy(target->pool, target->sections);
707 
708   svn_hash_sets(target->sections, section,
709                 svn_hash_gets(source->sections, section));
710 }
711 
712 
713 
714 svn_error_t *
svn_config__parse_file(svn_config_t * cfg,const char * file,svn_boolean_t must_exist,apr_pool_t * result_pool)715 svn_config__parse_file(svn_config_t *cfg, const char *file,
716                        svn_boolean_t must_exist, apr_pool_t *result_pool)
717 {
718   svn_error_t *err = SVN_NO_ERROR;
719   apr_file_t *apr_file;
720   svn_stream_t *stream;
721   apr_pool_t *scratch_pool = svn_pool_create(result_pool);
722 
723   /* Use unbuffered IO since we use our own buffering. */
724   err = svn_io_file_open(&apr_file, file, APR_READ, APR_OS_DEFAULT,
725                          scratch_pool);
726 
727   if (! must_exist && err && APR_STATUS_IS_ENOENT(err->apr_err))
728     {
729       svn_error_clear(err);
730       svn_pool_destroy(scratch_pool);
731       return SVN_NO_ERROR;
732     }
733   else
734     SVN_ERR(err);
735 
736   stream = svn_stream_from_aprfile2(apr_file, FALSE, scratch_pool);
737   err = svn_config__parse_stream(stream,
738                                  svn_config__constructor_create(
739                                      NULL, NULL,
740                                      svn_config__default_add_value_fn,
741                                      scratch_pool),
742                                  cfg, scratch_pool);
743 
744   if (err != SVN_NO_ERROR)
745     {
746       /* Add the filename to the error stack. */
747       err = svn_error_createf(err->apr_err, err,
748                               _("Error while parsing config file: %s:"),
749                               svn_dirent_local_style(file, scratch_pool));
750     }
751 
752   /* Close the streams (and other cleanup): */
753   svn_pool_destroy(scratch_pool);
754 
755   return err;
756 }
757 
758 svn_error_t *
svn_config__parse_stream(svn_stream_t * stream,svn_config__constructor_t * constructor,void * constructor_baton,apr_pool_t * scratch_pool)759 svn_config__parse_stream(svn_stream_t *stream,
760                          svn_config__constructor_t *constructor,
761                          void *constructor_baton,
762                          apr_pool_t *scratch_pool)
763 {
764   parse_context_t *ctx;
765   svn_boolean_t stop;
766   int ch, count;
767 
768   ctx = apr_palloc(scratch_pool, sizeof(*ctx));
769 
770   ctx->constructor = constructor;
771   ctx->constructor_baton = constructor_baton;
772   ctx->stream = stream;
773   ctx->line = 1;
774   ctx->ungotten_char = EOF;
775   ctx->in_section = FALSE;
776   ctx->section = svn_stringbuf_create_empty(scratch_pool);
777   ctx->option = svn_stringbuf_create_empty(scratch_pool);
778   ctx->value = svn_stringbuf_create_empty(scratch_pool);
779   ctx->line_read = svn_stringbuf_create_empty(scratch_pool);
780   ctx->buffer_pos = 0;
781   ctx->buffer_size = 0;
782   ctx->hit_stream_eof = FALSE;
783 
784   SVN_ERR(skip_bom(ctx));
785 
786   do
787     {
788       SVN_ERR(skip_whitespace(ctx, &ch, &count));
789 
790       switch (ch)
791         {
792         case '[':               /* Start of section header */
793           if (count != 0)
794             return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
795                                      _("line %d: Section header"
796                                        " must start in the first column"),
797                                      ctx->line);
798 
799           /* Close the previous section before starting a new one. */
800           if (ctx->in_section)
801             {
802               SVN_ERR(close_section(ctx, &stop));
803               if (stop)
804                 return SVN_NO_ERROR;
805             }
806           SVN_ERR(parse_section_name(&ch, ctx, scratch_pool));
807           SVN_ERR(open_section(ctx, &stop));
808           if (stop)
809             return SVN_NO_ERROR;
810           break;
811 
812         case '#':               /* Comment */
813           if (count == 0)
814             {
815               SVN_ERR(skip_to_eoln(ctx, &ch));
816               ++(ctx->line);
817             }
818           else
819             return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
820                                      _("line %d: Comment"
821                                        " must start in the first column"),
822                                      ctx->line);
823           break;
824 
825         case '\n':              /* Empty line */
826           ++(ctx->line);
827           break;
828 
829         case EOF:               /* End of file or read error */
830           break;
831 
832         default:
833           if (svn_stringbuf_isempty(ctx->section))
834             return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
835                                      _("line %d: Section header expected"),
836                                      ctx->line);
837           else if (count != 0)
838             return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
839                                      _("line %d: Option expected"),
840                                      ctx->line);
841           else
842             SVN_ERR(parse_option(&ch, ctx, scratch_pool));
843           break;
844         }
845     }
846   while (ch != EOF);
847 
848   /* Emit the last close-section call to wrap up. */
849   if (ctx->in_section)
850     SVN_ERR(close_section(ctx, &stop));
851   return SVN_NO_ERROR;
852 }
853 
854 
855 /* Helper for ensure_auth_dirs: create SUBDIR under AUTH_DIR, iff
856    SUBDIR does not already exist, but ignore any errors.  Use POOL for
857    temporary allocation. */
858 static void
ensure_auth_subdir(const char * auth_dir,const char * subdir,apr_pool_t * pool)859 ensure_auth_subdir(const char *auth_dir,
860                    const char *subdir,
861                    apr_pool_t *pool)
862 {
863   svn_error_t *err;
864   const char *subdir_full_path;
865   svn_node_kind_t kind;
866 
867   subdir_full_path = svn_dirent_join(auth_dir, subdir, pool);
868   err = svn_io_check_path(subdir_full_path, &kind, pool);
869   if (err || kind == svn_node_none)
870     {
871       svn_error_clear(err);
872       svn_error_clear(svn_io_dir_make(subdir_full_path, APR_OS_DEFAULT, pool));
873     }
874 }
875 
876 /* Helper for svn_config_ensure:  see if ~/.subversion/auth/ and its
877    subdirs exist, try to create them, but don't throw errors on
878    failure.  PATH is assumed to be a path to the user's private config
879    directory. */
880 static void
ensure_auth_dirs(const char * path,apr_pool_t * pool)881 ensure_auth_dirs(const char *path,
882                  apr_pool_t *pool)
883 {
884   svn_node_kind_t kind;
885   const char *auth_dir;
886   svn_error_t *err;
887 
888   /* Ensure ~/.subversion/auth/ */
889   auth_dir = svn_dirent_join(path, SVN_CONFIG__AUTH_SUBDIR, pool);
890   err = svn_io_check_path(auth_dir, &kind, pool);
891   if (err || kind == svn_node_none)
892     {
893       svn_error_clear(err);
894       /* 'chmod 700' permissions: */
895       err = svn_io_dir_make(auth_dir,
896                             (APR_UREAD | APR_UWRITE | APR_UEXECUTE),
897                             pool);
898       if (err)
899         {
900           /* Don't try making subdirs if we can't make the top-level dir. */
901           svn_error_clear(err);
902           return;
903         }
904     }
905 
906   /* If a provider exists that wants to store credentials in
907      ~/.subversion, a subdirectory for the cred_kind must exist. */
908   ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SIMPLE, pool);
909   ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_USERNAME, pool);
910   ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_SERVER_TRUST, pool);
911   ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, pool);
912 }
913 
914 
915 svn_error_t *
svn_config_ensure(const char * config_dir,apr_pool_t * pool)916 svn_config_ensure(const char *config_dir, apr_pool_t *pool)
917 {
918   const char *path;
919   svn_node_kind_t kind;
920   svn_error_t *err;
921 
922   /* Ensure that the user-specific config directory exists.  */
923   SVN_ERR(svn_config_get_user_config_path(&path, config_dir, NULL, pool));
924 
925   if (! path)
926     return SVN_NO_ERROR;
927 
928   err = svn_io_check_resolved_path(path, &kind, pool);
929   if (err)
930     {
931       /* Don't throw an error, but don't continue. */
932       svn_error_clear(err);
933       return SVN_NO_ERROR;
934     }
935 
936   if (kind == svn_node_none)
937     {
938       err = svn_io_dir_make(path, APR_OS_DEFAULT, pool);
939       if (err)
940         {
941           /* Don't throw an error, but don't continue. */
942           svn_error_clear(err);
943           return SVN_NO_ERROR;
944         }
945     }
946   else if (kind == svn_node_file)
947     {
948       /* Somebody put a file where the config directory should be.
949          Wacky.  Let's bail. */
950       return SVN_NO_ERROR;
951     }
952 
953   /* Else, there's a configuration directory. */
954 
955   /* If we get errors trying to do things below, just stop and return
956      success.  There's no _need_ to init a config directory if
957      something's preventing it. */
958 
959   /** If non-existent, try to create a number of auth/ subdirectories. */
960   ensure_auth_dirs(path, pool);
961 
962   /** Ensure that the `README.txt' file exists. **/
963   SVN_ERR(svn_config_get_user_config_path
964           (&path, config_dir, SVN_CONFIG__USR_README_FILE, pool));
965 
966   if (! path)  /* highly unlikely, since a previous call succeeded */
967     return SVN_NO_ERROR;
968 
969   err = svn_io_check_path(path, &kind, pool);
970   if (err)
971     {
972       svn_error_clear(err);
973       return SVN_NO_ERROR;
974     }
975 
976   if (kind == svn_node_none)
977     {
978       apr_file_t *f;
979       const char *contents =
980    "This directory holds run-time configuration information for Subversion"  NL
981    "clients.  The configuration files all share the same syntax, but you"    NL
982    "should examine a particular file to learn what configuration"            NL
983    "directives are valid for that file."                                     NL
984    ""                                                                        NL
985    "The syntax is standard INI format:"                                      NL
986    ""                                                                        NL
987    "   - Empty lines, and lines starting with '#', are ignored."             NL
988    "     The first significant line in a file must be a section header."     NL
989    ""                                                                        NL
990    "   - A section starts with a section header, which must start in"        NL
991    "     the first column:"                                                  NL
992    ""                                                                        NL
993    "       [section-name]"                                                   NL
994    ""                                                                        NL
995    "   - An option, which must always appear within a section, is a pair"    NL
996    "     (name, value).  There are two valid forms for defining an"          NL
997    "     option, both of which must start in the first column:"              NL
998    ""                                                                        NL
999    "       name: value"                                                      NL
1000    "       name = value"                                                     NL
1001    ""                                                                        NL
1002    "     Whitespace around the separator (:, =) is optional."                NL
1003    ""                                                                        NL
1004    "   - Section and option names are case-insensitive, but case is"         NL
1005    "     preserved."                                                         NL
1006    ""                                                                        NL
1007    "   - An option's value may be broken into several lines.  The value"     NL
1008    "     continuation lines must start with at least one whitespace."        NL
1009    "     Trailing whitespace in the previous line, the newline character"    NL
1010    "     and the leading whitespace in the continuation line is compressed"  NL
1011    "     into a single space character."                                     NL
1012    ""                                                                        NL
1013    "   - All leading and trailing whitespace around a value is trimmed,"     NL
1014    "     but the whitespace within a value is preserved, with the"           NL
1015    "     exception of whitespace around line continuations, as"              NL
1016    "     described above."                                                   NL
1017    ""                                                                        NL
1018    "   - When a value is a boolean, any of the following strings are"        NL
1019    "     recognised as truth values (case does not matter):"                 NL
1020    ""                                                                        NL
1021    "       true      false"                                                  NL
1022    "       yes       no"                                                     NL
1023    "       on        off"                                                    NL
1024    "       1         0"                                                      NL
1025    ""                                                                        NL
1026    "   - When a value is a list, it is comma-separated.  Again, the"         NL
1027    "     whitespace around each element of the list is trimmed."             NL
1028    ""                                                                        NL
1029    "   - Option values may be expanded within a value by enclosing the"      NL
1030    "     option name in parentheses, preceded by a percent sign and"         NL
1031    "     followed by an 's':"                                                NL
1032    ""                                                                        NL
1033    "       %(name)s"                                                         NL
1034    ""                                                                        NL
1035    "     The expansion is performed recursively and on demand, during"       NL
1036    "     svn_option_get.  The name is first searched for in the same"        NL
1037    "     section, then in the special [DEFAULT] section. If the name"        NL
1038    "     is not found, the whole '%(name)s' placeholder is left"             NL
1039    "     unchanged."                                                         NL
1040    ""                                                                        NL
1041    "     Any modifications to the configuration data invalidate all"         NL
1042    "     previously expanded values, so that the next svn_option_get"        NL
1043    "     will take the modifications into account."                          NL
1044    ""                                                                        NL
1045    "The syntax of the configuration files is a subset of the one used by"    NL
1046    "Python's ConfigParser module; see"                                       NL
1047    ""                                                                        NL
1048    "   https://docs.python.org/3/library/configparser.html"                  NL
1049    ""                                                                        NL
1050    "Configuration data in the Windows registry"                              NL
1051    "=========================================="                              NL
1052    ""                                                                        NL
1053    "On Windows, configuration data may also be stored in the registry. The"  NL
1054    "functions svn_config_read and svn_config_merge will read from the"       NL
1055    "registry when passed file names of the form:"                            NL
1056    ""                                                                        NL
1057    "   REGISTRY:<hive>/path/to/config-key"                                   NL
1058    ""                                                                        NL
1059    "The REGISTRY: prefix must be in upper case. The <hive> part must be"     NL
1060    "one of:"                                                                 NL
1061    ""                                                                        NL
1062    "   HKLM for HKEY_LOCAL_MACHINE"                                          NL
1063    "   HKCU for HKEY_CURRENT_USER"                                           NL
1064    ""                                                                        NL
1065    "The values in config-key represent the options in the [DEFAULT] section."NL
1066    "The keys below config-key represent other sections, and their values"    NL
1067    "represent the options. Only values of type REG_SZ whose name doesn't"    NL
1068    "start with a '#' will be used; other values, as well as the keys'"       NL
1069    "default values, will be ignored."                                        NL
1070    ""                                                                        NL
1071    ""                                                                        NL
1072    "File locations"                                                          NL
1073    "=============="                                                          NL
1074    ""                                                                        NL
1075    "Typically, Subversion uses two config directories, one for site-wide"    NL
1076    "configuration,"                                                          NL
1077    ""                                                                        NL
1078    "  Unix:"                                                                 NL
1079    "    /etc/subversion/servers"                                             NL
1080    "    /etc/subversion/config"                                              NL
1081    "    /etc/subversion/hairstyles"                                          NL
1082    "  Windows:"                                                              NL
1083    "    %ALLUSERSPROFILE%\\Application Data\\Subversion\\servers"            NL
1084    "    %ALLUSERSPROFILE%\\Application Data\\Subversion\\config"             NL
1085    "    %ALLUSERSPROFILE%\\Application Data\\Subversion\\hairstyles"         NL
1086    "    REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Servers"            NL
1087    "    REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Config"             NL
1088    "    REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Hairstyles"         NL
1089    ""                                                                        NL
1090    "and one for per-user configuration:"                                     NL
1091    ""                                                                        NL
1092    "  Unix:"                                                                 NL
1093    "    ~/.subversion/servers"                                               NL
1094    "    ~/.subversion/config"                                                NL
1095    "    ~/.subversion/hairstyles"                                            NL
1096    "  Windows:"                                                              NL
1097    "    %APPDATA%\\Subversion\\servers"                                      NL
1098    "    %APPDATA%\\Subversion\\config"                                       NL
1099    "    %APPDATA%\\Subversion\\hairstyles"                                   NL
1100    "    REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Servers"            NL
1101    "    REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Config"             NL
1102    "    REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Hairstyles"         NL
1103    ""                                                                        NL;
1104 
1105       err = svn_io_file_open(&f, path,
1106                              (APR_WRITE | APR_CREATE | APR_EXCL),
1107                              APR_OS_DEFAULT,
1108                              pool);
1109 
1110       if (! err)
1111         {
1112           SVN_ERR(svn_io_file_write_full(f, contents,
1113                                          strlen(contents), NULL, pool));
1114           SVN_ERR(svn_io_file_close(f, pool));
1115         }
1116 
1117       svn_error_clear(err);
1118     }
1119 
1120   /** Ensure that the `servers' file exists. **/
1121   SVN_ERR(svn_config_get_user_config_path
1122           (&path, config_dir, SVN_CONFIG_CATEGORY_SERVERS, pool));
1123 
1124   if (! path)  /* highly unlikely, since a previous call succeeded */
1125     return SVN_NO_ERROR;
1126 
1127   err = svn_io_check_path(path, &kind, pool);
1128   if (err)
1129     {
1130       svn_error_clear(err);
1131       return SVN_NO_ERROR;
1132     }
1133 
1134   if (kind == svn_node_none)
1135     {
1136       apr_file_t *f;
1137       const char *contents =
1138         "### This file specifies server-specific parameters,"                NL
1139         "### including HTTP proxy information, HTTP timeout settings,"       NL
1140         "### and authentication settings."                                   NL
1141         "###"                                                                NL
1142         "### The currently defined server options are:"                      NL
1143         "###   http-proxy-host            Proxy host for HTTP connection"    NL
1144         "###   http-proxy-port            Port number of proxy host service" NL
1145         "###   http-proxy-username        Username for auth to proxy service"NL
1146         "###   http-proxy-password        Password for auth to proxy service"NL
1147         "###   http-proxy-exceptions      List of sites that do not use proxy"
1148                                                                              NL
1149         "###   http-timeout               Timeout for HTTP requests in seconds"
1150                                                                              NL
1151         "###   http-compression           Whether to compress HTTP requests" NL
1152         "###                              (yes/no/auto)."                    NL
1153         "###   http-max-connections       Maximum number of parallel server" NL
1154         "###                              connections to use for any given"  NL
1155         "###                              HTTP operation."                   NL
1156         "###   http-chunked-requests      Whether to use chunked transfer"   NL
1157         "###                              encoding for HTTP requests body."  NL
1158         "###   http-auth-types            List of HTTP authentication types."NL
1159         "###   ssl-authority-files        List of files, each of a trusted CA"
1160                                                                              NL
1161         "###   ssl-trust-default-ca       Trust the system 'default' CAs"    NL
1162         "###   ssl-client-cert-file       PKCS#12 format client certificate file"
1163                                                                              NL
1164         "###   ssl-client-cert-password   Client Key password, if needed."   NL
1165         "###   ssl-pkcs11-provider        Name of PKCS#11 provider to use."  NL
1166         "###   http-library               Which library to use for http/https"
1167                                                                              NL
1168         "###                              connections."                      NL
1169         "###   http-bulk-updates          Whether to request bulk update"    NL
1170         "###                              responses or to fetch each file"   NL
1171         "###                              in an individual request. "        NL
1172         "###   store-passwords            Specifies whether passwords used"  NL
1173         "###                              to authenticate against a"         NL
1174         "###                              Subversion server may be cached"   NL
1175         "###                              to disk in any way."               NL
1176 #ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
1177         "###   store-plaintext-passwords  Specifies whether passwords may"   NL
1178         "###                              be cached on disk unencrypted."    NL
1179 #endif
1180         "###   store-ssl-client-cert-pp   Specifies whether passphrase used" NL
1181         "###                              to authenticate against a client"  NL
1182         "###                              certificate may be cached to disk" NL
1183         "###                              in any way"                        NL
1184 #ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
1185         "###   store-ssl-client-cert-pp-plaintext"                           NL
1186         "###                              Specifies whether client cert"     NL
1187         "###                              passphrases may be cached on disk" NL
1188         "###                              unencrypted (i.e., as plaintext)." NL
1189 #endif
1190         "###   store-auth-creds           Specifies whether any auth info"   NL
1191         "###                              (passwords, server certs, etc.)"   NL
1192         "###                              may be cached to disk."            NL
1193         "###   username                   Specifies the default username."   NL
1194         "###"                                                                NL
1195         "### Set store-passwords to 'no' to avoid storing new passwords on"  NL
1196         "### disk in any way, including in password stores.  It defaults to" NL
1197         "### 'yes', but Subversion will never save your password to disk in" NL
1198         "### plaintext unless explicitly configured to do so."               NL
1199         "###"                                                                NL
1200 #ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
1201         "### Set store-plaintext-passwords to 'no' to avoid storing new"     NL
1202         "### passwords in unencrypted form in the auth/ area of your config" NL
1203         "### directory. Set it to 'yes' to allow Subversion to store"        NL
1204         "### unencrypted passwords in the auth/ area.  The default is"       NL
1205         "### 'ask', which means that Subversion will ask you before"         NL
1206         "### saving a password to disk in unencrypted form.  Note that"      NL
1207         "### this option has no effect if either 'store-passwords' or "      NL
1208         "### 'store-auth-creds' is set to 'no'."                             NL
1209         "###"                                                                NL
1210 #endif
1211         "### Set store-ssl-client-cert-pp to 'no' to avoid storing new ssl"  NL
1212         "### client certificate passphrases in the auth/ area of your"       NL
1213         "### config directory.  It defaults to 'yes', but Subversion will"   NL
1214         "### never save your passphrase to disk in plaintext unless"         NL
1215         "### explicitly configured to do so."                                NL
1216         "###"                                                                NL
1217 #ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
1218         "### Set store-ssl-client-cert-pp-plaintext to 'no' to avoid storing"NL
1219         "### new passphrases in unencrypted form in the auth/ area of your"  NL
1220         "### config directory.  Set it to 'yes' to allow Subversion to"      NL
1221         "### store unencrypted passphrases in the auth/ area.  The default"  NL
1222         "### is 'ask', which means that Subversion will prompt before"       NL
1223         "### saving a passphrase to disk in unencrypted form.  Note that"    NL
1224         "### this option has no effect if either 'store-auth-creds' or "     NL
1225         "### 'store-ssl-client-cert-pp' is set to 'no'."                     NL
1226         "###"                                                                NL
1227 #endif
1228         "### Set store-auth-creds to 'no' to avoid storing any new Subversion"
1229                                                                              NL
1230         "### credentials in the auth/ area of your config directory."        NL
1231         "### Note that this includes SSL server certificates."               NL
1232         "### It defaults to 'yes'."                                          NL
1233         "###"                                                                NL
1234         "### Note that setting a 'store-*' option to 'no' only prevents"     NL
1235         "### saving of *new* passwords, passphrases or other credentials."   NL
1236         "### It does not remove or invalidate existing stored credentials."  NL
1237         "### To do that, see the 'svn auth --remove' command, or remove the" NL
1238         "### cache files by hand as described in the Subversion book at"     NL
1239         "### http://svnbook.red-bean.com/nightly/en/svn.serverconfig.netmodel.html#svn.tour.initial.authn-cache-purge"
1240                                                                              NL
1241         "###"                                                                NL
1242         "### HTTP timeouts, if given, are specified in seconds.  A timeout"  NL
1243         "### of 0, i.e. zero, causes a builtin default to be used."          NL
1244         "###"                                                                NL
1245         "### Most users will not need to explicitly set the http-library"    NL
1246         "### option, but valid values for the option include:"               NL
1247         "###    'serf': Serf-based module (Subversion 1.5 - present)"        NL
1248         "### Availability of these modules may depend on your specific"      NL
1249         "### Subversion distribution."                                       NL
1250         "###"                                                                NL
1251         "### The commented-out examples below are intended only to"          NL
1252         "### demonstrate how to use this file; any resemblance to actual"    NL
1253         "### servers, living or dead, is entirely coincidental."             NL
1254         ""                                                                   NL
1255         "### In the 'groups' section, the URL of the repository you're"      NL
1256         "### trying to access is matched against the patterns on the right." NL
1257         "### If a match is found, the server options are taken from the"     NL
1258         "### section with the corresponding name on the left."               NL
1259         ""                                                                   NL
1260         "[groups]"                                                           NL
1261         "# group1 = *.collab.net"                                            NL
1262         "# othergroup = repository.blarggitywhoomph.com"                     NL
1263         "# thirdgroup = *.example.com"                                       NL
1264         ""                                                                   NL
1265         "### Information for the first group:"                               NL
1266         "# [group1]"                                                         NL
1267         "# http-proxy-host = proxy1.some-domain-name.com"                    NL
1268         "# http-proxy-port = 80"                                             NL
1269         "# http-proxy-username = blah"                                       NL
1270         "# http-proxy-password = doubleblah"                                 NL
1271         "# http-timeout = 60"                                                NL
1272 #ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
1273         "# store-plaintext-passwords = no"                                   NL
1274 #endif
1275         "# username = harry"                                                 NL
1276         ""                                                                   NL
1277         "### Information for the second group:"                              NL
1278         "# [othergroup]"                                                     NL
1279         "# http-proxy-host = proxy2.some-domain-name.com"                    NL
1280         "# http-proxy-port = 9000"                                           NL
1281         "# No username and password for the proxy, so use the defaults below."
1282                                                                              NL
1283         ""                                                                   NL
1284         "### You can set default parameters in the 'global' section."        NL
1285         "### These parameters apply if no corresponding parameter is set in" NL
1286         "### a specifically matched group as shown above.  Thus, if you go"  NL
1287         "### through the same proxy server to reach every site on the"       NL
1288         "### Internet, you probably just want to put that server's"          NL
1289         "### information in the 'global' section and not bother with"        NL
1290         "### 'groups' or any other sections."                                NL
1291         "###"                                                                NL
1292         "### Most people might want to configure password caching"           NL
1293         "### parameters here, but you can also configure them per server"    NL
1294         "### group (per-group settings override global settings)."           NL
1295         "###"                                                                NL
1296         "### If you go through a proxy for all but a few sites, you can"     NL
1297         "### list those exceptions under 'http-proxy-exceptions'.  This only"NL
1298         "### overrides defaults, not explicitly matched server names."       NL
1299         "###"                                                                NL
1300         "### 'ssl-authority-files' is a semicolon-delimited list of files,"  NL
1301         "### each pointing to a PEM-encoded Certificate Authority (CA) "     NL
1302         "### SSL certificate.  See details above for overriding security "   NL
1303         "### due to SSL."                                                    NL
1304         "[global]"                                                           NL
1305         "# http-proxy-exceptions = *.exception.com, www.internal-site.org"   NL
1306         "# http-proxy-host = defaultproxy.whatever.com"                      NL
1307         "# http-proxy-port = 7000"                                           NL
1308         "# http-proxy-username = defaultusername"                            NL
1309         "# http-proxy-password = defaultpassword"                            NL
1310         "# http-compression = auto"                                          NL
1311         "# No http-timeout, so just use the builtin default."                NL
1312         "# ssl-authority-files = /path/to/CAcert.pem;/path/to/CAcert2.pem"   NL
1313         "#"                                                                  NL
1314         "# Password / passphrase caching parameters:"                        NL
1315         "# store-passwords = no"                                             NL
1316         "# store-ssl-client-cert-pp = no"                                    NL
1317 #ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
1318         "# store-plaintext-passwords = no"                                   NL
1319         "# store-ssl-client-cert-pp-plaintext = no"                          NL
1320 #endif
1321         ;
1322 
1323       err = svn_io_file_open(&f, path,
1324                              (APR_WRITE | APR_CREATE | APR_EXCL),
1325                              APR_OS_DEFAULT,
1326                              pool);
1327 
1328       if (! err)
1329         {
1330           SVN_ERR(svn_io_file_write_full(f, contents,
1331                                          strlen(contents), NULL, pool));
1332           SVN_ERR(svn_io_file_close(f, pool));
1333         }
1334 
1335       svn_error_clear(err);
1336     }
1337 
1338   /** Ensure that the `config' file exists. **/
1339   SVN_ERR(svn_config_get_user_config_path
1340           (&path, config_dir, SVN_CONFIG_CATEGORY_CONFIG, pool));
1341 
1342   if (! path)  /* highly unlikely, since a previous call succeeded */
1343     return SVN_NO_ERROR;
1344 
1345   err = svn_io_check_path(path, &kind, pool);
1346   if (err)
1347     {
1348       svn_error_clear(err);
1349       return SVN_NO_ERROR;
1350     }
1351 
1352   if (kind == svn_node_none)
1353     {
1354       apr_file_t *f;
1355       const char *contents =
1356         "### This file configures various client-side behaviors."            NL
1357         "###"                                                                NL
1358         "### The commented-out examples below are intended to demonstrate"   NL
1359         "### how to use this file."                                          NL
1360         ""                                                                   NL
1361         "### Section for authentication and authorization customizations."   NL
1362         "[auth]"                                                             NL
1363         "### Set password stores used by Subversion. They should be"         NL
1364         "### delimited by spaces or commas. The order of values determines"  NL
1365         "### the order in which password stores are used."                   NL
1366         "### Valid password stores:"                                         NL
1367         "###   gnome-keyring        (Unix-like systems)"                     NL
1368         "###   kwallet              (Unix-like systems)"                     NL
1369         "###   gpg-agent            (Unix-like systems)"                     NL
1370         "###   keychain             (Mac OS X)"                              NL
1371         "###   windows-cryptoapi    (Windows)"                               NL
1372 #ifdef SVN_HAVE_KEYCHAIN_SERVICES
1373         "# password-stores = keychain"                                       NL
1374 #elif defined(WIN32) && !defined(__MINGW32__)
1375         "# password-stores = windows-cryptoapi"                              NL
1376 #else
1377         "# password-stores = gpg-agent,gnome-keyring,kwallet"                NL
1378 #endif
1379         "### To disable all password stores, use an empty list:"             NL
1380         "# password-stores ="                                                NL
1381 #ifdef SVN_HAVE_KWALLET
1382         "###"                                                                NL
1383         "### Set KWallet wallet used by Subversion. If empty or unset,"      NL
1384         "### then the default network wallet will be used."                  NL
1385         "# kwallet-wallet ="                                                 NL
1386         "###"                                                                NL
1387         "### Include PID (Process ID) in Subversion application name when"   NL
1388         "### using KWallet. It defaults to 'no'."                            NL
1389         "# kwallet-svn-application-name-with-pid = yes"                      NL
1390 #endif
1391         "###"                                                                NL
1392         "### Set ssl-client-cert-file-prompt to 'yes' to cause the client"   NL
1393         "### to prompt for a path to a client cert file when the server"     NL
1394         "### requests a client cert but no client cert file is found in the" NL
1395         "### expected place (see the 'ssl-client-cert-file' option in the"   NL
1396         "### 'servers' configuration file). Defaults to 'no'."               NL
1397         "# ssl-client-cert-file-prompt = no"                                 NL
1398         "###"                                                                NL
1399         "### The rest of the [auth] section in this file has been deprecated."
1400                                                                              NL
1401         "### Both 'store-passwords' and 'store-auth-creds' can now be"       NL
1402         "### specified in the 'servers' file in your config directory"       NL
1403         "### and are documented there. Anything specified in this section "  NL
1404         "### is overridden by settings specified in the 'servers' file."     NL
1405         "# store-passwords = no"                                             NL
1406         "# store-auth-creds = no"                                            NL
1407         ""                                                                   NL
1408         "### Section for configuring external helper applications."          NL
1409         "[helpers]"                                                          NL
1410         "### Set editor-cmd to the command used to invoke your text editor." NL
1411         "###   This will override the environment variables that Subversion" NL
1412         "###   examines by default to find this information ($EDITOR, "      NL
1413         "###   et al)."                                                      NL
1414         "# editor-cmd = editor (vi, emacs, notepad, etc.)"                   NL
1415         "### Set diff-cmd to the absolute path of your 'diff' program."      NL
1416         "###   This will override the compile-time default, which is to use" NL
1417         "###   Subversion's internal diff implementation."                   NL
1418         "# diff-cmd = diff_program (diff, gdiff, etc.)"                      NL
1419         "### Diff-extensions are arguments passed to an external diff"       NL
1420         "### program or to Subversion's internal diff implementation."       NL
1421         "### Set diff-extensions to override the default arguments ('-u')."  NL
1422         "# diff-extensions = -u -p"                                          NL
1423         "### Set diff3-cmd to the absolute path of your 'diff3' program."    NL
1424         "###   This will override the compile-time default, which is to use" NL
1425         "###   Subversion's internal diff3 implementation."                  NL
1426         "# diff3-cmd = diff3_program (diff3, gdiff3, etc.)"                  NL
1427         "### Set diff3-has-program-arg to 'yes' if your 'diff3' program"     NL
1428         "###   accepts the '--diff-program' option."                         NL
1429         "# diff3-has-program-arg = [yes | no]"                               NL
1430         "### Set merge-tool-cmd to the command used to invoke your external" NL
1431         "### merging tool of choice. Subversion will pass 5 arguments to"    NL
1432         "### the specified command: base theirs mine merged wcfile"          NL
1433         "# merge-tool-cmd = merge_command"                                   NL
1434         ""                                                                   NL
1435         "### Section for configuring tunnel agents."                         NL
1436         "[tunnels]"                                                          NL
1437         "### Configure svn protocol tunnel schemes here.  By default, only"  NL
1438         "### the 'ssh' scheme is defined.  You can define other schemes to"  NL
1439         "### be used with 'svn+scheme://hostname/path' URLs.  A scheme"      NL
1440         "### definition is simply a command, optionally prefixed by an"      NL
1441         "### environment variable name which can override the command if it" NL
1442         "### is defined.  The command (or environment variable) may contain" NL
1443         "### arguments, using standard shell quoting for arguments with"     NL
1444         "### spaces.  The command will be invoked as:"                       NL
1445         "###   <command> <hostname> svnserve -t"                             NL
1446         "### (If the URL includes a username, then the hostname will be"     NL
1447         "### passed to the tunnel agent as <user>@<hostname>.)  If the"      NL
1448         "### built-in ssh scheme were not predefined, it could be defined"   NL
1449         "### as:"                                                            NL
1450         "# ssh = $SVN_SSH ssh -q --"                                         NL
1451         "### If you wanted to define a new 'rsh' scheme, to be used with"    NL
1452         "### 'svn+rsh:' URLs, you could do so as follows:"                   NL
1453         "# rsh = rsh --"                                                     NL
1454         "### Or, if you wanted to specify a full path and arguments:"        NL
1455         "# rsh = /path/to/rsh -l myusername --"                              NL
1456         "### On Windows, if you are specifying a full path to a command,"    NL
1457         "### use a forward slash (/) or a paired backslash (\\\\) as the"    NL
1458         "### path separator.  A single backslash will be treated as an"      NL
1459         "### escape for the following character."                            NL
1460         ""                                                                   NL
1461         "### Section for configuring miscellaneous Subversion options."      NL
1462         "[miscellany]"                                                       NL
1463         "### Set global-ignores to a set of whitespace-delimited globs"      NL
1464         "### which Subversion will ignore in its 'status' output, and"       NL
1465         "### while importing or adding files and directories."               NL
1466         "### '*' matches leading dots, e.g. '*.rej' matches '.foo.rej'."     NL
1467         "# global-ignores = " SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_1      NL
1468         "#   " SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_2                     NL
1469         "### Set log-encoding to the default encoding for log messages"      NL
1470         "# log-encoding = latin1"                                            NL
1471         "### Set use-commit-times to make checkout/update/switch/revert"     NL
1472         "### put last-committed timestamps on every file touched."           NL
1473         "# use-commit-times = yes"                                           NL
1474         "### Set no-unlock to prevent 'svn commit' from automatically"       NL
1475         "### releasing locks on files."                                      NL
1476         "# no-unlock = yes"                                                  NL
1477         "### Set mime-types-file to a MIME type registry file, used to"      NL
1478         "### provide hints to Subversion's MIME type auto-detection"         NL
1479         "### algorithm."                                                     NL
1480         "# mime-types-file = /path/to/mime.types"                            NL
1481         "### Set preserved-conflict-file-exts to a whitespace-delimited"     NL
1482         "### list of patterns matching file extensions which should be"      NL
1483         "### preserved in generated conflict file names.  By default,"       NL
1484         "### conflict files use custom extensions."                          NL
1485         "# preserved-conflict-file-exts = doc ppt xls od?"                   NL
1486         "### Set enable-auto-props to 'yes' to enable automatic properties"  NL
1487         "### for 'svn add' and 'svn import', it defaults to 'no'."           NL
1488         "### Automatic properties are defined in the section 'auto-props'."  NL
1489         "# enable-auto-props = yes"                                          NL
1490 #ifdef SVN_HAVE_LIBMAGIC
1491         "### Set enable-magic-file to 'no' to disable magic file detection"  NL
1492         "### of the file type when automatically setting svn:mime-type. It"  NL
1493         "### defaults to 'yes' if magic file support is possible."           NL
1494         "# enable-magic-file = yes"                                          NL
1495 #endif
1496         "### Set interactive-conflicts to 'no' to disable interactive"       NL
1497         "### conflict resolution prompting.  It defaults to 'yes'."          NL
1498         "# interactive-conflicts = no"                                       NL
1499         "### Set memory-cache-size to define the size of the memory cache"   NL
1500         "### used by the client when accessing a FSFS repository via"        NL
1501         "### ra_local (the file:// scheme). The value represents the number" NL
1502         "### of MB used by the cache."                                       NL
1503         "# memory-cache-size = 16"                                           NL
1504         "### Set diff-ignore-content-type to 'yes' to cause 'svn diff' to"   NL
1505         "### attempt to show differences of all modified files regardless"   NL
1506         "### of their MIME content type.  By default, Subversion will only"  NL
1507         "### attempt to show differences for files believed to have human-"  NL
1508         "### readable (non-binary) content.  This option is especially"      NL
1509         "### useful when Subversion is configured (via the 'diff-cmd'"       NL
1510         "### option) to employ an external differencing tool which is able"  NL
1511         "### to show meaningful differences for binary file formats.  [New"  NL
1512         "### in 1.9]"                                                        NL
1513         "# diff-ignore-content-type = no"                                    NL
1514         ""                                                                   NL
1515         "### Section for configuring automatic properties."                  NL
1516         "[auto-props]"                                                       NL
1517         "### The format of the entries is:"                                  NL
1518         "###   file-name-pattern = propname[=value][;propname[=value]...]"   NL
1519         "### The file-name-pattern can contain wildcards (such as '*' and"   NL
1520         "### '?').  All entries which match (case-insensitively) will be"    NL
1521         "### applied to the file.  Note that auto-props functionality"       NL
1522         "### must be enabled, which is typically done by setting the"        NL
1523         "### 'enable-auto-props' option."                                    NL
1524         "# *.c = svn:eol-style=native"                                       NL
1525         "# *.cpp = svn:eol-style=native"                                     NL
1526         "# *.h = svn:keywords=Author Date Id Rev URL;svn:eol-style=native"   NL
1527         "# *.dsp = svn:eol-style=CRLF"                                       NL
1528         "# *.dsw = svn:eol-style=CRLF"                                       NL
1529         "# *.sh = svn:eol-style=native;svn:executable"                       NL
1530         "# *.txt = svn:eol-style=native;svn:keywords=Author Date Id Rev URL;"NL
1531         "# *.png = svn:mime-type=image/png"                                  NL
1532         "# *.jpg = svn:mime-type=image/jpeg"                                 NL
1533         "# Makefile = svn:eol-style=native"                                  NL
1534         ""                                                                   NL
1535         "### Section for configuring working copies."                        NL
1536         "[working-copy]"                                                     NL
1537         "### Set to a list of the names of specific clients that should use" NL
1538         "### exclusive SQLite locking of working copies.  This increases the"NL
1539         "### performance of the client but prevents concurrent access by"    NL
1540         "### other clients.  Third-party clients may also support this"      NL
1541         "### option."                                                        NL
1542         "### Possible values:"                                               NL
1543         "###   svn                (the command line client)"                 NL
1544         "# exclusive-locking-clients ="                                      NL
1545         "### Set to true to enable exclusive SQLite locking of working"      NL
1546         "### copies by all clients using the 1.8 APIs.  Enabling this may"   NL
1547         "### cause some clients to fail to work properly. This does not have"NL
1548         "### to be set for exclusive-locking-clients to work."               NL
1549         "# exclusive-locking = false"                                        NL
1550         "### Set the SQLite busy timeout in milliseconds: the maximum time"  NL
1551         "### the client waits to get access to the SQLite database before"   NL
1552         "### returning an error.  The default is 10000, i.e. 10 seconds."    NL
1553         "### Longer values may be useful when exclusive locking is enabled." NL
1554         "# busy-timeout = 10000"                                             NL
1555         ;
1556 
1557       err = svn_io_file_open(&f, path,
1558                              (APR_WRITE | APR_CREATE | APR_EXCL),
1559                              APR_OS_DEFAULT,
1560                              pool);
1561 
1562       if (! err)
1563         {
1564           SVN_ERR(svn_io_file_write_full(f, contents,
1565                                          strlen(contents), NULL, pool));
1566           SVN_ERR(svn_io_file_close(f, pool));
1567         }
1568 
1569       svn_error_clear(err);
1570     }
1571 
1572   return SVN_NO_ERROR;
1573 }
1574 
1575 svn_error_t *
svn_config_get_user_config_path(const char ** path,const char * config_dir,const char * fname,apr_pool_t * pool)1576 svn_config_get_user_config_path(const char **path,
1577                                 const char *config_dir,
1578                                 const char *fname,
1579                                 apr_pool_t *pool)
1580 {
1581   *path= NULL;
1582 
1583   /* Note that even if fname is null, svn_dirent_join_many will DTRT. */
1584 
1585   if (config_dir)
1586     {
1587       *path = svn_dirent_join_many(pool, config_dir, fname, SVN_VA_NULL);
1588       return SVN_NO_ERROR;
1589     }
1590 
1591 #ifdef WIN32
1592   {
1593     const char *folder;
1594     SVN_ERR(svn_config__win_config_path(&folder, FALSE, pool, pool));
1595 
1596     if (! folder)
1597       return SVN_NO_ERROR;
1598 
1599     *path = svn_dirent_join_many(pool, folder,
1600                                  SVN_CONFIG__SUBDIRECTORY, fname, SVN_VA_NULL);
1601   }
1602 
1603 #elif defined(__HAIKU__)
1604   {
1605     char folder[B_PATH_NAME_LENGTH];
1606 
1607     status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, -1, false,
1608                                     folder, sizeof(folder));
1609     if (error)
1610       return SVN_NO_ERROR;
1611 
1612     *path = svn_dirent_join_many(pool, folder,
1613                                  SVN_CONFIG__USR_DIRECTORY, fname,
1614                                  SVN_VA_NULL);
1615   }
1616 #else  /* ! WIN32 && !__HAIKU__ */
1617 
1618   {
1619     const char *homedir = svn_user_get_homedir(pool);
1620     if (! homedir)
1621       return SVN_NO_ERROR;
1622     *path = svn_dirent_join_many(pool,
1623                                  homedir,
1624                                SVN_CONFIG__USR_DIRECTORY, fname, SVN_VA_NULL);
1625   }
1626 #endif /* WIN32 */
1627 
1628   return SVN_NO_ERROR;
1629 }
1630 
1631