📄 update.c
字号:
/* Else this not a resource walk, so either send props or cache them
to send later, depending on whether this is a modern report
response or not. */
qname = apr_xml_quote_string (b->pool, name, 1);
/* apr_xml_quote_string doesn't realloc if there is nothing to
quote, so dup the name, but only if necessary. */
if (qname == name)
qname = apr_pstrdup (b->pool, name);
if (b->uc->send_all)
{
if (value)
{
const svn_string_t *qval;
if (svn_xml_is_xml_safe(value->data, value->len))
{
svn_stringbuf_t *tmp = NULL;
svn_xml_escape_cdata_string(&tmp, value, pool);
qval = svn_string_create (tmp->data, pool);
SVN_ERR( send_xml(b->uc, "<S:set-prop name=\"%s\">", qname) );
}
else
{
qval = svn_base64_encode_string(value, pool);
SVN_ERR( send_xml(b->uc,
"<S:set-prop name=\"%s\" encoding=\"base64\">"
DEBUG_CR, qname) );
}
SVN_ERR( send_xml(b->uc, "%s", qval->data) );
SVN_ERR( send_xml(b->uc, "</S:set-prop>" DEBUG_CR) );
}
else /* value is null, so this is a prop removal */
{
SVN_ERR( send_xml(b->uc, "<S:remove-prop name=\"%s\"/>" DEBUG_CR,
qname) );
}
}
else /* don't do inline response, just cache prop names for close_helper */
{
/* For now, store certain entry props, because we'll need to send
them later as standard DAV ("D:") props. ### this should go
away and we should just tunnel those props on through for the
client to deal with. */
#define NSLEN (sizeof(SVN_PROP_ENTRY_PREFIX) - 1)
if (! strncmp(name, SVN_PROP_ENTRY_PREFIX, NSLEN))
{
if (! strcmp(name, SVN_PROP_ENTRY_COMMITTED_REV))
{
b->committed_rev = value ?
apr_pstrdup(b->pool, value->data) : NULL;
}
else if (! strcmp(name, SVN_PROP_ENTRY_COMMITTED_DATE))
{
b->committed_date = value ?
apr_pstrdup(b->pool, value->data) : NULL;
}
else if (! strcmp(name, SVN_PROP_ENTRY_LAST_AUTHOR))
{
b->last_author = value ?
apr_pstrdup(b->pool, value->data) : NULL;
}
return SVN_NO_ERROR;
}
#undef NSLEN
if (value)
{
if (! b->changed_props)
b->changed_props = apr_array_make (b->pool, 1, sizeof (name));
(*((const char **)(apr_array_push (b->changed_props)))) = qname;
}
else
{
if (! b->removed_props)
b->removed_props = apr_array_make (b->pool, 1, sizeof (name));
(*((const char **)(apr_array_push (b->removed_props)))) = qname;
}
}
return SVN_NO_ERROR;
}
static svn_error_t * upd_close_directory(void *dir_baton,
apr_pool_t *pool)
{
return close_helper(TRUE /* is_dir */, dir_baton);
}
static svn_error_t * upd_add_file(const char *path,
void *parent_baton,
const char *copyfrom_path,
svn_revnum_t copyfrom_revision,
apr_pool_t *pool,
void **file_baton)
{
return add_helper(FALSE /* is_dir */,
path, parent_baton, copyfrom_path, copyfrom_revision, pool,
file_baton);
}
static svn_error_t * upd_open_file(const char *path,
void *parent_baton,
svn_revnum_t base_revision,
apr_pool_t *pool,
void **file_baton)
{
return open_helper(FALSE /* is_dir */,
path, parent_baton, base_revision, pool, file_baton);
}
/* We have our own window handler and baton as a simple wrapper around
the real handler (which converts vdelta windows to base64-encoded
svndiff data). The wrapper is responsible for sending the opening
and closing XML tags around the svndiff data. */
struct window_handler_baton
{
svn_boolean_t seen_first_window; /* False until first window seen. */
update_ctx_t *uc;
/* The _real_ window handler and baton. */
svn_txdelta_window_handler_t handler;
void *handler_baton;
};
/* This implements 'svn_txdelta_window_handler_t'. */
static svn_error_t * window_handler(svn_txdelta_window_t *window, void *baton)
{
struct window_handler_baton *wb = baton;
if (! wb->seen_first_window)
{
wb->seen_first_window = TRUE;
SVN_ERR( send_xml(wb->uc, "<S:txdelta>") );
}
SVN_ERR( wb->handler(window, wb->handler_baton) );
if (window == NULL)
SVN_ERR( send_xml(wb->uc, "</S:txdelta>") );
return SVN_NO_ERROR;
}
/* This implements 'svn_txdelta_window_handler_t'.
During a resource walk, the driver sends an empty window as a
boolean indicating that a change happened to this file, but we
don't want to send anything over the wire as a result. */
static svn_error_t * dummy_window_handler(svn_txdelta_window_t *window,
void *baton)
{
return SVN_NO_ERROR;
}
static svn_error_t * upd_apply_textdelta(void *file_baton,
const char *base_checksum,
apr_pool_t *pool,
svn_txdelta_window_handler_t *handler,
void **handler_baton)
{
item_baton_t *file = file_baton;
struct window_handler_baton *wb = apr_palloc(file->pool, sizeof(*wb));
svn_stream_t *base64_stream;
if (file->uc->resource_walk)
{
*handler = dummy_window_handler;
*handler_baton = NULL;
return SVN_NO_ERROR;
}
file->base_checksum = apr_pstrdup(file->pool, base_checksum);
file->text_changed = TRUE;
wb->seen_first_window = FALSE;
wb->uc = file->uc;
base64_stream = dav_svn_make_base64_output_stream(wb->uc->bb, wb->uc->output,
file->pool);
svn_txdelta_to_svndiff(base64_stream, file->pool,
&(wb->handler), &(wb->handler_baton));
*handler = window_handler;
*handler_baton = wb;
return SVN_NO_ERROR;
}
static svn_error_t * upd_close_file(void *file_baton,
const char *text_checksum,
apr_pool_t *pool)
{
item_baton_t *file = file_baton;
file->text_checksum = apr_pstrdup(file->pool, text_checksum);
/* If we are not in "send all" mode, and this file is not a new
addition or didn't otherwise have changed text, tell the client
to fetch it. */
if ((! file->uc->send_all) && (! file->added) && file->text_changed)
{
const char *elt;
elt = apr_psprintf(pool, "<S:fetch-file%s%s%s/>" DEBUG_CR,
file->base_checksum ? " base-checksum=\"" : "",
file->base_checksum ? file->base_checksum : "",
file->base_checksum ? "\"" : "");
SVN_ERR( send_xml(file->uc, elt) );
}
return close_helper(FALSE /* is_dir */, file);
}
static svn_error_t * upd_close_edit(void *edit_baton,
apr_pool_t *pool)
{
update_ctx_t *uc = edit_baton;
/* Our driver will unconditionally close the update report... So if
the report hasn't even been started yet, start it now. */
return maybe_start_update_report(uc);
}
dav_error * dav_svn__update_report(const dav_resource *resource,
const apr_xml_doc *doc,
ap_filter_t *output)
{
svn_delta_editor_t *editor;
apr_xml_elem *child;
void *rbaton = NULL;
update_ctx_t uc = { 0 };
svn_revnum_t revnum = SVN_INVALID_REVNUM;
int ns;
svn_error_t *serr;
dav_error *derr = NULL;
apr_status_t apr_err;
const char *src_path = NULL;
const char *dst_path = NULL;
const dav_svn_repos *repos = resource->info->repos;
const char *target = "";
svn_boolean_t recurse = TRUE;
svn_boolean_t resource_walk = FALSE;
svn_boolean_t ignore_ancestry = FALSE;
dav_svn_authz_read_baton arb;
apr_pool_t *subpool = svn_pool_create(resource->pool);
/* Construct the authz read check baton. */
arb.r = resource->info->r;
arb.repos = repos;
if (resource->info->restype != DAV_SVN_RESTYPE_VCC)
{
return dav_new_error_tag(resource->pool, HTTP_CONFLICT, 0,
"This report can only be run against a VCC.",
SVN_DAV_ERROR_NAMESPACE,
SVN_DAV_ERROR_TAG);
}
ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
if (ns == -1)
{
return dav_new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
"The request does not contain the 'svn:' "
"namespace, so it is not going to have an "
"svn:target-revision element. That element "
"is required.",
SVN_DAV_ERROR_NAMESPACE,
SVN_DAV_ERROR_TAG);
}
/* Look to see if client wants a report with props and textdeltas
inline, rather than placeholder tags that tell the client to do
further fetches. Modern clients prefer inline. */
{
apr_xml_attr *this_attr;
for (this_attr = doc->root->attr; this_attr; this_attr = this_attr->next)
{
if ((strcmp(this_attr->name, "send-all") == 0)
&& (strcmp(this_attr->value, "true") == 0))
{
uc.send_all = TRUE;
break;
}
}
}
for (child = doc->root->first_child; child != NULL; child = child->next)
{
/* Note that child->name might not match any of the cases below.
Thus, the check for non-empty cdata in each of these cases
cannot be moved to the top of the loop, because then it would
wrongly catch other elements that do allow empty cdata. */
if (child->ns == ns && strcmp(child->name, "target-revision") == 0)
{
if (! child->first_cdata.first)
return dav_new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
"The request's 'target-revision' element contains empty cdata; "
"there is a problem with the client.",
SVN_DAV_ERROR_NAMESPACE,
SVN_DAV_ERROR_TAG);
/* ### assume no white space, no child elems, etc */
revnum = SVN_STR_TO_REV(child->first_cdata.first->text);
}
if (child->ns == ns && strcmp(child->name, "src-path") == 0)
{
/* ### assume no white space, no child elems, etc */
dav_svn_uri_info this_info;
if (! child->first_cdata.first)
return dav_new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
"The request's 'src-path' element contains empty cdata; "
"there is a problem with the client.",
SVN_DAV_ERROR_NAMESPACE,
SVN_DAV_ERROR_TAG);
/* split up the 1st public URL. */
if ((derr = dav_svn__test_canonical
(child->first_cdata.first->text, resource->pool)))
return derr;
serr = dav_svn_simple_parse_uri(&this_info, resource,
child->first_cdata.first->text,
resource->pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Could not parse src-path URL.",
resource->pool);
}
src_path = this_info.repos_path;
}
if (child->ns == ns && strcmp(child->name, "dst-path") == 0)
{
/* ### assume no white space, no child elems, etc */
dav_svn_uri_info this_info;
if (! child->first_cdata.first)
return dav_new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
"The request's 'dst-path' element contains empty cdata; "
"there is a problem with the client. See "
"http://subversion.tigris.org/issues/show_bug.cgi?id=1055",
SVN_DAV_ERROR_NAMESPACE,
SVN_DAV_ERROR_TAG);
/* split up the 2nd public URL. */
if ((derr = dav_svn__test_canonical
(child->first_cdata.first->text, resource->pool)))
return derr;
serr = dav_svn_simple_parse_uri(&this_info, resource,
child->first_cdata.first->text,
resource->pool);
if (serr != NULL)
{
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Could not parse dst-path URL.",
resource->pool);
}
dst_path = this_info.repos_path;
}
if (child->ns == ns && strcmp(child->name, "update-target") == 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -