1251881Speter/*
2251881Speter * config_file.c :  parsing configuration files
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <apr_lib.h>
27251881Speter#include <apr_env.h>
28251881Speter#include "config_impl.h"
29251881Speter#include "svn_io.h"
30251881Speter#include "svn_types.h"
31251881Speter#include "svn_dirent_uri.h"
32251881Speter#include "svn_auth.h"
33251881Speter#include "svn_subst.h"
34251881Speter#include "svn_utf.h"
35251881Speter#include "svn_pools.h"
36251881Speter#include "svn_user.h"
37251881Speter#include "svn_ctype.h"
38251881Speter
39251881Speter#include "svn_private_config.h"
40251881Speter
41251881Speter#ifdef __HAIKU__
42251881Speter#  include <FindDirectory.h>
43251881Speter#  include <StorageDefs.h>
44251881Speter#endif
45251881Speter
46251881Speter/* Used to terminate lines in large multi-line string literals. */
47251881Speter#define NL APR_EOL_STR
48251881Speter
49251881Speter
50251881Speter/* File parsing context */
51251881Spetertypedef struct parse_context_t
52251881Speter{
53251881Speter  /* This config struct */
54251881Speter  svn_config_t *cfg;
55251881Speter
56251881Speter  /* The stream struct */
57251881Speter  svn_stream_t *stream;
58251881Speter
59251881Speter  /* The current line in the file */
60251881Speter  int line;
61251881Speter
62251881Speter  /* Emulate an ungetc */
63251881Speter  int ungotten_char;
64251881Speter
65251881Speter  /* Temporary strings */
66251881Speter  svn_stringbuf_t *section;
67251881Speter  svn_stringbuf_t *option;
68251881Speter  svn_stringbuf_t *value;
69251881Speter
70251881Speter  /* Parser buffer for getc() to avoid call overhead into several libraries
71251881Speter     for every character */
72251881Speter  char parser_buffer[SVN_STREAM_CHUNK_SIZE]; /* Larger than most config files */
73251881Speter  size_t buffer_pos; /* Current position within parser_buffer */
74251881Speter  size_t buffer_size; /* parser_buffer contains this many bytes */
75251881Speter} parse_context_t;
76251881Speter
77251881Speter
78251881Speter
79251881Speter/* Emulate getc() because streams don't support it.
80251881Speter *
81251881Speter * In order to be able to ungetc(), use the CXT instead of the stream
82251881Speter * to be able to store the 'ungotton' character.
83251881Speter *
84251881Speter */
85251881Speterstatic APR_INLINE svn_error_t *
86251881Speterparser_getc(parse_context_t *ctx, int *c)
87251881Speter{
88251881Speter  do
89251881Speter    {
90251881Speter      if (ctx->ungotten_char != EOF)
91251881Speter        {
92251881Speter          *c = ctx->ungotten_char;
93251881Speter          ctx->ungotten_char = EOF;
94251881Speter        }
95251881Speter      else if (ctx->buffer_pos < ctx->buffer_size)
96251881Speter        {
97253734Speter          *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
98251881Speter          ctx->buffer_pos++;
99251881Speter        }
100251881Speter      else
101251881Speter        {
102251881Speter          ctx->buffer_pos = 0;
103251881Speter          ctx->buffer_size = sizeof(ctx->parser_buffer);
104251881Speter
105251881Speter          SVN_ERR(svn_stream_read(ctx->stream, ctx->parser_buffer,
106251881Speter                                  &(ctx->buffer_size)));
107251881Speter
108251881Speter          if (ctx->buffer_pos < ctx->buffer_size)
109251881Speter            {
110253734Speter              *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
111251881Speter              ctx->buffer_pos++;
112251881Speter            }
113251881Speter          else
114251881Speter            *c = EOF;
115251881Speter        }
116251881Speter    }
117251881Speter  while (*c == '\r');
118251881Speter
119251881Speter  return SVN_NO_ERROR;
120251881Speter}
121251881Speter
122251881Speter/* Simplified version of parser_getc() to be used inside skipping loops.
123251881Speter * It will not check for 'ungotton' chars and may or may not ignore '\r'.
124251881Speter *
125251881Speter * In a 'while(cond) getc();' loop, the first iteration must call
126251881Speter * parser_getc to handle all the special cases.  Later iterations should
127251881Speter * use parser_getc_plain for maximum performance.
128251881Speter */
129251881Speterstatic APR_INLINE svn_error_t *
130251881Speterparser_getc_plain(parse_context_t *ctx, int *c)
131251881Speter{
132251881Speter  if (ctx->buffer_pos < ctx->buffer_size)
133251881Speter    {
134253734Speter      *c = (unsigned char)ctx->parser_buffer[ctx->buffer_pos];
135251881Speter      ctx->buffer_pos++;
136251881Speter
137251881Speter      return SVN_NO_ERROR;
138251881Speter    }
139251881Speter
140251881Speter  return parser_getc(ctx, c);
141251881Speter}
142251881Speter
143251881Speter/* Emulate ungetc() because streams don't support it.
144251881Speter *
145251881Speter * Use CTX to store the ungotten character C.
146251881Speter */
147251881Speterstatic APR_INLINE svn_error_t *
148251881Speterparser_ungetc(parse_context_t *ctx, int c)
149251881Speter{
150251881Speter  ctx->ungotten_char = c;
151251881Speter
152251881Speter  return SVN_NO_ERROR;
153251881Speter}
154251881Speter
155251881Speter/* Eat chars from STREAM until encounter non-whitespace, newline, or EOF.
156251881Speter   Set *PCOUNT to the number of characters eaten, not counting the
157251881Speter   last one, and return the last char read (the one that caused the
158251881Speter   break).  */
159251881Speterstatic APR_INLINE svn_error_t *
160251881Speterskip_whitespace(parse_context_t *ctx, int *c, int *pcount)
161251881Speter{
162251881Speter  int ch = 0;
163251881Speter  int count = 0;
164251881Speter
165251881Speter  SVN_ERR(parser_getc(ctx, &ch));
166251881Speter  while (svn_ctype_isspace(ch) && ch != '\n' && ch != EOF)
167251881Speter    {
168251881Speter      ++count;
169251881Speter      SVN_ERR(parser_getc_plain(ctx, &ch));
170251881Speter    }
171251881Speter  *pcount = count;
172251881Speter  *c = ch;
173251881Speter  return SVN_NO_ERROR;
174251881Speter}
175251881Speter
176251881Speter
177251881Speter/* Skip to the end of the line (or file).  Returns the char that ended
178251881Speter   the line; the char is either EOF or newline. */
179251881Speterstatic APR_INLINE svn_error_t *
180251881Speterskip_to_eoln(parse_context_t *ctx, int *c)
181251881Speter{
182251881Speter  int ch;
183251881Speter
184251881Speter  SVN_ERR(parser_getc(ctx, &ch));
185251881Speter  while (ch != '\n' && ch != EOF)
186251881Speter    SVN_ERR(parser_getc_plain(ctx, &ch));
187251881Speter
188251881Speter  *c = ch;
189251881Speter  return SVN_NO_ERROR;
190251881Speter}
191251881Speter
192253734Speter/* Skip a UTF-8 Byte Order Mark if found. */
193253734Speterstatic APR_INLINE svn_error_t *
194253734Speterskip_bom(parse_context_t *ctx)
195253734Speter{
196253734Speter  int ch;
197251881Speter
198253734Speter  SVN_ERR(parser_getc(ctx, &ch));
199253734Speter  if (ch == 0xEF)
200253734Speter    {
201253734Speter      const unsigned char *buf = (unsigned char *)ctx->parser_buffer;
202253734Speter      /* This makes assumptions about the implementation of parser_getc and
203253734Speter       * the use of skip_bom.  Specifically that parser_getc() will get all
204253734Speter       * of the BOM characters into the parse_context_t buffer.  This can
205253734Speter       * safely be assumed as long as we only try to use skip_bom() at the
206253734Speter       * start of the stream and the buffer is longer than 3 characters. */
207253734Speter      SVN_ERR_ASSERT(ctx->buffer_size > ctx->buffer_pos + 1);
208253734Speter      if (buf[ctx->buffer_pos] == 0xBB && buf[ctx->buffer_pos + 1] == 0xBF)
209253734Speter        ctx->buffer_pos += 2;
210253734Speter      else
211253734Speter        SVN_ERR(parser_ungetc(ctx, ch));
212253734Speter    }
213253734Speter  else
214253734Speter    SVN_ERR(parser_ungetc(ctx, ch));
215253734Speter
216253734Speter  return SVN_NO_ERROR;
217253734Speter}
218253734Speter
219251881Speter/* Parse a single option value */
220251881Speterstatic svn_error_t *
221251881Speterparse_value(int *pch, parse_context_t *ctx)
222251881Speter{
223251881Speter  svn_boolean_t end_of_val = FALSE;
224251881Speter  int ch;
225251881Speter
226251881Speter  /* Read the first line of the value */
227251881Speter  svn_stringbuf_setempty(ctx->value);
228251881Speter  SVN_ERR(parser_getc(ctx, &ch));
229251881Speter  while (ch != EOF && ch != '\n')
230251881Speter    /* last ch seen was ':' or '=' in parse_option. */
231251881Speter    {
232251881Speter      const char char_from_int = (char)ch;
233251881Speter      svn_stringbuf_appendbyte(ctx->value, char_from_int);
234251881Speter      SVN_ERR(parser_getc(ctx, &ch));
235251881Speter    }
236251881Speter  /* Leading and trailing whitespace is ignored. */
237251881Speter  svn_stringbuf_strip_whitespace(ctx->value);
238251881Speter
239251881Speter  /* Look for any continuation lines. */
240251881Speter  for (;;)
241251881Speter    {
242251881Speter
243251881Speter      if (ch == EOF || end_of_val)
244251881Speter        {
245251881Speter          /* At end of file. The value is complete, there can't be
246251881Speter             any continuation lines. */
247251881Speter          svn_config_set(ctx->cfg, ctx->section->data,
248251881Speter                         ctx->option->data, ctx->value->data);
249251881Speter          break;
250251881Speter        }
251251881Speter      else
252251881Speter        {
253251881Speter          int count;
254251881Speter          ++ctx->line;
255251881Speter          SVN_ERR(skip_whitespace(ctx, &ch, &count));
256251881Speter
257251881Speter          switch (ch)
258251881Speter            {
259251881Speter            case '\n':
260251881Speter              /* The next line was empty. Ergo, it can't be a
261251881Speter                 continuation line. */
262251881Speter              ++ctx->line;
263251881Speter              end_of_val = TRUE;
264251881Speter              continue;
265251881Speter
266251881Speter            case EOF:
267251881Speter              /* This is also an empty line. */
268251881Speter              end_of_val = TRUE;
269251881Speter              continue;
270251881Speter
271251881Speter            default:
272251881Speter              if (count == 0)
273251881Speter                {
274251881Speter                  /* This line starts in the first column.  That means
275251881Speter                     it's either a section, option or comment.  Put
276251881Speter                     the char back into the stream, because it doesn't
277251881Speter                     belong to us. */
278251881Speter                  SVN_ERR(parser_ungetc(ctx, ch));
279251881Speter                  end_of_val = TRUE;
280251881Speter                }
281251881Speter              else
282251881Speter                {
283251881Speter                  /* This is a continuation line. Read it. */
284251881Speter                  svn_stringbuf_appendbyte(ctx->value, ' ');
285251881Speter
286251881Speter                  while (ch != EOF && ch != '\n')
287251881Speter                    {
288251881Speter                      const char char_from_int = (char)ch;
289251881Speter                      svn_stringbuf_appendbyte(ctx->value, char_from_int);
290251881Speter                      SVN_ERR(parser_getc(ctx, &ch));
291251881Speter                    }
292251881Speter                  /* Trailing whitespace is ignored. */
293251881Speter                  svn_stringbuf_strip_whitespace(ctx->value);
294251881Speter                }
295251881Speter            }
296251881Speter        }
297251881Speter    }
298251881Speter
299251881Speter  *pch = ch;
300251881Speter  return SVN_NO_ERROR;
301251881Speter}
302251881Speter
303251881Speter
304251881Speter/* Parse a single option */
305251881Speterstatic svn_error_t *
306251881Speterparse_option(int *pch, parse_context_t *ctx, apr_pool_t *scratch_pool)
307251881Speter{
308251881Speter  svn_error_t *err = SVN_NO_ERROR;
309251881Speter  int ch;
310251881Speter
311251881Speter  svn_stringbuf_setempty(ctx->option);
312251881Speter  ch = *pch;   /* Yes, the first char is relevant. */
313251881Speter  while (ch != EOF && ch != ':' && ch != '=' && ch != '\n')
314251881Speter    {
315251881Speter      const char char_from_int = (char)ch;
316251881Speter      svn_stringbuf_appendbyte(ctx->option, char_from_int);
317251881Speter      SVN_ERR(parser_getc(ctx, &ch));
318251881Speter    }
319251881Speter
320251881Speter  if (ch != ':' && ch != '=')
321251881Speter    {
322251881Speter      ch = EOF;
323251881Speter      err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
324251881Speter                              "line %d: Option must end with ':' or '='",
325251881Speter                              ctx->line);
326251881Speter    }
327251881Speter  else
328251881Speter    {
329251881Speter      /* Whitespace around the name separator is ignored. */
330251881Speter      svn_stringbuf_strip_whitespace(ctx->option);
331251881Speter      err = parse_value(&ch, ctx);
332251881Speter    }
333251881Speter
334251881Speter  *pch = ch;
335251881Speter  return err;
336251881Speter}
337251881Speter
338251881Speter
339251881Speter/* Read chars until enounter ']', then skip everything to the end of
340251881Speter * the line.  Set *PCH to the character that ended the line (either
341251881Speter * newline or EOF), and set CTX->section to the string of characters
342251881Speter * seen before ']'.
343251881Speter *
344251881Speter * This is meant to be called immediately after reading the '[' that
345251881Speter * starts a section name.
346251881Speter */
347251881Speterstatic svn_error_t *
348251881Speterparse_section_name(int *pch, parse_context_t *ctx,
349251881Speter                   apr_pool_t *scratch_pool)
350251881Speter{
351251881Speter  svn_error_t *err = SVN_NO_ERROR;
352251881Speter  int ch;
353251881Speter
354251881Speter  svn_stringbuf_setempty(ctx->section);
355251881Speter  SVN_ERR(parser_getc(ctx, &ch));
356251881Speter  while (ch != EOF && ch != ']' && ch != '\n')
357251881Speter    {
358251881Speter      const char char_from_int = (char)ch;
359251881Speter      svn_stringbuf_appendbyte(ctx->section, char_from_int);
360251881Speter      SVN_ERR(parser_getc(ctx, &ch));
361251881Speter    }
362251881Speter
363251881Speter  if (ch != ']')
364251881Speter    {
365251881Speter      ch = EOF;
366251881Speter      err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
367251881Speter                              "line %d: Section header must end with ']'",
368251881Speter                              ctx->line);
369251881Speter    }
370251881Speter  else
371251881Speter    {
372251881Speter      /* Everything from the ']' to the end of the line is ignored. */
373251881Speter      SVN_ERR(skip_to_eoln(ctx, &ch));
374251881Speter      if (ch != EOF)
375251881Speter        ++ctx->line;
376251881Speter    }
377251881Speter
378251881Speter  *pch = ch;
379251881Speter  return err;
380251881Speter}
381251881Speter
382251881Speter
383251881Spetersvn_error_t *
384251881Spetersvn_config__sys_config_path(const char **path_p,
385251881Speter                            const char *fname,
386251881Speter                            apr_pool_t *pool)
387251881Speter{
388251881Speter  *path_p = NULL;
389251881Speter
390251881Speter  /* Note that even if fname is null, svn_dirent_join_many will DTRT. */
391251881Speter
392251881Speter#ifdef WIN32
393251881Speter  {
394251881Speter    const char *folder;
395251881Speter    SVN_ERR(svn_config__win_config_path(&folder, TRUE, pool));
396251881Speter    *path_p = svn_dirent_join_many(pool, folder,
397251881Speter                                   SVN_CONFIG__SUBDIRECTORY, fname, NULL);
398251881Speter  }
399251881Speter
400251881Speter#elif defined(__HAIKU__)
401251881Speter  {
402251881Speter    char folder[B_PATH_NAME_LENGTH];
403251881Speter
404251881Speter    status_t error = find_directory(B_COMMON_SETTINGS_DIRECTORY, -1, false,
405251881Speter                                    folder, sizeof(folder));
406251881Speter    if (error)
407251881Speter      return SVN_NO_ERROR;
408251881Speter
409251881Speter    *path_p = svn_dirent_join_many(pool, folder,
410251881Speter                                   SVN_CONFIG__SYS_DIRECTORY, fname, NULL);
411251881Speter  }
412251881Speter#else  /* ! WIN32 && !__HAIKU__ */
413251881Speter
414251881Speter  *path_p = svn_dirent_join_many(pool, SVN_CONFIG__SYS_DIRECTORY, fname, NULL);
415251881Speter
416251881Speter#endif /* WIN32 */
417251881Speter
418251881Speter  return SVN_NO_ERROR;
419251881Speter}
420251881Speter
421251881Speter
422251881Speter/*** Exported interfaces. ***/
423251881Speter
424251881Speter
425251881Spetersvn_error_t *
426251881Spetersvn_config__parse_file(svn_config_t *cfg, const char *file,
427251881Speter                       svn_boolean_t must_exist, apr_pool_t *result_pool)
428251881Speter{
429251881Speter  svn_error_t *err = SVN_NO_ERROR;
430251881Speter  svn_stream_t *stream;
431251881Speter  apr_pool_t *scratch_pool = svn_pool_create(result_pool);
432251881Speter
433251881Speter  err = svn_stream_open_readonly(&stream, file, scratch_pool, scratch_pool);
434251881Speter
435251881Speter  if (! must_exist && err && APR_STATUS_IS_ENOENT(err->apr_err))
436251881Speter    {
437251881Speter      svn_error_clear(err);
438251881Speter      svn_pool_destroy(scratch_pool);
439251881Speter      return SVN_NO_ERROR;
440251881Speter    }
441251881Speter  else
442251881Speter    SVN_ERR(err);
443251881Speter
444251881Speter  err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
445251881Speter
446251881Speter  if (err != SVN_NO_ERROR)
447251881Speter    {
448251881Speter      /* Add the filename to the error stack. */
449251881Speter      err = svn_error_createf(err->apr_err, err,
450251881Speter                              "Error while parsing config file: %s:",
451251881Speter                              svn_dirent_local_style(file, scratch_pool));
452251881Speter    }
453251881Speter
454251881Speter  /* Close the streams (and other cleanup): */
455251881Speter  svn_pool_destroy(scratch_pool);
456251881Speter
457251881Speter  return err;
458251881Speter}
459251881Speter
460251881Spetersvn_error_t *
461251881Spetersvn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream,
462251881Speter                         apr_pool_t *result_pool, apr_pool_t *scratch_pool)
463251881Speter{
464251881Speter  parse_context_t *ctx;
465251881Speter  int ch, count;
466251881Speter
467251881Speter  ctx = apr_palloc(scratch_pool, sizeof(*ctx));
468251881Speter
469251881Speter  ctx->cfg = cfg;
470251881Speter  ctx->stream = stream;
471251881Speter  ctx->line = 1;
472251881Speter  ctx->ungotten_char = EOF;
473251881Speter  ctx->section = svn_stringbuf_create_empty(scratch_pool);
474251881Speter  ctx->option = svn_stringbuf_create_empty(scratch_pool);
475251881Speter  ctx->value = svn_stringbuf_create_empty(scratch_pool);
476251881Speter  ctx->buffer_pos = 0;
477251881Speter  ctx->buffer_size = 0;
478251881Speter
479253734Speter  SVN_ERR(skip_bom(ctx));
480253734Speter
481251881Speter  do
482251881Speter    {
483251881Speter      SVN_ERR(skip_whitespace(ctx, &ch, &count));
484251881Speter
485251881Speter      switch (ch)
486251881Speter        {
487251881Speter        case '[':               /* Start of section header */
488251881Speter          if (count == 0)
489251881Speter            SVN_ERR(parse_section_name(&ch, ctx, scratch_pool));
490251881Speter          else
491251881Speter            return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
492251881Speter                                     "line %d: Section header"
493251881Speter                                     " must start in the first column",
494251881Speter                                     ctx->line);
495251881Speter          break;
496251881Speter
497251881Speter        case '#':               /* Comment */
498251881Speter          if (count == 0)
499251881Speter            {
500251881Speter              SVN_ERR(skip_to_eoln(ctx, &ch));
501251881Speter              ++(ctx->line);
502251881Speter            }
503251881Speter          else
504251881Speter            return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
505251881Speter                                     "line %d: Comment"
506251881Speter                                     " must start in the first column",
507251881Speter                                     ctx->line);
508251881Speter          break;
509251881Speter
510251881Speter        case '\n':              /* Empty line */
511251881Speter          ++(ctx->line);
512251881Speter          break;
513251881Speter
514251881Speter        case EOF:               /* End of file or read error */
515251881Speter          break;
516251881Speter
517251881Speter        default:
518251881Speter          if (svn_stringbuf_isempty(ctx->section))
519251881Speter            return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
520251881Speter                                     "line %d: Section header expected",
521251881Speter                                     ctx->line);
522251881Speter          else if (count != 0)
523251881Speter            return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL,
524251881Speter                                     "line %d: Option expected",
525251881Speter                                     ctx->line);
526251881Speter          else
527251881Speter            SVN_ERR(parse_option(&ch, ctx, scratch_pool));
528251881Speter          break;
529251881Speter        }
530251881Speter    }
531251881Speter  while (ch != EOF);
532251881Speter
533251881Speter  return SVN_NO_ERROR;
534251881Speter}
535251881Speter
536251881Speter
537251881Speter/* Helper for ensure_auth_dirs: create SUBDIR under AUTH_DIR, iff
538251881Speter   SUBDIR does not already exist, but ignore any errors.  Use POOL for
539251881Speter   temporary allocation. */
540251881Speterstatic void
541251881Speterensure_auth_subdir(const char *auth_dir,
542251881Speter                   const char *subdir,
543251881Speter                   apr_pool_t *pool)
544251881Speter{
545251881Speter  svn_error_t *err;
546251881Speter  const char *subdir_full_path;
547251881Speter  svn_node_kind_t kind;
548251881Speter
549251881Speter  subdir_full_path = svn_dirent_join(auth_dir, subdir, pool);
550251881Speter  err = svn_io_check_path(subdir_full_path, &kind, pool);
551251881Speter  if (err || kind == svn_node_none)
552251881Speter    {
553251881Speter      svn_error_clear(err);
554251881Speter      svn_error_clear(svn_io_dir_make(subdir_full_path, APR_OS_DEFAULT, pool));
555251881Speter    }
556251881Speter}
557251881Speter
558251881Speter/* Helper for svn_config_ensure:  see if ~/.subversion/auth/ and its
559251881Speter   subdirs exist, try to create them, but don't throw errors on
560251881Speter   failure.  PATH is assumed to be a path to the user's private config
561251881Speter   directory. */
562251881Speterstatic void
563251881Speterensure_auth_dirs(const char *path,
564251881Speter                 apr_pool_t *pool)
565251881Speter{
566251881Speter  svn_node_kind_t kind;
567251881Speter  const char *auth_dir;
568251881Speter  svn_error_t *err;
569251881Speter
570251881Speter  /* Ensure ~/.subversion/auth/ */
571251881Speter  auth_dir = svn_dirent_join(path, SVN_CONFIG__AUTH_SUBDIR, pool);
572251881Speter  err = svn_io_check_path(auth_dir, &kind, pool);
573251881Speter  if (err || kind == svn_node_none)
574251881Speter    {
575251881Speter      svn_error_clear(err);
576251881Speter      /* 'chmod 700' permissions: */
577251881Speter      err = svn_io_dir_make(auth_dir,
578251881Speter                            (APR_UREAD | APR_UWRITE | APR_UEXECUTE),
579251881Speter                            pool);
580251881Speter      if (err)
581251881Speter        {
582251881Speter          /* Don't try making subdirs if we can't make the top-level dir. */
583251881Speter          svn_error_clear(err);
584251881Speter          return;
585251881Speter        }
586251881Speter    }
587251881Speter
588251881Speter  /* If a provider exists that wants to store credentials in
589251881Speter     ~/.subversion, a subdirectory for the cred_kind must exist. */
590251881Speter  ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SIMPLE, pool);
591251881Speter  ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_USERNAME, pool);
592251881Speter  ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_SERVER_TRUST, pool);
593251881Speter  ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, pool);
594251881Speter}
595251881Speter
596251881Speter
597251881Spetersvn_error_t *
598251881Spetersvn_config_ensure(const char *config_dir, apr_pool_t *pool)
599251881Speter{
600251881Speter  const char *path;
601251881Speter  svn_node_kind_t kind;
602251881Speter  svn_error_t *err;
603251881Speter
604251881Speter  /* Ensure that the user-specific config directory exists.  */
605251881Speter  SVN_ERR(svn_config_get_user_config_path(&path, config_dir, NULL, pool));
606251881Speter
607251881Speter  if (! path)
608251881Speter    return SVN_NO_ERROR;
609251881Speter
610251881Speter  err = svn_io_check_resolved_path(path, &kind, pool);
611251881Speter  if (err)
612251881Speter    {
613251881Speter      /* Don't throw an error, but don't continue. */
614251881Speter      svn_error_clear(err);
615251881Speter      return SVN_NO_ERROR;
616251881Speter    }
617251881Speter
618251881Speter  if (kind == svn_node_none)
619251881Speter    {
620251881Speter      err = svn_io_dir_make(path, APR_OS_DEFAULT, pool);
621251881Speter      if (err)
622251881Speter        {
623251881Speter          /* Don't throw an error, but don't continue. */
624251881Speter          svn_error_clear(err);
625251881Speter          return SVN_NO_ERROR;
626251881Speter        }
627251881Speter    }
628251881Speter  else if (kind == svn_node_file)
629251881Speter    {
630251881Speter      /* Somebody put a file where the config directory should be.
631251881Speter         Wacky.  Let's bail. */
632251881Speter      return SVN_NO_ERROR;
633251881Speter    }
634251881Speter
635251881Speter  /* Else, there's a configuration directory. */
636251881Speter
637251881Speter  /* If we get errors trying to do things below, just stop and return
638251881Speter     success.  There's no _need_ to init a config directory if
639251881Speter     something's preventing it. */
640251881Speter
641251881Speter  /** If non-existent, try to create a number of auth/ subdirectories. */
642251881Speter  ensure_auth_dirs(path, pool);
643251881Speter
644251881Speter  /** Ensure that the `README.txt' file exists. **/
645251881Speter  SVN_ERR(svn_config_get_user_config_path
646251881Speter          (&path, config_dir, SVN_CONFIG__USR_README_FILE, pool));
647251881Speter
648251881Speter  if (! path)  /* highly unlikely, since a previous call succeeded */
649251881Speter    return SVN_NO_ERROR;
650251881Speter
651251881Speter  err = svn_io_check_path(path, &kind, pool);
652251881Speter  if (err)
653251881Speter    {
654251881Speter      svn_error_clear(err);
655251881Speter      return SVN_NO_ERROR;
656251881Speter    }
657251881Speter
658251881Speter  if (kind == svn_node_none)
659251881Speter    {
660251881Speter      apr_file_t *f;
661251881Speter      const char *contents =
662251881Speter   "This directory holds run-time configuration information for Subversion"  NL
663251881Speter   "clients.  The configuration files all share the same syntax, but you"    NL
664251881Speter   "should examine a particular file to learn what configuration"            NL
665251881Speter   "directives are valid for that file."                                     NL
666251881Speter   ""                                                                        NL
667251881Speter   "The syntax is standard INI format:"                                      NL
668251881Speter   ""                                                                        NL
669251881Speter   "   - Empty lines, and lines starting with '#', are ignored."             NL
670251881Speter   "     The first significant line in a file must be a section header."     NL
671251881Speter   ""                                                                        NL
672251881Speter   "   - A section starts with a section header, which must start in"        NL
673251881Speter   "     the first column:"                                                  NL
674251881Speter   ""                                                                        NL
675251881Speter   "       [section-name]"                                                   NL
676251881Speter   ""                                                                        NL
677251881Speter   "   - An option, which must always appear within a section, is a pair"    NL
678251881Speter   "     (name, value).  There are two valid forms for defining an"          NL
679251881Speter   "     option, both of which must start in the first column:"              NL
680251881Speter   ""                                                                        NL
681251881Speter   "       name: value"                                                      NL
682251881Speter   "       name = value"                                                     NL
683251881Speter   ""                                                                        NL
684251881Speter   "     Whitespace around the separator (:, =) is optional."                NL
685251881Speter   ""                                                                        NL
686251881Speter   "   - Section and option names are case-insensitive, but case is"         NL
687251881Speter   "     preserved."                                                         NL
688251881Speter   ""                                                                        NL
689251881Speter   "   - An option's value may be broken into several lines.  The value"     NL
690251881Speter   "     continuation lines must start with at least one whitespace."        NL
691251881Speter   "     Trailing whitespace in the previous line, the newline character"    NL
692251881Speter   "     and the leading whitespace in the continuation line is compressed"  NL
693251881Speter   "     into a single space character."                                     NL
694251881Speter   ""                                                                        NL
695251881Speter   "   - All leading and trailing whitespace around a value is trimmed,"     NL
696251881Speter   "     but the whitespace within a value is preserved, with the"           NL
697251881Speter   "     exception of whitespace around line continuations, as"              NL
698251881Speter   "     described above."                                                   NL
699251881Speter   ""                                                                        NL
700251881Speter   "   - When a value is a boolean, any of the following strings are"        NL
701251881Speter   "     recognised as truth values (case does not matter):"                 NL
702251881Speter   ""                                                                        NL
703251881Speter   "       true      false"                                                  NL
704251881Speter   "       yes       no"                                                     NL
705251881Speter   "       on        off"                                                    NL
706251881Speter   "       1         0"                                                      NL
707251881Speter   ""                                                                        NL
708251881Speter   "   - When a value is a list, it is comma-separated.  Again, the"         NL
709251881Speter   "     whitespace around each element of the list is trimmed."             NL
710251881Speter   ""                                                                        NL
711251881Speter   "   - Option values may be expanded within a value by enclosing the"      NL
712251881Speter   "     option name in parentheses, preceded by a percent sign and"         NL
713251881Speter   "     followed by an 's':"                                                NL
714251881Speter   ""                                                                        NL
715251881Speter   "       %(name)s"                                                         NL
716251881Speter   ""                                                                        NL
717251881Speter   "     The expansion is performed recursively and on demand, during"       NL
718251881Speter   "     svn_option_get.  The name is first searched for in the same"        NL
719251881Speter   "     section, then in the special [DEFAULT] section. If the name"        NL
720251881Speter   "     is not found, the whole '%(name)s' placeholder is left"             NL
721251881Speter   "     unchanged."                                                         NL
722251881Speter   ""                                                                        NL
723251881Speter   "     Any modifications to the configuration data invalidate all"         NL
724251881Speter   "     previously expanded values, so that the next svn_option_get"        NL
725251881Speter   "     will take the modifications into account."                          NL
726251881Speter   ""                                                                        NL
727251881Speter   "The syntax of the configuration files is a subset of the one used by"    NL
728251881Speter   "Python's ConfigParser module; see"                                       NL
729251881Speter   ""                                                                        NL
730251881Speter   "   http://www.python.org/doc/current/lib/module-ConfigParser.html"       NL
731251881Speter   ""                                                                        NL
732251881Speter   "Configuration data in the Windows registry"                              NL
733251881Speter   "=========================================="                              NL
734251881Speter   ""                                                                        NL
735251881Speter   "On Windows, configuration data may also be stored in the registry. The"  NL
736251881Speter   "functions svn_config_read and svn_config_merge will read from the"       NL
737251881Speter   "registry when passed file names of the form:"                            NL
738251881Speter   ""                                                                        NL
739251881Speter   "   REGISTRY:<hive>/path/to/config-key"                                   NL
740251881Speter   ""                                                                        NL
741251881Speter   "The REGISTRY: prefix must be in upper case. The <hive> part must be"     NL
742251881Speter   "one of:"                                                                 NL
743251881Speter   ""                                                                        NL
744251881Speter   "   HKLM for HKEY_LOCAL_MACHINE"                                          NL
745251881Speter   "   HKCU for HKEY_CURRENT_USER"                                           NL
746251881Speter   ""                                                                        NL
747251881Speter   "The values in config-key represent the options in the [DEFAULT] section."NL
748251881Speter   "The keys below config-key represent other sections, and their values"    NL
749251881Speter   "represent the options. Only values of type REG_SZ whose name doesn't"    NL
750251881Speter   "start with a '#' will be used; other values, as well as the keys'"       NL
751251881Speter   "default values, will be ignored."                                        NL
752251881Speter   ""                                                                        NL
753251881Speter   ""                                                                        NL
754251881Speter   "File locations"                                                          NL
755251881Speter   "=============="                                                          NL
756251881Speter   ""                                                                        NL
757251881Speter   "Typically, Subversion uses two config directories, one for site-wide"    NL
758251881Speter   "configuration,"                                                          NL
759251881Speter   ""                                                                        NL
760251881Speter   "  Unix:"                                                                 NL
761251881Speter   "    /etc/subversion/servers"                                             NL
762251881Speter   "    /etc/subversion/config"                                              NL
763251881Speter   "    /etc/subversion/hairstyles"                                          NL
764251881Speter   "  Windows:"                                                              NL
765251881Speter   "    %ALLUSERSPROFILE%\\Application Data\\Subversion\\servers"            NL
766251881Speter   "    %ALLUSERSPROFILE%\\Application Data\\Subversion\\config"             NL
767251881Speter   "    %ALLUSERSPROFILE%\\Application Data\\Subversion\\hairstyles"         NL
768251881Speter   "    REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Servers"            NL
769251881Speter   "    REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Config"             NL
770251881Speter   "    REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Hairstyles"         NL
771251881Speter   ""                                                                        NL
772251881Speter   "and one for per-user configuration:"                                     NL
773251881Speter   ""                                                                        NL
774251881Speter   "  Unix:"                                                                 NL
775251881Speter   "    ~/.subversion/servers"                                               NL
776251881Speter   "    ~/.subversion/config"                                                NL
777251881Speter   "    ~/.subversion/hairstyles"                                            NL
778251881Speter   "  Windows:"                                                              NL
779251881Speter   "    %APPDATA%\\Subversion\\servers"                                      NL
780251881Speter   "    %APPDATA%\\Subversion\\config"                                       NL
781251881Speter   "    %APPDATA%\\Subversion\\hairstyles"                                   NL
782251881Speter   "    REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Servers"            NL
783251881Speter   "    REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Config"             NL
784251881Speter   "    REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Hairstyles"         NL
785251881Speter   ""                                                                        NL;
786251881Speter
787251881Speter      err = svn_io_file_open(&f, path,
788251881Speter                             (APR_WRITE | APR_CREATE | APR_EXCL),
789251881Speter                             APR_OS_DEFAULT,
790251881Speter                             pool);
791251881Speter
792251881Speter      if (! err)
793251881Speter        {
794251881Speter          SVN_ERR(svn_io_file_write_full(f, contents,
795251881Speter                                         strlen(contents), NULL, pool));
796251881Speter          SVN_ERR(svn_io_file_close(f, pool));
797251881Speter        }
798251881Speter
799251881Speter      svn_error_clear(err);
800251881Speter    }
801251881Speter
802251881Speter  /** Ensure that the `servers' file exists. **/
803251881Speter  SVN_ERR(svn_config_get_user_config_path
804251881Speter          (&path, config_dir, SVN_CONFIG_CATEGORY_SERVERS, pool));
805251881Speter
806251881Speter  if (! path)  /* highly unlikely, since a previous call succeeded */
807251881Speter    return SVN_NO_ERROR;
808251881Speter
809251881Speter  err = svn_io_check_path(path, &kind, pool);
810251881Speter  if (err)
811251881Speter    {
812251881Speter      svn_error_clear(err);
813251881Speter      return SVN_NO_ERROR;
814251881Speter    }
815251881Speter
816251881Speter  if (kind == svn_node_none)
817251881Speter    {
818251881Speter      apr_file_t *f;
819251881Speter      const char *contents =
820251881Speter        "### This file specifies server-specific parameters,"                NL
821251881Speter        "### including HTTP proxy information, HTTP timeout settings,"       NL
822251881Speter        "### and authentication settings."                                   NL
823251881Speter        "###"                                                                NL
824251881Speter        "### The currently defined server options are:"                      NL
825251881Speter        "###   http-proxy-host            Proxy host for HTTP connection"    NL
826251881Speter        "###   http-proxy-port            Port number of proxy host service" NL
827251881Speter        "###   http-proxy-username        Username for auth to proxy service"NL
828251881Speter        "###   http-proxy-password        Password for auth to proxy service"NL
829251881Speter        "###   http-proxy-exceptions      List of sites that do not use proxy"
830251881Speter                                                                             NL
831251881Speter        "###   http-timeout               Timeout for HTTP requests in seconds"
832251881Speter                                                                             NL
833251881Speter        "###   http-compression           Whether to compress HTTP requests" NL
834251881Speter        "###   http-max-connections       Maximum number of parallel server" NL
835251881Speter        "###                              connections to use for any given"  NL
836251881Speter        "###                              HTTP operation."                   NL
837253734Speter        "###   http-chunked-requests      Whether to use chunked transfer"   NL
838253734Speter        "###                              encoding for HTTP requests body."  NL
839251881Speter        "###   neon-debug-mask            Debug mask for Neon HTTP library"  NL
840251881Speter        "###   ssl-authority-files        List of files, each of a trusted CA"
841251881Speter                                                                             NL
842251881Speter        "###   ssl-trust-default-ca       Trust the system 'default' CAs"    NL
843251881Speter        "###   ssl-client-cert-file       PKCS#12 format client certificate file"
844251881Speter                                                                             NL
845251881Speter        "###   ssl-client-cert-password   Client Key password, if needed."   NL
846251881Speter        "###   ssl-pkcs11-provider        Name of PKCS#11 provider to use."  NL
847251881Speter        "###   http-library               Which library to use for http/https"
848251881Speter                                                                             NL
849251881Speter        "###                              connections."                      NL
850251881Speter        "###   http-bulk-updates          Whether to request bulk update"    NL
851251881Speter        "###                              responses or to fetch each file"   NL
852251881Speter        "###                              in an individual request. "        NL
853251881Speter        "###   store-passwords            Specifies whether passwords used"  NL
854251881Speter        "###                              to authenticate against a"         NL
855251881Speter        "###                              Subversion server may be cached"   NL
856251881Speter        "###                              to disk in any way."               NL
857251881Speter#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
858251881Speter        "###   store-plaintext-passwords  Specifies whether passwords may"   NL
859251881Speter        "###                              be cached on disk unencrypted."    NL
860251881Speter#endif
861251881Speter        "###   store-ssl-client-cert-pp   Specifies whether passphrase used" NL
862251881Speter        "###                              to authenticate against a client"  NL
863251881Speter        "###                              certificate may be cached to disk" NL
864251881Speter        "###                              in any way"                        NL
865251881Speter#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
866251881Speter        "###   store-ssl-client-cert-pp-plaintext"                           NL
867251881Speter        "###                              Specifies whether client cert"     NL
868251881Speter        "###                              passphrases may be cached on disk" NL
869251881Speter        "###                              unencrypted (i.e., as plaintext)." NL
870251881Speter#endif
871251881Speter        "###   store-auth-creds           Specifies whether any auth info"   NL
872251881Speter        "###                              (passwords, server certs, etc.)"   NL
873251881Speter        "###                              may be cached to disk."            NL
874251881Speter        "###   username                   Specifies the default username."   NL
875251881Speter        "###"                                                                NL
876251881Speter        "### Set store-passwords to 'no' to avoid storing passwords on disk" NL
877251881Speter        "### in any way, including in password stores.  It defaults to"      NL
878251881Speter        "### 'yes', but Subversion will never save your password to disk in" NL
879251881Speter        "### plaintext unless explicitly configured to do so."               NL
880251881Speter        "### Note that this option only prevents saving of *new* passwords;" NL
881251881Speter        "### it doesn't invalidate existing passwords.  (To do that, remove" NL
882251881Speter        "### the cache files by hand as described in the Subversion book.)"  NL
883251881Speter        "###"                                                                NL
884251881Speter#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
885251881Speter        "### Set store-plaintext-passwords to 'no' to avoid storing"         NL
886251881Speter        "### passwords in unencrypted form in the auth/ area of your config" NL
887251881Speter        "### directory. Set it to 'yes' to allow Subversion to store"        NL
888251881Speter        "### unencrypted passwords in the auth/ area.  The default is"       NL
889251881Speter        "### 'ask', which means that Subversion will ask you before"         NL
890251881Speter        "### saving a password to disk in unencrypted form.  Note that"      NL
891251881Speter        "### this option has no effect if either 'store-passwords' or "      NL
892251881Speter        "### 'store-auth-creds' is set to 'no'."                             NL
893251881Speter        "###"                                                                NL
894251881Speter#endif
895251881Speter        "### Set store-ssl-client-cert-pp to 'no' to avoid storing ssl"      NL
896251881Speter        "### client certificate passphrases in the auth/ area of your"       NL
897251881Speter        "### config directory.  It defaults to 'yes', but Subversion will"   NL
898251881Speter        "### never save your passphrase to disk in plaintext unless"         NL
899251881Speter        "### explicitly configured to do so."                                NL
900251881Speter        "###"                                                                NL
901251881Speter        "### Note store-ssl-client-cert-pp only prevents the saving of *new*"NL
902251881Speter        "### passphrases; it doesn't invalidate existing passphrases.  To do"NL
903251881Speter        "### that, remove the cache files by hand as described in the"       NL
904251881Speter        "### Subversion book at http://svnbook.red-bean.com/nightly/en/\\"   NL
905251881Speter        "###                    svn.serverconfig.netmodel.html\\"            NL
906251881Speter        "###                    #svn.serverconfig.netmodel.credcache"        NL
907251881Speter        "###"                                                                NL
908251881Speter#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
909251881Speter        "### Set store-ssl-client-cert-pp-plaintext to 'no' to avoid storing"NL
910251881Speter        "### passphrases in unencrypted form in the auth/ area of your"      NL
911251881Speter        "### config directory.  Set it to 'yes' to allow Subversion to"      NL
912251881Speter        "### store unencrypted passphrases in the auth/ area.  The default"  NL
913251881Speter        "### is 'ask', which means that Subversion will prompt before"       NL
914251881Speter        "### saving a passphrase to disk in unencrypted form.  Note that"    NL
915251881Speter        "### this option has no effect if either 'store-auth-creds' or "     NL
916251881Speter        "### 'store-ssl-client-cert-pp' is set to 'no'."                     NL
917251881Speter        "###"                                                                NL
918251881Speter#endif
919251881Speter        "### Set store-auth-creds to 'no' to avoid storing any Subversion"   NL
920251881Speter        "### credentials in the auth/ area of your config directory."        NL
921251881Speter        "### Note that this includes SSL server certificates."               NL
922251881Speter        "### It defaults to 'yes'.  Note that this option only prevents"     NL
923251881Speter        "### saving of *new* credentials;  it doesn't invalidate existing"   NL
924251881Speter        "### caches.  (To do that, remove the cache files by hand.)"         NL
925251881Speter        "###"                                                                NL
926251881Speter        "### HTTP timeouts, if given, are specified in seconds.  A timeout"  NL
927251881Speter        "### of 0, i.e. zero, causes a builtin default to be used."          NL
928251881Speter        "###"                                                                NL
929251881Speter        "### Most users will not need to explicitly set the http-library"    NL
930251881Speter        "### option, but valid values for the option include:"               NL
931251881Speter        "###    'serf': Serf-based module (Subversion 1.5 - present)"        NL
932251881Speter        "###    'neon': Neon-based module (Subversion 1.0 - 1.7)"            NL
933251881Speter        "### Availability of these modules may depend on your specific"      NL
934251881Speter        "### Subversion distribution."                                       NL
935251881Speter        "###"                                                                NL
936251881Speter        "### The commented-out examples below are intended only to"          NL
937251881Speter        "### demonstrate how to use this file; any resemblance to actual"    NL
938251881Speter        "### servers, living or dead, is entirely coincidental."             NL
939251881Speter        ""                                                                   NL
940251881Speter        "### In the 'groups' section, the URL of the repository you're"      NL
941251881Speter        "### trying to access is matched against the patterns on the right." NL
942251881Speter        "### If a match is found, the server options are taken from the"     NL
943251881Speter        "### section with the corresponding name on the left."               NL
944251881Speter        ""                                                                   NL
945251881Speter        "[groups]"                                                           NL
946251881Speter        "# group1 = *.collab.net"                                            NL
947251881Speter        "# othergroup = repository.blarggitywhoomph.com"                     NL
948251881Speter        "# thirdgroup = *.example.com"                                       NL
949251881Speter        ""                                                                   NL
950251881Speter        "### Information for the first group:"                               NL
951251881Speter        "# [group1]"                                                         NL
952251881Speter        "# http-proxy-host = proxy1.some-domain-name.com"                    NL
953251881Speter        "# http-proxy-port = 80"                                             NL
954251881Speter        "# http-proxy-username = blah"                                       NL
955251881Speter        "# http-proxy-password = doubleblah"                                 NL
956251881Speter        "# http-timeout = 60"                                                NL
957251881Speter        "# neon-debug-mask = 130"                                            NL
958251881Speter#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
959251881Speter        "# store-plaintext-passwords = no"                                   NL
960251881Speter#endif
961251881Speter        "# username = harry"                                                 NL
962251881Speter        ""                                                                   NL
963251881Speter        "### Information for the second group:"                              NL
964251881Speter        "# [othergroup]"                                                     NL
965251881Speter        "# http-proxy-host = proxy2.some-domain-name.com"                    NL
966251881Speter        "# http-proxy-port = 9000"                                           NL
967251881Speter        "# No username and password for the proxy, so use the defaults below."
968251881Speter                                                                             NL
969251881Speter        ""                                                                   NL
970251881Speter        "### You can set default parameters in the 'global' section."        NL
971251881Speter        "### These parameters apply if no corresponding parameter is set in" NL
972251881Speter        "### a specifically matched group as shown above.  Thus, if you go"  NL
973251881Speter        "### through the same proxy server to reach every site on the"       NL
974251881Speter        "### Internet, you probably just want to put that server's"          NL
975251881Speter        "### information in the 'global' section and not bother with"        NL
976251881Speter        "### 'groups' or any other sections."                                NL
977251881Speter        "###"                                                                NL
978251881Speter        "### Most people might want to configure password caching"           NL
979251881Speter        "### parameters here, but you can also configure them per server"    NL
980251881Speter        "### group (per-group settings override global settings)."           NL
981251881Speter        "###"                                                                NL
982251881Speter        "### If you go through a proxy for all but a few sites, you can"     NL
983251881Speter        "### list those exceptions under 'http-proxy-exceptions'.  This only"NL
984251881Speter        "### overrides defaults, not explicitly matched server names."       NL
985251881Speter        "###"                                                                NL
986251881Speter        "### 'ssl-authority-files' is a semicolon-delimited list of files,"  NL
987251881Speter        "### each pointing to a PEM-encoded Certificate Authority (CA) "     NL
988251881Speter        "### SSL certificate.  See details above for overriding security "   NL
989251881Speter        "### due to SSL."                                                    NL
990251881Speter        "[global]"                                                           NL
991251881Speter        "# http-proxy-exceptions = *.exception.com, www.internal-site.org"   NL
992251881Speter        "# http-proxy-host = defaultproxy.whatever.com"                      NL
993251881Speter        "# http-proxy-port = 7000"                                           NL
994251881Speter        "# http-proxy-username = defaultusername"                            NL
995251881Speter        "# http-proxy-password = defaultpassword"                            NL
996251881Speter        "# http-compression = no"                                            NL
997251881Speter        "# No http-timeout, so just use the builtin default."                NL
998251881Speter        "# No neon-debug-mask, so neon debugging is disabled."               NL
999251881Speter        "# ssl-authority-files = /path/to/CAcert.pem;/path/to/CAcert2.pem"   NL
1000251881Speter        "#"                                                                  NL
1001251881Speter        "# Password / passphrase caching parameters:"                        NL
1002251881Speter        "# store-passwords = no"                                             NL
1003251881Speter        "# store-ssl-client-cert-pp = no"                                    NL
1004251881Speter#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE
1005251881Speter        "# store-plaintext-passwords = no"                                   NL
1006251881Speter        "# store-ssl-client-cert-pp-plaintext = no"                          NL
1007251881Speter#endif
1008251881Speter        ;
1009251881Speter
1010251881Speter      err = svn_io_file_open(&f, path,
1011251881Speter                             (APR_WRITE | APR_CREATE | APR_EXCL),
1012251881Speter                             APR_OS_DEFAULT,
1013251881Speter                             pool);
1014251881Speter
1015251881Speter      if (! err)
1016251881Speter        {
1017251881Speter          SVN_ERR(svn_io_file_write_full(f, contents,
1018251881Speter                                         strlen(contents), NULL, pool));
1019251881Speter          SVN_ERR(svn_io_file_close(f, pool));
1020251881Speter        }
1021251881Speter
1022251881Speter      svn_error_clear(err);
1023251881Speter    }
1024251881Speter
1025251881Speter  /** Ensure that the `config' file exists. **/
1026251881Speter  SVN_ERR(svn_config_get_user_config_path
1027251881Speter          (&path, config_dir, SVN_CONFIG_CATEGORY_CONFIG, pool));
1028251881Speter
1029251881Speter  if (! path)  /* highly unlikely, since a previous call succeeded */
1030251881Speter    return SVN_NO_ERROR;
1031251881Speter
1032251881Speter  err = svn_io_check_path(path, &kind, pool);
1033251881Speter  if (err)
1034251881Speter    {
1035251881Speter      svn_error_clear(err);
1036251881Speter      return SVN_NO_ERROR;
1037251881Speter    }
1038251881Speter
1039251881Speter  if (kind == svn_node_none)
1040251881Speter    {
1041251881Speter      apr_file_t *f;
1042251881Speter      const char *contents =
1043251881Speter        "### This file configures various client-side behaviors."            NL
1044251881Speter        "###"                                                                NL
1045251881Speter        "### The commented-out examples below are intended to demonstrate"   NL
1046251881Speter        "### how to use this file."                                          NL
1047251881Speter        ""                                                                   NL
1048251881Speter        "### Section for authentication and authorization customizations."   NL
1049251881Speter        "[auth]"                                                             NL
1050251881Speter        "### Set password stores used by Subversion. They should be"         NL
1051251881Speter        "### delimited by spaces or commas. The order of values determines"  NL
1052251881Speter        "### the order in which password stores are used."                   NL
1053251881Speter        "### Valid password stores:"                                         NL
1054251881Speter        "###   gnome-keyring        (Unix-like systems)"                     NL
1055251881Speter        "###   kwallet              (Unix-like systems)"                     NL
1056251881Speter        "###   gpg-agent            (Unix-like systems)"                     NL
1057251881Speter        "###   keychain             (Mac OS X)"                              NL
1058251881Speter        "###   windows-cryptoapi    (Windows)"                               NL
1059251881Speter#ifdef SVN_HAVE_KEYCHAIN_SERVICES
1060251881Speter        "# password-stores = keychain"                                       NL
1061251881Speter#elif defined(WIN32) && !defined(__MINGW32__)
1062251881Speter        "# password-stores = windows-cryptoapi"                              NL
1063251881Speter#else
1064251881Speter        "# password-stores = gpg-agent,gnome-keyring,kwallet"                NL
1065251881Speter#endif
1066251881Speter        "### To disable all password stores, use an empty list:"             NL
1067251881Speter        "# password-stores ="                                                NL
1068251881Speter#ifdef SVN_HAVE_KWALLET
1069251881Speter        "###"                                                                NL
1070251881Speter        "### Set KWallet wallet used by Subversion. If empty or unset,"      NL
1071251881Speter        "### then the default network wallet will be used."                  NL
1072251881Speter        "# kwallet-wallet ="                                                 NL
1073251881Speter        "###"                                                                NL
1074251881Speter        "### Include PID (Process ID) in Subversion application name when"   NL
1075251881Speter        "### using KWallet. It defaults to 'no'."                            NL
1076251881Speter        "# kwallet-svn-application-name-with-pid = yes"                      NL
1077251881Speter#endif
1078251881Speter        "###"                                                                NL
1079251881Speter        "### Set ssl-client-cert-file-prompt to 'yes' to cause the client"   NL
1080251881Speter        "### to prompt for a path to a client cert file when the server"     NL
1081251881Speter        "### requests a client cert but no client cert file is found in the" NL
1082251881Speter        "### expected place (see the 'ssl-client-cert-file' option in the"   NL
1083251881Speter        "### 'servers' configuration file). Defaults to 'no'."               NL
1084251881Speter        "# ssl-client-cert-file-prompt = no"                                 NL
1085251881Speter        "###"                                                                NL
1086251881Speter        "### The rest of the [auth] section in this file has been deprecated."
1087251881Speter                                                                             NL
1088251881Speter        "### Both 'store-passwords' and 'store-auth-creds' can now be"       NL
1089251881Speter        "### specified in the 'servers' file in your config directory"       NL
1090251881Speter        "### and are documented there. Anything specified in this section "  NL
1091251881Speter        "### is overridden by settings specified in the 'servers' file."     NL
1092251881Speter        "# store-passwords = no"                                             NL
1093251881Speter        "# store-auth-creds = no"                                            NL
1094251881Speter        ""                                                                   NL
1095251881Speter        "### Section for configuring external helper applications."          NL
1096251881Speter        "[helpers]"                                                          NL
1097251881Speter        "### Set editor-cmd to the command used to invoke your text editor." NL
1098251881Speter        "###   This will override the environment variables that Subversion" NL
1099251881Speter        "###   examines by default to find this information ($EDITOR, "      NL
1100251881Speter        "###   et al)."                                                      NL
1101251881Speter        "# editor-cmd = editor (vi, emacs, notepad, etc.)"                   NL
1102251881Speter        "### Set diff-cmd to the absolute path of your 'diff' program."      NL
1103251881Speter        "###   This will override the compile-time default, which is to use" NL
1104251881Speter        "###   Subversion's internal diff implementation."                   NL
1105251881Speter        "# diff-cmd = diff_program (diff, gdiff, etc.)"                      NL
1106251881Speter        "### Diff-extensions are arguments passed to an external diff"       NL
1107251881Speter        "### program or to Subversion's internal diff implementation."       NL
1108251881Speter        "### Set diff-extensions to override the default arguments ('-u')."  NL
1109251881Speter        "# diff-extensions = -u -p"                                          NL
1110251881Speter        "### Set diff3-cmd to the absolute path of your 'diff3' program."    NL
1111251881Speter        "###   This will override the compile-time default, which is to use" NL
1112251881Speter        "###   Subversion's internal diff3 implementation."                  NL
1113251881Speter        "# diff3-cmd = diff3_program (diff3, gdiff3, etc.)"                  NL
1114251881Speter        "### Set diff3-has-program-arg to 'yes' if your 'diff3' program"     NL
1115251881Speter        "###   accepts the '--diff-program' option."                         NL
1116251881Speter        "# diff3-has-program-arg = [yes | no]"                               NL
1117251881Speter        "### Set merge-tool-cmd to the command used to invoke your external" NL
1118251881Speter        "### merging tool of choice. Subversion will pass 5 arguments to"    NL
1119251881Speter        "### the specified command: base theirs mine merged wcfile"          NL
1120251881Speter        "# merge-tool-cmd = merge_command"                                   NL
1121251881Speter        ""                                                                   NL
1122251881Speter        "### Section for configuring tunnel agents."                         NL
1123251881Speter        "[tunnels]"                                                          NL
1124251881Speter        "### Configure svn protocol tunnel schemes here.  By default, only"  NL
1125251881Speter        "### the 'ssh' scheme is defined.  You can define other schemes to"  NL
1126251881Speter        "### be used with 'svn+scheme://hostname/path' URLs.  A scheme"      NL
1127251881Speter        "### definition is simply a command, optionally prefixed by an"      NL
1128251881Speter        "### environment variable name which can override the command if it" NL
1129251881Speter        "### is defined.  The command (or environment variable) may contain" NL
1130251881Speter        "### arguments, using standard shell quoting for arguments with"     NL
1131251881Speter        "### spaces.  The command will be invoked as:"                       NL
1132251881Speter        "###   <command> <hostname> svnserve -t"                             NL
1133251881Speter        "### (If the URL includes a username, then the hostname will be"     NL
1134251881Speter        "### passed to the tunnel agent as <user>@<hostname>.)  If the"      NL
1135251881Speter        "### built-in ssh scheme were not predefined, it could be defined"   NL
1136251881Speter        "### as:"                                                            NL
1137251881Speter        "# ssh = $SVN_SSH ssh -q"                                            NL
1138251881Speter        "### If you wanted to define a new 'rsh' scheme, to be used with"    NL
1139251881Speter        "### 'svn+rsh:' URLs, you could do so as follows:"                   NL
1140251881Speter        "# rsh = rsh"                                                        NL
1141251881Speter        "### Or, if you wanted to specify a full path and arguments:"        NL
1142251881Speter        "# rsh = /path/to/rsh -l myusername"                                 NL
1143251881Speter        "### On Windows, if you are specifying a full path to a command,"    NL
1144251881Speter        "### use a forward slash (/) or a paired backslash (\\\\) as the"    NL
1145251881Speter        "### path separator.  A single backslash will be treated as an"      NL
1146251881Speter        "### escape for the following character."                            NL
1147251881Speter        ""                                                                   NL
1148251881Speter        "### Section for configuring miscellaneous Subversion options."      NL
1149251881Speter        "[miscellany]"                                                       NL
1150251881Speter        "### Set global-ignores to a set of whitespace-delimited globs"      NL
1151251881Speter        "### which Subversion will ignore in its 'status' output, and"       NL
1152251881Speter        "### while importing or adding files and directories."               NL
1153251881Speter        "### '*' matches leading dots, e.g. '*.rej' matches '.foo.rej'."     NL
1154251881Speter        "# global-ignores = " SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_1      NL
1155251881Speter        "#   " SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_2                     NL
1156251881Speter        "### Set log-encoding to the default encoding for log messages"      NL
1157251881Speter        "# log-encoding = latin1"                                            NL
1158251881Speter        "### Set use-commit-times to make checkout/update/switch/revert"     NL
1159251881Speter        "### put last-committed timestamps on every file touched."           NL
1160251881Speter        "# use-commit-times = yes"                                           NL
1161251881Speter        "### Set no-unlock to prevent 'svn commit' from automatically"       NL
1162251881Speter        "### releasing locks on files."                                      NL
1163251881Speter        "# no-unlock = yes"                                                  NL
1164251881Speter        "### Set mime-types-file to a MIME type registry file, used to"      NL
1165251881Speter        "### provide hints to Subversion's MIME type auto-detection"         NL
1166251881Speter        "### algorithm."                                                     NL
1167251881Speter        "# mime-types-file = /path/to/mime.types"                            NL
1168251881Speter        "### Set preserved-conflict-file-exts to a whitespace-delimited"     NL
1169251881Speter        "### list of patterns matching file extensions which should be"      NL
1170251881Speter        "### preserved in generated conflict file names.  By default,"       NL
1171251881Speter        "### conflict files use custom extensions."                          NL
1172251881Speter        "# preserved-conflict-file-exts = doc ppt xls od?"                   NL
1173251881Speter        "### Set enable-auto-props to 'yes' to enable automatic properties"  NL
1174251881Speter        "### for 'svn add' and 'svn import', it defaults to 'no'."           NL
1175251881Speter        "### Automatic properties are defined in the section 'auto-props'."  NL
1176251881Speter        "# enable-auto-props = yes"                                          NL
1177251881Speter        "### Set interactive-conflicts to 'no' to disable interactive"       NL
1178251881Speter        "### conflict resolution prompting.  It defaults to 'yes'."          NL
1179251881Speter        "# interactive-conflicts = no"                                       NL
1180251881Speter        "### Set memory-cache-size to define the size of the memory cache"   NL
1181251881Speter        "### used by the client when accessing a FSFS repository via"        NL
1182251881Speter        "### ra_local (the file:// scheme). The value represents the number" NL
1183251881Speter        "### of MB used by the cache."                                       NL
1184251881Speter        "# memory-cache-size = 16"                                           NL
1185251881Speter        ""                                                                   NL
1186251881Speter        "### Section for configuring automatic properties."                  NL
1187251881Speter        "[auto-props]"                                                       NL
1188251881Speter        "### The format of the entries is:"                                  NL
1189251881Speter        "###   file-name-pattern = propname[=value][;propname[=value]...]"   NL
1190251881Speter        "### The file-name-pattern can contain wildcards (such as '*' and"   NL
1191251881Speter        "### '?').  All entries which match (case-insensitively) will be"    NL
1192251881Speter        "### applied to the file.  Note that auto-props functionality"       NL
1193251881Speter        "### must be enabled, which is typically done by setting the"        NL
1194251881Speter        "### 'enable-auto-props' option."                                    NL
1195251881Speter        "# *.c = svn:eol-style=native"                                       NL
1196251881Speter        "# *.cpp = svn:eol-style=native"                                     NL
1197251881Speter        "# *.h = svn:keywords=Author Date Id Rev URL;svn:eol-style=native"   NL
1198251881Speter        "# *.dsp = svn:eol-style=CRLF"                                       NL
1199251881Speter        "# *.dsw = svn:eol-style=CRLF"                                       NL
1200251881Speter        "# *.sh = svn:eol-style=native;svn:executable"                       NL
1201251881Speter        "# *.txt = svn:eol-style=native;svn:keywords=Author Date Id Rev URL;"NL
1202251881Speter        "# *.png = svn:mime-type=image/png"                                  NL
1203251881Speter        "# *.jpg = svn:mime-type=image/jpeg"                                 NL
1204251881Speter        "# Makefile = svn:eol-style=native"                                  NL
1205251881Speter        ""                                                                   NL
1206251881Speter        "### Section for configuring working copies."                        NL
1207251881Speter        "[working-copy]"                                                     NL
1208251881Speter        "### Set to a list of the names of specific clients that should use" NL
1209251881Speter        "### exclusive SQLite locking of working copies.  This increases the"NL
1210251881Speter        "### performance of the client but prevents concurrent access by"    NL
1211251881Speter        "### other clients.  Third-party clients may also support this"      NL
1212251881Speter        "### option."                                                        NL
1213251881Speter        "### Possible values:"                                               NL
1214251881Speter        "###   svn                (the command line client)"                 NL
1215251881Speter        "# exclusive-locking-clients ="                                      NL
1216251881Speter        "### Set to true to enable exclusive SQLite locking of working"      NL
1217251881Speter        "### copies by all clients using the 1.8 APIs.  Enabling this may"   NL
1218251881Speter        "### cause some clients to fail to work properly. This does not have"NL
1219251881Speter        "### to be set for exclusive-locking-clients to work."               NL
1220251881Speter        "# exclusive-locking = false"                                        NL;
1221251881Speter
1222251881Speter      err = svn_io_file_open(&f, path,
1223251881Speter                             (APR_WRITE | APR_CREATE | APR_EXCL),
1224251881Speter                             APR_OS_DEFAULT,
1225251881Speter                             pool);
1226251881Speter
1227251881Speter      if (! err)
1228251881Speter        {
1229251881Speter          SVN_ERR(svn_io_file_write_full(f, contents,
1230251881Speter                                         strlen(contents), NULL, pool));
1231251881Speter          SVN_ERR(svn_io_file_close(f, pool));
1232251881Speter        }
1233251881Speter
1234251881Speter      svn_error_clear(err);
1235251881Speter    }
1236251881Speter
1237251881Speter  return SVN_NO_ERROR;
1238251881Speter}
1239251881Speter
1240251881Spetersvn_error_t *
1241251881Spetersvn_config_get_user_config_path(const char **path,
1242251881Speter                                const char *config_dir,
1243251881Speter                                const char *fname,
1244251881Speter                                apr_pool_t *pool)
1245251881Speter{
1246251881Speter  *path= NULL;
1247251881Speter
1248251881Speter  /* Note that even if fname is null, svn_dirent_join_many will DTRT. */
1249251881Speter
1250251881Speter  if (config_dir)
1251251881Speter    {
1252251881Speter      *path = svn_dirent_join_many(pool, config_dir, fname, NULL);
1253251881Speter      return SVN_NO_ERROR;
1254251881Speter    }
1255251881Speter
1256251881Speter#ifdef WIN32
1257251881Speter  {
1258251881Speter    const char *folder;
1259251881Speter    SVN_ERR(svn_config__win_config_path(&folder, FALSE, pool));
1260251881Speter    *path = svn_dirent_join_many(pool, folder,
1261251881Speter                                 SVN_CONFIG__SUBDIRECTORY, fname, NULL);
1262251881Speter  }
1263251881Speter
1264251881Speter#elif defined(__HAIKU__)
1265251881Speter  {
1266251881Speter    char folder[B_PATH_NAME_LENGTH];
1267251881Speter
1268251881Speter    status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, -1, false,
1269251881Speter                                    folder, sizeof(folder));
1270251881Speter    if (error)
1271251881Speter      return SVN_NO_ERROR;
1272251881Speter
1273251881Speter    *path = svn_dirent_join_many(pool, folder,
1274251881Speter                                 SVN_CONFIG__USR_DIRECTORY, fname, NULL);
1275251881Speter  }
1276251881Speter#else  /* ! WIN32 && !__HAIKU__ */
1277251881Speter
1278251881Speter  {
1279251881Speter    const char *homedir = svn_user_get_homedir(pool);
1280251881Speter    if (! homedir)
1281251881Speter      return SVN_NO_ERROR;
1282251881Speter    *path = svn_dirent_join_many(pool,
1283251881Speter                               svn_dirent_canonicalize(homedir, pool),
1284251881Speter                               SVN_CONFIG__USR_DIRECTORY, fname, NULL);
1285251881Speter  }
1286251881Speter#endif /* WIN32 */
1287251881Speter
1288251881Speter  return SVN_NO_ERROR;
1289251881Speter}
1290251881Speter
1291