diff options
Diffstat (limited to 'subversion/libsvn_subr/config_file.c')
-rw-r--r-- | subversion/libsvn_subr/config_file.c | 1260 |
1 files changed, 1260 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/config_file.c b/subversion/libsvn_subr/config_file.c new file mode 100644 index 0000000..9d15f6b --- /dev/null +++ b/subversion/libsvn_subr/config_file.c @@ -0,0 +1,1260 @@ +/* + * config_file.c : parsing configuration files + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + + + +#include <apr_lib.h> +#include <apr_env.h> +#include "config_impl.h" +#include "svn_io.h" +#include "svn_types.h" +#include "svn_dirent_uri.h" +#include "svn_auth.h" +#include "svn_subst.h" +#include "svn_utf.h" +#include "svn_pools.h" +#include "svn_user.h" +#include "svn_ctype.h" + +#include "svn_private_config.h" + +#ifdef __HAIKU__ +# include <FindDirectory.h> +# include <StorageDefs.h> +#endif + +/* Used to terminate lines in large multi-line string literals. */ +#define NL APR_EOL_STR + + +/* File parsing context */ +typedef struct parse_context_t +{ + /* This config struct */ + svn_config_t *cfg; + + /* The stream struct */ + svn_stream_t *stream; + + /* The current line in the file */ + int line; + + /* Emulate an ungetc */ + int ungotten_char; + + /* Temporary strings */ + svn_stringbuf_t *section; + svn_stringbuf_t *option; + svn_stringbuf_t *value; + + /* Parser buffer for getc() to avoid call overhead into several libraries + for every character */ + char parser_buffer[SVN_STREAM_CHUNK_SIZE]; /* Larger than most config files */ + size_t buffer_pos; /* Current position within parser_buffer */ + size_t buffer_size; /* parser_buffer contains this many bytes */ +} parse_context_t; + + + +/* Emulate getc() because streams don't support it. + * + * In order to be able to ungetc(), use the CXT instead of the stream + * to be able to store the 'ungotton' character. + * + */ +static APR_INLINE svn_error_t * +parser_getc(parse_context_t *ctx, int *c) +{ + do + { + if (ctx->ungotten_char != EOF) + { + *c = ctx->ungotten_char; + ctx->ungotten_char = EOF; + } + else if (ctx->buffer_pos < ctx->buffer_size) + { + *c = ctx->parser_buffer[ctx->buffer_pos]; + ctx->buffer_pos++; + } + else + { + ctx->buffer_pos = 0; + ctx->buffer_size = sizeof(ctx->parser_buffer); + + SVN_ERR(svn_stream_read(ctx->stream, ctx->parser_buffer, + &(ctx->buffer_size))); + + if (ctx->buffer_pos < ctx->buffer_size) + { + *c = ctx->parser_buffer[ctx->buffer_pos]; + ctx->buffer_pos++; + } + else + *c = EOF; + } + } + while (*c == '\r'); + + return SVN_NO_ERROR; +} + +/* Simplified version of parser_getc() to be used inside skipping loops. + * It will not check for 'ungotton' chars and may or may not ignore '\r'. + * + * In a 'while(cond) getc();' loop, the first iteration must call + * parser_getc to handle all the special cases. Later iterations should + * use parser_getc_plain for maximum performance. + */ +static APR_INLINE svn_error_t * +parser_getc_plain(parse_context_t *ctx, int *c) +{ + if (ctx->buffer_pos < ctx->buffer_size) + { + *c = ctx->parser_buffer[ctx->buffer_pos]; + ctx->buffer_pos++; + + return SVN_NO_ERROR; + } + + return parser_getc(ctx, c); +} + +/* Emulate ungetc() because streams don't support it. + * + * Use CTX to store the ungotten character C. + */ +static APR_INLINE svn_error_t * +parser_ungetc(parse_context_t *ctx, int c) +{ + ctx->ungotten_char = c; + + return SVN_NO_ERROR; +} + +/* Eat chars from STREAM until encounter non-whitespace, newline, or EOF. + Set *PCOUNT to the number of characters eaten, not counting the + last one, and return the last char read (the one that caused the + break). */ +static APR_INLINE svn_error_t * +skip_whitespace(parse_context_t *ctx, int *c, int *pcount) +{ + int ch = 0; + int count = 0; + + SVN_ERR(parser_getc(ctx, &ch)); + while (svn_ctype_isspace(ch) && ch != '\n' && ch != EOF) + { + ++count; + SVN_ERR(parser_getc_plain(ctx, &ch)); + } + *pcount = count; + *c = ch; + return SVN_NO_ERROR; +} + + +/* Skip to the end of the line (or file). Returns the char that ended + the line; the char is either EOF or newline. */ +static APR_INLINE svn_error_t * +skip_to_eoln(parse_context_t *ctx, int *c) +{ + int ch; + + SVN_ERR(parser_getc(ctx, &ch)); + while (ch != '\n' && ch != EOF) + SVN_ERR(parser_getc_plain(ctx, &ch)); + + *c = ch; + return SVN_NO_ERROR; +} + + +/* Parse a single option value */ +static svn_error_t * +parse_value(int *pch, parse_context_t *ctx) +{ + svn_boolean_t end_of_val = FALSE; + int ch; + + /* Read the first line of the value */ + svn_stringbuf_setempty(ctx->value); + SVN_ERR(parser_getc(ctx, &ch)); + while (ch != EOF && ch != '\n') + /* last ch seen was ':' or '=' in parse_option. */ + { + const char char_from_int = (char)ch; + svn_stringbuf_appendbyte(ctx->value, char_from_int); + SVN_ERR(parser_getc(ctx, &ch)); + } + /* Leading and trailing whitespace is ignored. */ + svn_stringbuf_strip_whitespace(ctx->value); + + /* Look for any continuation lines. */ + for (;;) + { + + if (ch == EOF || end_of_val) + { + /* At end of file. The value is complete, there can't be + any continuation lines. */ + svn_config_set(ctx->cfg, ctx->section->data, + ctx->option->data, ctx->value->data); + break; + } + else + { + int count; + ++ctx->line; + SVN_ERR(skip_whitespace(ctx, &ch, &count)); + + switch (ch) + { + case '\n': + /* The next line was empty. Ergo, it can't be a + continuation line. */ + ++ctx->line; + end_of_val = TRUE; + continue; + + case EOF: + /* This is also an empty line. */ + end_of_val = TRUE; + continue; + + default: + if (count == 0) + { + /* This line starts in the first column. That means + it's either a section, option or comment. Put + the char back into the stream, because it doesn't + belong to us. */ + SVN_ERR(parser_ungetc(ctx, ch)); + end_of_val = TRUE; + } + else + { + /* This is a continuation line. Read it. */ + svn_stringbuf_appendbyte(ctx->value, ' '); + + while (ch != EOF && ch != '\n') + { + const char char_from_int = (char)ch; + svn_stringbuf_appendbyte(ctx->value, char_from_int); + SVN_ERR(parser_getc(ctx, &ch)); + } + /* Trailing whitespace is ignored. */ + svn_stringbuf_strip_whitespace(ctx->value); + } + } + } + } + + *pch = ch; + return SVN_NO_ERROR; +} + + +/* Parse a single option */ +static svn_error_t * +parse_option(int *pch, parse_context_t *ctx, apr_pool_t *scratch_pool) +{ + svn_error_t *err = SVN_NO_ERROR; + int ch; + + svn_stringbuf_setempty(ctx->option); + ch = *pch; /* Yes, the first char is relevant. */ + while (ch != EOF && ch != ':' && ch != '=' && ch != '\n') + { + const char char_from_int = (char)ch; + svn_stringbuf_appendbyte(ctx->option, char_from_int); + SVN_ERR(parser_getc(ctx, &ch)); + } + + if (ch != ':' && ch != '=') + { + ch = EOF; + err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, + "line %d: Option must end with ':' or '='", + ctx->line); + } + else + { + /* Whitespace around the name separator is ignored. */ + svn_stringbuf_strip_whitespace(ctx->option); + err = parse_value(&ch, ctx); + } + + *pch = ch; + return err; +} + + +/* Read chars until enounter ']', then skip everything to the end of + * the line. Set *PCH to the character that ended the line (either + * newline or EOF), and set CTX->section to the string of characters + * seen before ']'. + * + * This is meant to be called immediately after reading the '[' that + * starts a section name. + */ +static svn_error_t * +parse_section_name(int *pch, parse_context_t *ctx, + apr_pool_t *scratch_pool) +{ + svn_error_t *err = SVN_NO_ERROR; + int ch; + + svn_stringbuf_setempty(ctx->section); + SVN_ERR(parser_getc(ctx, &ch)); + while (ch != EOF && ch != ']' && ch != '\n') + { + const char char_from_int = (char)ch; + svn_stringbuf_appendbyte(ctx->section, char_from_int); + SVN_ERR(parser_getc(ctx, &ch)); + } + + if (ch != ']') + { + ch = EOF; + err = svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, + "line %d: Section header must end with ']'", + ctx->line); + } + else + { + /* Everything from the ']' to the end of the line is ignored. */ + SVN_ERR(skip_to_eoln(ctx, &ch)); + if (ch != EOF) + ++ctx->line; + } + + *pch = ch; + return err; +} + + +svn_error_t * +svn_config__sys_config_path(const char **path_p, + const char *fname, + apr_pool_t *pool) +{ + *path_p = NULL; + + /* Note that even if fname is null, svn_dirent_join_many will DTRT. */ + +#ifdef WIN32 + { + const char *folder; + SVN_ERR(svn_config__win_config_path(&folder, TRUE, pool)); + *path_p = svn_dirent_join_many(pool, folder, + SVN_CONFIG__SUBDIRECTORY, fname, NULL); + } + +#elif defined(__HAIKU__) + { + char folder[B_PATH_NAME_LENGTH]; + + status_t error = find_directory(B_COMMON_SETTINGS_DIRECTORY, -1, false, + folder, sizeof(folder)); + if (error) + return SVN_NO_ERROR; + + *path_p = svn_dirent_join_many(pool, folder, + SVN_CONFIG__SYS_DIRECTORY, fname, NULL); + } +#else /* ! WIN32 && !__HAIKU__ */ + + *path_p = svn_dirent_join_many(pool, SVN_CONFIG__SYS_DIRECTORY, fname, NULL); + +#endif /* WIN32 */ + + return SVN_NO_ERROR; +} + + +/*** Exported interfaces. ***/ + + +svn_error_t * +svn_config__parse_file(svn_config_t *cfg, const char *file, + svn_boolean_t must_exist, apr_pool_t *result_pool) +{ + svn_error_t *err = SVN_NO_ERROR; + svn_stream_t *stream; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); + + err = svn_stream_open_readonly(&stream, file, scratch_pool, scratch_pool); + + if (! must_exist && err && APR_STATUS_IS_ENOENT(err->apr_err)) + { + svn_error_clear(err); + svn_pool_destroy(scratch_pool); + return SVN_NO_ERROR; + } + else + SVN_ERR(err); + + err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool); + + if (err != SVN_NO_ERROR) + { + /* Add the filename to the error stack. */ + err = svn_error_createf(err->apr_err, err, + "Error while parsing config file: %s:", + svn_dirent_local_style(file, scratch_pool)); + } + + /* Close the streams (and other cleanup): */ + svn_pool_destroy(scratch_pool); + + return err; +} + +svn_error_t * +svn_config__parse_stream(svn_config_t *cfg, svn_stream_t *stream, + apr_pool_t *result_pool, apr_pool_t *scratch_pool) +{ + parse_context_t *ctx; + int ch, count; + + ctx = apr_palloc(scratch_pool, sizeof(*ctx)); + + ctx->cfg = cfg; + ctx->stream = stream; + ctx->line = 1; + ctx->ungotten_char = EOF; + ctx->section = svn_stringbuf_create_empty(scratch_pool); + ctx->option = svn_stringbuf_create_empty(scratch_pool); + ctx->value = svn_stringbuf_create_empty(scratch_pool); + ctx->buffer_pos = 0; + ctx->buffer_size = 0; + + do + { + SVN_ERR(skip_whitespace(ctx, &ch, &count)); + + switch (ch) + { + case '[': /* Start of section header */ + if (count == 0) + SVN_ERR(parse_section_name(&ch, ctx, scratch_pool)); + else + return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, + "line %d: Section header" + " must start in the first column", + ctx->line); + break; + + case '#': /* Comment */ + if (count == 0) + { + SVN_ERR(skip_to_eoln(ctx, &ch)); + ++(ctx->line); + } + else + return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, + "line %d: Comment" + " must start in the first column", + ctx->line); + break; + + case '\n': /* Empty line */ + ++(ctx->line); + break; + + case EOF: /* End of file or read error */ + break; + + default: + if (svn_stringbuf_isempty(ctx->section)) + return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, + "line %d: Section header expected", + ctx->line); + else if (count != 0) + return svn_error_createf(SVN_ERR_MALFORMED_FILE, NULL, + "line %d: Option expected", + ctx->line); + else + SVN_ERR(parse_option(&ch, ctx, scratch_pool)); + break; + } + } + while (ch != EOF); + + return SVN_NO_ERROR; +} + + +/* Helper for ensure_auth_dirs: create SUBDIR under AUTH_DIR, iff + SUBDIR does not already exist, but ignore any errors. Use POOL for + temporary allocation. */ +static void +ensure_auth_subdir(const char *auth_dir, + const char *subdir, + apr_pool_t *pool) +{ + svn_error_t *err; + const char *subdir_full_path; + svn_node_kind_t kind; + + subdir_full_path = svn_dirent_join(auth_dir, subdir, pool); + err = svn_io_check_path(subdir_full_path, &kind, pool); + if (err || kind == svn_node_none) + { + svn_error_clear(err); + svn_error_clear(svn_io_dir_make(subdir_full_path, APR_OS_DEFAULT, pool)); + } +} + +/* Helper for svn_config_ensure: see if ~/.subversion/auth/ and its + subdirs exist, try to create them, but don't throw errors on + failure. PATH is assumed to be a path to the user's private config + directory. */ +static void +ensure_auth_dirs(const char *path, + apr_pool_t *pool) +{ + svn_node_kind_t kind; + const char *auth_dir; + svn_error_t *err; + + /* Ensure ~/.subversion/auth/ */ + auth_dir = svn_dirent_join(path, SVN_CONFIG__AUTH_SUBDIR, pool); + err = svn_io_check_path(auth_dir, &kind, pool); + if (err || kind == svn_node_none) + { + svn_error_clear(err); + /* 'chmod 700' permissions: */ + err = svn_io_dir_make(auth_dir, + (APR_UREAD | APR_UWRITE | APR_UEXECUTE), + pool); + if (err) + { + /* Don't try making subdirs if we can't make the top-level dir. */ + svn_error_clear(err); + return; + } + } + + /* If a provider exists that wants to store credentials in + ~/.subversion, a subdirectory for the cred_kind must exist. */ + ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SIMPLE, pool); + ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_USERNAME, pool); + ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_SERVER_TRUST, pool); + ensure_auth_subdir(auth_dir, SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, pool); +} + + +svn_error_t * +svn_config_ensure(const char *config_dir, apr_pool_t *pool) +{ + const char *path; + svn_node_kind_t kind; + svn_error_t *err; + + /* Ensure that the user-specific config directory exists. */ + SVN_ERR(svn_config_get_user_config_path(&path, config_dir, NULL, pool)); + + if (! path) + return SVN_NO_ERROR; + + err = svn_io_check_resolved_path(path, &kind, pool); + if (err) + { + /* Don't throw an error, but don't continue. */ + svn_error_clear(err); + return SVN_NO_ERROR; + } + + if (kind == svn_node_none) + { + err = svn_io_dir_make(path, APR_OS_DEFAULT, pool); + if (err) + { + /* Don't throw an error, but don't continue. */ + svn_error_clear(err); + return SVN_NO_ERROR; + } + } + else if (kind == svn_node_file) + { + /* Somebody put a file where the config directory should be. + Wacky. Let's bail. */ + return SVN_NO_ERROR; + } + + /* Else, there's a configuration directory. */ + + /* If we get errors trying to do things below, just stop and return + success. There's no _need_ to init a config directory if + something's preventing it. */ + + /** If non-existent, try to create a number of auth/ subdirectories. */ + ensure_auth_dirs(path, pool); + + /** Ensure that the `README.txt' file exists. **/ + SVN_ERR(svn_config_get_user_config_path + (&path, config_dir, SVN_CONFIG__USR_README_FILE, pool)); + + if (! path) /* highly unlikely, since a previous call succeeded */ + return SVN_NO_ERROR; + + err = svn_io_check_path(path, &kind, pool); + if (err) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + + if (kind == svn_node_none) + { + apr_file_t *f; + const char *contents = + "This directory holds run-time configuration information for Subversion" NL + "clients. The configuration files all share the same syntax, but you" NL + "should examine a particular file to learn what configuration" NL + "directives are valid for that file." NL + "" NL + "The syntax is standard INI format:" NL + "" NL + " - Empty lines, and lines starting with '#', are ignored." NL + " The first significant line in a file must be a section header." NL + "" NL + " - A section starts with a section header, which must start in" NL + " the first column:" NL + "" NL + " [section-name]" NL + "" NL + " - An option, which must always appear within a section, is a pair" NL + " (name, value). There are two valid forms for defining an" NL + " option, both of which must start in the first column:" NL + "" NL + " name: value" NL + " name = value" NL + "" NL + " Whitespace around the separator (:, =) is optional." NL + "" NL + " - Section and option names are case-insensitive, but case is" NL + " preserved." NL + "" NL + " - An option's value may be broken into several lines. The value" NL + " continuation lines must start with at least one whitespace." NL + " Trailing whitespace in the previous line, the newline character" NL + " and the leading whitespace in the continuation line is compressed" NL + " into a single space character." NL + "" NL + " - All leading and trailing whitespace around a value is trimmed," NL + " but the whitespace within a value is preserved, with the" NL + " exception of whitespace around line continuations, as" NL + " described above." NL + "" NL + " - When a value is a boolean, any of the following strings are" NL + " recognised as truth values (case does not matter):" NL + "" NL + " true false" NL + " yes no" NL + " on off" NL + " 1 0" NL + "" NL + " - When a value is a list, it is comma-separated. Again, the" NL + " whitespace around each element of the list is trimmed." NL + "" NL + " - Option values may be expanded within a value by enclosing the" NL + " option name in parentheses, preceded by a percent sign and" NL + " followed by an 's':" NL + "" NL + " %(name)s" NL + "" NL + " The expansion is performed recursively and on demand, during" NL + " svn_option_get. The name is first searched for in the same" NL + " section, then in the special [DEFAULT] section. If the name" NL + " is not found, the whole '%(name)s' placeholder is left" NL + " unchanged." NL + "" NL + " Any modifications to the configuration data invalidate all" NL + " previously expanded values, so that the next svn_option_get" NL + " will take the modifications into account." NL + "" NL + "The syntax of the configuration files is a subset of the one used by" NL + "Python's ConfigParser module; see" NL + "" NL + " http://www.python.org/doc/current/lib/module-ConfigParser.html" NL + "" NL + "Configuration data in the Windows registry" NL + "==========================================" NL + "" NL + "On Windows, configuration data may also be stored in the registry. The" NL + "functions svn_config_read and svn_config_merge will read from the" NL + "registry when passed file names of the form:" NL + "" NL + " REGISTRY:<hive>/path/to/config-key" NL + "" NL + "The REGISTRY: prefix must be in upper case. The <hive> part must be" NL + "one of:" NL + "" NL + " HKLM for HKEY_LOCAL_MACHINE" NL + " HKCU for HKEY_CURRENT_USER" NL + "" NL + "The values in config-key represent the options in the [DEFAULT] section."NL + "The keys below config-key represent other sections, and their values" NL + "represent the options. Only values of type REG_SZ whose name doesn't" NL + "start with a '#' will be used; other values, as well as the keys'" NL + "default values, will be ignored." NL + "" NL + "" NL + "File locations" NL + "==============" NL + "" NL + "Typically, Subversion uses two config directories, one for site-wide" NL + "configuration," NL + "" NL + " Unix:" NL + " /etc/subversion/servers" NL + " /etc/subversion/config" NL + " /etc/subversion/hairstyles" NL + " Windows:" NL + " %ALLUSERSPROFILE%\\Application Data\\Subversion\\servers" NL + " %ALLUSERSPROFILE%\\Application Data\\Subversion\\config" NL + " %ALLUSERSPROFILE%\\Application Data\\Subversion\\hairstyles" NL + " REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Servers" NL + " REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Config" NL + " REGISTRY:HKLM\\Software\\Tigris.org\\Subversion\\Hairstyles" NL + "" NL + "and one for per-user configuration:" NL + "" NL + " Unix:" NL + " ~/.subversion/servers" NL + " ~/.subversion/config" NL + " ~/.subversion/hairstyles" NL + " Windows:" NL + " %APPDATA%\\Subversion\\servers" NL + " %APPDATA%\\Subversion\\config" NL + " %APPDATA%\\Subversion\\hairstyles" NL + " REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Servers" NL + " REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Config" NL + " REGISTRY:HKCU\\Software\\Tigris.org\\Subversion\\Hairstyles" NL + "" NL; + + err = svn_io_file_open(&f, path, + (APR_WRITE | APR_CREATE | APR_EXCL), + APR_OS_DEFAULT, + pool); + + if (! err) + { + SVN_ERR(svn_io_file_write_full(f, contents, + strlen(contents), NULL, pool)); + SVN_ERR(svn_io_file_close(f, pool)); + } + + svn_error_clear(err); + } + + /** Ensure that the `servers' file exists. **/ + SVN_ERR(svn_config_get_user_config_path + (&path, config_dir, SVN_CONFIG_CATEGORY_SERVERS, pool)); + + if (! path) /* highly unlikely, since a previous call succeeded */ + return SVN_NO_ERROR; + + err = svn_io_check_path(path, &kind, pool); + if (err) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + + if (kind == svn_node_none) + { + apr_file_t *f; + const char *contents = + "### This file specifies server-specific parameters," NL + "### including HTTP proxy information, HTTP timeout settings," NL + "### and authentication settings." NL + "###" NL + "### The currently defined server options are:" NL + "### http-proxy-host Proxy host for HTTP connection" NL + "### http-proxy-port Port number of proxy host service" NL + "### http-proxy-username Username for auth to proxy service"NL + "### http-proxy-password Password for auth to proxy service"NL + "### http-proxy-exceptions List of sites that do not use proxy" + NL + "### http-timeout Timeout for HTTP requests in seconds" + NL + "### http-compression Whether to compress HTTP requests" NL + "### http-max-connections Maximum number of parallel server" NL + "### connections to use for any given" NL + "### HTTP operation." NL + "### neon-debug-mask Debug mask for Neon HTTP library" NL + "### ssl-authority-files List of files, each of a trusted CA" + NL + "### ssl-trust-default-ca Trust the system 'default' CAs" NL + "### ssl-client-cert-file PKCS#12 format client certificate file" + NL + "### ssl-client-cert-password Client Key password, if needed." NL + "### ssl-pkcs11-provider Name of PKCS#11 provider to use." NL + "### http-library Which library to use for http/https" + NL + "### connections." NL + "### http-bulk-updates Whether to request bulk update" NL + "### responses or to fetch each file" NL + "### in an individual request. " NL + "### store-passwords Specifies whether passwords used" NL + "### to authenticate against a" NL + "### Subversion server may be cached" NL + "### to disk in any way." NL +#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE + "### store-plaintext-passwords Specifies whether passwords may" NL + "### be cached on disk unencrypted." NL +#endif + "### store-ssl-client-cert-pp Specifies whether passphrase used" NL + "### to authenticate against a client" NL + "### certificate may be cached to disk" NL + "### in any way" NL +#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE + "### store-ssl-client-cert-pp-plaintext" NL + "### Specifies whether client cert" NL + "### passphrases may be cached on disk" NL + "### unencrypted (i.e., as plaintext)." NL +#endif + "### store-auth-creds Specifies whether any auth info" NL + "### (passwords, server certs, etc.)" NL + "### may be cached to disk." NL + "### username Specifies the default username." NL + "###" NL + "### Set store-passwords to 'no' to avoid storing passwords on disk" NL + "### in any way, including in password stores. It defaults to" NL + "### 'yes', but Subversion will never save your password to disk in" NL + "### plaintext unless explicitly configured to do so." NL + "### Note that this option only prevents saving of *new* passwords;" NL + "### it doesn't invalidate existing passwords. (To do that, remove" NL + "### the cache files by hand as described in the Subversion book.)" NL + "###" NL +#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE + "### Set store-plaintext-passwords to 'no' to avoid storing" NL + "### passwords in unencrypted form in the auth/ area of your config" NL + "### directory. Set it to 'yes' to allow Subversion to store" NL + "### unencrypted passwords in the auth/ area. The default is" NL + "### 'ask', which means that Subversion will ask you before" NL + "### saving a password to disk in unencrypted form. Note that" NL + "### this option has no effect if either 'store-passwords' or " NL + "### 'store-auth-creds' is set to 'no'." NL + "###" NL +#endif + "### Set store-ssl-client-cert-pp to 'no' to avoid storing ssl" NL + "### client certificate passphrases in the auth/ area of your" NL + "### config directory. It defaults to 'yes', but Subversion will" NL + "### never save your passphrase to disk in plaintext unless" NL + "### explicitly configured to do so." NL + "###" NL + "### Note store-ssl-client-cert-pp only prevents the saving of *new*"NL + "### passphrases; it doesn't invalidate existing passphrases. To do"NL + "### that, remove the cache files by hand as described in the" NL + "### Subversion book at http://svnbook.red-bean.com/nightly/en/\\" NL + "### svn.serverconfig.netmodel.html\\" NL + "### #svn.serverconfig.netmodel.credcache" NL + "###" NL +#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE + "### Set store-ssl-client-cert-pp-plaintext to 'no' to avoid storing"NL + "### passphrases in unencrypted form in the auth/ area of your" NL + "### config directory. Set it to 'yes' to allow Subversion to" NL + "### store unencrypted passphrases in the auth/ area. The default" NL + "### is 'ask', which means that Subversion will prompt before" NL + "### saving a passphrase to disk in unencrypted form. Note that" NL + "### this option has no effect if either 'store-auth-creds' or " NL + "### 'store-ssl-client-cert-pp' is set to 'no'." NL + "###" NL +#endif + "### Set store-auth-creds to 'no' to avoid storing any Subversion" NL + "### credentials in the auth/ area of your config directory." NL + "### Note that this includes SSL server certificates." NL + "### It defaults to 'yes'. Note that this option only prevents" NL + "### saving of *new* credentials; it doesn't invalidate existing" NL + "### caches. (To do that, remove the cache files by hand.)" NL + "###" NL + "### HTTP timeouts, if given, are specified in seconds. A timeout" NL + "### of 0, i.e. zero, causes a builtin default to be used." NL + "###" NL + "### Most users will not need to explicitly set the http-library" NL + "### option, but valid values for the option include:" NL + "### 'serf': Serf-based module (Subversion 1.5 - present)" NL + "### 'neon': Neon-based module (Subversion 1.0 - 1.7)" NL + "### Availability of these modules may depend on your specific" NL + "### Subversion distribution." NL + "###" NL + "### The commented-out examples below are intended only to" NL + "### demonstrate how to use this file; any resemblance to actual" NL + "### servers, living or dead, is entirely coincidental." NL + "" NL + "### In the 'groups' section, the URL of the repository you're" NL + "### trying to access is matched against the patterns on the right." NL + "### If a match is found, the server options are taken from the" NL + "### section with the corresponding name on the left." NL + "" NL + "[groups]" NL + "# group1 = *.collab.net" NL + "# othergroup = repository.blarggitywhoomph.com" NL + "# thirdgroup = *.example.com" NL + "" NL + "### Information for the first group:" NL + "# [group1]" NL + "# http-proxy-host = proxy1.some-domain-name.com" NL + "# http-proxy-port = 80" NL + "# http-proxy-username = blah" NL + "# http-proxy-password = doubleblah" NL + "# http-timeout = 60" NL + "# neon-debug-mask = 130" NL +#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE + "# store-plaintext-passwords = no" NL +#endif + "# username = harry" NL + "" NL + "### Information for the second group:" NL + "# [othergroup]" NL + "# http-proxy-host = proxy2.some-domain-name.com" NL + "# http-proxy-port = 9000" NL + "# No username and password for the proxy, so use the defaults below." + NL + "" NL + "### You can set default parameters in the 'global' section." NL + "### These parameters apply if no corresponding parameter is set in" NL + "### a specifically matched group as shown above. Thus, if you go" NL + "### through the same proxy server to reach every site on the" NL + "### Internet, you probably just want to put that server's" NL + "### information in the 'global' section and not bother with" NL + "### 'groups' or any other sections." NL + "###" NL + "### Most people might want to configure password caching" NL + "### parameters here, but you can also configure them per server" NL + "### group (per-group settings override global settings)." NL + "###" NL + "### If you go through a proxy for all but a few sites, you can" NL + "### list those exceptions under 'http-proxy-exceptions'. This only"NL + "### overrides defaults, not explicitly matched server names." NL + "###" NL + "### 'ssl-authority-files' is a semicolon-delimited list of files," NL + "### each pointing to a PEM-encoded Certificate Authority (CA) " NL + "### SSL certificate. See details above for overriding security " NL + "### due to SSL." NL + "[global]" NL + "# http-proxy-exceptions = *.exception.com, www.internal-site.org" NL + "# http-proxy-host = defaultproxy.whatever.com" NL + "# http-proxy-port = 7000" NL + "# http-proxy-username = defaultusername" NL + "# http-proxy-password = defaultpassword" NL + "# http-compression = no" NL + "# No http-timeout, so just use the builtin default." NL + "# No neon-debug-mask, so neon debugging is disabled." NL + "# ssl-authority-files = /path/to/CAcert.pem;/path/to/CAcert2.pem" NL + "#" NL + "# Password / passphrase caching parameters:" NL + "# store-passwords = no" NL + "# store-ssl-client-cert-pp = no" NL +#ifndef SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE + "# store-plaintext-passwords = no" NL + "# store-ssl-client-cert-pp-plaintext = no" NL +#endif + ; + + err = svn_io_file_open(&f, path, + (APR_WRITE | APR_CREATE | APR_EXCL), + APR_OS_DEFAULT, + pool); + + if (! err) + { + SVN_ERR(svn_io_file_write_full(f, contents, + strlen(contents), NULL, pool)); + SVN_ERR(svn_io_file_close(f, pool)); + } + + svn_error_clear(err); + } + + /** Ensure that the `config' file exists. **/ + SVN_ERR(svn_config_get_user_config_path + (&path, config_dir, SVN_CONFIG_CATEGORY_CONFIG, pool)); + + if (! path) /* highly unlikely, since a previous call succeeded */ + return SVN_NO_ERROR; + + err = svn_io_check_path(path, &kind, pool); + if (err) + { + svn_error_clear(err); + return SVN_NO_ERROR; + } + + if (kind == svn_node_none) + { + apr_file_t *f; + const char *contents = + "### This file configures various client-side behaviors." NL + "###" NL + "### The commented-out examples below are intended to demonstrate" NL + "### how to use this file." NL + "" NL + "### Section for authentication and authorization customizations." NL + "[auth]" NL + "### Set password stores used by Subversion. They should be" NL + "### delimited by spaces or commas. The order of values determines" NL + "### the order in which password stores are used." NL + "### Valid password stores:" NL + "### gnome-keyring (Unix-like systems)" NL + "### kwallet (Unix-like systems)" NL + "### gpg-agent (Unix-like systems)" NL + "### keychain (Mac OS X)" NL + "### windows-cryptoapi (Windows)" NL +#ifdef SVN_HAVE_KEYCHAIN_SERVICES + "# password-stores = keychain" NL +#elif defined(WIN32) && !defined(__MINGW32__) + "# password-stores = windows-cryptoapi" NL +#else + "# password-stores = gpg-agent,gnome-keyring,kwallet" NL +#endif + "### To disable all password stores, use an empty list:" NL + "# password-stores =" NL +#ifdef SVN_HAVE_KWALLET + "###" NL + "### Set KWallet wallet used by Subversion. If empty or unset," NL + "### then the default network wallet will be used." NL + "# kwallet-wallet =" NL + "###" NL + "### Include PID (Process ID) in Subversion application name when" NL + "### using KWallet. It defaults to 'no'." NL + "# kwallet-svn-application-name-with-pid = yes" NL +#endif + "###" NL + "### Set ssl-client-cert-file-prompt to 'yes' to cause the client" NL + "### to prompt for a path to a client cert file when the server" NL + "### requests a client cert but no client cert file is found in the" NL + "### expected place (see the 'ssl-client-cert-file' option in the" NL + "### 'servers' configuration file). Defaults to 'no'." NL + "# ssl-client-cert-file-prompt = no" NL + "###" NL + "### The rest of the [auth] section in this file has been deprecated." + NL + "### Both 'store-passwords' and 'store-auth-creds' can now be" NL + "### specified in the 'servers' file in your config directory" NL + "### and are documented there. Anything specified in this section " NL + "### is overridden by settings specified in the 'servers' file." NL + "# store-passwords = no" NL + "# store-auth-creds = no" NL + "" NL + "### Section for configuring external helper applications." NL + "[helpers]" NL + "### Set editor-cmd to the command used to invoke your text editor." NL + "### This will override the environment variables that Subversion" NL + "### examines by default to find this information ($EDITOR, " NL + "### et al)." NL + "# editor-cmd = editor (vi, emacs, notepad, etc.)" NL + "### Set diff-cmd to the absolute path of your 'diff' program." NL + "### This will override the compile-time default, which is to use" NL + "### Subversion's internal diff implementation." NL + "# diff-cmd = diff_program (diff, gdiff, etc.)" NL + "### Diff-extensions are arguments passed to an external diff" NL + "### program or to Subversion's internal diff implementation." NL + "### Set diff-extensions to override the default arguments ('-u')." NL + "# diff-extensions = -u -p" NL + "### Set diff3-cmd to the absolute path of your 'diff3' program." NL + "### This will override the compile-time default, which is to use" NL + "### Subversion's internal diff3 implementation." NL + "# diff3-cmd = diff3_program (diff3, gdiff3, etc.)" NL + "### Set diff3-has-program-arg to 'yes' if your 'diff3' program" NL + "### accepts the '--diff-program' option." NL + "# diff3-has-program-arg = [yes | no]" NL + "### Set merge-tool-cmd to the command used to invoke your external" NL + "### merging tool of choice. Subversion will pass 5 arguments to" NL + "### the specified command: base theirs mine merged wcfile" NL + "# merge-tool-cmd = merge_command" NL + "" NL + "### Section for configuring tunnel agents." NL + "[tunnels]" NL + "### Configure svn protocol tunnel schemes here. By default, only" NL + "### the 'ssh' scheme is defined. You can define other schemes to" NL + "### be used with 'svn+scheme://hostname/path' URLs. A scheme" NL + "### definition is simply a command, optionally prefixed by an" NL + "### environment variable name which can override the command if it" NL + "### is defined. The command (or environment variable) may contain" NL + "### arguments, using standard shell quoting for arguments with" NL + "### spaces. The command will be invoked as:" NL + "### <command> <hostname> svnserve -t" NL + "### (If the URL includes a username, then the hostname will be" NL + "### passed to the tunnel agent as <user>@<hostname>.) If the" NL + "### built-in ssh scheme were not predefined, it could be defined" NL + "### as:" NL + "# ssh = $SVN_SSH ssh -q" NL + "### If you wanted to define a new 'rsh' scheme, to be used with" NL + "### 'svn+rsh:' URLs, you could do so as follows:" NL + "# rsh = rsh" NL + "### Or, if you wanted to specify a full path and arguments:" NL + "# rsh = /path/to/rsh -l myusername" NL + "### On Windows, if you are specifying a full path to a command," NL + "### use a forward slash (/) or a paired backslash (\\\\) as the" NL + "### path separator. A single backslash will be treated as an" NL + "### escape for the following character." NL + "" NL + "### Section for configuring miscellaneous Subversion options." NL + "[miscellany]" NL + "### Set global-ignores to a set of whitespace-delimited globs" NL + "### which Subversion will ignore in its 'status' output, and" NL + "### while importing or adding files and directories." NL + "### '*' matches leading dots, e.g. '*.rej' matches '.foo.rej'." NL + "# global-ignores = " SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_1 NL + "# " SVN_CONFIG__DEFAULT_GLOBAL_IGNORES_LINE_2 NL + "### Set log-encoding to the default encoding for log messages" NL + "# log-encoding = latin1" NL + "### Set use-commit-times to make checkout/update/switch/revert" NL + "### put last-committed timestamps on every file touched." NL + "# use-commit-times = yes" NL + "### Set no-unlock to prevent 'svn commit' from automatically" NL + "### releasing locks on files." NL + "# no-unlock = yes" NL + "### Set mime-types-file to a MIME type registry file, used to" NL + "### provide hints to Subversion's MIME type auto-detection" NL + "### algorithm." NL + "# mime-types-file = /path/to/mime.types" NL + "### Set preserved-conflict-file-exts to a whitespace-delimited" NL + "### list of patterns matching file extensions which should be" NL + "### preserved in generated conflict file names. By default," NL + "### conflict files use custom extensions." NL + "# preserved-conflict-file-exts = doc ppt xls od?" NL + "### Set enable-auto-props to 'yes' to enable automatic properties" NL + "### for 'svn add' and 'svn import', it defaults to 'no'." NL + "### Automatic properties are defined in the section 'auto-props'." NL + "# enable-auto-props = yes" NL + "### Set interactive-conflicts to 'no' to disable interactive" NL + "### conflict resolution prompting. It defaults to 'yes'." NL + "# interactive-conflicts = no" NL + "### Set memory-cache-size to define the size of the memory cache" NL + "### used by the client when accessing a FSFS repository via" NL + "### ra_local (the file:// scheme). The value represents the number" NL + "### of MB used by the cache." NL + "# memory-cache-size = 16" NL + "" NL + "### Section for configuring automatic properties." NL + "[auto-props]" NL + "### The format of the entries is:" NL + "### file-name-pattern = propname[=value][;propname[=value]...]" NL + "### The file-name-pattern can contain wildcards (such as '*' and" NL + "### '?'). All entries which match (case-insensitively) will be" NL + "### applied to the file. Note that auto-props functionality" NL + "### must be enabled, which is typically done by setting the" NL + "### 'enable-auto-props' option." NL + "# *.c = svn:eol-style=native" NL + "# *.cpp = svn:eol-style=native" NL + "# *.h = svn:keywords=Author Date Id Rev URL;svn:eol-style=native" NL + "# *.dsp = svn:eol-style=CRLF" NL + "# *.dsw = svn:eol-style=CRLF" NL + "# *.sh = svn:eol-style=native;svn:executable" NL + "# *.txt = svn:eol-style=native;svn:keywords=Author Date Id Rev URL;"NL + "# *.png = svn:mime-type=image/png" NL + "# *.jpg = svn:mime-type=image/jpeg" NL + "# Makefile = svn:eol-style=native" NL + "" NL + "### Section for configuring working copies." NL + "[working-copy]" NL + "### Set to a list of the names of specific clients that should use" NL + "### exclusive SQLite locking of working copies. This increases the"NL + "### performance of the client but prevents concurrent access by" NL + "### other clients. Third-party clients may also support this" NL + "### option." NL + "### Possible values:" NL + "### svn (the command line client)" NL + "# exclusive-locking-clients =" NL + "### Set to true to enable exclusive SQLite locking of working" NL + "### copies by all clients using the 1.8 APIs. Enabling this may" NL + "### cause some clients to fail to work properly. This does not have"NL + "### to be set for exclusive-locking-clients to work." NL + "# exclusive-locking = false" NL; + + err = svn_io_file_open(&f, path, + (APR_WRITE | APR_CREATE | APR_EXCL), + APR_OS_DEFAULT, + pool); + + if (! err) + { + SVN_ERR(svn_io_file_write_full(f, contents, + strlen(contents), NULL, pool)); + SVN_ERR(svn_io_file_close(f, pool)); + } + + svn_error_clear(err); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_config_get_user_config_path(const char **path, + const char *config_dir, + const char *fname, + apr_pool_t *pool) +{ + *path= NULL; + + /* Note that even if fname is null, svn_dirent_join_many will DTRT. */ + + if (config_dir) + { + *path = svn_dirent_join_many(pool, config_dir, fname, NULL); + return SVN_NO_ERROR; + } + +#ifdef WIN32 + { + const char *folder; + SVN_ERR(svn_config__win_config_path(&folder, FALSE, pool)); + *path = svn_dirent_join_many(pool, folder, + SVN_CONFIG__SUBDIRECTORY, fname, NULL); + } + +#elif defined(__HAIKU__) + { + char folder[B_PATH_NAME_LENGTH]; + + status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, -1, false, + folder, sizeof(folder)); + if (error) + return SVN_NO_ERROR; + + *path = svn_dirent_join_many(pool, folder, + SVN_CONFIG__USR_DIRECTORY, fname, NULL); + } +#else /* ! WIN32 && !__HAIKU__ */ + + { + const char *homedir = svn_user_get_homedir(pool); + if (! homedir) + return SVN_NO_ERROR; + *path = svn_dirent_join_many(pool, + svn_dirent_canonicalize(homedir, pool), + SVN_CONFIG__USR_DIRECTORY, fname, NULL); + } +#endif /* WIN32 */ + + return SVN_NO_ERROR; +} + |