📄 serve.c
字号:
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
/* This implements svn_write_fn_t. Write LEN bytes starting at DATA to the
client as a string. */
static svn_error_t *svndiff_handler(void *baton, const char *data,
apr_size_t *len)
{
file_revs_baton_t *b = baton;
svn_string_t str;
str.data = data;
str.len = *len;
return svn_ra_svn_write_string(b->conn, b->pool, &str);
}
/* This implements svn_close_fn_t. Mark the end of the data by writing an
empty string to the client. */
static svn_error_t *svndiff_close_handler(void *baton)
{
file_revs_baton_t *b = baton;
SVN_ERR(svn_ra_svn_write_cstring(b->conn, b->pool, ""));
return SVN_NO_ERROR;
}
/* This implements the svn_repos_file_rev_handler_t interface. */
static svn_error_t *file_rev_handler(void *baton, const char *path,
svn_revnum_t rev, apr_hash_t *rev_props,
svn_txdelta_window_handler_t *d_handler,
void **d_baton,
apr_array_header_t *prop_diffs,
apr_pool_t *pool)
{
file_revs_baton_t *frb = baton;
svn_stream_t *stream;
SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "cr(!",
path, rev));
SVN_ERR(write_proplist(frb->conn, pool, rev_props));
SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "!)(!"));
SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs));
SVN_ERR(svn_ra_svn_write_tuple(frb->conn, pool, "!)"));
/* Store the pool for the delta stream. */
frb->pool = pool;
/* Prepare for the delta or just write an empty string. */
if (d_handler)
{
stream = svn_stream_create(baton, pool);
svn_stream_set_write(stream, svndiff_handler);
svn_stream_set_close(stream, svndiff_close_handler);
svn_txdelta_to_svndiff(stream, pool, d_handler, d_baton);
}
else
SVN_ERR(svn_ra_svn_write_cstring(frb->conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_error_t *err, *write_err;
file_revs_baton_t frb;
svn_revnum_t start_rev, end_rev;
const char *path;
const char *full_path;
/* Parse arguments. */
SVN_ERR(svn_ra_svn_parse_tuple(params, pool, "c(?r)(?r)",
&path, &start_rev, &end_rev));
path = svn_path_canonicalize(path, pool);
SVN_ERR(trivial_auth_request(conn, pool, b));
full_path = svn_path_join(b->fs_path, path, pool);
frb.conn = conn;
frb.pool = NULL;
err = svn_repos_get_file_revs(b->repos, full_path, start_rev, end_rev, NULL,
NULL, file_rev_handler, &frb, pool);
write_err = svn_ra_svn_write_word(conn, pool, "done");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static const svn_ra_svn_cmd_entry_t main_commands[] = {
{ "get-latest-rev", get_latest_rev },
{ "get-dated-rev", get_dated_rev },
{ "change-rev-prop", change_rev_prop },
{ "rev-proplist", rev_proplist },
{ "rev-prop", rev_prop },
{ "commit", commit },
{ "get-file", get_file },
{ "get-dir", get_dir },
{ "update", update },
{ "switch", switch_cmd },
{ "status", status },
{ "diff", diff },
{ "log", log_cmd },
{ "check-path", check_path },
{ "get-locations", get_locations },
{ "get-file-revs", get_file_revs },
{ NULL }
};
/* Skip past the scheme part of a URL, including the tunnel specification
* if present. Return NULL if the scheme part is invalid for ra_svn. */
static const char *skip_scheme_part(const char *url)
{
if (strncmp(url, "svn", 3) != 0)
return NULL;
url += 3;
if (*url == '+')
url += strcspn(url, ":");
if (strncmp(url, "://", 3) != 0)
return NULL;
return url + 3;
}
/* Look for the repository given by URL, using ROOT as the virtual
* repository root. If we find one, fill in the repos, fs, cfg,
* repos_url, and fs_path fields of B. */
static svn_error_t *find_repos(const char *url, const char *root,
server_baton_t *b, apr_pool_t *pool)
{
apr_status_t apr_err;
const char *path, *full_path, *path_apr, *root_apr, *repos_root, *pwdb_path;
char *buffer;
svn_stringbuf_t *url_buf;
/* Skip past the scheme and authority part. */
path = skip_scheme_part(url);
if (path == NULL)
return svn_error_createf(SVN_ERR_BAD_URL, NULL,
"Non-svn URL passed to svn server: '%s'", url);
path = strchr(path, '/');
path = (path == NULL) ? "" : path + 1;
/* Decode URI escapes from the path. */
path = svn_path_uri_decode(path, pool);
SVN_ERR(svn_path_cstring_from_utf8(&path_apr,
svn_path_canonicalize(path, pool),
pool));
SVN_ERR(svn_path_cstring_from_utf8(&root_apr,
svn_path_canonicalize(root, pool),
pool));
/* Join the server-configured root with the client path. */
apr_err = apr_filepath_merge(&buffer, root_apr, path_apr,
APR_FILEPATH_SECUREROOT, pool);
if(apr_err)
return svn_error_create(SVN_ERR_BAD_FILENAME, NULL,
"Couldn't determine repository path");
SVN_ERR(svn_path_cstring_to_utf8(&full_path, buffer, pool));
full_path = svn_path_canonicalize(full_path, pool);
/* Search for a repository in the full path. */
repos_root = svn_repos_find_root_path(full_path, pool);
if (!repos_root)
return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL,
"No repository found in '%s'", url);
/* Open the repository and fill in b with the resulting information. */
SVN_ERR(svn_repos_open(&b->repos, repos_root, pool));
b->fs = svn_repos_fs(b->repos);
b->fs_path = apr_pstrdup(pool, full_path + strlen(repos_root));
url_buf = svn_stringbuf_create(url, pool);
svn_path_remove_components(url_buf, svn_path_component_count(b->fs_path));
b->repos_url = url_buf->data;
/* Read repository configuration. */
SVN_ERR(svn_config_read(&b->cfg, svn_repos_svnserve_conf(b->repos, pool),
FALSE, pool));
svn_config_get(b->cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_PASSWORD_DB, NULL);
if (pwdb_path)
{
pwdb_path = svn_path_join(svn_repos_conf_dir(b->repos, pool),
pwdb_path, pool);
SVN_ERR(svn_config_read(&b->pwdb, pwdb_path, TRUE, pool));
/* Use the repository UUID as the default realm. */
SVN_ERR(svn_fs_get_uuid(b->fs, &b->realm, pool));
svn_config_get(b->cfg, &b->realm, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_REALM, b->realm);
}
else
{
b->pwdb = NULL;
b->realm = "";
}
/* Make sure it's possible for the client to authenticate. */
if (get_access(b, UNAUTHENTICATED) == NO_ACCESS
&& (get_access(b, AUTHENTICATED) == NO_ACCESS
|| (!b->tunnel_user && !b->pwdb)))
return svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
"No access allowed to this repository");
return SVN_NO_ERROR;
}
/* Compute the authentication name EXTERNAL should be able to get, if any. */
static const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool)
{
apr_uid_t uid;
apr_gid_t gid;
char *user;
/* Only offer EXTERNAL for connections tunneled over a login agent. */
if (!params->tunnel)
return NULL;
/* If a tunnel user was provided on the command line, use that. */
if (params->tunnel_user)
return params->tunnel_user;
#if APR_HAS_USER
/* Use the current uid's name, if we can. */
if (apr_uid_current(&uid, &gid, pool) == APR_SUCCESS
&& apr_uid_name_get(&user, uid, pool) == APR_SUCCESS)
return user;
#endif
/* Give up and don't offer EXTERNAL. */
return NULL;
}
svn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params,
apr_pool_t *pool)
{
svn_error_t *err, *io_err;
apr_uint64_t ver;
const char *mech, *mecharg, *uuid, *client_url;
apr_array_header_t *caplist;
server_baton_t b;
svn_boolean_t success;
svn_ra_svn_item_t *item, *first;
b.tunnel = params->tunnel;
b.tunnel_user = get_tunnel_user(params, pool);
b.read_only = params->read_only;
b.user = NULL;
b.cfg = NULL; /* Ugly; can drop when we remove v1 support. */
b.pwdb = NULL; /* Likewise */
/* Send greeting. When we drop support for version 1, we can
* start sending an empty mechlist. */
SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(nn(!", "success",
(apr_uint64_t) 1, (apr_uint64_t) 2));
SVN_ERR(send_mechs(conn, pool, &b, READ_ACCESS));
SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "!)(w))",
SVN_RA_SVN_CAP_EDIT_PIPELINE));
/* Read client response. Because the client response form changed
* between version 1 and version 2, we have to do some of this by
* hand until we punt support for version 1. */
SVN_ERR(svn_ra_svn_read_item(conn, pool, &item));
if (item->kind != SVN_RA_SVN_LIST || item->u.list->nelts < 2)
return SVN_NO_ERROR;
first = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
if (first->kind != SVN_RA_SVN_NUMBER)
return SVN_NO_ERROR;
b.protocol_version = (int) first->u.number;
if (b.protocol_version == 1)
{
/* Version 1: auth exchange is mixed with client version and
* capability list, and happens before the client URL is received. */
SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, pool, "nw(?c)l",
&ver, &mech, &mecharg, &caplist));
SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist));
SVN_ERR(auth(conn, pool, mech, mecharg, &b, READ_ACCESS, &success));
if (!success)
return svn_ra_svn_flush(conn, pool);
SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "c", &client_url));
client_url = svn_path_canonicalize(client_url, pool);
err = find_repos(client_url, params->root, &b, pool);
if (!err && current_access(&b) == NO_ACCESS)
err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
"Not authorized for access");
if (err)
{
io_err = svn_ra_svn_write_cmd_failure(conn, pool, err);
svn_error_clear(err);
SVN_ERR(io_err);
return svn_ra_svn_flush(conn, pool);
}
}
else if (b.protocol_version == 2)
{
/* Version 2: client sends version, capability list, and client
* URL, and then we do an auth request. */
SVN_ERR(svn_ra_svn_parse_tuple(item->u.list, pool, "nlc", &ver,
&caplist, &client_url));
client_url = svn_path_canonicalize(client_url, pool);
SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist));
err = find_repos(client_url, params->root, &b, pool);
if (!err)
{
SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS));
if (current_access(&b) == NO_ACCESS)
err = svn_error_create(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
"Not authorized for access");
}
if (err)
{
io_err = svn_ra_svn_write_cmd_failure(conn, pool, err);
svn_error_clear(err);
SVN_ERR(io_err);
return svn_ra_svn_flush(conn, pool);
}
}
else
return SVN_NO_ERROR;
SVN_ERR(svn_fs_get_uuid(b.fs, &uuid, pool));
SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "cc", uuid, b.repos_url));
return svn_ra_svn_handle_commands(conn, pool, main_commands, &b);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -