2
* log.c: handle the log-report request and response
4
* ====================================================================
5
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
7
* This software is licensed as described in the file COPYING, which
8
* you should have received as part of this distribution. The terms
9
* are also available at http://subversion.tigris.org/license-1.html.
10
* If newer versions of this license are posted there, you may use a
11
* newer version instead, at your option.
13
* This software consists of voluntary contributions made by many
14
* individuals. For exact contribution history, see the revision
15
* history and logs, available at http://subversion.tigris.org/.
16
* ====================================================================
21
#include <apr_pools.h>
22
#include <apr_strings.h>
26
#include "svn_repos.h"
27
#include "svn_types.h"
35
struct log_receiver_baton
37
/* this buffers the output for a bit and is automatically flushed,
38
at appropriate times, by the Apache filter system. */
39
apr_bucket_brigade *bb;
41
/* where to deliver the output */
44
/* Whether we've written the <S:log-report> header. Allows for lazy
45
writes to support mod_dav-based error handling. */
46
svn_boolean_t needs_header;
50
/* If LRB->needs_header is true, send the "<S:log-report>" start
51
element and set LRB->needs_header to zero. Else do nothing.
52
This is basically duplicated in file_revs.c. Consider factoring if
54
static svn_error_t * maybe_send_header(struct log_receiver_baton *lrb)
56
if (lrb->needs_header)
58
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
59
DAV_XML_HEADER DEBUG_CR
60
"<S:log-report xmlns:S=\"" SVN_XML_NAMESPACE
61
"\" " "xmlns:D=\"DAV:\">" DEBUG_CR) );
62
lrb->needs_header = FALSE;
67
/* This implements `svn_log_message_receiver_t'.
68
BATON is a `struct log_receiver_baton *'. */
69
static svn_error_t * log_receiver(void *baton,
70
apr_hash_t *changed_paths,
77
struct log_receiver_baton *lrb = baton;
79
SVN_ERR( maybe_send_header(lrb) );
81
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
82
"<S:log-item>" DEBUG_CR "<D:version-name>%ld"
83
"</D:version-name>" DEBUG_CR, rev) );
86
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
87
"<D:creator-displayname>%s"
88
"</D:creator-displayname>" DEBUG_CR,
89
apr_xml_quote_string(pool, author, 0)) );
91
/* ### this should be DAV:creation-date, but we need to format
92
### that date a bit differently */
94
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
95
"<S:date>%s</S:date>" DEBUG_CR,
96
apr_xml_quote_string(pool, date, 0)) );
99
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
100
"<D:comment>%s</D:comment>" DEBUG_CR,
102
(pool, svn_xml_fuzzy_escape (msg, pool), 0)) );
107
apr_hash_index_t *hi;
110
for (hi = apr_hash_first(pool, changed_paths);
112
hi = apr_hash_next(hi))
115
svn_log_changed_path_t *log_item;
117
apr_hash_this(hi, (void *) &path, NULL, &val);
120
/* ### todo: is there a D: namespace equivalent for
121
`changed-path'? Should use it if so. */
122
switch (log_item->action)
125
if (log_item->copyfrom_path
126
&& SVN_IS_VALID_REVNUM(log_item->copyfrom_rev))
127
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
129
" copyfrom-path=\"%s\""
130
" copyfrom-rev=\"%ld\">"
131
"%s</S:added-path>" DEBUG_CR,
134
log_item->copyfrom_path,
135
1), /* escape quotes */
136
log_item->copyfrom_rev,
137
apr_xml_quote_string(pool,
140
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
141
"<S:added-path>%s</S:added-path>"
143
apr_xml_quote_string(pool, path,
148
if (log_item->copyfrom_path
149
&& SVN_IS_VALID_REVNUM(log_item->copyfrom_rev))
150
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
152
" copyfrom-path=\"%s\""
153
" copyfrom-rev=\"%ld\">"
154
"%s</S:replaced-path>" DEBUG_CR,
157
log_item->copyfrom_path,
158
1), /* escape quotes */
159
log_item->copyfrom_rev,
160
apr_xml_quote_string(pool,
163
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
164
"<S:replaced-path>%s"
165
"</S:replaced-path>" DEBUG_CR,
166
apr_xml_quote_string(pool, path,
171
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
172
"<S:deleted-path>%s</S:deleted-path>"
174
apr_xml_quote_string(pool, path,
179
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output,
180
"<S:modified-path>%s"
181
"</S:modified-path>" DEBUG_CR,
182
apr_xml_quote_string(pool, path, 0)) );
191
SVN_ERR( dav_svn__send_xml(lrb->bb, lrb->output, "</S:log-item>" DEBUG_CR) );
199
dav_error * dav_svn__log_report(const dav_resource *resource,
200
const apr_xml_doc *doc,
204
apr_status_t apr_err;
205
dav_error *derr = NULL;
207
struct log_receiver_baton lrb;
208
dav_svn_authz_read_baton arb;
209
const dav_svn_repos *repos = resource->info->repos;
210
const char *target = NULL;
214
/* These get determined from the request document. */
215
svn_revnum_t start = SVN_INVALID_REVNUM; /* defaults to HEAD */
216
svn_revnum_t end = SVN_INVALID_REVNUM; /* defaults to HEAD */
217
svn_boolean_t discover_changed_paths = 0; /* off by default */
218
svn_boolean_t strict_node_history = 0; /* off by default */
219
apr_array_header_t *paths
220
= apr_array_make(resource->pool, 0, sizeof(const char *));
223
ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
226
return dav_new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
227
"The request does not contain the 'svn:' "
228
"namespace, so it is not going to have certain "
229
"required elements.",
230
SVN_DAV_ERROR_NAMESPACE,
234
/* ### todo: okay, now go fill in svn_ra_dav__get_log() based on the
235
syntax implied below... */
236
for (child = doc->root->first_child; child != NULL; child = child->next)
238
/* if this element isn't one of ours, then skip it */
242
if (strcmp(child->name, "start-revision") == 0)
243
start = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
244
else if (strcmp(child->name, "end-revision") == 0)
245
end = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
246
else if (strcmp(child->name, "limit") == 0)
247
limit = atoi(child->first_cdata.first->text);
248
else if (strcmp(child->name, "discover-changed-paths") == 0)
249
discover_changed_paths = 1; /* presence indicates positivity */
250
else if (strcmp(child->name, "strict-node-history") == 0)
251
strict_node_history = 1; /* presence indicates positivity */
252
else if (strcmp(child->name, "path") == 0)
254
const char *rel_path = dav_xml_get_cdata(child, resource->pool, 0);
255
if ((derr = dav_svn__test_canonical (rel_path, resource->pool)))
257
target = svn_path_join(resource->info->repos_path, rel_path,
259
(*((const char **)(apr_array_push (paths)))) = target;
261
/* else unknown element; skip it */
264
/* Build authz read baton */
265
arb.r = resource->info->r;
266
arb.repos = resource->info->repos;
268
/* Build log receiver baton */
269
lrb.bb = apr_brigade_create(resource->pool, /* not the subpool! */
270
output->c->bucket_alloc);
272
lrb.needs_header = TRUE;
274
/* Our svn_log_message_receiver_t sends the <S:log-report> header in
275
a lazy fashion. Before writing the first log message, it assures
276
that the header has already been sent (checking the needs_header
277
flag in our log_receiver_baton structure). */
279
/* Send zero or more log items. */
280
serr = svn_repos_get_logs3(repos->repos,
285
discover_changed_paths,
294
derr = dav_svn_convert_err(serr, HTTP_BAD_REQUEST, serr->message,
299
if ((serr = maybe_send_header(&lrb)))
301
derr = dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
302
"Error beginning REPORT response.",
307
if ((serr = dav_svn__send_xml(lrb.bb, lrb.output, "</S:log-report>"
310
derr = dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
311
"Error ending REPORT response.",
318
/* Flush the contents of the brigade (returning an error only if we
319
don't already have one). */
320
if (((apr_err = ap_fflush(output, lrb.bb))) && (! derr))
321
derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
322
HTTP_INTERNAL_SERVER_ERROR,
323
"Error flushing brigade.",