client.c

来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 1,321 行 · 第 1/4 页

C
1,321
字号
/*
 * client.c :  Functions for repository access via the Subversion protocol
 *
 * ====================================================================
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 */



#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_general.h>
#include <apr_lib.h>
#include <apr_strings.h>
#include <apr_network_io.h>
#include <apr_md5.h>

#include "svn_types.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_time.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "svn_config.h"
#include "svn_private_config.h"
#include "svn_ra.h"
#include "svn_ra_svn.h"
#include "svn_md5.h"

#include "ra_svn.h"

typedef struct {
  svn_ra_svn_conn_t *conn;
  int protocol_version;
  svn_boolean_t is_tunneled;
  svn_auth_baton_t *auth_baton;
  const char *user;
  const char *realm_prefix;
} ra_svn_session_baton_t;

typedef struct {
  ra_svn_session_baton_t *sess;
  apr_pool_t *pool;
  svn_revnum_t *new_rev;
  svn_commit_callback_t callback;
  void *callback_baton;
} ra_svn_commit_callback_baton_t;

typedef struct {
  ra_svn_session_baton_t *sess;
  svn_ra_svn_conn_t *conn;
  apr_pool_t *pool;
  const svn_delta_editor_t *editor;
  void *edit_baton;
} ra_svn_reporter_baton_t;

/* Parse an svn URL's authority section into tunnel, user, host, and
 * port components.  Return 0 on success, -1 on failure.  *tunnel
 * and *user may be set to NULL. */
static int parse_url(const char *url, const char **tunnel, const char **user,
                     unsigned short *port, const char **hostname,
                     apr_pool_t *pool)
{
  const char *p;

  *tunnel = NULL;
  *user = NULL;
  *port = SVN_RA_SVN_PORT;
  *hostname = NULL;

  if (strncasecmp(url, "svn", 3) != 0)
    return -1;
  url += 3;

  /* Get the tunnel specification, if any. */
  if (*url == '+')
    {
      url++;
      p = strchr(url, ':');
      if (!p)
        return -1;
      *tunnel = apr_pstrmemdup(pool, url, p - url);
      url = p;
    }

  if (strncmp(url, "://", 3) != 0)
    return -1;
  url += 3;

  while (1)
    {
      p = url + strcspn(url, "@:/");
      if (*p == '@' && !*user)
        *user = apr_pstrmemdup(pool, url, p - url);
      else if (*p == ':' && !*hostname)
        *hostname = apr_pstrmemdup(pool, url, p - url);
      else if (*p == '/' || *p == '\0')
        {
          if (!*hostname)
            *hostname = apr_pstrmemdup(pool, url, p - url);
          else
            *port = atoi(url);
          break;
        }
      else
        return -1;
      url = p + 1;
    }

  /* Decode any escaped characters in the hostname and user. */
  *hostname = svn_path_uri_decode(*hostname, pool);
  if (*user)
    *user = svn_path_uri_decode(*user, pool);
  return 0;
}

static svn_error_t *make_connection(const char *hostname, unsigned short port,
                                    apr_socket_t **sock, apr_pool_t *pool)
{
  apr_sockaddr_t *sa;
  apr_status_t status;

  /* Resolve the hostname. */
  status = apr_sockaddr_info_get(&sa, hostname, APR_INET, port, 0, pool);
  if (status)
    return svn_error_createf(status, NULL, "Unknown hostname '%s'", hostname);

  /* Create the socket. */
#ifdef MAX_SECS_TO_LINGER
  /* ### old APR interface */
  status = apr_socket_create(sock, APR_INET, SOCK_STREAM, pool);
#else
  status = apr_socket_create(sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool);
#endif
  if (status)
    return svn_error_wrap_apr(status, "Can't create socket");

  status = apr_socket_connect(*sock, sa);
  if (status)
    return svn_error_wrap_apr(status, "Can't connect to host '%s'", hostname);

  return SVN_NO_ERROR;
}

/* Convert a property list received from the server into a hash table. */
static svn_error_t *parse_proplist(apr_array_header_t *list, apr_pool_t *pool,
                                   apr_hash_t **props)
{
  char *name;
  svn_string_t *value;
  svn_ra_svn_item_t *elt;
  int i;

  *props = apr_hash_make(pool);
  for (i = 0; i < list->nelts; i++)
    {
      elt = &((svn_ra_svn_item_t *) list->elts)[i];
      if (elt->kind != SVN_RA_SVN_LIST)
        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                "Proplist element not a list");
      SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, pool, "cs", &name, &value));
      apr_hash_set(*props, name, APR_HASH_KEY_STRING, value);
    }
  return SVN_NO_ERROR;
}

/* Set *DIFFS to an array of svn_prop_t, allocated in POOL, based on the
   property diffs in LIST, received from the server. */
static svn_error_t *parse_prop_diffs(apr_array_header_t *list,
                                     apr_pool_t *pool,
                                     apr_array_header_t **diffs)
{
  svn_ra_svn_item_t *elt;
  svn_prop_t *prop;
  int i;

  *diffs = apr_array_make(pool, list->nelts, sizeof(svn_prop_t));

  for (i = 0; i < list->nelts; i++)
    {
      elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
      if (elt->kind != SVN_RA_SVN_LIST)
        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                                _("Prop diffs element not a list"));
      prop = apr_array_push(*diffs);
      SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, pool, "c(?s)", &prop->name,
                                     &prop->value));
    }
  return SVN_NO_ERROR;
}

static svn_error_t *interpret_kind(const char *str, apr_pool_t *pool,
                                   svn_node_kind_t *kind)
{
  if (strcmp(str, "none") == 0)
    *kind = svn_node_none;
  else if (strcmp(str, "file") == 0)
    *kind = svn_node_file;
  else if (strcmp(str, "dir") == 0)
    *kind = svn_node_dir;
  else if (strcmp(str, "unknown") == 0)
    *kind = svn_node_unknown;
  else
    return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
                             "Unrecognized node kind '%s' from server", str);
  return SVN_NO_ERROR;
}

/* --- AUTHENTICATION ROUTINES --- */

static svn_boolean_t find_mech(apr_array_header_t *mechlist, const char *mech)
{
  int i;
  svn_ra_svn_item_t *elt;

  for (i = 0; i < mechlist->nelts; i++)
    {
      elt = &((svn_ra_svn_item_t *) mechlist->elts)[i];
      if (elt->kind == SVN_RA_SVN_WORD && strcmp(elt->u.word, mech) == 0)
        return TRUE;
    }
  return FALSE;
}

/* Having picked a mechanism, start authentication by writing out an
 * auth response.  If COMPAT is true, also write out a version number
 * and capability list.  MECH_ARG may be NULL for mechanisms with no
 * initial client response. */
static svn_error_t *auth_response(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
                                  const char *mech, const char *mech_arg,
                                  svn_boolean_t compat)
{
  if (compat)
    return svn_ra_svn_write_tuple(conn, pool, "nw(?c)(w)", (apr_uint64_t) 1,
                                  mech, mech_arg,
                                  SVN_RA_SVN_CAP_EDIT_PIPELINE);
  else
    return svn_ra_svn_write_tuple(conn, pool, "w(?c)", mech, mech_arg);
}

/* Read the "success" response to ANONYMOUS or EXTERNAL authentication. */
static svn_error_t *read_success(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
{
  const char *status, *arg;

  SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?c)", &status, &arg));
  if (strcmp(status, "failure") == 0 && arg)
    return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                             "Authentication error from server: %s", arg);
  else if (strcmp(status, "success") != 0 || arg)
    return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                            "Unexpected server response to authentication");
  return SVN_NO_ERROR;
}

/* Respond to an auth request and perform authentication.  REALM may
 * be NULL for the initial authentication exchange of protocol version
 * 1. */
static svn_error_t *do_auth(ra_svn_session_baton_t *sess,
                            apr_array_header_t *mechlist,
                            const char *realm, apr_pool_t *pool)
{
  svn_ra_svn_conn_t *conn = sess->conn;
  const char *realmstring, *user, *password, *msg;
  svn_auth_iterstate_t *iterstate;
  void *creds;
  svn_boolean_t compat = (realm == NULL);

  realmstring = realm ? apr_psprintf(pool, "%s %s", sess->realm_prefix, realm)
    : sess->realm_prefix;
  if (sess->is_tunneled && find_mech(mechlist, "EXTERNAL"))
    {
      /* Ask the server to use the tunnel connection environment (on
       * Unix, that means uid) to determine the authentication name. */
      SVN_ERR(auth_response(conn, pool, "EXTERNAL", "", compat));
      return read_success(conn, pool);
    }
  else if (find_mech(mechlist, "ANONYMOUS"))
    {
      SVN_ERR(auth_response(conn, pool, "ANONYMOUS", "", compat));
      return read_success(conn, pool);
    }
  else if (find_mech(mechlist, "CRAM-MD5"))
    {
      SVN_ERR(svn_auth_first_credentials(&creds, &iterstate,
                                         SVN_AUTH_CRED_SIMPLE, realmstring,
                                         sess->auth_baton, pool));
      if (!creds)
        return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                "Can't get password");
      while (creds)
        {
          user = ((svn_auth_cred_simple_t *) creds)->username;
          password = ((svn_auth_cred_simple_t *) creds)->password;
          SVN_ERR(auth_response(conn, pool, "CRAM-MD5", NULL, compat));
          SVN_ERR(svn_ra_svn__cram_client(conn, pool, user, password, &msg));
          if (!msg)
            break;
          SVN_ERR(svn_auth_next_credentials(&creds, iterstate, pool));
        }
      if (!creds)
        return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                                 "Authentication error from server: %s", msg);
      SVN_ERR(svn_auth_save_credentials(iterstate, pool));
      return SVN_NO_ERROR;
    }
  else
    return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
                            "Cannot negotiate authentication mechanism");
}

static svn_error_t *handle_auth_request(ra_svn_session_baton_t *sess,
                                        apr_pool_t *pool)
{
  svn_ra_svn_conn_t *conn = sess->conn;
  apr_array_header_t *mechlist;
  const char *realm;

  if (sess->protocol_version < 2)
    return SVN_NO_ERROR;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?