#include "svn_pools.h"
#include "svn_error.h"
#include "svn_private_config.h"
#include "../libsvn_ra/ra_loader.h"
#include "ra_neon.h"
static const svn_ra_neon__xml_elm_t options_elements[] =
{
{ "DAV:", "activity-collection-set", ELEM_activity_coll_set, 0 },
{ "DAV:", "href", ELEM_href, SVN_RA_NEON__XML_CDATA },
{ "DAV:", "options-response", ELEM_options_response, 0 },
{ NULL }
};
typedef struct {
svn_stringbuf_t *want_cdata;
svn_stringbuf_t *cdata;
apr_pool_t *pool;
svn_string_t *activity_coll;
} options_ctx_t;
static int
validate_element(svn_ra_neon__xml_elmid parent, svn_ra_neon__xml_elmid child)
{
switch (parent)
{
case ELEM_root:
if (child == ELEM_options_response)
return child;
else
return SVN_RA_NEON__XML_INVALID;
case ELEM_options_response:
if (child == ELEM_activity_coll_set)
return child;
else
return SVN_RA_NEON__XML_DECLINE;
case ELEM_activity_coll_set:
if (child == ELEM_href)
return child;
else
return SVN_RA_NEON__XML_DECLINE;
default:
return SVN_RA_NEON__XML_DECLINE;
}
}
static svn_error_t *
start_element(int *elem, void *baton, int parent,
const char *nspace, const char *name, const char **atts)
{
options_ctx_t *oc = baton;
const svn_ra_neon__xml_elm_t *elm
= svn_ra_neon__lookup_xml_elem(options_elements, nspace, name);
*elem = elm ? validate_element(parent, elm->id) : SVN_RA_NEON__XML_DECLINE;
if (*elem < 1)
return SVN_NO_ERROR;
if (elm->id == ELEM_href)
oc->want_cdata = oc->cdata;
else
oc->want_cdata = NULL;
return SVN_NO_ERROR;
}
static svn_error_t *
end_element(void *baton, int state,
const char *nspace, const char *name)
{
options_ctx_t *oc = baton;
if (state == ELEM_href)
oc->activity_coll = svn_string_create_from_buf(oc->cdata, oc->pool);
return SVN_NO_ERROR;
}
static svn_boolean_t
match_list(const char *str, const apr_array_header_t *list)
{
int i;
for (i = 0; i < list->nelts; i++)
{
const char *this_str = APR_ARRAY_IDX(list, i, char *);
if (strcmp(this_str, str) == 0)
return TRUE;
}
return FALSE;
}
static const char *capability_yes = "yes";
static const char *capability_no = "no";
static const char *capability_server_yes = "server-yes";
static void
parse_capabilities(ne_request *req,
svn_ra_neon__session_t *ras,
apr_pool_t *pool)
{
const char *header_value;
apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_DEPTH,
APR_HASH_KEY_STRING, capability_no);
apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
APR_HASH_KEY_STRING, capability_no);
apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
APR_HASH_KEY_STRING, capability_no);
header_value = ne_get_response_header(req, "dav");
if (header_value)
{
apr_array_header_t *vals =
svn_cstring_split(header_value, ",", TRUE, pool);
if (match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals))
apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_DEPTH,
APR_HASH_KEY_STRING, capability_yes);
if (match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals))
apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_MERGEINFO,
APR_HASH_KEY_STRING, capability_server_yes);
if (match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals))
apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS,
APR_HASH_KEY_STRING, capability_yes);
if (match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals))
apr_hash_set(ras->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY,
APR_HASH_KEY_STRING, capability_yes);
}
}
svn_error_t *
svn_ra_neon__exchange_capabilities(svn_ra_neon__session_t *ras,
apr_pool_t *pool)
{
svn_ra_neon__request_t* req;
svn_error_t *err = SVN_NO_ERROR;
ne_xml_parser *parser = NULL;
options_ctx_t oc = { 0 };
const char *msg;
int status_code;
oc.pool = pool;
oc.cdata = svn_stringbuf_create("", pool);
req = svn_ra_neon__request_create(ras, "OPTIONS", ras->url->data, pool);
ne_add_request_header(req->ne_req, "Content-Type", "text/xml");
parser = svn_ra_neon__xml_parser_create(req, ne_accept_2xx, start_element,
svn_ra_neon__xml_collect_cdata,
end_element, &oc);
if ((err = svn_ra_neon__request_dispatch(&status_code, req, NULL,
"<?xml version=\"1.0\" "
"encoding=\"utf-8\"?>"
"<D:options xmlns:D=\"DAV:\">"
"<D:activity-collection-set/>"
"</D:options>",
200, 0, pool)))
goto cleanup;
msg = ne_xml_get_error(parser);
if (msg && *msg)
{
err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
_("The %s request returned invalid XML "
"in the response: %s (%s)"),
"OPTIONS", msg, ras->url->data);
goto cleanup;
}
if (oc.activity_coll == NULL)
{
err = svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
_("The OPTIONS response did not include the "
"requested activity-collection-set; this often "
"means that the URL is not WebDAV-enabled"));
goto cleanup;
}
ras->act_coll = apr_pstrdup(ras->pool, oc.activity_coll->data);
parse_capabilities(req->ne_req, ras, pool);
cleanup:
svn_ra_neon__request_destroy(req);
return err;
}
svn_error_t *
svn_ra_neon__get_activity_collection(const svn_string_t **activity_coll,
svn_ra_neon__session_t *ras,
apr_pool_t *pool)
{
if (! ras->act_coll)
SVN_ERR(svn_ra_neon__exchange_capabilities(ras, pool));
*activity_coll = svn_string_create(ras->act_coll, pool);
return SVN_NO_ERROR;
}
svn_error_t *
svn_ra_neon__has_capability(svn_ra_session_t *session,
svn_boolean_t *has,
const char *capability,
apr_pool_t *pool)
{
svn_ra_neon__session_t *ras = session->priv;
const char *cap_result;
if (strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0)
{
*has = TRUE;
return SVN_NO_ERROR;
}
cap_result = apr_hash_get(ras->capabilities,
capability,
APR_HASH_KEY_STRING);
if (cap_result == NULL)
SVN_ERR(svn_ra_neon__exchange_capabilities(ras, pool));
cap_result = apr_hash_get(ras->capabilities,
capability, APR_HASH_KEY_STRING);
if (cap_result == capability_server_yes)
{
if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)
{
svn_mergeinfo_catalog_t ignored;
svn_error_t *err;
apr_array_header_t *paths = apr_array_make(pool, 1,
sizeof(char *));
APR_ARRAY_PUSH(paths, const char *) = "";
err = svn_ra_neon__get_mergeinfo(session, &ignored, paths, 0,
FALSE, FALSE, pool);
if (err)
{
if (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
{
svn_error_clear(err);
cap_result = capability_no;
}
else if (err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
cap_result = capability_yes;
}
else
return err;
}
else
cap_result = capability_yes;
apr_hash_set(ras->capabilities,
SVN_RA_CAPABILITY_MERGEINFO, APR_HASH_KEY_STRING,
cap_result);
}
else
{
return svn_error_createf
(SVN_ERR_UNKNOWN_CAPABILITY, NULL,
_("Don't know how to handle '%s' for capability '%s'"),
capability_server_yes, capability);
}
}
if (cap_result == capability_yes)
{
*has = TRUE;
}
else if (cap_result == capability_no)
{
*has = FALSE;
}
else if (cap_result == NULL)
{
return svn_error_createf
(SVN_ERR_UNKNOWN_CAPABILITY, NULL,
_("Don't know anything about capability '%s'"), capability);
}
else
{
return svn_error_createf
(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
_("Attempt to fetch capability '%s' resulted in '%s'"),
capability, cap_result);
}
return SVN_NO_ERROR;
}