/* Validate that property NAME is valid for use in a Subversion repository; return SVN_ERR_REPOS_BAD_ARGS if it isn't. For some "svn:" properties, also validate the value, and return SVN_ERR_BAD_PROPERTY_VALUE if it is not valid. Use POOL for temporary allocations. */ static svn_error_t * validate_prop(const char *name, const svn_string_t *value, apr_pool_t *pool) { svn_prop_kind_t kind = svn_property_kind(NULL, name); /* Disallow setting non-regular properties. */ if (kind != svn_prop_regular_kind) return svn_error_createf (SVN_ERR_REPOS_BAD_ARGS, NULL, _("Storage of non-regular property '%s' is disallowed through the " "repository interface, and could indicate a bug in your client"), name); /* Validate "svn:" properties. */ if (svn_prop_is_svn_prop(name) && value != NULL) { /* Validate that translated props (e.g., svn:log) are UTF-8 with * LF line endings. */ if (svn_prop_needs_translation(name)) { if (svn_utf__is_valid(value->data, value->len) == FALSE) { return svn_error_createf (SVN_ERR_BAD_PROPERTY_VALUE, NULL, _("Cannot accept '%s' property because it is not encoded in " "UTF-8"), name); } /* Disallow inconsistent line ending style, by simply looking for * carriage return characters ('\r'). */ if (strchr(value->data, '\r') != NULL) { return svn_error_createf (SVN_ERR_BAD_PROPERTY_VALUE, NULL, _("Cannot accept non-LF line endings in '%s' property"), name); } } /* "svn:date" should be a valid date. */ if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) { apr_time_t temp; svn_error_t *err; err = svn_time_from_cstring(&temp, value->data, pool); if (err) return svn_error_create(SVN_ERR_BAD_PROPERTY_VALUE, err, NULL); } } return SVN_NO_ERROR; }
/* Return an SVN_ERR_CLIENT_PROPERTY_NAME error if NAME is a wcprop, else return SVN_NO_ERROR. */ static svn_error_t * error_if_wcprop_name(const char *name) { if (svn_property_kind(NULL, name) == svn_prop_wc_kind) { return svn_error_createf (SVN_ERR_CLIENT_PROPERTY_NAME, NULL, _("'%s' is a wcprop, thus not accessible to clients"), name); } return SVN_NO_ERROR; }
/* An editor function, implementing both change_file_prop and * change_dir_prop. */ static svn_error_t * change_prop(void *entry_baton, const char *name, const svn_string_t *value, apr_pool_t *pool) { struct item_baton *ib = entry_baton; if (svn_property_kind(NULL, name) == svn_prop_regular_kind) { ensure_summarize(ib); ib->summarize->prop_changed = TRUE; } return SVN_NO_ERROR; }
/* Subversion delta editor callback */ static svn_error_t *de_change_dir_prop(void *dir_baton, const char *name, const svn_string_t *value, apr_pool_t *pool) { de_node_baton_t *node = (de_node_baton_t *)dir_baton; /* We're only interested in regular properties */ if (svn_property_kind(NULL, name) != svn_prop_regular_kind) { return SVN_NO_ERROR; } DEBUG_MSG("de_change_dir_prop(%s) %s = %s\n", ((de_node_baton_t *)dir_baton)->path, name, value ? value->data : NULL); if (value != NULL) { apr_hash_set(node->properties, apr_pstrdup(node->pool, name), APR_HASH_KEY_STRING, svn_string_dup(value, node->pool)); } else { apr_hash_set(node->del_properties, apr_pstrdup(node->pool, name), APR_HASH_KEY_STRING, (void *)0x1); } node->props_changed = 1; node->dump_needed = 1; return SVN_NO_ERROR; }
/* Helper for the remote case of svn_client_proplist. * * Push a new 'svn_client_proplist_item_t *' item onto PROPLIST, * containing the properties for "TARGET_PREFIX/TARGET_RELATIVE" in * REVNUM, obtained using RA_LIB and SESSION. The item->node_name * will be "TARGET_PREFIX/TARGET_RELATIVE", and the value will be a * hash mapping 'const char *' property names onto 'svn_string_t *' * property values. * * Allocate the new item and its contents in POOL. * Do all looping, recursion, and temporary work in SCRATCHPOOL. * * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE". * * If the target is a directory, only fetch properties for the files * and directories at depth DEPTH. */ static svn_error_t * remote_proplist(const char *target_prefix, const char *target_relative, svn_node_kind_t kind, svn_revnum_t revnum, svn_ra_session_t *ra_session, svn_depth_t depth, svn_proplist_receiver_t receiver, void *receiver_baton, apr_pool_t *pool, apr_pool_t *scratchpool) { apr_hash_t *dirents; apr_hash_t *prop_hash, *final_hash; apr_hash_index_t *hi; const char *target_full_url = svn_path_url_add_component2(target_prefix, target_relative, scratchpool); if (kind == svn_node_dir) { SVN_ERR(svn_ra_get_dir2(ra_session, (depth > svn_depth_empty) ? &dirents : NULL, NULL, &prop_hash, target_relative, revnum, SVN_DIRENT_KIND, scratchpool)); } else if (kind == svn_node_file) { SVN_ERR(svn_ra_get_file(ra_session, target_relative, revnum, NULL, NULL, &prop_hash, scratchpool)); } else { return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("Unknown node kind for '%s'"), target_full_url); } /* Filter out non-regular properties, since the RA layer returns all kinds. Copy regular properties keys/vals from the prop_hash allocated in SCRATCHPOOL to the "final" hash allocated in POOL. */ final_hash = apr_hash_make(pool); for (hi = apr_hash_first(scratchpool, prop_hash); hi; hi = apr_hash_next(hi)) { const char *name = svn__apr_hash_index_key(hi); apr_ssize_t klen = svn__apr_hash_index_klen(hi); svn_string_t *value = svn__apr_hash_index_val(hi); svn_prop_kind_t prop_kind; prop_kind = svn_property_kind(NULL, name); if (prop_kind == svn_prop_regular_kind) { name = apr_pstrdup(pool, name); value = svn_string_dup(value, pool); apr_hash_set(final_hash, name, klen, value); } } SVN_ERR(call_receiver(target_full_url, final_hash, receiver, receiver_baton, pool)); if (depth > svn_depth_empty && (kind == svn_node_dir) && (apr_hash_count(dirents) > 0)) { apr_pool_t *subpool = svn_pool_create(scratchpool); for (hi = apr_hash_first(scratchpool, dirents); hi; hi = apr_hash_next(hi)) { const char *this_name = svn__apr_hash_index_key(hi); svn_dirent_t *this_ent = svn__apr_hash_index_val(hi); const char *new_target_relative; svn_pool_clear(subpool); new_target_relative = svn_relpath_join(target_relative, this_name, subpool); if (this_ent->kind == svn_node_file || depth > svn_depth_files) { svn_depth_t depth_below_here = depth; if (depth == svn_depth_immediates) depth_below_here = svn_depth_empty; SVN_ERR(remote_proplist(target_prefix, new_target_relative, this_ent->kind, revnum, ra_session, depth_below_here, receiver, receiver_baton, pool, subpool)); } } svn_pool_destroy(subpool); } return SVN_NO_ERROR; }
static svn_error_t * propset_on_url(const char *propname, const svn_string_t *propval, const char *target, svn_boolean_t skip_checks, svn_revnum_t base_revision_for_url, const apr_hash_t *revprop_table, svn_commit_callback2_t commit_callback, void *commit_baton, svn_client_ctx_t *ctx, apr_pool_t *pool) { enum svn_prop_kind prop_kind = svn_property_kind(NULL, propname); svn_ra_session_t *ra_session; svn_node_kind_t node_kind; const char *message; const svn_delta_editor_t *editor; void *edit_baton; apr_hash_t *commit_revprops; svn_error_t *err; if (prop_kind != svn_prop_regular_kind) return svn_error_createf (SVN_ERR_BAD_PROP_KIND, NULL, _("Property '%s' is not a regular property"), propname); /* Open an RA session for the URL. Note that we don't have a local directory, nor a place to put temp files. */ SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, target, NULL, NULL, FALSE, TRUE, ctx, pool)); SVN_ERR(svn_ra_check_path(ra_session, "", base_revision_for_url, &node_kind, pool)); if (node_kind == svn_node_none) return svn_error_createf (SVN_ERR_FS_NOT_FOUND, NULL, _("Path '%s' does not exist in revision %ld"), target, base_revision_for_url); /* Setting an inappropriate property is not allowed (unless overridden by 'skip_checks', in some circumstances). Deleting an inappropriate property is allowed, however, since older clients allowed (and other clients possibly still allow) setting it in the first place. */ if (propval && svn_prop_is_svn_prop(propname)) { const svn_string_t *new_value; struct getter_baton gb; gb.ra_session = ra_session; gb.base_revision_for_url = base_revision_for_url; SVN_ERR(svn_wc_canonicalize_svn_prop(&new_value, propname, propval, target, node_kind, skip_checks, get_file_for_validation, &gb, pool)); propval = new_value; } /* Create a new commit item and add it to the array. */ if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx)) { svn_client_commit_item3_t *item; const char *tmp_file; apr_array_header_t *commit_items = apr_array_make(pool, 1, sizeof(item)); item = svn_client_commit_item3_create(pool); item->url = target; item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS; APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item; SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items, ctx, pool)); if (! message) return SVN_NO_ERROR; } else message = ""; SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table, message, ctx, pool)); /* Fetch RA commit editor. */ SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton, commit_revprops, commit_callback, commit_baton, NULL, TRUE, /* No lock tokens */ pool)); err = do_url_propset(propname, propval, node_kind, base_revision_for_url, editor, edit_baton, pool); if (err) { /* At least try to abort the edit (and fs txn) before throwing err. */ svn_error_clear(editor->abort_edit(edit_baton, pool)); return svn_error_trace(err); } /* Close the edit. */ return editor->close_edit(edit_baton, pool); }