/* Send a property named NAME with value VAL in an element named ELEM_NAME. Quote NAME and base64-encode VAL if necessary. */ static svn_error_t * send_prop(struct file_rev_baton *frb, const char *elem_name, const char *name, const svn_string_t *val, apr_pool_t *pool) { name = apr_xml_quote_string(pool, name, 1); if (svn_xml_is_xml_safe(val->data, val->len)) { svn_stringbuf_t *tmp = NULL; svn_xml_escape_cdata_string(&tmp, val, pool); val = svn_string_create(tmp->data, pool); SVN_ERR(dav_svn__brigade_printf(frb->bb, frb->output, "<S:%s name=\"%s\">%s</S:%s>" DEBUG_CR, elem_name, name, val->data, elem_name)); } else { val = svn_base64_encode_string2(val, TRUE, pool); SVN_ERR(dav_svn__brigade_printf(frb->bb, frb->output, "<S:%s name=\"%s\" encoding=\"base64\">" "%s</S:%s>" DEBUG_CR, elem_name, name, val->data, elem_name)); } return SVN_NO_ERROR; }
/* Helper function for svn_ra_neon__do_proppatch() below. */ static void append_setprop(svn_stringbuf_t *body, const char *name, const svn_string_t *value, apr_pool_t *pool) { const char *encoding = ""; const char *xml_safe; const char *xml_tag_name; /* Map property names to namespaces */ #define NSLEN (sizeof(SVN_PROP_PREFIX) - 1) if (strncmp(name, SVN_PROP_PREFIX, NSLEN) == 0) { xml_tag_name = apr_pstrcat(pool, "S:", name + NSLEN, NULL); } #undef NSLEN else { xml_tag_name = apr_pstrcat(pool, "C:", name, NULL); } /* If there is no value, just generate an empty tag and get outta here. */ if (! value) { svn_stringbuf_appendcstr(body, apr_psprintf(pool, "<%s />", xml_tag_name)); return; } /* If a property is XML-safe, XML-encode it. Else, base64-encode it. */ if (svn_xml_is_xml_safe(value->data, value->len)) { svn_stringbuf_t *xml_esc = NULL; svn_xml_escape_cdata_string(&xml_esc, value, pool); xml_safe = xml_esc->data; } else { const svn_string_t *base64ed = svn_base64_encode_string2(value, TRUE, pool); encoding = " V:encoding=\"base64\""; xml_safe = base64ed->data; } svn_stringbuf_appendcstr(body, apr_psprintf(pool,"<%s %s>%s</%s>", xml_tag_name, encoding, xml_safe, xml_tag_name)); return; }
/* Set *MC_KEY to a memcache key for the given key KEY for CACHE, allocated in POOL. */ static svn_error_t * build_key(const char **mc_key, memcache_t *cache, const void *raw_key, apr_pool_t *pool) { const char *encoded_suffix; const char *long_key; apr_size_t long_key_len; if (cache->klen == APR_HASH_KEY_STRING) encoded_suffix = svn_path_uri_encode(raw_key, pool); else { const svn_string_t *raw = svn_string_ncreate(raw_key, cache->klen, pool); const svn_string_t *encoded = svn_base64_encode_string2(raw, FALSE, pool); encoded_suffix = encoded->data; } long_key = apr_pstrcat(pool, "SVN:", cache->prefix, ":", encoded_suffix, (char *)NULL); long_key_len = strlen(long_key); /* We don't want to have a key that's too big. If it was going to be too big, we MD5 the entire string, then replace the last bit with the checksum. Note that APR_MD5_DIGESTSIZE is for the pure binary digest; we have to double that when we convert to hex. Every key we use will either be at most MEMCACHED_KEY_UNHASHED_LEN bytes long, or be exactly MAX_MEMCACHED_KEY_LEN bytes long. */ if (long_key_len > MEMCACHED_KEY_UNHASHED_LEN) { svn_checksum_t *checksum; SVN_ERR(svn_checksum(&checksum, svn_checksum_md5, long_key, long_key_len, pool)); long_key = apr_pstrcat(pool, apr_pstrmemdup(pool, long_key, MEMCACHED_KEY_UNHASHED_LEN), svn_checksum_to_cstring_display(checksum, pool), (char *)NULL); } *mc_key = long_key; return SVN_NO_ERROR; }
const svn_string_t * SVNAuthData::encrypt_data(const svn_string_t *orig, apr_pool_t *pool) { DATA_BLOB blobin; DATA_BLOB blobout; const svn_string_t * crypted = NULL; blobin.cbData = (DWORD)orig->len; blobin.pbData = (BYTE *)orig->data; if (CryptProtectData(&blobin, description, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &blobout)) { const svn_string_t * crypt = svn_string_ncreate((const char *)blobout.pbData, blobout.cbData, pool); if (crypt) crypted = svn_base64_encode_string2(crypt, false, pool); LocalFree(blobout.pbData); } return crypted; }
static svn_error_t * change_file_or_dir_prop(const char *file_or_dir, edit_baton_t *eb, const char *name, const svn_string_t *value, apr_pool_t *pool) { const char *qname = apr_xml_quote_string(pool, name, 1); SVN_ERR(maybe_close_textdelta(eb)); if (value) { const svn_string_t *enc_value = svn_base64_encode_string2(value, TRUE, pool); /* Some versions of apr_brigade_vprintf() have a buffer overflow bug that can be triggered by just the wrong size of a large property value. The bug has been fixed (see http://svn.apache.org/viewvc?view=rev&revision=768417), but we need a workaround for the buggy APR versions, so we write our potentially large block of property data using a different underlying function. */ SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, "<S:change-%s-prop name=\"%s\">", file_or_dir, qname)); SVN_ERR(dav_svn__brigade_write(eb->bb, eb->output, enc_value->data, enc_value->len)); SVN_ERR(dav_svn__brigade_printf(eb->bb, eb->output, "</S:change-%s-prop>" DEBUG_CR, file_or_dir)); } else { SVN_ERR(dav_svn__brigade_printf (eb->bb, eb->output, "<S:change-%s-prop name=\"%s\" del=\"true\"/>" DEBUG_CR, file_or_dir, qname)); } return SVN_NO_ERROR; }
/* Implementation of svn_auth__password_set_t that encrypts the incoming password using the Windows CryptoAPI. */ static svn_error_t * windows_password_encrypter(svn_boolean_t *done, apr_hash_t *creds, const char *realmstring, const char *username, const char *in, apr_hash_t *parameters, svn_boolean_t non_interactive, apr_pool_t *pool) { const svn_string_t *coded; coded = encrypt_data(svn_string_create(in, pool), pool); if (coded) { coded = svn_base64_encode_string2(coded, FALSE, pool); SVN_ERR(svn_auth__simple_password_set(done, creds, realmstring, username, coded->data, parameters, non_interactive, pool)); } return SVN_NO_ERROR; }
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); }
static svn_error_t *try_auth(svn_ra_svn_conn_t *conn, sasl_conn_t *sasl_ctx, apr_pool_t *pool, server_baton_t *b, svn_boolean_t *success) { const char *out, *mech; const svn_string_t *arg = NULL, *in; unsigned int outlen; int result; svn_boolean_t use_base64; *success = FALSE; /* Read the client's chosen mech and the initial token. */ SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "w(?s)", &mech, &in)); if (strcmp(mech, "EXTERNAL") == 0 && !in) in = svn_string_create(b->tunnel_user, pool); else if (in) in = svn_base64_decode_string(in, pool); /* For CRAM-MD5, we don't base64-encode stuff. */ use_base64 = (strcmp(mech, "CRAM-MD5") != 0); result = sasl_server_start(sasl_ctx, mech, in ? in->data : NULL, in ? in->len : 0, &out, &outlen); if (result != SASL_OK && result != SASL_CONTINUE) return fail_auth(conn, pool, sasl_ctx); while (result == SASL_CONTINUE) { svn_ra_svn_item_t *item; arg = svn_string_ncreate(out, outlen, pool); /* Encode what we send to the client. */ if (use_base64) arg = svn_base64_encode_string2(arg, TRUE, pool); SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(s)", "step", arg)); /* Read and decode the client response. */ SVN_ERR(svn_ra_svn_read_item(conn, pool, &item)); if (item->kind != SVN_RA_SVN_STRING) return SVN_NO_ERROR; in = item->u.string; if (use_base64) in = svn_base64_decode_string(in, pool); result = sasl_server_step(sasl_ctx, in->data, in->len, &out, &outlen); } if (result != SASL_OK) return fail_auth(conn, pool, sasl_ctx); /* Send our last response, if necessary. */ if (outlen) arg = svn_base64_encode_string2(svn_string_ncreate(out, outlen, pool), TRUE, pool); else arg = NULL; *success = TRUE; SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "w(?s)", "success", arg)); return SVN_NO_ERROR; }
static dav_error * db_output_value(dav_db *db, const dav_prop_name *name, dav_xmlns_info *xi, apr_text_header *phdr, int *found) { const char *prefix; const char *s; svn_string_t *propval; dav_error *err; apr_pool_t *pool = db->resource->pool; if ((err = get_value(db, name, &propval)) != NULL) return err; /* return whether the prop was found, then punt or handle it. */ *found = (propval != NULL); if (propval == NULL) return NULL; if (strcmp(name->ns, SVN_DAV_PROP_NS_CUSTOM) == 0) prefix = "C:"; else prefix = "S:"; if (propval->len == 0) { /* empty value. add an empty elem. */ s = apr_psprintf(pool, "<%s%s/>" DEBUG_CR, prefix, name->name); apr_text_append(pool, phdr, s); } else { /* add <prefix:name [V:encoding="base64"]>value</prefix:name> */ const char *xml_safe; const char *encoding = ""; /* Ensure XML-safety of our property values before sending them across the wire. */ if (! svn_xml_is_xml_safe(propval->data, propval->len)) { const svn_string_t *enc_propval = svn_base64_encode_string2(propval, TRUE, pool); xml_safe = enc_propval->data; encoding = " V:encoding=\"base64\""; } else { svn_stringbuf_t *xmlval = NULL; svn_xml_escape_cdata_string(&xmlval, propval, pool); xml_safe = xmlval->data; } s = apr_psprintf(pool, "<%s%s%s>", prefix, name->name, encoding); apr_text_append(pool, phdr, s); /* the value is in our pool which means it has the right lifetime. */ /* ### at least, per the current mod_dav architecture/API */ apr_text_append(pool, phdr, xml_safe); s = apr_psprintf(pool, "</%s%s>" DEBUG_CR, prefix, name->name); apr_text_append(pool, phdr, s); } return NULL; }
/* Transmit LOCKS (a hash of Subversion filesystem locks keyed by path) across OUTPUT using BB. Use POOL for necessary allocations. NOTE: As written, this function currently returns one of only two status values -- "success", and "we had trouble writing out to the output stream". If you need to return something more interesting, you'll probably want to generate dav_error's here instead of passing back only apr_status_t's. */ static apr_status_t send_get_lock_response(apr_hash_t *locks, ap_filter_t *output, apr_bucket_brigade *bb, apr_pool_t *pool) { apr_pool_t *subpool; apr_hash_index_t *hi; /* start sending report */ SVN_APR_ERR(ap_fprintf(output, bb, DAV_XML_HEADER DEBUG_CR "<S:get-locks-report xmlns:S=\"" SVN_XML_NAMESPACE "\" xmlns:D=\"DAV:\">" DEBUG_CR)); /* stream the locks */ subpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) { void *val; const svn_lock_t *lock; svn_pool_clear(subpool); apr_hash_this(hi, NULL, NULL, &val); lock = val; /* Begin the <S:lock> tag, transmitting the path, token, and creation date. */ SVN_APR_ERR(ap_fprintf(output, bb, "<S:lock>" DEBUG_CR "<S:path>%s</S:path>" DEBUG_CR "<S:token>%s</S:token>" DEBUG_CR "<S:creationdate>%s</S:creationdate>" DEBUG_CR, apr_xml_quote_string(subpool, lock->path, 1), apr_xml_quote_string(subpool, lock->token, 1), svn_time_to_cstring(lock->creation_date, subpool))); /* Got expiration date? Tell the client. */ if (lock->expiration_date) SVN_APR_ERR(ap_fprintf(output, bb, "<S:expirationdate>%s</S:expirationdate>" DEBUG_CR, svn_time_to_cstring(lock->expiration_date, subpool))); /* Transmit the lock ownership information. */ if (lock->owner) { const char *owner; svn_boolean_t owner_base64 = FALSE; if (svn_xml_is_xml_safe(lock->owner, strlen(lock->owner))) { owner = apr_xml_quote_string(subpool, lock->owner, 1); } else { svn_string_t owner_string; const svn_string_t *encoded_owner; owner_string.data = lock->owner; owner_string.len = strlen(lock->owner); encoded_owner = svn_base64_encode_string2(&owner_string, TRUE, subpool); owner = encoded_owner->data; owner_base64 = TRUE; } SVN_APR_ERR(ap_fprintf(output, bb, "<S:owner %s>%s</S:owner>" DEBUG_CR, owner_base64 ? "encoding=\"base64\"" : "", owner)); } /* If the lock carries a comment, transmit that, too. */ if (lock->comment) { const char *comment; svn_boolean_t comment_base64 = FALSE; if (svn_xml_is_xml_safe(lock->comment, strlen(lock->comment))) { comment = apr_xml_quote_string(subpool, lock->comment, 1); } else { svn_string_t comment_string; const svn_string_t *encoded_comment; comment_string.data = lock->comment; comment_string.len = strlen(lock->comment); encoded_comment = svn_base64_encode_string2(&comment_string, TRUE, subpool); comment = encoded_comment->data; comment_base64 = TRUE; } SVN_APR_ERR(ap_fprintf(output, bb, "<S:comment %s>%s</S:comment>" DEBUG_CR, comment_base64 ? "encoding=\"base64\"" : "", comment)); } /* Okay, finish up this lock by closing the <S:lock> tag. */ SVN_APR_ERR(ap_fprintf(output, bb, "</S:lock>" DEBUG_CR)); } svn_pool_destroy(subpool); /* Finish the report */ SVN_APR_ERR(ap_fprintf(output, bb, "</S:get-locks-report>" DEBUG_CR)); return APR_SUCCESS; }
static svn_error_t *try_auth(svn_ra_svn_conn_t *conn, sasl_conn_t *sasl_ctx, apr_pool_t *pool, server_baton_t *b, svn_boolean_t *success) { const char *out, *mech; const svn_string_t *arg = NULL, *in; unsigned int outlen; int result; svn_boolean_t use_base64; *success = FALSE; /* Read the client's chosen mech and the initial token. */ SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?s)", &mech, &in)); if (strcmp(mech, "EXTERNAL") == 0 && !in) in = svn_string_create(b->client_info->tunnel_user, pool); else if (in) in = svn_base64_decode_string(in, pool); /* For CRAM-MD5, we don't base64-encode stuff. */ use_base64 = (strcmp(mech, "CRAM-MD5") != 0); /* sasl uses unsigned int for the length of strings, we use apr_size_t * which may not be the same size. Deal with potential integer overflow */ if (in && in->len > UINT_MAX) return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Initial token is too long")); result = svn_sasl__server_start(sasl_ctx, mech, in ? in->data : NULL, in ? (unsigned int) in->len : 0, &out, &outlen); if (result != SASL_OK && result != SASL_CONTINUE) return fail_auth(conn, pool, sasl_ctx); while (result == SASL_CONTINUE) { svn_ra_svn__item_t *item; arg = svn_string_ncreate(out, outlen, pool); /* Encode what we send to the client. */ if (use_base64) arg = svn_base64_encode_string2(arg, TRUE, pool); SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(s)", "step", arg)); /* Read and decode the client response. */ SVN_ERR(svn_ra_svn__read_item(conn, pool, &item)); if (item->kind != SVN_RA_SVN_STRING) return SVN_NO_ERROR; in = &item->u.string; if (use_base64) in = svn_base64_decode_string(in, pool); if (in->len > UINT_MAX) return svn_error_createf(SVN_ERR_RA_NOT_AUTHORIZED, NULL, _("Step response is too long")); result = svn_sasl__server_step(sasl_ctx, in->data, (unsigned int) in->len, &out, &outlen); } if (result != SASL_OK) return fail_auth(conn, pool, sasl_ctx); /* Send our last response, if necessary. */ if (outlen) arg = svn_base64_encode_string2(svn_string_ncreate(out, outlen, pool), TRUE, pool); else arg = NULL; *success = TRUE; SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(?s)", "success", arg)); return SVN_NO_ERROR; }
/* This implements `svn_log_entry_receiver_t'. BATON is a `struct log_receiver_baton *'. */ static svn_error_t * log_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool) { struct log_receiver_baton *lrb = baton; apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(maybe_send_header(lrb)); if (log_entry->revision == SVN_INVALID_REVNUM) { /* If the stack depth is zero, we've seen the last revision, so don't send it, just return. The footer will be sent later. */ if (lrb->stack_depth == 0) return SVN_NO_ERROR; else lrb->stack_depth--; } SVN_ERR(dav_svn__brigade_printf(lrb->bb, lrb->output, "<S:log-item>" DEBUG_CR "<D:version-name>%ld" "</D:version-name>" DEBUG_CR, log_entry->revision)); if (log_entry->revprops) { apr_hash_index_t *hi; for (hi = apr_hash_first(pool, log_entry->revprops); hi != NULL; hi = apr_hash_next(hi)) { char *name; void *val; const svn_string_t *value; const char *encoding_str = ""; svn_pool_clear(iterpool); apr_hash_this(hi, (void *)&name, NULL, &val); value = val; /* If the client is okay with us encoding binary (or really, any non-XML-safe) property values, do so as necessary. */ if (lrb->encode_binary_props) { if (! svn_xml_is_xml_safe(value->data, value->len)) { value = svn_base64_encode_string2(value, TRUE, iterpool); encoding_str = " encoding=\"base64\""; } } if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<D:creator-displayname%s>%s</D:creator-displayname>" DEBUG_CR, encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) /* ### this should be DAV:creation-date, but we need to format ### that date a bit differently */ SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:date%s>%s</S:date>" DEBUG_CR, encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<D:comment%s>%s</D:comment>" DEBUG_CR, encoding_str, apr_xml_quote_string(pool, svn_xml_fuzzy_escape(value->data, iterpool), 0))); else SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:revprop name=\"%s\"%s>%s</S:revprop>" DEBUG_CR, apr_xml_quote_string(iterpool, name, 0), encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); } } if (log_entry->has_children) { SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:has-children/>")); lrb->stack_depth++; } if (log_entry->subtractive_merge) SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:subtractive-merge/>")); if (log_entry->changed_paths2) { apr_hash_index_t *hi; char *path; for (hi = apr_hash_first(pool, log_entry->changed_paths2); hi != NULL; hi = apr_hash_next(hi)) { void *val; svn_log_changed_path2_t *log_item; const char *close_element = NULL; svn_pool_clear(iterpool); apr_hash_this(hi, (void *) &path, NULL, &val); log_item = val; /* ### todo: is there a D: namespace equivalent for `changed-path'? Should use it if so. */ switch (log_item->action) { case 'A': if (log_item->copyfrom_path && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:added-path copyfrom-path=\"%s\"" " copyfrom-rev=\"%ld\"", apr_xml_quote_string(iterpool, log_item->copyfrom_path, 1), /* escape quotes */ log_item->copyfrom_rev)); else SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:added-path")); close_element = "S:added-path"; break; case 'R': if (log_item->copyfrom_path && SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:replaced-path copyfrom-path=\"%s\"" " copyfrom-rev=\"%ld\"", apr_xml_quote_string(iterpool, log_item->copyfrom_path, 1), /* escape quotes */ log_item->copyfrom_rev)); else SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:replaced-path")); close_element = "S:replaced-path"; break; case 'D': SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:deleted-path")); close_element = "S:deleted-path"; break; case 'M': SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:modified-path")); close_element = "S:modified-path"; break; default: break; } /* If we need to close the element, then send the attributes that apply to all changed items and then close the element. */ if (close_element) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, " node-kind=\"%s\"" " text-mods=\"%s\"" " prop-mods=\"%s\">%s</%s>" DEBUG_CR, svn_node_kind_to_word(log_item->node_kind), svn_tristate__to_word(log_item->text_modified), svn_tristate__to_word(log_item->props_modified), apr_xml_quote_string(iterpool, path, 0), close_element)); } } svn_pool_destroy(iterpool); SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "</S:log-item>" DEBUG_CR)); return SVN_NO_ERROR; }
/* This implements `svn_log_entry_receiver_t'. BATON is a `struct log_receiver_baton *'. */ static svn_error_t * log_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool) { struct log_receiver_baton *lrb = baton; apr_pool_t *iterpool = svn_pool_create(pool); SVN_ERR(maybe_send_header(lrb)); if (log_entry->revision == SVN_INVALID_REVNUM) { /* If the stack depth is zero, we've seen the last revision, so don't send it, just return. The footer will be sent later. */ if (lrb->stack_depth == 0) return SVN_NO_ERROR; else lrb->stack_depth--; } SVN_ERR(dav_svn__brigade_printf(lrb->bb, lrb->output, "<S:log-item>" DEBUG_CR "<D:version-name>%ld" "</D:version-name>" DEBUG_CR, log_entry->revision)); if (log_entry->revprops) { apr_hash_index_t *hi; for (hi = apr_hash_first(pool, log_entry->revprops); hi != NULL; hi = apr_hash_next(hi)) { char *name; void *val; const svn_string_t *value; const char *encoding_str = ""; svn_pool_clear(iterpool); apr_hash_this(hi, (void *)&name, NULL, &val); value = val; /* If the client is okay with us encoding binary (or really, any non-XML-safe) property values, do so as necessary. */ if (lrb->encode_binary_props) { if (! svn_xml_is_xml_safe(value->data, value->len)) { value = svn_base64_encode_string2(value, TRUE, iterpool); encoding_str = " encoding=\"base64\""; } } if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<D:creator-displayname%s>%s</D:creator-displayname>" DEBUG_CR, encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0) /* ### this should be DAV:creation-date, but we need to format ### that date a bit differently */ SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:date%s>%s</S:date>" DEBUG_CR, encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<D:comment%s>%s</D:comment>" DEBUG_CR, encoding_str, apr_xml_quote_string(pool, svn_xml_fuzzy_escape(value->data, iterpool), 0))); else SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, "<S:revprop name=\"%s\"%s>%s</S:revprop>" DEBUG_CR, apr_xml_quote_string(iterpool, name, 0), encoding_str, apr_xml_quote_string(iterpool, value->data, 0))); } } if (log_entry->has_children) { SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:has-children/>")); lrb->stack_depth++; } if (log_entry->subtractive_merge) SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:subtractive-merge/>")); if (log_entry->changed_paths2) { apr_hash_index_t *hi; char *path; for (hi = apr_hash_first(pool, log_entry->changed_paths2); hi != NULL; hi = apr_hash_next(hi)) { void *val; svn_log_changed_path2_t *log_item; const char *close_element = NULL; svn_pool_clear(iterpool); apr_hash_this(hi, (void *) &path, NULL, &val); log_item = val; /* ### todo: is there a D: namespace equivalent for `changed-path'? Should use it if so. */ switch (log_item->action) { case 'A': case 'R': SVN_ERR(start_path_with_copy_from(&close_element, lrb, log_item, iterpool)); break; case 'D': SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:deleted-path")); close_element = "S:deleted-path"; break; case 'M': SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "<S:modified-path")); close_element = "S:modified-path"; break; default: break; } /* If we need to close the element, then send the attributes that apply to all changed items and then close the element. */ if (close_element) SVN_ERR(dav_svn__brigade_printf (lrb->bb, lrb->output, " node-kind=\"%s\"" " text-mods=\"%s\"" " prop-mods=\"%s\">%s</%s>" DEBUG_CR, svn_node_kind_to_word(log_item->node_kind), svn_tristate__to_word(log_item->text_modified), svn_tristate__to_word(log_item->props_modified), apr_xml_quote_string(iterpool, path, 0), close_element)); } } svn_pool_destroy(iterpool); SVN_ERR(dav_svn__brigade_puts(lrb->bb, lrb->output, "</S:log-item>" DEBUG_CR)); /* In general APR will flush the brigade every 8000 bytes through the filter stack, but log items may not be generated that fast, especially in combination with authz and busy servers. We now explictly flush after log-item 4, 16, 64 and 256 to produce a few results fast. This introduces 4 full flushes of our brigade and the installed output filters at growing intervals and then falls back to the standard buffering of 8000 bytes + whatever buffers are added in output filters. */ lrb->result_count++; if (lrb->result_count == lrb->next_forced_flush) { apr_status_t apr_err; /* This flush is similar to that in dav_svn__final_flush_or_error(). Compared to using ap_filter_flush(), which we use in other place this adds a flush frame before flushing the brigade, to make output filters perform a flush as well */ /* No brigade empty check. We want output filters to flush anyway */ apr_err = ap_fflush(lrb->output, lrb->bb); if (apr_err) return svn_error_create(apr_err, NULL, NULL); /* Check for an aborted connection, just like our brigade write helper functions, since the brigade functions don't appear to be return useful errors when the connection is dropped. */ if (lrb->output->c->aborted) return svn_error_create(SVN_ERR_APMOD_CONNECTION_ABORTED, NULL, NULL); if (lrb->result_count < 256) lrb->next_forced_flush = lrb->next_forced_flush * 4; } return SVN_NO_ERROR; }