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 + -
显示快捷键?