/* This implements the `svn_ra_neon__startelem_cb_t' prototype. */ static svn_error_t * gls_start_element(int *elem, void *userdata, int parent_state, const char *ns, const char *ln, const char **atts) { get_location_segments_baton_t *baton = userdata; const svn_ra_neon__xml_elm_t *elm; /* Just skip unknown elements. */ if (! ((elm = svn_ra_neon__lookup_xml_elem(gls_report_elements, ns, ln)))) { *elem = NE_XML_DECLINE; return SVN_NO_ERROR; } if (parent_state == ELEM_get_location_segments_report && elm->id == ELEM_location_segment) { const char *rev_str; svn_revnum_t range_start = SVN_INVALID_REVNUM; svn_revnum_t range_end = SVN_INVALID_REVNUM; const char *path = NULL; path = svn_xml_get_attr_value("path", atts); rev_str = svn_xml_get_attr_value("range-start", atts); if (rev_str) range_start = SVN_STR_TO_REV(rev_str); rev_str = svn_xml_get_attr_value("range-end", atts); if (rev_str) range_end = SVN_STR_TO_REV(rev_str); if (SVN_IS_VALID_REVNUM(range_start) && SVN_IS_VALID_REVNUM(range_end)) { svn_location_segment_t *segment = apr_pcalloc(baton->subpool, sizeof(*segment)); segment->path = path; segment->range_start = range_start; segment->range_end = range_end; SVN_ERR(baton->receiver(segment, baton->receiver_baton, baton->subpool)); svn_pool_clear(baton->subpool); } else { return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Expected valid revision range")); } } *elem = elm->id; return SVN_NO_ERROR; }
static svn_error_t * start_gls(svn_ra_serf__xml_parser_t *parser, void *userData, svn_ra_serf__dav_props_t name, const char **attrs) { gls_context_t *gls_ctx = userData; if ((! gls_ctx->inside_report) && strcmp(name.name, "get-location-segments-report") == 0) { gls_ctx->inside_report = TRUE; } else if (gls_ctx->inside_report && strcmp(name.name, "location-segment") == 0) { const char *rev_str; svn_revnum_t range_start = SVN_INVALID_REVNUM; svn_revnum_t range_end = SVN_INVALID_REVNUM; const char *path = NULL; path = svn_xml_get_attr_value("path", attrs); rev_str = svn_xml_get_attr_value("range-start", attrs); if (rev_str) range_start = SVN_STR_TO_REV(rev_str); rev_str = svn_xml_get_attr_value("range-end", attrs); if (rev_str) range_end = SVN_STR_TO_REV(rev_str); if (SVN_IS_VALID_REVNUM(range_start) && SVN_IS_VALID_REVNUM(range_end)) { svn_location_segment_t *segment = apr_pcalloc(gls_ctx->subpool, sizeof(*segment)); segment->path = path; segment->range_start = range_start; segment->range_end = range_end; SVN_ERR(gls_ctx->receiver(segment, gls_ctx->receiver_baton, gls_ctx->subpool)); svn_pool_clear(gls_ctx->subpool); } else { return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Expected valid revision range")); } } return SVN_NO_ERROR; }
/* Make a revision baton, parsing the relevant HEADERS. * * Set RB->skipped iff the revision number is outside the range given in PB. */ static struct revision_baton * make_revision_baton(apr_hash_t *headers, struct parse_baton *pb, apr_pool_t *pool) { struct revision_baton *rb = apr_pcalloc(pool, sizeof(*rb)); const char *val; rb->pb = pb; rb->pool = pool; rb->rev = SVN_INVALID_REVNUM; rb->revprops = apr_array_make(rb->pool, 8, sizeof(svn_prop_t)); if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER))) { rb->rev = SVN_STR_TO_REV(val); /* If we're filtering revisions, is this one we'll skip? */ rb->skipped = (SVN_IS_VALID_REVNUM(pb->start_rev) && ((rb->rev < pb->start_rev) || (rb->rev > pb->end_rev))); } return rb; }
/* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * getloc_closed(svn_ra_serf__xml_estate_t *xes, void *baton, int leaving_state, const svn_string_t *cdata, apr_hash_t *attrs, apr_pool_t *scratch_pool) { loc_context_t *loc_ctx = baton; const char *revstr; const char *path; SVN_ERR_ASSERT(leaving_state == LOCATION); revstr = svn_hash_gets(attrs, "rev"); path = svn_hash_gets(attrs, "path"); if (revstr != NULL && path != NULL) { svn_revnum_t rev = SVN_STR_TO_REV(revstr); apr_hash_set(loc_ctx->paths, apr_pmemdup(loc_ctx->pool, &rev, sizeof(rev)), sizeof(rev), apr_pstrdup(loc_ctx->pool, path)); } return SVN_NO_ERROR; }
/* Conforms to svn_ra_serf__xml_opened_t */ static svn_error_t * blame_opened(svn_ra_serf__xml_estate_t *xes, void *baton, int entered_state, const svn_ra_serf__dav_props_t *tag, apr_pool_t *scratch_pool) { blame_context_t *blame_ctx = baton; if (entered_state == FILE_REV) { apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes); /* Child elements will store properties in these structures. */ blame_ctx->rev_props = apr_hash_make(state_pool); blame_ctx->prop_diffs = apr_array_make(state_pool, 5, sizeof(svn_prop_t)); blame_ctx->state_pool = state_pool; /* Clear this, so we can detect the absence of a TXDELTA. */ blame_ctx->stream = NULL; } else if (entered_state == TXDELTA) { apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes); apr_hash_t *gathered = svn_ra_serf__xml_gather_since(xes, FILE_REV); const char *path; const char *rev; const char *merged_revision; svn_txdelta_window_handler_t txdelta; void *txdelta_baton; path = svn_hash_gets(gathered, "path"); rev = svn_hash_gets(gathered, "rev"); merged_revision = svn_hash_gets(gathered, "merged-revision"); SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton, path, SVN_STR_TO_REV(rev), blame_ctx->rev_props, merged_revision != NULL, &txdelta, &txdelta_baton, blame_ctx->prop_diffs, state_pool)); blame_ctx->stream = svn_base64_decode(svn_txdelta_parse_svndiff( txdelta, txdelta_baton, TRUE /* error_on_early_close */, state_pool), state_pool); } return SVN_NO_ERROR; }
/* Parse the conflict info fields from SKEL into *VERSION_INFO. */ static svn_error_t * read_node_version_info(const svn_wc_conflict_version_t **version_info, const svn_skel_t *skel, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { int n; const char *repos_root; const char *repos_relpath; svn_revnum_t peg_rev; svn_node_kind_t kind; if (!is_valid_version_info_skel(skel)) return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, _("Invalid version info in tree conflict " "description")); repos_root = apr_pstrmemdup(scratch_pool, skel->children->next->data, skel->children->next->len); if (*repos_root == '\0') { *version_info = NULL; return SVN_NO_ERROR; } /* Apply the Subversion 1.7+ url canonicalization rules to a pre 1.7 url */ repos_root = svn_uri_canonicalize(repos_root, result_pool); peg_rev = SVN_STR_TO_REV(apr_pstrmemdup(scratch_pool, skel->children->next->next->data, skel->children->next->next->len)); repos_relpath = apr_pstrmemdup(result_pool, skel->children->next->next->next->data, skel->children->next->next->next->len); SVN_ERR(read_enum_field(&n, node_kind_map, skel->children->next->next->next->next)); kind = (svn_node_kind_t)n; *version_info = svn_wc_conflict_version_create2(repos_root, NULL, repos_relpath, peg_rev, kind, result_pool); return SVN_NO_ERROR; }
/* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * getdrev_closed(svn_ra_serf__xml_estate_t *xes, void *baton, int leaving_state, const svn_string_t *cdata, apr_hash_t *attrs, apr_pool_t *scratch_pool) { drev_context_t *drev_ctx = baton; SVN_ERR_ASSERT(leaving_state == VERSION_NAME); SVN_ERR_ASSERT(cdata != NULL); *drev_ctx->revision_deleted = SVN_STR_TO_REV(cdata->data); return SVN_NO_ERROR; }
/* Read a revision number from [*BUF, END) stopping at the terminator. Set *RESULT to the revision number, or SVN_INVALID_REVNUM if there is none. Use POOL for temporary allocations. Make *BUF point after the terminator. */ static svn_error_t * read_revnum(svn_revnum_t *result, char **buf, const char *end, apr_pool_t *pool) { const char *val; SVN_ERR(read_val(&val, buf, end)); if (val) *result = SVN_STR_TO_REV(val); else *result = SVN_INVALID_REVNUM; return SVN_NO_ERROR; }
/* Record ACTION on the path in CDATA into PATHS. Other properties about the action are pulled from ATTRS. */ static svn_error_t * collect_path(apr_hash_t *paths, char action, const svn_string_t *cdata, apr_hash_t *attrs) { apr_pool_t *result_pool = apr_hash_pool_get(paths); svn_log_changed_path2_t *lcp; const char *copyfrom_path; const char *copyfrom_rev; const char *path; lcp = svn_log_changed_path2_create(result_pool); lcp->action = action; lcp->copyfrom_rev = SVN_INVALID_REVNUM; /* COPYFROM_* are only recorded for ADDED_PATH and REPLACED_PATH. */ copyfrom_path = apr_hash_get(attrs, "copyfrom-path", APR_HASH_KEY_STRING); copyfrom_rev = apr_hash_get(attrs, "copyfrom-rev", APR_HASH_KEY_STRING); if (copyfrom_path && copyfrom_rev) { svn_revnum_t rev = SVN_STR_TO_REV(copyfrom_rev); if (SVN_IS_VALID_REVNUM(rev)) { lcp->copyfrom_path = apr_pstrdup(result_pool, copyfrom_path); lcp->copyfrom_rev = rev; } } lcp->node_kind = svn_node_kind_from_word(apr_hash_get( attrs, "node-kind", APR_HASH_KEY_STRING)); lcp->text_modified = svn_tristate__from_word(apr_hash_get( attrs, "text-mods", APR_HASH_KEY_STRING)); lcp->props_modified = svn_tristate__from_word(apr_hash_get( attrs, "prop-mods", APR_HASH_KEY_STRING)); path = apr_pstrmemdup(result_pool, cdata->data, cdata->len); apr_hash_set(paths, path, APR_HASH_KEY_STRING, lcp); return SVN_NO_ERROR; }
/* This implements the `svn_ra_neon__startelem_cb_t' prototype. */ static svn_error_t * gloc_start_element(int *elem, void *userdata, int parent_state, const char *ns, const char *ln, const char **atts) { get_locations_baton_t *baton = userdata; const svn_ra_neon__xml_elm_t *elm; elm = svn_ra_neon__lookup_xml_elem(gloc_report_elements, ns, ln); /* Just skip unknown elements. */ if (!elm) { *elem = NE_XML_DECLINE; return SVN_NO_ERROR; } if (parent_state == ELEM_get_locations_report && elm->id == ELEM_location) { svn_revnum_t rev = SVN_INVALID_REVNUM; const char *path; const char *r; r = svn_xml_get_attr_value("rev", atts); if (r) rev = SVN_STR_TO_REV(r); path = svn_xml_get_attr_value("path", atts); if (SVN_IS_VALID_REVNUM(rev) && path) apr_hash_set(baton->hash, apr_pmemdup(baton->pool, &rev, sizeof(rev)), sizeof(rev), apr_pstrdup(baton->pool, path)); else return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Expected a valid revnum and path")); } *elem = elm->id; return SVN_NO_ERROR; }
/* Open the provider's lock database. * * The provider may or may not use a "real" database for locks * (a lock could be an attribute on a resource, for example). * * The provider may choose to use the value of the DAVLockDB directive * (as returned by dav_get_lockdb_path()) to decide where to place * any storage it may need. * * The request storage pool should be associated with the lockdb, * so it can be used in subsequent operations. * * If ro != 0, only readonly operations will be performed. * If force == 0, the open can be "lazy"; no subsequent locking operations * may occur. * If force != 0, locking operations will definitely occur. */ static dav_error * open_lockdb(request_rec *r, int ro, int force, dav_lockdb **lockdb) { const char *svn_client_options, *version_name; dav_lockdb *db = apr_pcalloc(r->pool, sizeof(*db)); dav_lockdb_private *info = apr_pcalloc(r->pool, sizeof(*info)); info->r = r; /* Is this an svn client? */ /* Check to see if an svn client sent any custom X-SVN-* headers in the request. */ svn_client_options = apr_table_get(r->headers_in, SVN_DAV_OPTIONS_HEADER); if (svn_client_options) { /* 'svn [lock | unlock] --force' */ if (ap_strstr_c(svn_client_options, SVN_DAV_OPTION_LOCK_BREAK)) info->lock_break = TRUE; if (ap_strstr_c(svn_client_options, SVN_DAV_OPTION_LOCK_STEAL)) info->lock_steal = TRUE; if (ap_strstr_c(svn_client_options, SVN_DAV_OPTION_KEEP_LOCKS)) info->keep_locks = TRUE; } /* 'svn lock' wants to make svn_fs_lock() do an out-of-dateness check. */ version_name = apr_table_get(r->headers_in, SVN_DAV_VERSION_NAME_HEADER); info->working_revnum = version_name ? SVN_STR_TO_REV(version_name): SVN_INVALID_REVNUM; /* The generic lockdb structure. */ db->hooks = &dav_svn__hooks_locks; db->ro = ro; db->info = info; *lockdb = db; return 0; }
static svn_error_t * start_getloc(svn_ra_serf__xml_parser_t *parser, void *userData, svn_ra_serf__dav_props_t name, const char **attrs) { loc_context_t *loc_ctx = userData; if (!loc_ctx->state && strcmp(name.name, "get-locations-report") == 0) { push_state(loc_ctx, REPORT); } else if (loc_ctx->state && loc_ctx->state->state == REPORT && strcmp(name.name, "location") == 0) { svn_revnum_t rev = SVN_INVALID_REVNUM; const char *revstr, *path; revstr = svn_xml_get_attr_value("rev", attrs); if (revstr) { rev = SVN_STR_TO_REV(revstr); } path = svn_xml_get_attr_value("path", attrs); if (SVN_IS_VALID_REVNUM(rev) && path) { apr_hash_set(loc_ctx->paths, apr_pmemdup(loc_ctx->pool, &rev, sizeof(rev)), sizeof(rev), apr_pstrdup(loc_ctx->pool, path)); } } return SVN_NO_ERROR; }
/* * This implements the `svn_ra_neon__xml_startelm_cb' prototype. */ static svn_error_t * log_start_element(int *elem, void *baton, int parent, const char *nspace, const char *name, const char **atts) { const char *copyfrom_path, *copyfrom_revstr; svn_revnum_t copyfrom_rev; struct log_baton *lb = baton; static const svn_ra_neon__xml_elm_t log_report_elements[] = { { SVN_XML_NAMESPACE, "log-report", ELEM_log_report, 0 }, { SVN_XML_NAMESPACE, "log-item", ELEM_log_item, 0 }, { SVN_XML_NAMESPACE, "date", ELEM_log_date, SVN_RA_NEON__XML_CDATA }, { SVN_XML_NAMESPACE, "added-path", ELEM_added_path, SVN_RA_NEON__XML_CDATA }, { SVN_XML_NAMESPACE, "deleted-path", ELEM_deleted_path, SVN_RA_NEON__XML_CDATA }, { SVN_XML_NAMESPACE, "modified-path", ELEM_modified_path, SVN_RA_NEON__XML_CDATA }, { SVN_XML_NAMESPACE, "replaced-path", ELEM_replaced_path, SVN_RA_NEON__XML_CDATA }, { SVN_XML_NAMESPACE, "revprop", ELEM_revprop, SVN_RA_NEON__XML_CDATA }, { "DAV:", SVN_DAV__VERSION_NAME, ELEM_version_name, SVN_RA_NEON__XML_CDATA }, { "DAV:", "creator-displayname", ELEM_creator_displayname, SVN_RA_NEON__XML_CDATA }, { "DAV:", "comment", ELEM_comment, SVN_RA_NEON__XML_CDATA }, { SVN_XML_NAMESPACE, "has-children", ELEM_has_children, SVN_RA_NEON__XML_CDATA }, { NULL } }; const svn_ra_neon__xml_elm_t *elm = svn_ra_neon__lookup_xml_elem(log_report_elements, nspace, name); *elem = elm ? elm->id : SVN_RA_NEON__XML_DECLINE; if (!elm) return SVN_NO_ERROR; switch (elm->id) { case ELEM_creator_displayname: case ELEM_log_date: case ELEM_version_name: case ELEM_added_path: case ELEM_replaced_path: case ELEM_deleted_path: case ELEM_modified_path: case ELEM_revprop: case ELEM_comment: lb->want_cdata = lb->cdata; svn_stringbuf_setempty(lb->cdata); if (elm->id == ELEM_revprop) { lb->revprop_name = apr_pstrdup(lb->subpool, svn_xml_get_attr_value("name", atts)); if (lb->revprop_name == NULL) return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Missing name attr in revprop element")); } break; case ELEM_has_children: lb->log_entry->has_children = TRUE; break; default: lb->want_cdata = NULL; break; } switch (elm->id) { case ELEM_added_path: case ELEM_replaced_path: case ELEM_deleted_path: case ELEM_modified_path: lb->this_path_item = apr_pcalloc(lb->subpool, sizeof(*(lb->this_path_item))); lb->this_path_item->copyfrom_rev = SVN_INVALID_REVNUM; /* See documentation for `svn_repos_node_t' in svn_repos.h, and `svn_log_changed_path_t' in svn_types.h, for more about these action codes. */ if ((elm->id == ELEM_added_path) || (elm->id == ELEM_replaced_path)) { lb->this_path_item->action = (elm->id == ELEM_added_path) ? 'A' : 'R'; copyfrom_path = svn_xml_get_attr_value("copyfrom-path", atts); copyfrom_revstr = svn_xml_get_attr_value("copyfrom-rev", atts); if (copyfrom_path && copyfrom_revstr && (SVN_IS_VALID_REVNUM (copyfrom_rev = SVN_STR_TO_REV(copyfrom_revstr)))) { lb->this_path_item->copyfrom_path = apr_pstrdup(lb->subpool, copyfrom_path); lb->this_path_item->copyfrom_rev = copyfrom_rev; } } else if (elm->id == ELEM_deleted_path) { lb->this_path_item->action = 'D'; } else { lb->this_path_item->action = 'M'; } break; default: lb->this_path_item = NULL; break; } return SVN_NO_ERROR; }
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); }
/* Make a node baton, parsing the relevant HEADERS. * * If RB->pb->parent_dir: * prefix it to NB->path * prefix it to NB->copyfrom_path (if present) */ static svn_error_t * make_node_baton(struct node_baton **node_baton_p, apr_hash_t *headers, struct revision_baton *rb, apr_pool_t *pool) { struct node_baton *nb = apr_pcalloc(pool, sizeof(*nb)); const char *val; /* Start with sensible defaults. */ nb->rb = rb; nb->pool = pool; nb->kind = svn_node_unknown; /* Then add info from the headers. */ if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH))) { val = svn_relpath_canonicalize(val, pool); if (rb->pb->parent_dir) nb->path = svn_relpath_join(rb->pb->parent_dir, val, pool); else nb->path = val; } if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND))) { if (! strcmp(val, "file")) nb->kind = svn_node_file; else if (! strcmp(val, "dir")) nb->kind = svn_node_dir; } nb->action = (enum svn_node_action)(-1); /* an invalid action code */ if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION))) { if (! strcmp(val, "change")) nb->action = svn_node_action_change; else if (! strcmp(val, "add")) nb->action = svn_node_action_add; else if (! strcmp(val, "delete")) nb->action = svn_node_action_delete; else if (! strcmp(val, "replace")) nb->action = svn_node_action_replace; } nb->copyfrom_rev = SVN_INVALID_REVNUM; if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV))) { nb->copyfrom_rev = SVN_STR_TO_REV(val); } if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH))) { val = svn_relpath_canonicalize(val, pool); if (rb->pb->parent_dir) nb->copyfrom_path = svn_relpath_join(rb->pb->parent_dir, val, pool); else nb->copyfrom_path = val; } if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_CHECKSUM))) { SVN_ERR(svn_checksum_parse_hex(&nb->result_checksum, svn_checksum_md5, val, pool)); } if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_DELTA_BASE_CHECKSUM))) { SVN_ERR(svn_checksum_parse_hex(&nb->base_checksum, svn_checksum_md5, val, pool)); } if ((val = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_COPY_SOURCE_CHECKSUM))) { SVN_ERR(svn_checksum_parse_hex(&nb->copy_source_checksum, svn_checksum_md5, val, pool)); } /* What's cool about this dump format is that the parser just ignores any unrecognized headers. :-) */ *node_baton_p = nb; return SVN_NO_ERROR; }
/* Parse one revision specification. Return pointer to character after revision, or NULL if the revision is invalid. Modifies str, so make sure to pass a copy of anything precious. Uses POOL for temporary allocation. */ static char *parse_one_rev(svn_opt_revision_t *revision, char *str, apr_pool_t *pool) { char *end, save; /* Allow any number of 'r's to prefix a revision number, because that way if a script pastes svn output into another svn command (like "svn log -r${REV_COPIED_FROM_OUTPUT}"), it'll Just Work, even when compounded. As it happens, none of our special revision words begins with "r". If any ever do, then this code will have to get smarter. Incidentally, this allows "r{DATE}". We could avoid that with some trivial code rearrangement, but it's not clear what would be gained by doing so. */ while (*str == 'r') str++; if (*str == '{') { svn_boolean_t matched; apr_time_t tm; svn_error_t *err; /* Brackets denote a date. */ str++; end = strchr(str, '}'); if (!end) return NULL; *end = '\0'; err = svn_parse_date(&matched, &tm, str, apr_time_now(), pool); if (err) { svn_error_clear(err); return NULL; } if (!matched) return NULL; revision->kind = svn_opt_revision_date; revision->value.date = tm; return end + 1; } else if (svn_ctype_isdigit(*str)) { /* It's a number. */ end = str + 1; while (svn_ctype_isdigit(*end)) end++; save = *end; *end = '\0'; revision->kind = svn_opt_revision_number; revision->value.number = SVN_STR_TO_REV(str); *end = save; return end; } else if (svn_ctype_isalpha(*str)) { end = str + 1; while (svn_ctype_isalpha(*end)) end++; save = *end; *end = '\0'; if (revision_from_word(revision, str) != 0) return NULL; *end = save; return end; } else return NULL; }
/* Conforms to svn_ra_serf__xml_closed_t */ static svn_error_t * blame_closed(svn_ra_serf__xml_estate_t *xes, void *baton, int leaving_state, const svn_string_t *cdata, apr_hash_t *attrs, apr_pool_t *scratch_pool) { blame_context_t *blame_ctx = baton; if (leaving_state == FILE_REV) { /* Note that we test STREAM, but any pointer is currently invalid. It was closed when left the TXDELTA state. */ if (blame_ctx->stream == NULL) { const char *path; const char *rev; path = svn_hash_gets(attrs, "path"); rev = svn_hash_gets(attrs, "rev"); /* Send a "no content" notification. */ SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton, path, SVN_STR_TO_REV(rev), blame_ctx->rev_props, FALSE /* result_of_merge */, NULL, NULL, /* txdelta / baton */ blame_ctx->prop_diffs, scratch_pool)); } } else if (leaving_state == MERGED_REVISION) { svn_ra_serf__xml_note(xes, FILE_REV, "merged-revision", "*"); } else if (leaving_state == TXDELTA) { SVN_ERR(svn_stream_close(blame_ctx->stream)); } else { const char *name; const svn_string_t *value; SVN_ERR_ASSERT(leaving_state == REV_PROP || leaving_state == SET_PROP || leaving_state == REMOVE_PROP); name = apr_pstrdup(blame_ctx->state_pool, svn_hash_gets(attrs, "name")); if (leaving_state == REMOVE_PROP) { value = NULL; } else { const char *encoding = svn_hash_gets(attrs, "encoding"); if (encoding && strcmp(encoding, "base64") == 0) value = svn_base64_decode_string(cdata, blame_ctx->state_pool); else value = svn_string_dup(cdata, blame_ctx->state_pool); } if (leaving_state == REV_PROP) { svn_hash_sets(blame_ctx->rev_props, name, value); } else { svn_prop_t *prop = apr_array_push(blame_ctx->prop_diffs); prop->name = name; prop->value = value; } } return SVN_NO_ERROR; }
/* This implements serf_bucket_headers_do_callback_fn_t. */ static int capabilities_headers_iterator_callback(void *baton, const char *key, const char *val) { options_context_t *opt_ctx = baton; svn_ra_serf__session_t *session = opt_ctx->session; if (svn_cstring_casecmp(key, "dav") == 0) { /* Each header may contain multiple values, separated by commas, e.g.: DAV: version-control,checkout,working-resource DAV: merge,baseline,activity,version-controlled-collection DAV: http://subversion.tigris.org/xmlns/dav/svn/depth */ apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, opt_ctx->pool); opt_ctx->received_dav_header = TRUE; /* Right now we only have a few capabilities to detect, so just seek for them directly. This could be written slightly more efficiently, but that wouldn't be worth it until we have many more capabilities. */ if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_DEPTH, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals)) { /* The server doesn't know what repository we're referring to, so it can't just say capability_yes. */ if (!svn_hash_gets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, capability_server_yes); } } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS, vals)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INHERITED_PROPS, vals)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_INHERITED_PROPS, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REVERSE_FILE_REVS, vals)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS, vals)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_INLINE_PROPS, vals)) { session->supports_inline_props = TRUE; } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_REPLAY_REV_RESOURCE, vals)) { session->supports_rev_rsrc_replay = TRUE; } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_SVNDIFF1, vals)) { /* Use compressed svndiff1 format for servers that properly advertise this capability (Subversion 1.10 and greater). */ session->supports_svndiff1 = TRUE; } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LIST, vals)) { svn_hash_sets(session->capabilities, SVN_RA_CAPABILITY_LIST, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_SVNDIFF2, vals)) { /* Same for svndiff2. */ session->supports_svndiff2 = TRUE; } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PUT_RESULT_CHECKSUM, vals)) { session->supports_put_result_checksum = TRUE; } } /* SVN-specific headers -- if present, server supports HTTP protocol v2 */ else if (!svn_ctype_casecmp(key[0], 'S') && !svn_ctype_casecmp(key[1], 'V') && !svn_ctype_casecmp(key[2], 'N')) { /* If we've not yet seen any information about supported POST requests, we'll initialize the list/hash with "create-txn" (which we know is supported by virtue of the server speaking HTTPv2 at all. */ if (! session->supported_posts) { session->supported_posts = apr_hash_make(session->pool); apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1); } if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0) { session->repos_root = session->session_url; session->repos_root.path = (char *)svn_fspath__canonicalize(val, session->pool); session->repos_root_str = svn_urlpath__canonicalize( apr_uri_unparse(session->pool, &session->repos_root, 0), session->pool); } else if (svn_cstring_casecmp(key, SVN_DAV_ME_RESOURCE_HEADER) == 0) { #ifdef SVN_DEBUG char *ignore_v2_env_var = getenv(SVN_IGNORE_V2_ENV_VAR); if (!(ignore_v2_env_var && apr_strnatcasecmp(ignore_v2_env_var, "yes") == 0)) session->me_resource = apr_pstrdup(session->pool, val); #else session->me_resource = apr_pstrdup(session->pool, val); #endif } else if (svn_cstring_casecmp(key, SVN_DAV_REV_STUB_HEADER) == 0) { session->rev_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_REV_ROOT_STUB_HEADER) == 0) { session->rev_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_TXN_STUB_HEADER) == 0) { session->txn_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_TXN_ROOT_STUB_HEADER) == 0) { session->txn_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0) { session->vtxn_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0) { session->vtxn_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0) { session->uuid = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_YOUNGEST_REV_HEADER) == 0) { opt_ctx->youngest_rev = SVN_STR_TO_REV(val); } else if (svn_cstring_casecmp(key, SVN_DAV_ALLOW_BULK_UPDATES) == 0) { session->server_allows_bulk = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0) { /* May contain multiple values, separated by commas. */ int i; apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, session->pool); for (i = 0; i < vals->nelts; i++) { const char *post_val = APR_ARRAY_IDX(vals, i, const char *); svn_hash_sets(session->supported_posts, post_val, (void *)1); } } else if (svn_cstring_casecmp(key, SVN_DAV_REPOSITORY_MERGEINFO) == 0)
dav_error * dav_svn__replay_report(const dav_resource *resource, const apr_xml_doc *doc, dav_svn__output *output) { dav_error *derr = NULL; svn_revnum_t low_water_mark = SVN_INVALID_REVNUM; svn_revnum_t rev; const svn_delta_editor_t *editor; svn_boolean_t send_deltas = TRUE; dav_svn__authz_read_baton arb; const char *base_dir; apr_bucket_brigade *bb; apr_xml_elem *child; svn_fs_root_t *root; svn_error_t *err; void *edit_baton; int ns; /* In Subversion 1.8, we allowed this REPORT to be issue against a revision resource. Doing so means the REV is part of the request URL, and BASE_DIR is embedded in the request body. The old-school (and incorrect, see issue #4287 -- https://issues.apache.org/jira/browse/SVN-4287) way was to REPORT on the public URL of the BASE_DIR and embed the REV in the report body. */ if (resource->baselined && (resource->type == DAV_RESOURCE_TYPE_VERSION)) { rev = resource->info->root.rev; base_dir = NULL; } else { rev = SVN_INVALID_REVNUM; base_dir = resource->info->repos_path; } arb.r = resource->info->r; arb.repos = resource->info->repos; 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, 0, "The request does not contain the 'svn:' " "namespace, so it is not going to have an " "svn:revision element. That element is " "required"); for (child = doc->root->first_child; child != NULL; child = child->next) { if (child->ns == ns) { const char *cdata; if (strcmp(child->name, "revision") == 0) { if (SVN_IS_VALID_REVNUM(rev)) { /* Uh... we already have a revision to use, either because this tag is non-unique or because the request was submitted against a revision-bearing resource URL. Either way, something's not right. */ return malformed_element_error("revision", resource->pool); } cdata = dav_xml_get_cdata(child, resource->pool, 1); rev = SVN_STR_TO_REV(cdata); } else if (strcmp(child->name, "low-water-mark") == 0) { cdata = dav_xml_get_cdata(child, resource->pool, 1); if (! cdata) return malformed_element_error("low-water-mark", resource->pool); low_water_mark = SVN_STR_TO_REV(cdata); } else if (strcmp(child->name, "send-deltas") == 0) { apr_int64_t parsed_val; cdata = dav_xml_get_cdata(child, resource->pool, 1); if (! cdata) return malformed_element_error("send-deltas", resource->pool); err = svn_cstring_strtoi64(&parsed_val, cdata, 0, 1, 10); if (err) { svn_error_clear(err); return malformed_element_error("send-deltas", resource->pool); } send_deltas = parsed_val != 0; } else if (strcmp(child->name, "include-path") == 0) { cdata = dav_xml_get_cdata(child, resource->pool, 1); if ((derr = dav_svn__test_canonical(cdata, resource->pool))) return derr; /* Force BASE_DIR to be a relative path, not an fspath. */ base_dir = svn_relpath_canonicalize(cdata, resource->pool); } } } if (! SVN_IS_VALID_REVNUM(rev)) return dav_svn__new_error_svn (resource->pool, HTTP_BAD_REQUEST, 0, 0, "Request was missing the revision argument"); if (! SVN_IS_VALID_REVNUM(low_water_mark)) return dav_svn__new_error_svn (resource->pool, HTTP_BAD_REQUEST, 0, 0, "Request was missing the low-water-mark argument"); if (! base_dir) base_dir = ""; bb = apr_brigade_create(resource->pool, dav_svn__output_get_bucket_alloc(output)); if ((err = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool))) { derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Couldn't retrieve revision root", resource->pool); goto cleanup; } make_editor(&editor, &edit_baton, bb, output, dav_svn__get_compression_level(resource->info->r), resource->info->svndiff_version, resource->pool); if ((err = svn_repos_replay2(root, base_dir, low_water_mark, send_deltas, editor, edit_baton, dav_svn__authz_read_func(&arb), &arb, resource->pool))) { derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Problem replaying revision", resource->pool); goto cleanup; } if ((err = end_report(edit_baton))) { derr = dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Problem closing editor drive", resource->pool); goto cleanup; } cleanup: dav_svn__operational_log(resource->info, svn_log__replay(base_dir, rev, resource->info->r->pool)); /* Flush the brigade. */ return dav_svn__final_flush_or_error(resource->info->r, bb, output, derr, resource->pool); }
/* Handle a open() call. Ensures that a file_info_t for the given NAME * exists. Auto-create and initialize a handle_info_t for it linked to * HANDLE. */ static void open_file(const char *name, int handle) { file_stats_t *file = apr_hash_get(files, name, APR_HASH_KEY_STRING); handle_info_t *handle_info = apr_hash_get(handles, &handle, sizeof(handle)); /* auto-create file info */ if (!file) { apr_pool_t *pool = apr_hash_pool_get(files); apr_pool_t *subpool = svn_pool_create(pool); apr_file_t *apr_file = NULL; apr_finfo_t finfo = { 0 }; int cluster_count = 0; /* determine file size (if file still exists) */ apr_file_open(&apr_file, name, APR_READ | APR_BUFFERED, APR_OS_DEFAULT, subpool); if (apr_file) apr_file_info_get(&finfo, APR_FINFO_SIZE, apr_file); svn_pool_destroy(subpool); file = apr_pcalloc(pool, sizeof(*file)); file->name = apr_pstrdup(pool, name); file->size = finfo.size; /* pre-allocate cluster map accordingly * (will be auto-expanded later if necessary) */ cluster_count = (int)(1 + (file->size - 1) / cluster_size); file->read_map = apr_array_make(pool, file->size ? cluster_count : 1, sizeof(word)); while (file->read_map->nelts < cluster_count) APR_ARRAY_PUSH(file->read_map, byte) = 0; /* determine first revision of rev / packed rev files */ if (strstr(name, "/db/revs/") != NULL && strstr(name, "manifest") == NULL) if (strstr(name, ".pack/pack") != NULL) file->rev_num = SVN_STR_TO_REV(strstr(name, "/db/revs/") + 9); else file->rev_num = SVN_STR_TO_REV(strrchr(name, '/') + 1); else file->rev_num = -1; /* filter out log/phys index files */ if (file->rev_num >= 0) { const char *suffix = name + strlen(name) - 4; if (strcmp(suffix, ".l2p") == 0 || strcmp(suffix, ".p2l") == 0) file->rev_num = -1; } apr_hash_set(files, file->name, APR_HASH_KEY_STRING, file); } file->open_count++; /* auto-create handle instance */ if (!handle_info) { apr_pool_t *pool = apr_hash_pool_get(handles); int *key = apr_palloc(pool, sizeof(*key)); *key = handle; handle_info = apr_pcalloc(pool, sizeof(*handle_info)); apr_hash_set(handles, key, sizeof(*key), handle_info); } /* link handle to file */ handle_info->file = file; handle_info->last_read_start = 0; handle_info->last_read_size = 0; }
dav_error * dav_svn__log_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; struct log_receiver_baton lrb; dav_svn__authz_read_baton arb; const dav_svn_repos *repos = resource->info->repos; const char *target = NULL; int limit = 0; int ns; svn_boolean_t seen_revprop_element; /* These get determined from the request document. */ svn_revnum_t start = SVN_INVALID_REVNUM; /* defaults to HEAD */ svn_revnum_t end = SVN_INVALID_REVNUM; /* defaults to HEAD */ svn_boolean_t discover_changed_paths = FALSE; /* off by default */ svn_boolean_t strict_node_history = FALSE; /* off by default */ svn_boolean_t include_merged_revisions = FALSE; /* off by default */ apr_array_header_t *revprops = apr_array_make(resource->pool, 3, sizeof(const char *)); apr_array_header_t *paths = apr_array_make(resource->pool, 1, 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); } /* If this is still FALSE after the loop, we haven't seen either of the revprop elements, meaning a pre-1.5 client; we'll return the standard author/date/log revprops. */ seen_revprop_element = FALSE; lrb.requested_custom_revprops = FALSE; lrb.encode_binary_props = FALSE; 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, "start-revision") == 0) start = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "end-revision") == 0) end = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "limit") == 0) { serr = svn_cstring_atoi(&limit, dav_xml_get_cdata(child, resource->pool, 1)); if (serr) { return dav_svn__convert_err(serr, HTTP_BAD_REQUEST, "Malformed CDATA in element " "\"limit\"", resource->pool); } } else if (strcmp(child->name, "discover-changed-paths") == 0) discover_changed_paths = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "strict-node-history") == 0) strict_node_history = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "include-merged-revisions") == 0) include_merged_revisions = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "encode-binary-props") == 0) lrb.encode_binary_props = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "all-revprops") == 0) { revprops = NULL; /* presence indicates fetch all revprops */ seen_revprop_element = lrb.requested_custom_revprops = TRUE; } else if (strcmp(child->name, "no-revprops") == 0) { /* presence indicates fetch no revprops */ seen_revprop_element = lrb.requested_custom_revprops = TRUE; } else if (strcmp(child->name, "revprop") == 0) { if (revprops) { /* We're not fetching all revprops, append to fetch list. */ const char *name = dav_xml_get_cdata(child, resource->pool, 0); APR_ARRAY_PUSH(revprops, const char *) = name; if (!lrb.requested_custom_revprops && strcmp(name, SVN_PROP_REVISION_AUTHOR) != 0 && strcmp(name, SVN_PROP_REVISION_DATE) != 0 && strcmp(name, SVN_PROP_REVISION_LOG) != 0) lrb.requested_custom_revprops = TRUE; } seen_revprop_element = TRUE; } else if (strcmp(child->name, "path") == 0) { 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); APR_ARRAY_PUSH(paths, const char *) = target; } /* else unknown element; skip it */ } if (!seen_revprop_element) { /* pre-1.5 client */ APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR; APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE; APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG; } /* Build authz read baton */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* Build log receiver baton */ lrb.bb = apr_brigade_create(resource->pool, /* not the subpool! */ output->c->bucket_alloc); lrb.output = output; lrb.needs_header = TRUE; lrb.stack_depth = 0; /* lrb.requested_custom_revprops set above */ /* Our svn_log_entry_receiver_t sends the <S:log-report> header in a lazy fashion. Before writing the first log message, it assures that the header has already been sent (checking the needs_header flag in our log_receiver_baton structure). */ /* Send zero or more log items. */ serr = svn_repos_get_logs4(repos->repos, paths, start, end, limit, discover_changed_paths, strict_node_history, include_merged_revisions, revprops, dav_svn__authz_read_func(&arb), &arb, log_receiver, &lrb, resource->pool); if (serr) { derr = dav_svn__convert_err(serr, HTTP_BAD_REQUEST, serr->message, resource->pool); goto cleanup; } if ((serr = maybe_send_header(&lrb))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error beginning REPORT response.", resource->pool); goto cleanup; } if ((serr = dav_svn__brigade_puts(lrb.bb, lrb.output, "</S:log-report>" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT response.", resource->pool); goto cleanup; } cleanup: dav_svn__operational_log(resource->info, svn_log__log(paths, start, end, limit, discover_changed_paths, strict_node_history, include_merged_revisions, revprops, resource->pool)); return dav_svn__final_flush_or_error(resource->info->r, lrb.bb, output, derr, resource->pool); }
dav_error * dav_svn__get_locations_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { svn_error_t *serr; dav_error *derr = NULL; apr_status_t apr_err; apr_bucket_brigade *bb; dav_svn__authz_read_baton arb; /* The parameters to do the operation on. */ const char *relative_path = NULL; const char *abs_path; svn_revnum_t peg_revision = SVN_INVALID_REVNUM; apr_array_header_t *location_revisions; /* XML Parsing Variables */ int ns; apr_xml_elem *child; apr_hash_t *fs_locations; location_revisions = apr_array_make(resource->pool, 0, sizeof(svn_revnum_t)); /* Sanity check. */ 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); } /* Gather the parameters. */ 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, "peg-revision") == 0) peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "location-revision") == 0) { svn_revnum_t revision = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision; } else if (strcmp(child->name, "path") == 0) { relative_path = dav_xml_get_cdata(child, resource->pool, 0); if ((derr = dav_svn__test_canonical(relative_path, resource->pool))) return derr; } } /* Now we should have the parameters ready - let's check if they are all present. */ if (! (relative_path && SVN_IS_VALID_REVNUM(peg_revision))) { return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, "Not all parameters passed.", SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); } /* Append the relative path to the base FS path to get an absolute repository path. */ abs_path = svn_path_join(resource->info->repos_path, relative_path, resource->pool); /* Build an authz read baton */ arb.r = resource->info->r; arb.repos = resource->info->repos; serr = svn_repos_trace_node_locations(resource->info->repos->fs, &fs_locations, abs_path, peg_revision, location_revisions, dav_svn__authz_read_func(&arb), &arb, resource->pool); if (serr) { return dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, serr->message, resource->pool); } bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); apr_err = send_get_locations_report(output, bb, resource, fs_locations); if (apr_err) derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), HTTP_INTERNAL_SERVER_ERROR, "Error writing REPORT response.", resource->pool); /* Flush the contents of the brigade (returning an error only if we don't already have one). */ if (((apr_err = ap_fflush(output, bb))) && (! derr)) return dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), HTTP_INTERNAL_SERVER_ERROR, "Error flushing brigade.", resource->pool); return derr; }
/* This implements serf_bucket_headers_do_callback_fn_t. */ static int capabilities_headers_iterator_callback(void *baton, const char *key, const char *val) { options_context_t *opt_ctx = baton; svn_ra_serf__session_t *session = opt_ctx->session; if (svn_cstring_casecmp(key, "dav") == 0) { /* Each header may contain multiple values, separated by commas, e.g.: DAV: version-control,checkout,working-resource DAV: merge,baseline,activity,version-controlled-collection DAV: http://subversion.tigris.org/xmlns/dav/svn/depth */ apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, opt_ctx->pool); /* Right now we only have a few capabilities to detect, so just seek for them directly. This could be written slightly more efficiently, but that wouldn't be worth it until we have many more capabilities. */ if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_DEPTH, vals)) { apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_DEPTH, APR_HASH_KEY_STRING, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_MERGEINFO, vals)) { /* The server doesn't know what repository we're referring to, so it can't just say capability_yes. */ apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_MERGEINFO, APR_HASH_KEY_STRING, capability_server_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_LOG_REVPROPS, vals)) { apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_LOG_REVPROPS, APR_HASH_KEY_STRING, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS, vals)) { apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_ATOMIC_REVPROPS, APR_HASH_KEY_STRING, capability_yes); } if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY, vals)) { apr_hash_set(session->capabilities, SVN_RA_CAPABILITY_PARTIAL_REPLAY, APR_HASH_KEY_STRING, capability_yes); } } /* SVN-specific headers -- if present, server supports HTTP protocol v2 */ else if (strncmp(key, "SVN", 3) == 0) { /* If we've not yet seen any information about supported POST requests, we'll initialize the list/hash with "create-txn" (which we know is supported by virtue of the server speaking HTTPv2 at all. */ if (! session->supported_posts) { session->supported_posts = apr_hash_make(session->pool); apr_hash_set(session->supported_posts, "create-txn", 10, (void *)1); } if (svn_cstring_casecmp(key, SVN_DAV_ROOT_URI_HEADER) == 0) { session->repos_root = session->session_url; session->repos_root.path = (char *)svn_fspath__canonicalize(val, session->pool); session->repos_root_str = svn_urlpath__canonicalize( apr_uri_unparse(session->pool, &session->repos_root, 0), session->pool); } else if (svn_cstring_casecmp(key, SVN_DAV_ME_RESOURCE_HEADER) == 0) { #ifdef SVN_DEBUG char *ignore_v2_env_var = getenv(SVN_IGNORE_V2_ENV_VAR); if (!(ignore_v2_env_var && apr_strnatcasecmp(ignore_v2_env_var, "yes") == 0)) session->me_resource = apr_pstrdup(session->pool, val); #else session->me_resource = apr_pstrdup(session->pool, val); #endif } else if (svn_cstring_casecmp(key, SVN_DAV_REV_STUB_HEADER) == 0) { session->rev_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_REV_ROOT_STUB_HEADER) == 0) { session->rev_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_TXN_STUB_HEADER) == 0) { session->txn_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_TXN_ROOT_STUB_HEADER) == 0) { session->txn_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_STUB_HEADER) == 0) { session->vtxn_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_VTXN_ROOT_STUB_HEADER) == 0) { session->vtxn_root_stub = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_REPOS_UUID_HEADER) == 0) { session->uuid = apr_pstrdup(session->pool, val); } else if (svn_cstring_casecmp(key, SVN_DAV_YOUNGEST_REV_HEADER) == 0) { opt_ctx->youngest_rev = SVN_STR_TO_REV(val); } else if (svn_cstring_casecmp(key, SVN_DAV_SUPPORTED_POSTS_HEADER) == 0) { /* May contain multiple values, separated by commas. */ int i; apr_array_header_t *vals = svn_cstring_split(val, ",", TRUE, opt_ctx->pool); for (i = 0; i < vals->nelts; i++) { const char *post_val = APR_ARRAY_IDX(vals, i, const char *); apr_hash_set(session->supported_posts, post_val, APR_HASH_KEY_STRING, (void *)1); } } }
/* * This implements the `svn_ra_neon__xml_endelm_cb' prototype. */ static svn_error_t * log_end_element(void *baton, int state, const char *nspace, const char *name) { struct log_baton *lb = baton; switch (state) { case ELEM_version_name: lb->log_entry->revision = SVN_STR_TO_REV(lb->cdata->data); break; case ELEM_creator_displayname: if (lb->want_author) { if (! lb->log_entry->revprops) lb->log_entry->revprops = apr_hash_make(lb->subpool); apr_hash_set(lb->log_entry->revprops, SVN_PROP_REVISION_AUTHOR, APR_HASH_KEY_STRING, svn_string_create_from_buf(lb->cdata, lb->subpool)); } break; case ELEM_log_date: if (lb->want_date) { if (! lb->log_entry->revprops) lb->log_entry->revprops = apr_hash_make(lb->subpool); apr_hash_set(lb->log_entry->revprops, SVN_PROP_REVISION_DATE, APR_HASH_KEY_STRING, svn_string_create_from_buf(lb->cdata, lb->subpool)); } break; case ELEM_added_path: case ELEM_replaced_path: case ELEM_deleted_path: case ELEM_modified_path: { char *path = apr_pstrdup(lb->subpool, lb->cdata->data); if (! lb->log_entry->changed_paths) lb->log_entry->changed_paths = apr_hash_make(lb->subpool); apr_hash_set(lb->log_entry->changed_paths, path, APR_HASH_KEY_STRING, lb->this_path_item); break; } case ELEM_revprop: if (! lb->log_entry->revprops) lb->log_entry->revprops = apr_hash_make(lb->subpool); apr_hash_set(lb->log_entry->revprops, lb->revprop_name, APR_HASH_KEY_STRING, svn_string_create_from_buf(lb->cdata, lb->subpool)); break; case ELEM_comment: if (lb->want_message) { if (! lb->log_entry->revprops) lb->log_entry->revprops = apr_hash_make(lb->subpool); apr_hash_set(lb->log_entry->revprops, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING, svn_string_create_from_buf(lb->cdata, lb->subpool)); } break; case ELEM_log_item: { /* Compatability cruft so that we can provide limit functionality even if the server doesn't support it. If we've seen as many log entries as we're going to show just error out of the XML parser so we can avoid having to parse the remaining XML, but set lb->err to SVN_NO_ERROR so no error will end up being shown to the user. */ if (lb->limit && (lb->nest_level == 0) && (++lb->count > lb->limit)) { lb->limit_compat_bailout = TRUE; return svn_error_create(APR_EGENERAL, NULL, NULL); } SVN_ERR((*(lb->receiver))(lb->receiver_baton, lb->log_entry, lb->subpool)); if (lb->log_entry->has_children) { lb->nest_level++; } if (! SVN_IS_VALID_REVNUM(lb->log_entry->revision)) { assert(lb->nest_level); lb->nest_level--; } reset_log_item(lb); } break; } /* Stop collecting cdata */ lb->want_cdata = NULL; return SVN_NO_ERROR; }
/* Respond to a client request for a REPORT of type file-revs-report for the RESOURCE. Get request body from DOC and send result to OUTPUT. */ dav_error * dav_svn__file_revs_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; int ns; struct file_rev_baton frb; dav_svn__authz_read_baton arb; const char *abs_path = NULL; /* These get determined from the request document. */ svn_revnum_t start = SVN_INVALID_REVNUM; svn_revnum_t end = SVN_INVALID_REVNUM; svn_boolean_t include_merged_revisions = FALSE; /* off by default */ /* Construct the authz read check baton. */ arb.r = resource->info->r; arb.repos = resource->info->repos; /* 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); /* ### This is done on other places, but the document element is in this namespace, so is this necessary at all? */ 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); } /* Get request information. */ 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, "start-revision") == 0) start = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "end-revision") == 0) end = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); else if (strcmp(child->name, "include-merged-revisions") == 0) include_merged_revisions = TRUE; /* presence indicates positivity */ else if (strcmp(child->name, "path") == 0) { 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. */ abs_path = svn_fspath__join(resource->info->repos_path, rel_path, resource->pool); } /* else unknown element; skip it */ } /* Check that all parameters are present and valid. */ if (! abs_path) return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, "Not all parameters passed.", SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); frb.bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); frb.output = output; frb.needs_header = TRUE; frb.svndiff_version = resource->info->svndiff_version; frb.compression_level = dav_svn__get_compression_level(resource->info->r); /* file_rev_handler will send header first time it is called. */ /* Get the revisions and send them. */ serr = svn_repos_get_file_revs2(resource->info->repos->repos, abs_path, start, end, include_merged_revisions, dav_svn__authz_read_func(&arb), &arb, file_rev_handler, &frb, resource->pool); if (serr) { /* We don't 'goto cleanup' because ap_fflush() tells httpd to write the HTTP headers out, and that includes whatever r->status is at that particular time. When we call dav_svn__convert_err(), we don't immediately set r->status right then, so r->status remains 0, hence HTTP status 200 would be misleadingly returned. */ return (dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, serr->message, resource->pool)); } if ((serr = maybe_send_header(&frb))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error beginning REPORT reponse", resource->pool); goto cleanup; } if ((serr = dav_svn__brigade_puts(frb.bb, frb.output, "</S:file-revs-report>" DEBUG_CR))) { derr = dav_svn__convert_err(serr, HTTP_INTERNAL_SERVER_ERROR, "Error ending REPORT reponse", resource->pool); goto cleanup; } cleanup: /* We've detected a 'high level' svn action to log. */ dav_svn__operational_log(resource->info, svn_log__get_file_revs(abs_path, start, end, include_merged_revisions, resource->pool)); return dav_svn__final_flush_or_error(resource->info->r, frb.bb, output, derr, resource->pool); }
dav_error * dav_svn__replay_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { svn_revnum_t low_water_mark = SVN_INVALID_REVNUM; svn_revnum_t rev = SVN_INVALID_REVNUM; const svn_delta_editor_t *editor; svn_boolean_t send_deltas = TRUE; dav_svn__authz_read_baton arb; const char *base_dir = resource->info->repos_path; apr_bucket_brigade *bb; apr_xml_elem *child; svn_fs_root_t *root; svn_error_t *err; void *edit_baton; int ns; /* The request won't have a repos_path if it's for the root. */ if (! base_dir) base_dir = ""; arb.r = resource->info->r; arb.repos = resource->info->repos; 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 an " "svn:revision element. That element is " "required.", SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); for (child = doc->root->first_child; child != NULL; child = child->next) { if (child->ns == ns) { const char *cdata; if (strcmp(child->name, "revision") == 0) { cdata = dav_xml_get_cdata(child, resource->pool, 1); if (! cdata) return malformed_element_error("revision", resource->pool); rev = SVN_STR_TO_REV(cdata); } else if (strcmp(child->name, "low-water-mark") == 0) { cdata = dav_xml_get_cdata(child, resource->pool, 1); if (! cdata) return malformed_element_error("low-water-mark", resource->pool); low_water_mark = SVN_STR_TO_REV(cdata); } else if (strcmp(child->name, "send-deltas") == 0) { cdata = dav_xml_get_cdata(child, resource->pool, 1); if (! cdata) return malformed_element_error("send-deltas", resource->pool); send_deltas = atoi(cdata); } } } if (! SVN_IS_VALID_REVNUM(rev)) return dav_svn__new_error_tag (resource->pool, HTTP_BAD_REQUEST, 0, "Request was missing the revision argument.", SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); if (! SVN_IS_VALID_REVNUM(low_water_mark)) return dav_svn__new_error_tag (resource->pool, HTTP_BAD_REQUEST, 0, "Request was missing the low-water-mark argument.", SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); if ((err = svn_fs_revision_root(&root, resource->info->repos->fs, rev, resource->pool))) return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Couldn't retrieve revision root", resource->pool); make_editor(&editor, &edit_baton, bb, output, resource->pool); if ((err = svn_repos_replay2(root, base_dir, low_water_mark, send_deltas, editor, edit_baton, dav_svn__authz_read_func(&arb), &arb, resource->pool))) return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Problem replaying revision", resource->pool); if ((err = end_report(edit_baton))) return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Problem closing editor drive", resource->pool); { const char *action, *log_base_dir; if (base_dir && base_dir[0] != '\0') log_base_dir = svn_path_uri_encode(base_dir, resource->info->r->pool); else log_base_dir = "/"; action = apr_psprintf(resource->info->r->pool, "replay %s r%ld", log_base_dir, rev); dav_svn__operational_log(resource->info, action); } ap_fflush(output, bb); return NULL; }
/* Respond to a S:deleted-rev-report request. */ dav_error * dav_svn__get_deleted_rev_report(const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output) { apr_xml_elem *child; int ns; const char *rel_path = NULL, *abs_path; svn_revnum_t peg_rev = SVN_INVALID_REVNUM, end_rev = SVN_INVALID_REVNUM, deleted_rev; apr_bucket_brigade *bb; svn_error_t *err; apr_status_t apr_err; dav_error *derr = NULL; /* Sanity check. */ 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, "peg-revision") == 0) { peg_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, "end-revision") == 0) { end_rev = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1)); } else if (strcmp(child->name, "path") == 0) { rel_path = dav_xml_get_cdata(child, resource->pool, 0); if ((derr = dav_svn__test_canonical(rel_path, resource->pool))) return derr; } } /* Check that all parameters are present. */ if (! (rel_path && SVN_IS_VALID_REVNUM(peg_rev) && SVN_IS_VALID_REVNUM(end_rev))) { return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0, "Not all parameters passed.", SVN_DAV_ERROR_NAMESPACE, SVN_DAV_ERROR_TAG); } /* Append the relative path to the base FS path to get an absolute repository path. */ abs_path = svn_path_join(resource->info->repos_path, rel_path, resource->pool); /* Do what we actually came here for: Find the rev abs_path was deleted. */ err = svn_repos_deleted_rev(resource->info->repos->fs, abs_path, peg_rev, end_rev, &deleted_rev, resource->pool); if (err) { svn_error_clear(err); return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Could not find revision path was deleted."); } bb = apr_brigade_create(resource->pool, output->c->bucket_alloc); apr_err = ap_fprintf(output, bb, DAV_XML_HEADER DEBUG_CR "<S:get-deleted-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" xmlns:D=\"DAV:\">" DEBUG_CR "<D:" SVN_DAV__VERSION_NAME ">%ld</D:" SVN_DAV__VERSION_NAME ">""</S:get-deleted-rev-report>", deleted_rev); if (apr_err) derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL), 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); }
svn_fs_id_t * svn_fs_fs__id_parse(const char *data, apr_size_t len, apr_pool_t *pool) { svn_fs_id_t *id; id_private_t *pvt; char *data_copy, *str, *last_str; /* Dup the ID data into POOL. Our returned ID will have references into this memory. */ data_copy = apr_pstrmemdup(pool, data, len); /* Alloc a new svn_fs_id_t structure. */ id = apr_palloc(pool, sizeof(*id)); pvt = apr_palloc(pool, sizeof(*pvt)); id->vtable = &id_vtable; id->fsap_data = pvt; /* Now, we basically just need to "split" this data on `.' characters. We will use apr_strtok, which will put terminators where each of the '.'s used to be. Then our new id field will reference string locations inside our duplicate string.*/ /* Node Id */ str = apr_strtok(data_copy, ".", &last_str); if (str == NULL) return NULL; pvt->node_id = str; /* Copy Id */ str = apr_strtok(NULL, ".", &last_str); if (str == NULL) return NULL; pvt->copy_id = str; /* Txn/Rev Id */ str = apr_strtok(NULL, ".", &last_str); if (str == NULL) return NULL; if (str[0] == 'r') { apr_int64_t val; svn_error_t *err; /* This is a revision type ID */ pvt->txn_id = NULL; str = apr_strtok(str + 1, "/", &last_str); if (str == NULL) return NULL; pvt->rev = SVN_STR_TO_REV(str); str = apr_strtok(NULL, "/", &last_str); if (str == NULL) return NULL; err = svn_cstring_atoi64(&val, str); if (err) { svn_error_clear(err); return NULL; } pvt->offset = (apr_off_t)val; } else if (str[0] == 't') { /* This is a transaction type ID */ pvt->txn_id = str + 1; pvt->rev = SVN_INVALID_REVNUM; pvt->offset = -1; } else return NULL; return id; }
static svn_error_t * start_element(int *elem, void *baton, int parent_state, const char *nspace, const char *elt_name, const char **atts) { replay_baton_t *rb = baton; const svn_ra_neon__xml_elm_t *elm = svn_ra_neon__lookup_xml_elem(editor_report_elements, nspace, elt_name); if (! elm) { *elem = NE_XML_DECLINE; return SVN_NO_ERROR; } if (parent_state == ELEM_root) { /* If we're at the root of the tree, the element has to be the editor * report itself. */ if (elm->id != ELEM_editor_report) return UNEXPECTED_ELEMENT(nspace, elt_name); } else if (parent_state != ELEM_editor_report) { /* If we're not at the root, our parent has to be the editor report, * since we don't actually nest any elements. */ return UNEXPECTED_ELEMENT(nspace, elt_name); } switch (elm->id) { case ELEM_target_revision: { const char *crev = svn_xml_get_attr_value("rev", atts); if (! crev) return MISSING_ATTR(nspace, elt_name, "rev"); else return rb->editor->set_target_revision(rb->edit_baton, SVN_STR_TO_REV(crev), rb->pool); } break; case ELEM_open_root: { const char *crev = svn_xml_get_attr_value("rev", atts); if (! crev) return MISSING_ATTR(nspace, elt_name, "rev"); else { apr_pool_t *subpool = svn_pool_create(rb->pool); void *dir_baton; SVN_ERR(rb->editor->open_root(rb->edit_baton, SVN_STR_TO_REV(crev), subpool, &dir_baton)); push_dir(rb, dir_baton, "", subpool); } } break; case ELEM_delete_entry: { const char *path = svn_xml_get_attr_value("name", atts); const char *crev = svn_xml_get_attr_value("rev", atts); if (! path) return MISSING_ATTR(nspace, elt_name, "name"); else if (! crev) return MISSING_ATTR(nspace, elt_name, "rev"); else { dir_item_t *di = &TOP_DIR(rb); SVN_ERR(rb->editor->delete_entry(path, SVN_STR_TO_REV(crev), di->baton, di->pool)); } } break; case ELEM_open_directory: case ELEM_add_directory: { const char *crev = svn_xml_get_attr_value("rev", atts); const char *name = svn_xml_get_attr_value("name", atts); if (! name) return MISSING_ATTR(nspace, elt_name, "name"); else { dir_item_t *parent = &TOP_DIR(rb); apr_pool_t *subpool = svn_pool_create(parent->pool); svn_revnum_t rev; void *dir_baton; if (crev) rev = SVN_STR_TO_REV(crev); else rev = SVN_INVALID_REVNUM; if (elm->id == ELEM_open_directory) SVN_ERR(rb->editor->open_directory(name, parent->baton, rev, subpool, &dir_baton)); else if (elm->id == ELEM_add_directory) { const char *cpath = svn_xml_get_attr_value("copyfrom-path", atts); crev = svn_xml_get_attr_value("copyfrom-rev", atts); if (crev) rev = SVN_STR_TO_REV(crev); else rev = SVN_INVALID_REVNUM; SVN_ERR(rb->editor->add_directory(name, parent->baton, cpath, rev, subpool, &dir_baton)); } else SVN_ERR_MALFUNCTION(); push_dir(rb, dir_baton, name, subpool); } } break; case ELEM_open_file: case ELEM_add_file: { const char *path = svn_xml_get_attr_value("name", atts); svn_revnum_t rev; dir_item_t *parent = &TOP_DIR(rb); if (! path) return MISSING_ATTR(nspace, elt_name, "name"); svn_pool_clear(parent->file_pool); if (elm->id == ELEM_add_file) { const char *cpath = svn_xml_get_attr_value("copyfrom-path", atts); const char *crev = svn_xml_get_attr_value("copyfrom-rev", atts); if (crev) rev = SVN_STR_TO_REV(crev); else rev = SVN_INVALID_REVNUM; SVN_ERR(rb->editor->add_file(path, parent->baton, cpath, rev, parent->file_pool, &rb->file_baton)); } else { const char *crev = svn_xml_get_attr_value("rev", atts); if (crev) rev = SVN_STR_TO_REV(crev); else rev = SVN_INVALID_REVNUM; SVN_ERR(rb->editor->open_file(path, parent->baton, rev, parent->file_pool, &rb->file_baton)); } } break; case ELEM_apply_textdelta: if (! rb->file_baton) return svn_error_create (SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Got apply-textdelta element without preceding " "add-file or open-file")); else { const char *checksum = svn_xml_get_attr_value("checksum", atts); SVN_ERR(rb->editor->apply_textdelta(rb->file_baton, checksum, TOP_DIR(rb).file_pool, &rb->whandler, &rb->whandler_baton)); rb->svndiff_decoder = svn_txdelta_parse_svndiff (rb->whandler, rb->whandler_baton, TRUE, TOP_DIR(rb).file_pool); rb->base64_decoder = svn_base64_decode(rb->svndiff_decoder, TOP_DIR(rb).file_pool); } break; case ELEM_close_file: if (! rb->file_baton) return svn_error_create (SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Got close-file element without preceding " "add-file or open-file")); else { const char *checksum = svn_xml_get_attr_value("checksum", atts); SVN_ERR(rb->editor->close_file(rb->file_baton, checksum, TOP_DIR(rb).file_pool)); rb->file_baton = NULL; } break; case ELEM_close_directory: if (rb->dirs->nelts == 0) return svn_error_create (SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, _("Got close-directory element without ever opening " "a directory")); else { dir_item_t *di = &TOP_DIR(rb); SVN_ERR(rb->editor->close_directory(di->baton, di->pool)); svn_pool_destroy(di->pool); apr_array_pop(rb->dirs); } break; case ELEM_change_file_prop: case ELEM_change_dir_prop: { const char *name = svn_xml_get_attr_value("name", atts); if (! name) return MISSING_ATTR(nspace, elt_name, "name"); else { svn_pool_clear(rb->prop_pool); if (svn_xml_get_attr_value("del", atts)) rb->prop_accum = NULL; else rb->prop_accum = svn_stringbuf_create("", rb->prop_pool); rb->prop_name = apr_pstrdup(rb->prop_pool, name); } } break; } *elem = elm->id; return SVN_NO_ERROR; }