svn_error_t *svn_ra_open4(svn_ra_session_t **session_p,
                          const char **corrected_url_p,
                          const char *repos_URL,
                          const char *uuid,
                          const svn_ra_callbacks2_t *callbacks,
                          void *callback_baton,
                          apr_hash_t *config,
                          apr_pool_t *pool)
{
  apr_pool_t *sesspool = svn_pool_create(pool);
  svn_ra_session_t *session;
  const struct ra_lib_defn *defn;
  const svn_ra__vtable_t *vtable = NULL;
  svn_config_t *servers = NULL;
  const char *server_group;
  apr_uri_t repos_URI;
  apr_status_t apr_err;
#ifdef CHOOSABLE_DAV_MODULE
  const char *http_library = DEFAULT_HTTP_LIBRARY;
#endif
  /* Auth caching parameters. */
  svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS;
  svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS;
  const char *store_plaintext_passwords
    = SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS;
  svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP;
  const char *store_pp_plaintext
    = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT;
  const char *corrected_url;

  /* Initialize the return variable. */
  *session_p = NULL;

  apr_err = apr_uri_parse(sesspool, repos_URL, &repos_URI);
  /* ### Should apr_uri_parse leave hostname NULL?  It doesn't
   * for "file:///" URLs, only for bogus URLs like "bogus".
   * If this is the right behavior for apr_uri_parse, maybe we
   * should have a svn_uri_parse wrapper. */
  if (apr_err != APR_SUCCESS || repos_URI.hostname == NULL)
    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                             _("Illegal repository URL '%s'"),
                             repos_URL);

  if (callbacks->auth_baton)
    {
      /* The 'store-passwords' and 'store-auth-creds' parameters used to
       * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility,
       * if values for these parameters have already been set by our
       * callers, we use those values as defaults.
       *
       * Note that we can only catch the case where users explicitly set
       * "store-passwords = no" or 'store-auth-creds = no".
       *
       * However, since the default value for both these options is
       * currently (and has always been) "yes", users won't know
       * the difference if they set "store-passwords = yes" or
       * "store-auth-creds = yes" -- they'll get the expected behaviour.
       */

      if (svn_auth_get_parameter(callbacks->auth_baton,
                                 SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL)
        store_passwords = FALSE;

      if (svn_auth_get_parameter(callbacks->auth_baton,
                                 SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL)
        store_auth_creds = FALSE;
    }

  if (config)
    {
      /* Grab the 'servers' config. */
      servers = apr_hash_get(config, SVN_CONFIG_CATEGORY_SERVERS,
                             APR_HASH_KEY_STRING);
      if (servers)
        {
          /* First, look in the global section. */

          SVN_ERR(svn_config_get_bool
            (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL,
             SVN_CONFIG_OPTION_STORE_PASSWORDS,
             store_passwords));

          SVN_ERR(svn_config_get_yes_no_ask
            (servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL,
             SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
             SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS));

          SVN_ERR(svn_config_get_bool
            (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL,
             SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
             store_pp));

          SVN_ERR(svn_config_get_yes_no_ask
            (servers, &store_pp_plaintext,
             SVN_CONFIG_SECTION_GLOBAL,
             SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
             SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT));

          SVN_ERR(svn_config_get_bool
            (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL,
              SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
              store_auth_creds));

          /* Find out where we're about to connect to, and
           * try to pick a server group based on the destination. */
          server_group = svn_config_find_group(servers, repos_URI.hostname,
                                               SVN_CONFIG_SECTION_GROUPS,
                                               sesspool);

          if (server_group)
            {
              /* Override global auth caching parameters with the ones
               * for the server group, if any. */
              SVN_ERR(svn_config_get_bool(servers, &store_auth_creds,
                                          server_group,
                                          SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
                                          store_auth_creds));

              SVN_ERR(svn_config_get_bool(servers, &store_passwords,
                                          server_group,
                                          SVN_CONFIG_OPTION_STORE_PASSWORDS,
                                          store_passwords));

              SVN_ERR(svn_config_get_yes_no_ask
                (servers, &store_plaintext_passwords, server_group,
                 SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS,
                 store_plaintext_passwords));

              SVN_ERR(svn_config_get_bool
                (servers, &store_pp,
                 server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
                 store_pp));

              SVN_ERR(svn_config_get_yes_no_ask
                (servers, &store_pp_plaintext, server_group,
                 SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
                 store_pp_plaintext));
            }
#ifdef CHOOSABLE_DAV_MODULE
          /* Now, which DAV-based RA method do we want to use today? */
          http_library
            = svn_config_get_server_setting(servers,
                                            server_group, /* NULL is OK */
                                            SVN_CONFIG_OPTION_HTTP_LIBRARY,
                                            DEFAULT_HTTP_LIBRARY);

          if (strcmp(http_library, "neon") != 0 &&
              strcmp(http_library, "serf") != 0)
            return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
                                     _("Invalid config: unknown HTTP library "
                                       "'%s'"),
                                     http_library);
#endif
        }
    }

  if (callbacks->auth_baton)
    {
      /* Save auth caching parameters in the auth parameter hash. */
      if (! store_passwords)
        svn_auth_set_parameter(callbacks->auth_baton,
                               SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");

      svn_auth_set_parameter(callbacks->auth_baton,
                             SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS,
                             store_plaintext_passwords);

      if (! store_pp)
        svn_auth_set_parameter(callbacks->auth_baton,
                               SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP,
                               "");

      svn_auth_set_parameter(callbacks->auth_baton,
                             SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
                             store_pp_plaintext);

      if (! store_auth_creds)
        svn_auth_set_parameter(callbacks->auth_baton,
                               SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
    }

  /* Find the library. */
  for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
    {
      const char *scheme;

      if ((scheme = has_scheme_of(defn->schemes, repos_URL)))
        {
          svn_ra__init_func_t initfunc = defn->initfunc;

#ifdef CHOOSABLE_DAV_MODULE
          if (defn->schemes == dav_schemes
              && strcmp(defn->ra_name, http_library) != 0)
            continue;
#endif

          if (! initfunc)
            SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
                                   sesspool));
          if (! initfunc)
            /* Library not found. */
            continue;

          SVN_ERR(initfunc(svn_ra_version(), &vtable, sesspool));

          SVN_ERR(check_ra_version(vtable->get_version(), scheme));

          if (! has_scheme_of(vtable->get_schemes(sesspool), repos_URL))
            /* Library doesn't support the scheme at runtime. */
            continue;


          break;
        }
    }

  if (vtable == NULL)
    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
                             _("Unrecognized URL scheme for '%s'"),
                             repos_URL);

  /* Create the session object. */
  session = apr_pcalloc(sesspool, sizeof(*session));
  session->vtable = vtable;
  session->pool = sesspool;

  /* Ask the library to open the session. */
  SVN_ERR_W(vtable->open_session(session, &corrected_url, repos_URL,
                                 callbacks, callback_baton, config, sesspool),
            apr_psprintf(pool, "Unable to connect to a repository at URL '%s'",
                         repos_URL));

  /* If the session open stuff detected a server-provided URL
     correction (a 301 or 302 redirect response during the initial
     OPTIONS request), then kill the session so the caller can decide
     what to do. */
  if (corrected_url_p && corrected_url)
    {
      if (! svn_path_is_url(corrected_url))
        {
          /* RFC1945 and RFC2616 state that the Location header's
             value (from whence this CORRECTED_URL ultimately comes),
             if present, must be an absolute URI.  But some Apache
             versions (those older than 2.2.11, it seems) transmit
             only the path portion of the URI.  See issue #3775 for
             details. */
          apr_uri_t corrected_URI = repos_URI;
          corrected_URI.path = (char *)corrected_url;
          corrected_url = apr_uri_unparse(pool, &corrected_URI, 0);
        }
      *corrected_url_p = svn_uri_canonicalize(corrected_url, pool);
      svn_pool_destroy(sesspool);
      return SVN_NO_ERROR;
    }

  /* Check the UUID. */
  if (uuid)
    {
      const char *repository_uuid;

      SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool));
      if (strcmp(uuid, repository_uuid) != 0)
        {
          /* Duplicate the uuid as it is allocated in sesspool */
          repository_uuid = apr_pstrdup(pool, repository_uuid);
          svn_pool_destroy(sesspool);
          return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL,
                                   _("Repository UUID '%s' doesn't match "
                                     "expected UUID '%s'"),
                                   repository_uuid, uuid);
        }
    }

  *session_p = session;
  return SVN_NO_ERROR;
}
Пример #2
0
static svn_error_t *
load_config(svn_ra_serf__session_t *session,
            apr_hash_t *config_hash,
            apr_pool_t *pool)
{
  svn_config_t *config, *config_client;
  const char *server_group;
  const char *proxy_host = NULL;
  const char *port_str = NULL;
  const char *timeout_str = NULL;
  const char *exceptions;
  apr_port_t proxy_port;
  svn_tristate_t chunked_requests;
#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING)
  apr_int64_t log_components;
  apr_int64_t log_level;
#endif

  if (config_hash)
    {
      config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_SERVERS);
      config_client = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG);
    }
  else
    {
      config = NULL;
      config_client = NULL;
    }

  SVN_ERR(svn_config_get_bool(config, &session->using_compression,
                              SVN_CONFIG_SECTION_GLOBAL,
                              SVN_CONFIG_OPTION_HTTP_COMPRESSION, TRUE));
  svn_config_get(config, &timeout_str, SVN_CONFIG_SECTION_GLOBAL,
                 SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL);

  if (session->auth_baton)
    {
      if (config_client)
        {
          svn_auth_set_parameter(session->auth_baton,
                                 SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG,
                                 config_client);
        }
      if (config)
        {
          svn_auth_set_parameter(session->auth_baton,
                                 SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS,
                                 config);
        }
    }

  /* Use the default proxy-specific settings if and only if
     "http-proxy-exceptions" is not set to exclude this host. */
  svn_config_get(config, &exceptions, SVN_CONFIG_SECTION_GLOBAL,
                 SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, "");
  if (! svn_cstring_match_glob_list(session->session_url.hostname,
                                    svn_cstring_split(exceptions, ",",
                                                      TRUE, pool)))
    {
      svn_config_get(config, &proxy_host, SVN_CONFIG_SECTION_GLOBAL,
                     SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL);
      svn_config_get(config, &port_str, SVN_CONFIG_SECTION_GLOBAL,
                     SVN_CONFIG_OPTION_HTTP_PROXY_PORT, NULL);
      svn_config_get(config, &session->proxy_username,
                     SVN_CONFIG_SECTION_GLOBAL,
                     SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, NULL);
      svn_config_get(config, &session->proxy_password,
                     SVN_CONFIG_SECTION_GLOBAL,
                     SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, NULL);
    }

  /* Load the global ssl settings, if set. */
  SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca,
                              SVN_CONFIG_SECTION_GLOBAL,
                              SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA,
                              TRUE));
  svn_config_get(config, &session->ssl_authorities, SVN_CONFIG_SECTION_GLOBAL,
                 SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, NULL);

  /* If set, read the flag that tells us to do bulk updates or not. Defaults
     to skelta updates. */
  SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates,
                                  SVN_CONFIG_SECTION_GLOBAL,
                                  SVN_CONFIG_OPTION_HTTP_BULK_UPDATES,
                                  "auto",
                                  svn_tristate_unknown));

  /* Load the maximum number of parallel session connections. */
  SVN_ERR(svn_config_get_int64(config, &session->max_connections,
                               SVN_CONFIG_SECTION_GLOBAL,
                               SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS,
                               SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS));

  /* Should we use chunked transfer encoding. */
  SVN_ERR(svn_config_get_tristate(config, &chunked_requests,
                                  SVN_CONFIG_SECTION_GLOBAL,
                                  SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS,
                                  "auto", svn_tristate_unknown));

#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING)
  SVN_ERR(svn_config_get_int64(config, &log_components,
                               SVN_CONFIG_SECTION_GLOBAL,
                               SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS,
                               SERF_LOGCOMP_NONE));
  SVN_ERR(svn_config_get_int64(config, &log_level,
                               SVN_CONFIG_SECTION_GLOBAL,
                               SVN_CONFIG_OPTION_SERF_LOG_LEVEL,
                               SERF_LOG_INFO));
#endif

  server_group = svn_auth_get_parameter(session->auth_baton,
                                        SVN_AUTH_PARAM_SERVER_GROUP);

  if (server_group)
    {
      SVN_ERR(svn_config_get_bool(config, &session->using_compression,
                                  server_group,
                                  SVN_CONFIG_OPTION_HTTP_COMPRESSION,
                                  session->using_compression));
      svn_config_get(config, &timeout_str, server_group,
                     SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str);

      /* Load the group proxy server settings, overriding global
         settings.  We intentionally ignore 'http-proxy-exceptions'
         here because, well, if this site was an exception, why is
         there a per-server proxy configuration for it?  */
      svn_config_get(config, &proxy_host, server_group,
                     SVN_CONFIG_OPTION_HTTP_PROXY_HOST, proxy_host);
      svn_config_get(config, &port_str, server_group,
                     SVN_CONFIG_OPTION_HTTP_PROXY_PORT, port_str);
      svn_config_get(config, &session->proxy_username, server_group,
                     SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME,
                     session->proxy_username);
      svn_config_get(config, &session->proxy_password, server_group,
                     SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD,
                     session->proxy_password);

      /* Load the group ssl settings. */
      SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca,
                                  server_group,
                                  SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA,
                                  session->trust_default_ca));
      svn_config_get(config, &session->ssl_authorities, server_group,
                     SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES,
                     session->ssl_authorities);

      /* Load the group bulk updates flag. */
      SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates,
                                      server_group,
                                      SVN_CONFIG_OPTION_HTTP_BULK_UPDATES,
                                      "auto",
                                      session->bulk_updates));

      /* Load the maximum number of parallel session connections,
         overriding global values. */
      SVN_ERR(svn_config_get_int64(config, &session->max_connections,
                                   server_group,
                                   SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS,
                                   session->max_connections));

      /* Should we use chunked transfer encoding. */
      SVN_ERR(svn_config_get_tristate(config, &chunked_requests,
                                      server_group,
                                      SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS,
                                      "auto", chunked_requests));

#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING)
      SVN_ERR(svn_config_get_int64(config, &log_components,
                                   server_group,
                                   SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS,
                                   log_components));
       SVN_ERR(svn_config_get_int64(config, &log_level,
                                    server_group,
                                    SVN_CONFIG_OPTION_SERF_LOG_LEVEL,
                                    log_level));
#endif
    }

#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING)
  if (log_components != SERF_LOGCOMP_NONE)
    {
      serf_log_output_t *output;
      apr_status_t status;

      status = serf_logging_create_stream_output(&output,
                                                 session->context,
                                                 (apr_uint32_t)log_level,
                                                 (apr_uint32_t)log_components,
                                                 SERF_LOG_DEFAULT_LAYOUT,
                                                 stderr,
                                                 pool);

      if (!status)
          serf_logging_add_output(session->context, output);
    }
#endif

  /* Don't allow the http-max-connections value to be larger than our
     compiled-in limit, or to be too small to operate.  Broken
     functionality and angry administrators are equally undesirable. */
  if (session->max_connections > SVN_RA_SERF__MAX_CONNECTIONS_LIMIT)
    session->max_connections = SVN_RA_SERF__MAX_CONNECTIONS_LIMIT;
  if (session->max_connections < 2)
    session->max_connections = 2;

  /* Parse the connection timeout value, if any. */
  session->timeout = apr_time_from_sec(DEFAULT_HTTP_TIMEOUT);
  if (timeout_str)
    {
      char *endstr;
      const long int timeout = strtol(timeout_str, &endstr, 10);

      if (*endstr)
        return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL,
                                _("Invalid config: illegal character in "
                                  "timeout value"));
      if (timeout < 0)
        return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL,
                                _("Invalid config: negative timeout value"));
      session->timeout = apr_time_from_sec(timeout);
    }
  SVN_ERR_ASSERT(session->timeout >= 0);

  /* Convert the proxy port value, if any. */
  if (port_str)
    {
      char *endstr;
      const long int port = strtol(port_str, &endstr, 10);

      if (*endstr)
        return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                _("Invalid URL: illegal character in proxy "
                                  "port number"));
      if (port < 0)
        return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                _("Invalid URL: negative proxy port number"));
      if (port > 65535)
        return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL,
                                _("Invalid URL: proxy port number greater "
                                  "than maximum TCP port number 65535"));
      proxy_port = (apr_port_t) port;
    }
  else
    {
      proxy_port = 80;
    }

  if (proxy_host)
    {
      apr_sockaddr_t *proxy_addr;
      apr_status_t status;

      status = apr_sockaddr_info_get(&proxy_addr, proxy_host,
                                     APR_UNSPEC, proxy_port, 0,
                                     session->pool);
      if (status)
        {
          return svn_ra_serf__wrap_err(
                   status, _("Could not resolve proxy server '%s'"),
                   proxy_host);
        }
      session->using_proxy = TRUE;
      serf_config_proxy(session->context, proxy_addr);
    }
  else
    {
      session->using_proxy = FALSE;
    }

  /* Setup detect_chunking and using_chunked_requests based on
   * the chunked_requests tristate */
  if (chunked_requests == svn_tristate_unknown)
    {
      session->detect_chunking = TRUE;
      session->using_chunked_requests = TRUE;
    }
  else if (chunked_requests == svn_tristate_true)
    {
      session->detect_chunking = FALSE;
      session->using_chunked_requests = TRUE;
    }
  else /* chunked_requests == svn_tristate_false */
    {
      session->detect_chunking = FALSE;
      session->using_chunked_requests = FALSE;
    }

  /* Setup authentication. */
  SVN_ERR(load_http_auth_types(pool, config, server_group,
                               &session->authn_types));
  serf_config_authn_types(session->context, session->authn_types);
  serf_config_credentials_callback(session->context,
                                   svn_ra_serf__credentials_callback);

  return SVN_NO_ERROR;
}