📄 client.c
字号:
/* * client.c : Functions for repository access via the Subversion protocol * * ==================================================================== * Copyright (c) 2000-2006 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_strings.h>#include <apr_network_io.h>#include <apr_md5.h>#include <apr_uri.h>#include <assert.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 "../libsvn_ra/ra_loader.h"#include "svn_ra_svn.h"#include "svn_md5.h"#include "svn_props.h"#include "ra_svn.h"typedef struct { apr_pool_t *pool; 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; const char **tunnel_argv;} ra_svn_session_baton_t;typedef struct { ra_svn_session_baton_t *sess_baton; apr_pool_t *pool; svn_revnum_t *new_rev; svn_commit_callback2_t callback; void *callback_baton;} ra_svn_commit_callback_baton_t;typedef struct { ra_svn_session_baton_t *sess_baton; 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 tunnel portion into tunnel, if there is a tunnel portion. */static void parse_tunnel(const char *url, const char **tunnel, apr_pool_t *pool){ const char *p; *tunnel = NULL; if (strncasecmp(url, "svn", 3) != 0) return; url += 3; /* Get the tunnel specification, if any. */ if (*url == '+') { url++; p = strchr(url, ':'); if (!p) return; *tunnel = apr_pstrmemdup(pool, url, p - url); url = p; }}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; int family = APR_INET; /* Make sure we have IPV6 support first before giving apr_sockaddr_info_get APR_UNSPEC, because it may give us back an IPV6 address even if we can't create IPV6 sockets. */ #if APR_HAVE_IPV6#ifdef MAX_SECS_TO_LINGER status = apr_socket_create(sock, APR_INET6, SOCK_STREAM, pool);#else status = apr_socket_create(sock, APR_INET6, SOCK_STREAM, APR_PROTO_TCP, pool);#endif if (status == 0) { apr_socket_close(*sock); family = APR_UNSPEC; }#endif /* Resolve the hostname. */ status = apr_sockaddr_info_get(&sa, hostname, family, 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, sa->family, SOCK_STREAM, pool);#else status = apr_socket_create(sock, sa->family, 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;}/* Parse a lockdesc, provided in LIST as specified by the protocol into LOCK, allocated in POOL. */static svn_error_t *parse_lock(apr_array_header_t *list, apr_pool_t *pool, svn_lock_t **lock){ const char *cdate, *edate; *lock = svn_lock_create(pool); SVN_ERR(svn_ra_svn_parse_tuple(list, pool, "ccc(?c)c(?c)", &(*lock)->path, &(*lock)->token, &(*lock)->owner, &(*lock)->comment, &cdate, &edate)); (*lock)->path = svn_path_canonicalize((*lock)->path, pool); SVN_ERR(svn_time_from_cstring(&(*lock)->creation_date, cdate, pool)); if (edate) SVN_ERR(svn_time_from_cstring(&(*lock)->expiration_date, edate, pool)); 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)(www)", (apr_uint64_t) 1, mech, mech_arg, SVN_RA_SVN_CAP_EDIT_PIPELINE, SVN_RA_SVN_CAP_SVNDIFF1, SVN_RA_SVN_CAP_ABSENT_ENTRIES); 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; SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "lc", &mechlist, &realm)); if (mechlist->nelts == 0) return SVN_NO_ERROR; return do_auth(sess, mechlist, realm, pool);}/* --- REPORTER IMPLEMENTATION --- */static svn_error_t *ra_svn_set_path(void *baton, const char *path, svn_revnum_t rev, svn_boolean_t start_empty, const char *lock_token, apr_pool_t *pool){ ra_svn_reporter_baton_t *b = baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "set-path", "crb(?c)", path, rev, start_empty, lock_token));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -