client.c
来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 1,321 行 · 第 1/4 页
C
1,321 行
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,
apr_pool_t *pool)
{
ra_svn_reporter_baton_t *b = baton;
SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "set-path", "crb", path, rev,
start_empty));
return SVN_NO_ERROR;
}
static svn_error_t *ra_svn_delete_path(void *baton, const char *path,
apr_pool_t *pool)
{
ra_svn_reporter_baton_t *b = baton;
SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "delete-path", "c", path));
return SVN_NO_ERROR;
}
static svn_error_t *ra_svn_link_path(void *baton, const char *path,
const char *url,
svn_revnum_t rev,
svn_boolean_t start_empty,
apr_pool_t *pool)
{
ra_svn_reporter_baton_t *b = baton;
SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "link-path", "ccrb", path, url,
rev, start_empty));
return SVN_NO_ERROR;
}
static svn_error_t *ra_svn_finish_report(void *baton,
apr_pool_t *pool)
{
ra_svn_reporter_baton_t *b = baton;
SVN_ERR(svn_ra_svn_write_cmd(b->conn, b->pool, "finish-report", ""));
SVN_ERR(handle_auth_request(b->sess, b->pool));
SVN_ERR(svn_ra_svn_drive_editor(b->conn, b->pool, b->editor, b->edit_baton,
NULL));
SVN_ERR(svn_ra_svn_read_cmd_response(b->conn, b->pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *ra_svn_abort_report(void *baton,
apr_pool_t *pool)
{
ra_svn_reporter_baton_t *b = baton;
SVN_ERR(svn_ra_svn_write_cmd(b->conn, b->pool, "abort-report", ""));
return SVN_NO_ERROR;
}
static svn_ra_reporter_t ra_svn_reporter = {
ra_svn_set_path,
ra_svn_delete_path,
ra_svn_link_path,
ra_svn_finish_report,
ra_svn_abort_report
};
static void ra_svn_get_reporter(ra_svn_session_baton_t *sess, apr_pool_t *pool,
const svn_delta_editor_t *editor,
void *edit_baton,
const svn_ra_reporter_t **reporter,
void **report_baton)
{
ra_svn_reporter_baton_t *b;
b = apr_palloc(pool, sizeof(*b));
b->sess = sess;
b->conn = sess->conn;
b->pool = pool;
b->editor = editor;
b->edit_baton = edit_baton;
*reporter = &ra_svn_reporter;
*report_baton = b;
}
/* --- RA LAYER IMPLEMENTATION --- */
static svn_error_t *find_tunnel_agent(const char *tunnel, const char *user,
const char *host, const char ***argv,
apr_hash_t *config, apr_pool_t *pool)
{
svn_config_t *cfg;
const char *val, *var, *cmd;
char **cmd_argv;
apr_size_t len;
apr_status_t status;
int n;
/* Look up the tunnel specification in config. */
cfg = config ? apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
APR_HASH_KEY_STRING) : NULL;
svn_config_get(cfg, &val, SVN_CONFIG_SECTION_TUNNELS, tunnel, NULL);
/* We have one predefined tunnel scheme, if it isn't overridden by config. */
if (!val && strcmp(tunnel, "ssh") == 0)
val = "$SVN_SSH ssh";
if (!val || !*val)
return svn_error_createf(SVN_ERR_BAD_URL, NULL,
"Undefined tunnel scheme '%s'", tunnel);
/* If the scheme definition begins with "$varname", it means there
* is an environment variable which can override the command. */
if (*val == '$')
{
val++;
len = strcspn(val, " ");
var = apr_pstrmemdup(pool, val, len);
cmd = getenv(var);
if (!cmd)
{
cmd = val + len;
while (*cmd == ' ')
cmd++;
if (!*cmd)
return svn_error_createf(SVN_ERR_BAD_URL, NULL,
"Tunnel scheme %s requires environment "
"variable %s to be defined", tunnel, var);
}
}
else
cmd = val;
/* Tokenize the command into a list of arguments. */
status = apr_tokenize_to_argv(cmd, &cmd_argv, pool);
if (status != APR_SUCCESS)
return svn_error_wrap_apr(status, "Can't tokenize command '%s'", cmd);
/* Append the fixed arguments to the result. */
for (n = 0; cmd_argv[n] != NULL; n++)
;
*argv = apr_palloc(pool, (n + 4) * sizeof(char *));
memcpy(*argv, cmd_argv, n * sizeof(char *));
(*argv)[n++] = (user) ? apr_psprintf(pool, "%s@%s", user, host) : host;
(*argv)[n++] = "svnserve";
(*argv)[n++] = "-t";
(*argv)[n] = NULL;
return SVN_NO_ERROR;
}
/* This function handles any errors which occur in the child process
* created for a tunnel agent. We write the error out as a command
* failure; the code in ra_svn_open() to read the server's greeting
* will see the error and return it to the caller. */
static void handle_child_process_error(apr_pool_t *pool, apr_status_t status,
const char *desc)
{
svn_ra_svn_conn_t *conn;
apr_file_t *in_file, *out_file;
svn_error_t *err;
apr_file_open_stdin(&in_file, pool);
apr_file_open_stdout(&out_file, pool);
conn = svn_ra_svn_create_conn(NULL, in_file, out_file, pool);
err = svn_error_wrap_apr(status, NULL, "Error in child process: %s", desc);
svn_error_clear(svn_ra_svn_write_cmd_failure(conn, pool, err));
svn_error_clear(svn_ra_svn_flush(conn, pool));
}
static svn_error_t *make_tunnel(const char **args, svn_ra_svn_conn_t **conn,
apr_pool_t *pool)
{
apr_status_t status;
apr_proc_t *proc;
apr_procattr_t *attr;
status = apr_procattr_create(&attr, pool);
if (status == APR_SUCCESS)
status = apr_procattr_io_set(attr, 1, 1, 0);
if (status == APR_SUCCESS)
status = apr_procattr_cmdtype_set(attr, APR_PROGRAM_PATH);
if (status == APR_SUCCESS)
status = apr_procattr_child_errfn_set(attr, handle_child_process_error);
proc = apr_palloc(pool, sizeof(*proc));
if (status == APR_SUCCESS)
status = apr_proc_create(proc, *args, args, NULL, attr, pool);
if (status != APR_SUCCESS)
return svn_error_wrap_apr(status, NULL, "Can't create tunnel");
/* Arrange for the tunnel agent to get a SIGKILL on pool
* cleanup. This is a little extreme, but the alternatives
* weren't working out:
* - Closing the pipes and waiting for the process to die
* was prone to mysterious hangs which are difficult to
* diagnose (e.g. svnserve dumps core due to unrelated bug;
* sshd goes into zombie state; ssh connection is never
* closed; ssh never terminates).
* - Killing the tunnel agent with SIGTERM leads to unsightly
* stderr output from ssh.
*/
apr_pool_note_subprocess(pool, proc, APR_KILL_ALWAYS);
/* APR pipe objects inherit by default. But we don't want the
* tunnel agent's pipes held open by future child processes
* (such as other ra_svn sessions), so turn that off. */
apr_file_inherit_unset(proc->in);
apr_file_inherit_unset(proc->out);
/* Guard against dotfile output to stdout on the server. */
*conn = svn_ra_svn_create_conn(NULL, proc->out, proc->in, pool);
(*conn)->proc = proc;
SVN_ERR(svn_ra_svn_skip_leading_garbage(*conn, pool));
return SVN_NO_ERROR;
}
static svn_error_t *ra_svn_open(void **baton, const char *url,
const svn_ra_callbacks_t *callbacks,
void *callback_baton,
apr_hash_t *config,
apr_pool_t *pool)
{
ra_svn_session_baton_t *sess;
svn_ra_svn_conn_t *conn;
apr_socket_t *sock;
const char *hostname, *user, *tunnel, **args;
unsigned short port;
apr_uint64_t minver, maxver;
apr_array_header_t *mechlist, *caplist;
if (parse_url(url, &tunnel, &user, &port, &hostname, pool) != 0)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
"Illegal svn repository URL '%s'", url);
if (tunnel)
{
SVN_ERR(find_tunnel_agent(tunnel, user, hostname, &args, config, pool));
SVN_ERR(make_tunnel(args, &conn, pool));
}
else
{
SVN_ERR(make_connection(hostname, port, &sock, pool));
conn = svn_ra_svn_create_conn(sock, NULL, NULL, pool);
}
/* Read server's greeting. */
SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "nnll", &minver, &maxver,
&mechlist, &caplist));
/* We support protocol versions 1 and 2. */
if (minver > 2)
return svn_error_createf(SVN_ERR_RA_SVN_BAD_VERSION, NULL,
"Server requires minimum version %d",
(int) minver);
SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist));
sess = apr_palloc(pool, sizeof(*sess));
sess->conn = conn;
sess->protocol_version = (maxver > 2) ? 2 : maxver;
sess->is_tunneled = (tunnel != NULL);
sess->auth_baton = callbacks->auth_baton;
sess->user = user;
sess->realm_prefix = apr_psprintf(pool, "<svn://%s:%d>", hostname, port);
/* In protocol version 2, we send back our protocol version, our
* capability list, and the URL, and subsequently there is an auth
* request. In version 1, we send back the protocol version, auth
* mechanism, mechanism initial response, and capability list, and;
* then send the URL after authentication. do_auth temporarily has
* support for the mixed-style response. */
/* When we punt support for protocol version 1, we should:
* - Eliminate this conditional and the similar one below
* - Remove v1 support from auth_response and inline it into do_auth
* - Remove the (realm == NULL) support from do_auth
* - Inline do_auth into handle_auth_request
* - Remove the protocol version check from handle_auth_request */
if (sess->protocol_version == 1)
{
SVN_ERR(do_auth(sess, mechlist, NULL, pool));
SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "c", url));
}
else
{
SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "n(w)c", (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE, url));
SVN_ERR(handle_auth_request(sess, pool));
}
/* This is where the security layer would go into effect if we
* supported security layers, which is a ways off. */
/* Read the repository's uuid. */
SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "c?c", &conn->uuid,
&conn->repos_root));
if (conn->repos_root)
{
conn->repos_root = svn_path_canonicalize(conn->repos_root, pool);
/* We should check that the returned string is a prefix of url, since
that's the API guarantee, but this isn't true for 1.0 servers.
Checking the length prevents client crashes. */
if (strlen(conn->repos_root) > strlen(url))
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Impossibly long repository root from "
"server"));
}
*baton = sess;
return SVN_NO_ERROR;
}
static svn_error_t *ra_svn_get_latest_rev(void *baton, svn_revnum_t *rev,
apr_pool_t *pool)
{
ra_svn_session_baton_t *sess = baton;
svn_ra_svn_conn_t *conn = sess->conn;
SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "get-latest-rev", ""));
SVN_ERR(handle_auth_request(sess, pool));
SVN_ERR(svn_ra_svn_read_cmd_response(conn, pool, "r", rev));
return SVN_NO_ERROR;
}
static svn_error_t *ra_svn_get_dated_rev(void *baton, svn_revnum_t *rev,
apr_time_t tm, apr_pool_t *pool)
{
ra_svn_session_baton_t *sess = baton;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?