/* Send a property named NAME with value VAL in an element named ELEM_NAME. Quote NAME and base64-encode VAL if necessary. */ static svn_error_t * send_prop(struct file_rev_baton *frb, const char *elem_name, const char *name, const svn_string_t *val, apr_pool_t *pool) { name = apr_xml_quote_string(pool, name, 1); if (svn_xml_is_xml_safe(val->data, val->len)) { svn_stringbuf_t *tmp = NULL; svn_xml_escape_cdata_string(&tmp, val, pool); val = svn_string_create(tmp->data, pool); SVN_ERR(dav_svn__brigade_printf(frb->bb, frb->output, "<S:%s name=\"%s\">%s</S:%s>" DEBUG_CR, elem_name, name, val->data, elem_name)); } else { val = svn_base64_encode_string2(val, TRUE, pool); SVN_ERR(dav_svn__brigade_printf(frb->bb, frb->output, "<S:%s name=\"%s\" encoding=\"base64\">" "%s</S:%s>" DEBUG_CR, elem_name, name, val->data, elem_name)); } return SVN_NO_ERROR; }
static svn_error_t * add_file_or_directory(const char *file_or_directory, const char *path, edit_baton_t *eb, const char *copyfrom_path, svn_revnum_t copyfrom_rev, apr_pool_t *pool, void **added_baton) { const char *qname = apr_xml_quote_string(pool, path, 1); const char *qcopy = copyfrom_path ? apr_xml_quote_string(pool, copyfrom_path, 1) : NULL; SVN_ERR(maybe_close_textdelta(eb)); *added_baton = eb; if (! copyfrom_path) SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, "<S:add-%s name=\"%s\"/>" DEBUG_CR, file_or_directory, qname)); else SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, "<S:add-%s name=\"%s\" " "copyfrom-path=\"%s\" " "copyfrom-rev=\"%ld\"/>" DEBUG_CR, file_or_directory, qname, qcopy, copyfrom_rev)); return SVN_NO_ERROR; }
/* Utility for log_receiver opening a new XML element in LRB's brigade for LOG_ITEM and return the element's name in *ELEMENT. Use POOL for temporary allocations. Call this function for items that may have a copy-from */ static svn_error_t * start_path_with_copy_from(const char **element, struct log_receiver_baton *lrb, svn_log_changed_path2_t *log_item, apr_pool_t *pool) { switch (log_item->action) { case 'A': *element = "S:added-path"; break; case 'R': *element = "S:replaced-path"; break; default: /* Caller, you did wrong! */ SVN_ERR_MALFUNCTION(); } if (log_item->copyfrom_path && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<%s copyfrom-path=\"%s\" copyfrom-rev=\"%ld\"", *element, apr_xml_quote_string(pool, log_item->copyfrom_path, 1), /* escape quotes */ log_item->copyfrom_rev)); else SVN_ERR(dav_svn__brigade_printf(lrb->bb, lrb->output, "<%s", *element)); return SVN_NO_ERROR; }
static svn_error_t * apply_textdelta(void *file_baton, const char *base_checksum, apr_pool_t *pool, svn_txdelta_window_handler_t *handler, void **handler_baton) { edit_baton_t *eb = file_baton; SVN_ERR(dav_svn__brigade_puts(eb->bb, eb->output, "<S:apply-textdelta")); if (base_checksum) SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, " checksum=\"%s\">", base_checksum)); else SVN_ERR(dav_svn__brigade_puts(eb->bb, eb->output, ">")); svn_txdelta_to_svndiff3(handler, handler_baton, dav_svn__make_base64_output_stream(eb->bb, eb->output, pool), eb->svndiff_version, eb->compression_level, pool); eb->sending_textdelta = TRUE; return SVN_NO_ERROR; }
static svn_error_t * set_target_revision(void *edit_baton, svn_revnum_t target_revision, apr_pool_t *pool) { edit_baton_t *eb = edit_baton; SVN_ERR(maybe_start_report(eb)); return dav_svn__brigade_printf(eb->bb, eb->output, "<S:target-revision rev=\"%ld\"/>" DEBUG_CR, target_revision); }
static svn_error_t * change_file_or_dir_prop(const char *file_or_dir, edit_baton_t *eb, const char *name, const svn_string_t *value, apr_pool_t *pool) { const char *qname = apr_xml_quote_string(pool, name, 1); SVN_ERR(maybe_close_textdelta(eb)); if (value) { const svn_string_t *enc_value = svn_base64_encode_string2(value, TRUE, pool); /* Some versions of apr_brigade_vprintf() have a buffer overflow bug that can be triggered by just the wrong size of a large property value. The bug has been fixed (see http://svn.apache.org/viewvc?view=rev&revision=768417), but we need a workaround for the buggy APR versions, so we write our potentially large block of property data using a different underlying function. */ SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, "<S:change-%s-prop name=\"%s\">", file_or_dir, qname)); SVN_ERR(dav_svn__brigade_write(eb->bb, eb->output, enc_value->data, enc_value->len)); SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, "</S:change-%s-prop>" DEBUG_CR, file_or_dir)); } else { SVN_ERR(dav_svn__brigade_printf (eb->bb, eb->output, "<S:change-%s-prop name=\"%s\" del=\"true\"/>" DEBUG_CR, file_or_dir, qname)); } return SVN_NO_ERROR; }
static svn_error_t * open_root(void *edit_baton, svn_revnum_t base_revision, apr_pool_t *pool, void **root_baton) { edit_baton_t *eb = edit_baton; *root_baton = edit_baton; SVN_ERR(maybe_start_report(eb)); return dav_svn__brigade_printf(eb->bb, eb->output, "<S:open-root rev=\"%ld\"/>" DEBUG_CR, base_revision); }
static svn_error_t * delete_entry(const char *path, svn_revnum_t revision, void *parent_baton, apr_pool_t *pool) { edit_baton_t *eb = parent_baton; const char *qname = apr_xml_quote_string(pool, path, 1); SVN_ERR(maybe_close_textdelta(eb)); return dav_svn__brigade_printf(eb->bb, eb->output, "<S:delete-entry name=\"%s\" rev=\"%ld\"/>" DEBUG_CR, qname, revision); }
static svn_error_t * close_file(void *file_baton, const char *text_checksum, apr_pool_t *pool) { edit_baton_t *eb = file_baton; SVN_ERR(maybe_close_textdelta(eb)); SVN_ERR(dav_svn__brigade_puts(eb->bb, eb->output, "<S:close-file")); if (text_checksum) SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, " checksum=\"%s\"/>" DEBUG_CR, text_checksum)); else SVN_ERR(dav_svn__brigade_puts(eb->bb, eb->output, "/>" DEBUG_CR)); return SVN_NO_ERROR; }
static svn_error_t * open_file_or_directory(const char *file_or_directory, const char *path, edit_baton_t *eb, svn_revnum_t base_revision, apr_pool_t *pool, void **opened_baton) { const char *qname = apr_xml_quote_string(pool, path, 1); SVN_ERR(maybe_close_textdelta(eb)); *opened_baton = eb; return dav_svn__brigade_printf(eb->bb, eb->output, "<S:open-%s name=\"%s\" rev=\"%ld\"/>" DEBUG_CR, file_or_directory, qname, base_revision); }
dav_error * dav_svn__get_mergeinfo_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { svn_error_t *serr; dav_error *derr = NULL; apr_xml_elem *child; svn_mergeinfo_catalog_t catalog; svn_boolean_t include_descendants = FALSE; dav_svn__authz_read_baton arb; const dav_svn_repos *repos = resource->info->repos; int ns; apr_bucket_brigade *bb; apr_hash_index_t *hi; /* These get determined from the request document. */ svn_revnum_t rev = SVN_INVALID_REVNUM; /* By default look for explicit mergeinfo only. */ svn_mergeinfo_inheritance_t inherit = svn_mergeinfo_explicit; apr_array_header_t *paths = apr_array_make(resource->pool, 0, sizeof(const char *)); /* Sanity check. */ if (!resource->info->repos_path) return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not specify a repository path"); ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " "certain required elements.", SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); } for (child = doc->root->first_child; child != NULL; child = child->next) { /* if this element isn't one of ours, then skip it */ if (child->ns != ns) continue; if (strcmp(child->name, SVN_DAV__REVISION) == 0) rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, SVN_DAV__INHERIT) == 0) { inherit = svn_inheritance_from_word( dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, SVN_DAV__PATH) == 0) { const char *target; const char *rel_path = dav_xml_get_cdata(child, resource->pool, 0); if ((derr = dav_svn__test_canonical(rel_path, resource->pool))) return derr; /* Force REL_PATH to be a relative path, not an fspath. */ rel_path = svn_relpath_canonicalize(rel_path, resource->pool); /* Append the REL_PATH to the base FS path to get an absolute repository path. */ target = svn_fspath__join(resource->info->repos_path, rel_path, resource->pool); (*((const char **)(apr_array_push(paths)))) = target; } else if (strcmp(child->name, SVN_DAV__INCLUDE_DESCENDANTS) == 0) { const char *word = dav_xml_get_cdata(child, resource->pool, 1); if (strcmp(word, "yes") == 0) include_descendants = TRUE; /* Else the client isn't supposed to send anyway, so just leave it false. */ } /* else unknown element; skip it */ } /* Build authz read baton */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* Build mergeinfo brigade */ bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); serr = svn_repos_fs_get_mergeinfo(&catalog, repos->repos, paths, rev, inherit, include_descendants, dav_svn__authz_read_func(&arb), &arb, resource->pool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, resource->pool); goto cleanup; } serr = svn_mergeinfo__remove_prefix_from_catalog(&catalog, catalog, resource->info->repos_path, resource->pool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, resource->pool); goto cleanup; } /* Ideally, dav_svn__brigade_printf() would set a flag in bb (or rather, in r->sent_bodyct, see dav_method_report()), and ap_fflush() would not set that flag unless it actually sent something. But we are condemned to live in another universe, so we must keep track ourselves of whether we've sent anything or not. See the long comment after the 'cleanup' label for more details. */ serr = dav_svn__brigade_puts(bb, output, DAV_XML_HEADER DEBUG_CR "<S:" SVN_DAV__MERGEINFO_REPORT " " "xmlns:S=\"" SVN_XML_NAMESPACE "\" " "xmlns:D=\"DAV:\">" DEBUG_CR); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, resource->pool); goto cleanup; } for (hi = apr_hash_first(resource->pool, catalog); hi; hi = apr_hash_next(hi)) { const void *key; void *value; const char *path; svn_mergeinfo_t mergeinfo; svn_string_t *mergeinfo_string; apr_hash_this(hi, &key, NULL, &value); path = key; mergeinfo = value; serr = svn_mergeinfo_to_string(&mergeinfo_string, mergeinfo, resource->pool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } serr = dav_svn__brigade_printf (bb, output, "<S:" SVN_DAV__MERGEINFO_ITEM ">" DEBUG_CR "<S:" SVN_DAV__MERGEINFO_PATH ">%s</S:" SVN_DAV__MERGEINFO_PATH ">" DEBUG_CR "<S:" SVN_DAV__MERGEINFO_INFO ">%s</S:" SVN_DAV__MERGEINFO_INFO ">" DEBUG_CR "</S:" SVN_DAV__MERGEINFO_ITEM ">", apr_xml_quote_string(resource->pool, path, 0), apr_xml_quote_string(resource->pool, mergeinfo_string->data, 0)); if (serr) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } } if ((serr = dav_svn__brigade_puts(bb, output, "</S:" SVN_DAV__MERGEINFO_REPORT ">" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } cleanup: /* We've detected a 'high level' svn action to log. */ dav_svn__operational_log(resource->info, svn_log__get_mergeinfo(paths, inherit, include_descendants, resource->pool)); return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
dav_error * dav_svn__get_inherited_props_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { svn_error_t *serr; dav_error *derr = NULL; apr_xml_elem *child; apr_array_header_t *inherited_props; dav_svn__authz_read_baton arb; int ns; apr_bucket_brigade *bb; const char *path = "/"; svn_fs_root_t *root; int i; svn_revnum_t rev = SVN_INVALID_REVNUM; apr_pool_t *iterpool; /* Sanity check. */ if (!resource->info->repos_path) return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not specify a repository path"); ns = dav_svn__find_ns(doc->namespaces, SVN_XML_NAMESPACE); if (ns == -1) { return dav_svn__new_error_svn(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have " "certain required elements"); } iterpool = svn_pool_create(resource->pool); for (child = doc->root->first_child; child != NULL; child = child->next) { /* if this element isn't one of ours, then skip it */ if (child->ns != ns) continue; if (strcmp(child->name, SVN_DAV__REVISION) == 0) { rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, iterpool, 1)); } else if (strcmp(child->name, SVN_DAV__PATH) == 0) { path = dav_xml_get_cdata(child, resource->pool, 0); if ((derr = dav_svn__test_canonical(path, iterpool))) return derr; path = svn_fspath__join(resource->info->repos_path, path, resource->pool); } /* else unknown element; skip it */ } /* Build authz read baton */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* Build inherited property brigade */ bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); serr = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool); if (serr != NULL) return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "couldn't retrieve revision root", resource->pool); serr = svn_repos_fs_get_inherited_props(&inherited_props, root, path, NULL, dav_svn__authz_read_func(&arb), &arb, resource->pool, iterpool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } serr = dav_svn__brigade_puts(bb, output, DAV_XML_HEADER DEBUG_CR "<S:" SVN_DAV__INHERITED_PROPS_REPORT " " "xmlns:S=\"" SVN_XML_NAMESPACE "\" " "xmlns:D=\"DAV:\">" DEBUG_CR); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, NULL, resource->pool); goto cleanup; } for (i = 0; i < inherited_props->nelts; i++) { svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); svn_pool_clear(iterpool); serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_ITEM ">" DEBUG_CR "<S:" SVN_DAV__IPROP_PATH ">%s</S:" SVN_DAV__IPROP_PATH ">" DEBUG_CR, apr_xml_quote_string(resource->pool, elt->path_or_url, 0)); if (!serr) { apr_hash_index_t *hi; for (hi = apr_hash_first(resource->pool, elt->prop_hash); hi; hi = apr_hash_next(hi)) { const char *propname = apr_hash_this_key(hi); svn_string_t *propval = apr_hash_this_val(hi); const char *xml_safe; serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_PROPNAME ">%s</S:" SVN_DAV__IPROP_PROPNAME ">" DEBUG_CR, apr_xml_quote_string(iterpool, propname, 0)); if (!serr) { if (svn_xml_is_xml_safe(propval->data, propval->len)) { svn_stringbuf_t *tmp = NULL; svn_xml_escape_cdata_string(&tmp, propval, iterpool); xml_safe = tmp->data; serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_PROPVAL ">%s</S:" SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe); } else { xml_safe = svn_base64_encode_string2( propval, TRUE, iterpool)->data; serr = dav_svn__brigade_printf( bb, output, "<S:" SVN_DAV__IPROP_PROPVAL " encoding=\"base64\"" ">%s</S:" SVN_DAV__IPROP_PROPVAL ">" DEBUG_CR, xml_safe); } } if (serr) break; } if (!serr) serr = dav_svn__brigade_printf(bb, output, "</S:" SVN_DAV__IPROP_ITEM ">" DEBUG_CR); } if (serr) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } } if ((serr = dav_svn__brigade_puts(bb, output, "</S:" SVN_DAV__INHERITED_PROPS_REPORT ">" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } cleanup: /* Log this 'high level' svn action. */ dav_svn__operational_log(resource->info, svn_log__get_inherited_props(path, rev, resource->pool)); svn_pool_destroy(iterpool); return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
/* This implements the svn_repos_file_rev_handler2_t interface. */ static svn_error_t * file_rev_handler(void *baton, const char *path, svn_revnum_t revnum, apr_hash_t *rev_props, svn_boolean_t merged_revision, svn_txdelta_window_handler_t *window_handler, void **window_baton, apr_array_header_t *props, apr_pool_t *pool) { struct file_rev_baton *frb = baton; apr_pool_t *subpool = svn_pool_create(pool); apr_hash_index_t *hi; int i; SVN_ERR(maybe_send_header(frb)); SVN_ERR(dav_svn__brigade_printf(frb->bb, frb->output, "<S:file-rev path=\"%s\" rev=\"%ld\">" DEBUG_CR, apr_xml_quote_string(pool, path, 1), revnum)); /* Send rev props. */ for (hi = apr_hash_first(pool, rev_props); hi; hi = apr_hash_next(hi)) { const void *key; void *val; const char *pname; const svn_string_t *pval; svn_pool_clear(subpool); apr_hash_this(hi, &key, NULL, &val); pname = key; pval = val; SVN_ERR(send_prop(frb, "rev-prop", pname, pval, subpool)); } /* Send file prop changes. */ for (i = 0; i < props->nelts; ++i) { const svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t); svn_pool_clear(subpool); if (prop->value) SVN_ERR(send_prop(frb, "set-prop", prop->name, prop->value, subpool)); else { /* Property was removed. */ SVN_ERR(dav_svn__brigade_printf(frb->bb, frb->output, "<S:remove-prop name=\"%s\"/>" DEBUG_CR, apr_xml_quote_string(subpool, prop->name, 1))); } } /* Send whether this was the result of a merge or not. */ if (merged_revision) SVN_ERR(dav_svn__brigade_puts(frb->bb, frb->output, "<S:merged-revision/>")); /* Maybe send text delta. */ if (window_handler) { svn_stream_t *base64_stream; base64_stream = dav_svn__make_base64_output_stream(frb->bb, frb->output, pool); svn_txdelta_to_svndiff3(&frb->window_handler, &frb->window_baton, base64_stream, frb->svndiff_version, frb->compression_level, pool); *window_handler = delta_window_handler; *window_baton = frb; /* Start the txdelta element wich will be terminated by the window handler together with the file-rev element. */ SVN_ERR(dav_svn__brigade_puts(frb->bb, frb->output, "<S:txdelta>")); } else /* No txdelta, so terminate the element here. */ SVN_ERR(dav_svn__brigade_puts(frb->bb, frb->output, "</S:file-rev>" DEBUG_CR)); svn_pool_destroy(subpool); return SVN_NO_ERROR; }
/* This implements `svn_log_entry_receiver_t'. BATON is a `struct log_receiver_baton *'. */ static svn_error_t * log_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool) { struct log_receiver_baton *lrb = baton; apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(maybe_send_header(lrb)); if (log_entry->revision == SVN_INVALID_REVNUM) { /* If the stack depth is zero, we've seen the last revision, so don't send it, just return. The footer will be sent later. */ if (lrb->stack_depth == 0) return SVN_NO_ERROR; else lrb->stack_depth--; } SVN_ERR(dav_svn__brigade_printf(lrb->bb, lrb->output, "<S:log-item>" DEBUG_CR "<D:version-name>%ld" "</D:version-name>" DEBUG_CR, log_entry->revision)); if (log_entry->revprops) { apr_hash_index_t *hi; for (hi = apr_hash_first(pool, log_entry->revprops); hi != NULL; hi = apr_hash_next(hi)) { char *name; void *val; const svn_string_t *value; const char *encoding_str = ""; svn_pool_clear(iterpool); apr_hash_this(hi, (void *)&name, NULL, &val); value = val; /* If the client is okay with us encoding binary (or really, any non-XML-safe) property values, do so as necessary. */ if (lrb->encode_binary_props) { if (! svn_xml_is_xml_safe(value->data, value->len)) { value = svn_base64_encode_string2(value, TRUE, iterpool); encoding_str = " encoding=\"base64\""; } } if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<D:creator-displayname%s>%s</D:creator-displayname>" DEBUG_CR, encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) /* ### this should be DAV:creation-date, but we need to format ### that date a bit differently */ SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:date%s>%s</S:date>" DEBUG_CR, encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<D:comment%s>%s</D:comment>" DEBUG_CR, encoding_str, apr_xml_quote_string(pool, svn_xml_fuzzy_escape(value->data, iterpool), 0))); else SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:revprop name=\"%s\"%s>%s</S:revprop>" DEBUG_CR, apr_xml_quote_string(iterpool, name, 0), encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); } } if (log_entry->has_children) { SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:has-children/>")); lrb->stack_depth++; } if (log_entry->subtractive_merge) SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:subtractive-merge/>")); if (log_entry->changed_paths2) { apr_hash_index_t *hi; char *path; for (hi = apr_hash_first(pool, log_entry->changed_paths2); hi != NULL; hi = apr_hash_next(hi)) { void *val; svn_log_changed_path2_t *log_item; const char *close_element = NULL; svn_pool_clear(iterpool); apr_hash_this(hi, (void *) &path, NULL, &val); log_item = val; /* ### todo: is there a D: namespace equivalent for `changed-path'? Should use it if so. */ switch (log_item->action) { case 'A': if (log_item->copyfrom_path && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:added-path copyfrom-path=\"%s\"" " copyfrom-rev=\"%ld\"", apr_xml_quote_string(iterpool, log_item->copyfrom_path, 1), /* escape quotes */ log_item->copyfrom_rev)); else SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:added-path")); close_element = "S:added-path"; break; case 'R': if (log_item->copyfrom_path && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:replaced-path copyfrom-path=\"%s\"" " copyfrom-rev=\"%ld\"", apr_xml_quote_string(iterpool, log_item->copyfrom_path, 1), /* escape quotes */ log_item->copyfrom_rev)); else SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:replaced-path")); close_element = "S:replaced-path"; break; case 'D': SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:deleted-path")); close_element = "S:deleted-path"; break; case 'M': SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:modified-path")); close_element = "S:modified-path"; break; default: break; } /* If we need to close the element, then send the attributes that apply to all changed items and then close the element. */ if (close_element) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, " node-kind=\"%s\"" " text-mods=\"%s\"" " prop-mods=\"%s\">%s</%s>" DEBUG_CR, svn_node_kind_to_word(log_item->node_kind), svn_tristate__to_word(log_item->text_modified), svn_tristate__to_word(log_item->props_modified), apr_xml_quote_string(iterpool, path, 0), close_element)); } } svn_pool_destroy(iterpool); SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "</S:log-item>" DEBUG_CR)); return SVN_NO_ERROR; }
/* Respond to a S:dated-rev-report request. The request contains a * DAV:creationdate element giving the requested date; the response * contains a DAV:version-name element giving the most recent revision * as of that date. */ dav_error * dav_svn__dated_rev_report(const dav_resource *resource, const apr_xml_doc *doc, dav_svn__output *output) { apr_xml_elem *child; int ns; apr_time_t tm = (apr_time_t) -1; svn_revnum_t rev; apr_bucket_brigade *bb; svn_error_t *err; dav_error *derr = NULL; /* Find the DAV:creationdate element and get the requested time from it. */ ns = dav_svn__find_ns(doc->namespaces, "DAV:"); if (ns != -1) { for (child = doc->root->first_child; child != NULL; child = child->next) { if (child->ns != ns || strcmp(child->name, SVN_DAV__CREATIONDATE) != 0) continue; /* If this fails, we'll notice below, so ignore any error for now. */ svn_error_clear (svn_time_from_cstring(&tm, dav_xml_get_cdata(child, resource->pool, 1), resource->pool)); } } if (tm == (apr_time_t) -1) { return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, "The request does not contain a valid " "'DAV:" SVN_DAV__CREATIONDATE "' element."); } /* Do the actual work of finding the revision by date. */ if ((err = svn_repos_dated_revision(&rev, resource->info->repos->repos, tm, resource->pool)) != SVN_NO_ERROR) { svn_error_clear(err); return dav_svn__new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Could not access revision times."); } bb = apr_brigade_create(resource->pool, dav_svn__output_get_bucket_alloc(output)); err = dav_svn__brigade_printf(bb, output, DAV_XML_HEADER DEBUG_CR "<S:dated-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" " "xmlns:D=\"DAV:\">" DEBUG_CR "<D:" SVN_DAV__VERSION_NAME ">%ld</D:" SVN_DAV__VERSION_NAME ">""</S:dated-rev-report>", rev); if (err) derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
/* This implements `svn_log_entry_receiver_t'. BATON is a `struct log_receiver_baton *'. */ static svn_error_t * log_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool) { struct log_receiver_baton *lrb = baton; apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(maybe_send_header(lrb)); if (log_entry->revision == SVN_INVALID_REVNUM) { /* If the stack depth is zero, we've seen the last revision, so don't send it, just return. The footer will be sent later. */ if (lrb->stack_depth == 0) return SVN_NO_ERROR; else lrb->stack_depth--; } SVN_ERR(dav_svn__brigade_printf(lrb->bb, lrb->output, "<S:log-item>" DEBUG_CR "<D:version-name>%ld" "</D:version-name>" DEBUG_CR, log_entry->revision)); if (log_entry->revprops) { apr_hash_index_t *hi; for (hi = apr_hash_first(pool, log_entry->revprops); hi != NULL; hi = apr_hash_next(hi)) { char *name; void *val; const svn_string_t *value; const char *encoding_str = ""; svn_pool_clear(iterpool); apr_hash_this(hi, (void *)&name, NULL, &val); value = val; /* If the client is okay with us encoding binary (or really, any non-XML-safe) property values, do so as necessary. */ if (lrb->encode_binary_props) { if (! svn_xml_is_xml_safe(value->data, value->len)) { value = svn_base64_encode_string2(value, TRUE, iterpool); encoding_str = " encoding=\"base64\""; } } if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<D:creator-displayname%s>%s</D:creator-displayname>" DEBUG_CR, encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) /* ### this should be DAV:creation-date, but we need to format ### that date a bit differently */ SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:date%s>%s</S:date>" DEBUG_CR, encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<D:comment%s>%s</D:comment>" DEBUG_CR, encoding_str, apr_xml_quote_string(pool, svn_xml_fuzzy_escape(value->data, iterpool), 0))); else SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:revprop name=\"%s\"%s>%s</S:revprop>" DEBUG_CR, apr_xml_quote_string(iterpool, name, 0), encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); } } if (log_entry->has_children) { SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:has-children/>")); lrb->stack_depth++; } if (log_entry->subtractive_merge) SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:subtractive-merge/>")); if (log_entry->changed_paths2) { apr_hash_index_t *hi; char *path; for (hi = apr_hash_first(pool, log_entry->changed_paths2); hi != NULL; hi = apr_hash_next(hi)) { void *val; svn_log_changed_path2_t *log_item; const char *close_element = NULL; svn_pool_clear(iterpool); apr_hash_this(hi, (void *) &path, NULL, &val); log_item = val; /* ### todo: is there a D: namespace equivalent for `changed-path'? Should use it if so. */ switch (log_item->action) { case 'A': case 'R': SVN_ERR(start_path_with_copy_from(&close_element, lrb, log_item, iterpool)); break; case 'D': SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:deleted-path")); close_element = "S:deleted-path"; break; case 'M': SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:modified-path")); close_element = "S:modified-path"; break; default: break; } /* If we need to close the element, then send the attributes that apply to all changed items and then close the element. */ if (close_element) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, " node-kind=\"%s\"" " text-mods=\"%s\"" " prop-mods=\"%s\">%s</%s>" DEBUG_CR, svn_node_kind_to_word(log_item->node_kind), svn_tristate__to_word(log_item->text_modified), svn_tristate__to_word(log_item->props_modified), apr_xml_quote_string(iterpool, path, 0), close_element)); } } svn_pool_destroy(iterpool); SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "</S:log-item>" DEBUG_CR)); /* In general APR will flush the brigade every 8000 bytes through the filter stack, but log items may not be generated that fast, especially in combination with authz and busy servers. We now explictly flush after log-item 4, 16, 64 and 256 to produce a few results fast. This introduces 4 full flushes of our brigade and the installed output filters at growing intervals and then falls back to the standard buffering of 8000 bytes + whatever buffers are added in output filters. */ lrb->result_count++; if (lrb->result_count == lrb->next_forced_flush) { apr_status_t apr_err; /* This flush is similar to that in dav_svn__final_flush_or_error(). Compared to using ap_filter_flush(), which we use in other place this adds a flush frame before flushing the brigade, to make output filters perform a flush as well */ /* No brigade empty check. We want output filters to flush anyway */ apr_err = ap_fflush(lrb->output, lrb->bb); if (apr_err) return svn_error_create(apr_err, NULL, NULL); /* Check for an aborted connection, just like our brigade write helper functions, since the brigade functions don't appear to be return useful errors when the connection is dropped. */ if (lrb->output->c->aborted) return svn_error_create(SVN_ERR_APMOD_CONNECTION_ABORTED, NULL, NULL); if (lrb->result_count < 256) lrb->next_forced_flush = lrb->next_forced_flush * 4; } return SVN_NO_ERROR; }