void Path::addComponent(const char * component) { Pool pool; if (0 == component) return; // in case of an empty string, return if (*component == 0) return; // if the @a component is absolute, simply // use it if (isAbsolute(component)) { m_path = component; return; } if (Url::isValid(m_path.c_str())) { const char * newPath = svn_path_url_add_component(m_path.c_str(), component, pool); m_path = newPath; } else { svn_stringbuf_t * pathStringbuf = svn_stringbuf_create(m_path.c_str(), pool); svn_path_add_component(pathStringbuf, component); m_path = pathStringbuf->data; } }
svn_error_t * svn_ra_serf__get_location_segments(svn_ra_session_t *ra_session, const char *path, svn_revnum_t peg_revision, svn_revnum_t start_rev, svn_revnum_t end_rev, svn_location_segment_receiver_t receiver, void *receiver_baton, apr_pool_t *pool) { gls_context_t *gls_ctx; svn_ra_serf__session_t *session = ra_session->priv; svn_ra_serf__handler_t *handler; svn_ra_serf__xml_parser_t *parser_ctx; serf_bucket_t *buckets; const char *relative_url, *basecoll_url, *req_url; svn_error_t *err; gls_ctx = apr_pcalloc(pool, sizeof(*gls_ctx)); gls_ctx->receiver = receiver; gls_ctx->receiver_baton = receiver_baton; gls_ctx->subpool = svn_pool_create(pool); gls_ctx->inside_report = FALSE; gls_ctx->error = SVN_NO_ERROR; gls_ctx->done = FALSE; buckets = serf_bucket_aggregate_create(session->bkt_alloc); svn_ra_serf__add_open_tag_buckets(buckets, session->bkt_alloc, "S:get-location-segments", "xmlns:S", SVN_XML_NAMESPACE, NULL); svn_ra_serf__add_tag_buckets(buckets, "S:path", path, session->bkt_alloc); svn_ra_serf__add_tag_buckets(buckets, "S:peg-revision", apr_ltoa(pool, peg_revision), session->bkt_alloc); svn_ra_serf__add_tag_buckets(buckets, "S:start-revision", apr_ltoa(pool, start_rev), session->bkt_alloc); svn_ra_serf__add_tag_buckets(buckets, "S:end-revision", apr_ltoa(pool, end_rev), session->bkt_alloc); svn_ra_serf__add_close_tag_buckets(buckets, session->bkt_alloc, "S:get-location-segments"); SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url, session, NULL, NULL, peg_revision, NULL, pool)); req_url = svn_path_url_add_component(basecoll_url, relative_url, pool); handler = apr_pcalloc(pool, sizeof(*handler)); handler->method = "REPORT"; handler->path = req_url; handler->body_buckets = buckets; handler->body_type = "text/xml"; handler->conn = session->conns[0]; handler->session = session; parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx)); parser_ctx->pool = pool; parser_ctx->user_data = gls_ctx; parser_ctx->start = start_gls; parser_ctx->end = end_gls; parser_ctx->status_code = &gls_ctx->status_code; parser_ctx->done = &gls_ctx->done; handler->response_handler = svn_ra_serf__handle_xml_parser; handler->response_baton = parser_ctx; svn_ra_serf__request_create(handler); err = svn_ra_serf__context_run_wait(&gls_ctx->done, session, pool); if (gls_ctx->error || parser_ctx->error) { svn_error_clear(err); err = SVN_NO_ERROR; SVN_ERR(gls_ctx->error); SVN_ERR(parser_ctx->error); } if (gls_ctx->inside_report) err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, _("Location segment report failed on '%s'@'%ld'"), path, peg_revision); SVN_ERR(svn_ra_serf__error_on_status(gls_ctx->status_code, handler->path)); svn_pool_destroy(gls_ctx->subpool); if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)) return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, err, NULL); return err; }
/* Helper func for recursively fetching svn_dirent_t's from a remote directory and pushing them at an info-receiver callback. DEPTH is the depth starting at DIR, even though RECEIVER is never invoked on DIR: if DEPTH is svn_depth_immediates, then invoke RECEIVER on all children of DIR, but none of their children; if svn_depth_files, then invoke RECEIVER on file children of DIR but not on subdirectories; if svn_depth_infinity, recurse fully. */ static svn_error_t * push_dir_info(svn_ra_session_t *ra_session, const char *session_URL, const char *dir, svn_revnum_t rev, const char *repos_UUID, const char *repos_root, svn_info_receiver_t receiver, void *receiver_baton, svn_depth_t depth, svn_client_ctx_t *ctx, apr_hash_t *locks, apr_pool_t *pool) { apr_hash_t *tmpdirents; svn_dirent_t *the_ent; svn_info_t *info; apr_hash_index_t *hi; apr_pool_t *subpool = svn_pool_create(pool); SVN_ERR(svn_ra_get_dir2(ra_session, &tmpdirents, NULL, NULL, dir, rev, DIRENT_FIELDS, pool)); for (hi = apr_hash_first(pool, tmpdirents); hi; hi = apr_hash_next(hi)) { const char *path, *URL, *fs_path; const void *key; svn_lock_t *lock; void *val; svn_pool_clear(subpool); if (ctx->cancel_func) SVN_ERR(ctx->cancel_func(ctx->cancel_baton)); apr_hash_this(hi, &key, NULL, &val); the_ent = val; path = svn_path_join(dir, key, subpool); URL = svn_path_url_add_component(session_URL, key, subpool); fs_path = svn_path_is_child(repos_root, URL, subpool); fs_path = apr_pstrcat(subpool, "/", fs_path, NULL); fs_path = svn_path_uri_decode(fs_path, subpool); lock = apr_hash_get(locks, fs_path, APR_HASH_KEY_STRING); SVN_ERR(build_info_from_dirent(&info, the_ent, lock, URL, rev, repos_UUID, repos_root, subpool)); if (depth >= svn_depth_immediates || (depth == svn_depth_files && the_ent->kind == svn_node_file)) { SVN_ERR(receiver(receiver_baton, path, info, subpool)); } if (depth == svn_depth_infinity && the_ent->kind == svn_node_dir) { SVN_ERR(push_dir_info(ra_session, URL, path, rev, repos_UUID, repos_root, receiver, receiver_baton, depth, ctx, locks, subpool)); } } svn_pool_destroy(subpool); return SVN_NO_ERROR; }
svn_error_t *svn_ra_neon__get_baseline_info(svn_boolean_t *is_dir, svn_string_t *bc_url, svn_string_t *bc_relative, svn_revnum_t *latest_rev, svn_ra_neon__session_t *sess, const char *url, svn_revnum_t revision, apr_pool_t *pool) { svn_ra_neon__resource_t *baseline_rsrc, *rsrc; const svn_string_t *my_bc_url; svn_string_t my_bc_rel; /* Go fetch a BASELINE_RSRC that contains specific properties we want. This routine will also fill in BC_RELATIVE as best it can. */ SVN_ERR(svn_ra_neon__get_baseline_props(&my_bc_rel, &baseline_rsrc, sess, url, revision, baseline_props, /* specific props */ pool)); /* baseline_rsrc now points at the Baseline. We will checkout from the DAV:baseline-collection. The revision we are checking out is in DAV:version-name */ /* Allocate our own copy of bc_url regardless. */ my_bc_url = apr_hash_get(baseline_rsrc->propset, SVN_RA_NEON__PROP_BASELINE_COLLECTION, APR_HASH_KEY_STRING); if (my_bc_url == NULL) { /* ### better error reporting... */ /* ### need an SVN_ERR here */ return svn_error_create(APR_EGENERAL, NULL, _("'DAV:baseline-collection' was not present " "on the baseline resource")); } /* maybe return bc_url to the caller */ if (bc_url) *bc_url = *my_bc_url; if (latest_rev != NULL) { const svn_string_t *vsn_name= apr_hash_get(baseline_rsrc->propset, SVN_RA_NEON__PROP_VERSION_NAME, APR_HASH_KEY_STRING); if (vsn_name == NULL) { /* ### better error reporting... */ /* ### need an SVN_ERR here */ return svn_error_createf(APR_EGENERAL, NULL, _("'%s' was not present on the baseline " "resource"), "DAV:" SVN_DAV__VERSION_NAME); } *latest_rev = SVN_STR_TO_REV(vsn_name->data); } if (is_dir != NULL) { /* query the DAV:resourcetype of the full, assembled URL. */ const char *full_bc_url = svn_path_url_add_component(my_bc_url->data, my_bc_rel.data, pool); SVN_ERR(svn_ra_neon__get_starting_props(&rsrc, sess, full_bc_url, NULL, pool)); *is_dir = rsrc->is_collection; } if (bc_relative) *bc_relative = my_bc_rel; return SVN_NO_ERROR; }
svn_error_t * svn_ra_neon__do_stat(svn_ra_session_t *session, const char *path, svn_revnum_t revision, svn_dirent_t **dirent, apr_pool_t *pool) { svn_ra_neon__session_t *ras = session->priv; const char *url = ras->url->data; const char *final_url; apr_hash_t *resources; apr_hash_index_t *hi; svn_error_t *err; /* If we were given a relative path to append, append it. */ if (path) url = svn_path_url_add_component(url, path, pool); /* Invalid revision means HEAD, which is just the public URL. */ if (! SVN_IS_VALID_REVNUM(revision)) { final_url = url; } else { /* Else, convert (rev, path) into an opaque server-generated URL. */ svn_string_t bc_url, bc_relative; err = svn_ra_neon__get_baseline_info(NULL, &bc_url, &bc_relative, NULL, ras, url, revision, pool); if (err) { if (err->apr_err == SVN_ERR_FS_NOT_FOUND) { /* easy out: */ svn_error_clear(err); *dirent = NULL; return SVN_NO_ERROR; } else return err; } final_url = svn_path_url_add_component(bc_url.data, bc_relative.data, pool); } /* Depth-zero PROPFIND is the One True DAV Way. */ err = svn_ra_neon__get_props(&resources, ras, final_url, SVN_RA_NEON__DEPTH_ZERO, NULL, NULL /* all props */, pool); if (err) { if (err->apr_err == SVN_ERR_FS_NOT_FOUND) { /* easy out: */ svn_error_clear(err); *dirent = NULL; return SVN_NO_ERROR; } else return err; } /* Copying parsing code from svn_ra_neon__get_dir() here. The hash of resources only contains one item, but there's no other way to get the item. */ for (hi = apr_hash_first(pool, resources); hi; hi = apr_hash_next(hi)) { void *val; svn_ra_neon__resource_t *resource; const svn_string_t *propval; apr_hash_index_t *h; svn_dirent_t *entry; apr_hash_this(hi, NULL, NULL, &val); resource = val; entry = apr_pcalloc(pool, sizeof(*entry)); entry->kind = resource->is_collection ? svn_node_dir : svn_node_file; /* entry->size is already 0 by virtue of pcalloc(). */ if (entry->kind == svn_node_file) { propval = apr_hash_get(resource->propset, SVN_RA_NEON__PROP_GETCONTENTLENGTH, APR_HASH_KEY_STRING); if (propval) entry->size = svn__atoui64(propval->data); } /* does this resource contain any 'dead' properties? */ for (h = apr_hash_first(pool, resource->propset); h; h = apr_hash_next(h)) { const void *kkey; apr_hash_this(h, &kkey, NULL, NULL); if (strncmp((const char *)kkey, SVN_DAV_PROP_NS_CUSTOM, sizeof(SVN_DAV_PROP_NS_CUSTOM) - 1) == 0) entry->has_props = TRUE; else if (strncmp((const char *)kkey, SVN_DAV_PROP_NS_SVN, sizeof(SVN_DAV_PROP_NS_SVN) - 1) == 0) entry->has_props = TRUE; } /* created_rev & friends */ propval = apr_hash_get(resource->propset, SVN_RA_NEON__PROP_VERSION_NAME, APR_HASH_KEY_STRING); if (propval != NULL) entry->created_rev = SVN_STR_TO_REV(propval->data); propval = apr_hash_get(resource->propset, SVN_RA_NEON__PROP_CREATIONDATE, APR_HASH_KEY_STRING); if (propval != NULL) SVN_ERR(svn_time_from_cstring(&(entry->time), propval->data, pool)); propval = apr_hash_get(resource->propset, SVN_RA_NEON__PROP_CREATOR_DISPLAYNAME, APR_HASH_KEY_STRING); if (propval != NULL) entry->last_author = propval->data; *dirent = entry; } return SVN_NO_ERROR; }
svn_error_t * svn_ra_neon__do_check_path(svn_ra_session_t *session, const char *path, svn_revnum_t revision, svn_node_kind_t *kind, apr_pool_t *pool) { svn_ra_neon__session_t *ras = session->priv; const char *url = ras->url->data; svn_error_t *err; svn_boolean_t is_dir; /* ### For now, using svn_ra_neon__get_baseline_info() works because we only have three possibilities: dir, file, or none. When we add symlinks, we will need to do something different. Here's one way described by Greg Stein: That is a PROPFIND (Depth:0) for the DAV:resourcetype property. You can use the svn_ra_neon__get_one_prop() function to fetch it. If the PROPFIND fails with a 404, then you have svn_node_none. If the resulting property looks like: <D:resourcetype> <D:collection/> </D:resourcetype> Then it is a collection (directory; svn_node_dir). Otherwise, it is a regular resource (svn_node_file). The harder part is parsing the resourcetype property. "Proper" parsing means treating it as an XML property and looking for the DAV:collection element in there. To do that, however, means that get_one_prop() can't be used. I think there may be some Neon functions for parsing XML properties; we'd need to look. That would probably be the best approach. (an alternative is to use apr_xml_* parsing functions on the returned string; get back a DOM-like thing, and look for the element). */ /* If we were given a relative path to append, append it. */ if (path) url = svn_path_url_add_component(url, path, pool); err = svn_ra_neon__get_baseline_info(&is_dir, NULL, NULL, NULL, ras, url, revision, pool); if (err == SVN_NO_ERROR) { if (is_dir) *kind = svn_node_dir; else *kind = svn_node_file; } else if (err->apr_err == SVN_ERR_FS_NOT_FOUND) { svn_error_clear(err); err = SVN_NO_ERROR; *kind = svn_node_none; } return err; }
svn_error_t * svn_ra_neon__get_locations(svn_ra_session_t *session, apr_hash_t **locations, const char *relative_path, svn_revnum_t peg_revision, apr_array_header_t *location_revisions, apr_pool_t *pool) { svn_ra_neon__session_t *ras = session->priv; svn_stringbuf_t *request_body; svn_error_t *err; get_locations_baton_t request_baton; const char *relative_path_quoted; svn_string_t bc_url, bc_relative; const char *final_bc_url; int i; int status_code = 0; *locations = apr_hash_make(pool); request_body = svn_stringbuf_create("", pool); svn_stringbuf_appendcstr(request_body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>" DEBUG_CR "<S:get-locations xmlns:S=\"" SVN_XML_NAMESPACE "\" xmlns:D=\"DAV:\">" DEBUG_CR); svn_stringbuf_appendcstr(request_body, "<S:path>"); /* We need to escape the path XML-wise. */ relative_path_quoted = apr_xml_quote_string(pool, relative_path, 0); svn_stringbuf_appendcstr(request_body, relative_path_quoted); svn_stringbuf_appendcstr(request_body, "</S:path>" DEBUG_CR); svn_stringbuf_appendcstr(request_body, apr_psprintf(pool, "<S:peg-revision>%ld" "</S:peg-revision>" DEBUG_CR, peg_revision)); for (i = 0; i < location_revisions->nelts; ++i) { svn_revnum_t rev = APR_ARRAY_IDX(location_revisions, i, svn_revnum_t); svn_stringbuf_appendcstr(request_body, apr_psprintf(pool, "<S:location-revision>%ld" "</S:location-revision>" DEBUG_CR, rev)); } svn_stringbuf_appendcstr(request_body, "</S:get-locations>"); request_baton.ras = ras; request_baton.hash = *locations; request_baton.pool = pool; /* ras's URL may not exist in HEAD, and thus it's not safe to send it as the main argument to the REPORT request; it might cause dav_get_resource() to choke on the server. So instead, we pass a baseline-collection URL, which we get from the peg revision. */ SVN_ERR(svn_ra_neon__get_baseline_info(NULL, &bc_url, &bc_relative, NULL, ras, ras->url->data, peg_revision, pool)); final_bc_url = svn_path_url_add_component(bc_url.data, bc_relative.data, pool); err = svn_ra_neon__parsed_request(ras, "REPORT", final_bc_url, request_body->data, NULL, NULL, gloc_start_element, NULL, NULL, &request_baton, NULL, &status_code, FALSE, pool); /* Map status 501: Method Not Implemented to our not implemented error. 1.0.x servers and older don't support this report. */ if (status_code == 501) return svn_error_createf(SVN_ERR_RA_NOT_IMPLEMENTED, err, _("'%s' REPORT not implemented"), "get-locations"); return err; }