In the command-line client, forbid
[svn.git] / subversion / libsvn_ra_serf / log.c
blob4159dea6bdec2ba70a5a8ac1b82218a78034d7b8
1 /*
2 * log.c : entry point for log RA functions for ra_serf
4 * ====================================================================
5 * Copyright (c) 2006-2007 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_uri.h>
23 #include <expat.h>
25 #include <serf.h>
27 #include "svn_pools.h"
28 #include "svn_ra.h"
29 #include "svn_dav.h"
30 #include "svn_xml.h"
31 #include "../libsvn_ra/ra_loader.h"
32 #include "svn_config.h"
33 #include "svn_delta.h"
34 #include "svn_version.h"
35 #include "svn_path.h"
37 #include "private/svn_dav_protocol.h"
38 #include "svn_private_config.h"
40 #include "ra_serf.h"
44 * This enum represents the current state of our XML parsing for a REPORT.
46 typedef enum {
47 NONE = 0,
48 REPORT,
49 ITEM,
50 VERSION,
51 CREATOR,
52 DATE,
53 COMMENT,
54 REVPROP,
55 HAS_CHILDREN,
56 ADDED_PATH,
57 REPLACED_PATH,
58 DELETED_PATH,
59 MODIFIED_PATH,
60 } log_state_e;
62 typedef struct {
63 apr_pool_t *pool;
65 /* The currently collected value as we build it up */
66 const char *tmp;
67 apr_size_t tmp_len;
69 /* Temporary change path - ultimately inserted into changed_paths hash. */
70 svn_log_changed_path_t *tmp_path;
72 /* Log information */
73 svn_log_entry_t *log_entry;
75 /* Place to hold revprop name. */
76 const char *revprop_name;
77 } log_info_t;
79 typedef struct {
80 apr_pool_t *pool;
82 /* parameters set by our caller */
83 int limit;
84 int count;
85 svn_boolean_t changed_paths;
87 /* are we done? */
88 svn_boolean_t done;
89 int status_code;
91 /* log receiver function and baton */
92 svn_log_entry_receiver_t receiver;
93 void *receiver_baton;
95 /* pre-1.5 compatibility */
96 svn_boolean_t want_author;
97 svn_boolean_t want_date;
98 svn_boolean_t want_message;
99 } log_context_t;
102 static log_info_t *
103 push_state(svn_ra_serf__xml_parser_t *parser,
104 log_context_t *log_ctx,
105 log_state_e state)
107 svn_ra_serf__xml_push_state(parser, state);
109 if (state == ITEM)
111 log_info_t *info;
113 info = apr_pcalloc(parser->state->pool, sizeof(*info));
114 info->log_entry = svn_log_entry_create(parser->state->pool);
116 info->pool = parser->state->pool;
117 info->log_entry->revision = SVN_INVALID_REVNUM;
119 parser->state->private = info;
122 if (state == ADDED_PATH || state == REPLACED_PATH ||
123 state == DELETED_PATH || state == MODIFIED_PATH)
125 log_info_t *info = parser->state->private;
127 if (!info->log_entry->changed_paths)
129 info->log_entry->changed_paths = apr_hash_make(info->pool);
132 info->tmp_path = apr_pcalloc(info->pool, sizeof(*info->tmp_path));
133 info->tmp_path->copyfrom_rev = SVN_INVALID_REVNUM;
136 if (state == CREATOR || state == DATE || state == COMMENT
137 || state == REVPROP)
139 log_info_t *info = parser->state->private;
141 if (!info->log_entry->revprops)
143 info->log_entry->revprops = apr_hash_make(info->pool);
147 return parser->state->private;
150 static svn_error_t *
151 start_log(svn_ra_serf__xml_parser_t *parser,
152 void *userData,
153 svn_ra_serf__dav_props_t name,
154 const char **attrs)
156 log_context_t *log_ctx = userData;
157 log_state_e state;
159 state = parser->state->current_state;
161 if (state == NONE &&
162 strcmp(name.name, "log-report") == 0)
164 push_state(parser, log_ctx, REPORT);
166 else if (state == REPORT &&
167 strcmp(name.name, "log-item") == 0)
169 log_ctx->count++;
170 if (log_ctx->limit && log_ctx->count > log_ctx->limit)
172 return SVN_NO_ERROR;
175 push_state(parser, log_ctx, ITEM);
177 else if (state == ITEM)
179 log_info_t *info;
181 if (strcmp(name.name, SVN_DAV__VERSION_NAME) == 0)
183 push_state(parser, log_ctx, VERSION);
185 else if (strcmp(name.name, "creator-displayname") == 0)
187 push_state(parser, log_ctx, CREATOR);
189 else if (strcmp(name.name, "date") == 0)
191 push_state(parser, log_ctx, DATE);
193 else if (strcmp(name.name, "comment") == 0)
195 push_state(parser, log_ctx, COMMENT);
197 else if (strcmp(name.name, "revprop") == 0)
199 info = push_state(parser, log_ctx, REVPROP);
200 info->revprop_name = svn_xml_get_attr_value("name", attrs);
201 if (info->revprop_name == NULL)
202 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
203 _("Missing name attr in revprop element"));
205 else if (strcmp(name.name, "has-children") == 0)
207 push_state(parser, log_ctx, HAS_CHILDREN);
209 else if (strcmp(name.name, "added-path") == 0)
211 const char *copy_path, *copy_rev_str;
213 info = push_state(parser, log_ctx, ADDED_PATH);
214 info->tmp_path->action = 'A';
216 copy_path = svn_xml_get_attr_value("copyfrom-path", attrs);
217 copy_rev_str = svn_xml_get_attr_value("copyfrom-rev", attrs);
218 if (copy_path && copy_rev_str)
220 svn_revnum_t copy_rev;
222 copy_rev = SVN_STR_TO_REV(copy_rev_str);
223 if (SVN_IS_VALID_REVNUM(copy_rev))
225 info->tmp_path->copyfrom_path = apr_pstrdup(info->pool,
226 copy_path);
227 info->tmp_path->copyfrom_rev = copy_rev;
231 else if (strcmp(name.name, "replaced-path") == 0)
233 const char *copy_path, *copy_rev_str;
235 info = push_state(parser, log_ctx, REPLACED_PATH);
236 info->tmp_path->action = 'R';
238 copy_path = svn_xml_get_attr_value("copyfrom-path", attrs);
239 copy_rev_str = svn_xml_get_attr_value("copyfrom-rev", attrs);
240 if (copy_path && copy_rev_str)
242 svn_revnum_t copy_rev;
244 copy_rev = SVN_STR_TO_REV(copy_rev_str);
245 if (SVN_IS_VALID_REVNUM(copy_rev))
247 info->tmp_path->copyfrom_path = apr_pstrdup(info->pool,
248 copy_path);
249 info->tmp_path->copyfrom_rev = copy_rev;
253 else if (strcmp(name.name, "deleted-path") == 0)
255 info = push_state(parser, log_ctx, DELETED_PATH);
256 info->tmp_path->action = 'D';
258 else if (strcmp(name.name, "modified-path") == 0)
260 info = push_state(parser, log_ctx, MODIFIED_PATH);
261 info->tmp_path->action = 'M';
265 return SVN_NO_ERROR;
268 static svn_error_t *
269 end_log(svn_ra_serf__xml_parser_t *parser,
270 void *userData,
271 svn_ra_serf__dav_props_t name)
273 log_context_t *log_ctx = userData;
274 log_state_e state;
275 log_info_t *info;
277 state = parser->state->current_state;
278 info = parser->state->private;
280 if (state == REPORT &&
281 strcmp(name.name, "log-report") == 0)
283 svn_ra_serf__xml_pop_state(parser);
285 else if (state == ITEM &&
286 strcmp(name.name, "log-item") == 0)
288 /* Give the info to the reporter */
289 SVN_ERR(log_ctx->receiver(log_ctx->receiver_baton,
290 info->log_entry,
291 info->pool));
293 svn_ra_serf__xml_pop_state(parser);
295 else if (state == VERSION &&
296 strcmp(name.name, SVN_DAV__VERSION_NAME) == 0)
298 info->log_entry->revision = SVN_STR_TO_REV(info->tmp);
299 info->tmp_len = 0;
300 svn_ra_serf__xml_pop_state(parser);
302 else if (state == CREATOR &&
303 strcmp(name.name, "creator-displayname") == 0)
305 if (log_ctx->want_author)
307 apr_hash_set(info->log_entry->revprops, SVN_PROP_REVISION_AUTHOR,
308 APR_HASH_KEY_STRING,
309 svn_string_ncreate(info->tmp, info->tmp_len,
310 info->pool));
312 info->tmp_len = 0;
313 svn_ra_serf__xml_pop_state(parser);
315 else if (state == DATE &&
316 strcmp(name.name, "date") == 0)
318 if (log_ctx->want_date)
320 apr_hash_set(info->log_entry->revprops, SVN_PROP_REVISION_DATE,
321 APR_HASH_KEY_STRING,
322 svn_string_ncreate(info->tmp, info->tmp_len,
323 info->pool));
325 info->tmp_len = 0;
326 svn_ra_serf__xml_pop_state(parser);
328 else if (state == COMMENT &&
329 strcmp(name.name, "comment") == 0)
331 if (log_ctx->want_message)
333 apr_hash_set(info->log_entry->revprops, SVN_PROP_REVISION_LOG,
334 APR_HASH_KEY_STRING,
335 svn_string_ncreate(info->tmp, info->tmp_len,
336 info->pool));
338 info->tmp_len = 0;
339 svn_ra_serf__xml_pop_state(parser);
341 else if (state == REVPROP)
343 apr_hash_set(info->log_entry->revprops, info->revprop_name,
344 APR_HASH_KEY_STRING,
345 svn_string_ncreate(info->tmp, info->tmp_len, info->pool));
346 info->tmp_len = 0;
347 svn_ra_serf__xml_pop_state(parser);
349 else if (state == HAS_CHILDREN &&
350 strcmp(name.name, "has-children") == 0)
352 info->log_entry->has_children = TRUE;
353 svn_ra_serf__xml_pop_state(parser);
355 else if ((state == ADDED_PATH &&
356 strcmp(name.name, "added-path") == 0) ||
357 (state == DELETED_PATH &&
358 strcmp(name.name, "deleted-path") == 0) ||
359 (state == MODIFIED_PATH &&
360 strcmp(name.name, "modified-path") == 0) ||
361 (state == REPLACED_PATH &&
362 strcmp(name.name, "replaced-path") == 0))
364 char *path;
366 path = apr_pstrmemdup(info->pool, info->tmp, info->tmp_len);
367 info->tmp_len = 0;
369 apr_hash_set(info->log_entry->changed_paths, path, APR_HASH_KEY_STRING,
370 info->tmp_path);
371 svn_ra_serf__xml_pop_state(parser);
374 return SVN_NO_ERROR;
377 static svn_error_t *
378 cdata_log(svn_ra_serf__xml_parser_t *parser,
379 void *userData,
380 const char *data,
381 apr_size_t len)
383 log_context_t *log_ctx = userData;
384 log_state_e state;
385 log_info_t *info;
387 UNUSED_CTX(log_ctx);
389 state = parser->state->current_state;
390 info = parser->state->private;
392 switch (state)
394 case VERSION:
395 case CREATOR:
396 case DATE:
397 case COMMENT:
398 case REVPROP:
399 case ADDED_PATH:
400 case REPLACED_PATH:
401 case DELETED_PATH:
402 case MODIFIED_PATH:
403 svn_ra_serf__expand_string(&info->tmp, &info->tmp_len,
404 data, len, parser->state->pool);
405 break;
406 default:
407 break;
410 return SVN_NO_ERROR;
413 svn_error_t *
414 svn_ra_serf__get_log(svn_ra_session_t *ra_session,
415 const apr_array_header_t *paths,
416 svn_revnum_t start,
417 svn_revnum_t end,
418 int limit,
419 svn_boolean_t discover_changed_paths,
420 svn_boolean_t strict_node_history,
421 svn_boolean_t include_merged_revisions,
422 apr_array_header_t *revprops,
423 svn_log_entry_receiver_t receiver,
424 void *receiver_baton,
425 apr_pool_t *pool)
427 log_context_t *log_ctx;
428 svn_ra_serf__session_t *session = ra_session->priv;
429 svn_ra_serf__handler_t *handler;
430 svn_ra_serf__xml_parser_t *parser_ctx;
431 serf_bucket_t *buckets, *tmp;
432 svn_boolean_t want_custom_revprops;
433 svn_revnum_t peg_rev;
434 const char *relative_url, *basecoll_url, *req_url;
435 svn_error_t *err;
437 log_ctx = apr_pcalloc(pool, sizeof(*log_ctx));
438 log_ctx->pool = pool;
439 log_ctx->receiver = receiver;
440 log_ctx->receiver_baton = receiver_baton;
441 log_ctx->limit = limit;
442 log_ctx->changed_paths = discover_changed_paths;
443 log_ctx->done = FALSE;
445 buckets = serf_bucket_aggregate_create(session->bkt_alloc);
447 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<S:log-report xmlns:S=\"",
448 sizeof("<S:log-report xmlns:S=\"")-1,
449 session->bkt_alloc);
450 serf_bucket_aggregate_append(buckets, tmp);
452 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(SVN_XML_NAMESPACE,
453 sizeof(SVN_XML_NAMESPACE)-1,
454 session->bkt_alloc);
455 serf_bucket_aggregate_append(buckets, tmp);
457 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\">",
458 sizeof("\">")-1,
459 session->bkt_alloc);
460 serf_bucket_aggregate_append(buckets, tmp);
462 svn_ra_serf__add_tag_buckets(buckets,
463 "S:start-revision", apr_ltoa(pool, start),
464 session->bkt_alloc);
465 svn_ra_serf__add_tag_buckets(buckets,
466 "S:end-revision", apr_ltoa(pool, end),
467 session->bkt_alloc);
469 if (limit)
471 svn_ra_serf__add_tag_buckets(buckets,
472 "S:limit", apr_ltoa(pool, limit),
473 session->bkt_alloc);
476 if (discover_changed_paths)
478 svn_ra_serf__add_tag_buckets(buckets,
479 "S:discover-changed-paths", NULL,
480 session->bkt_alloc);
483 if (strict_node_history)
485 svn_ra_serf__add_tag_buckets(buckets,
486 "S:strict-node-history", NULL,
487 session->bkt_alloc);
490 if (include_merged_revisions)
492 svn_ra_serf__add_tag_buckets(buckets,
493 "S:include-merged-revisions", NULL,
494 session->bkt_alloc);
497 want_custom_revprops = FALSE;
498 if (revprops)
500 int i;
501 for (i = 0; i < revprops->nelts; i++)
503 char *name = APR_ARRAY_IDX(revprops, i, char *);
504 svn_ra_serf__add_tag_buckets(buckets,
505 "S:revprop", name,
506 session->bkt_alloc);
507 if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0)
508 log_ctx->want_author = TRUE;
509 else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0)
510 log_ctx->want_date = TRUE;
511 else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0)
512 log_ctx->want_message = TRUE;
513 else
514 want_custom_revprops = TRUE;
517 else
519 svn_ra_serf__add_tag_buckets(buckets,
520 "S:all-revprops", NULL,
521 session->bkt_alloc);
522 log_ctx->want_author = log_ctx->want_date = log_ctx->want_message = TRUE;
523 want_custom_revprops = TRUE;
526 if (want_custom_revprops)
528 svn_boolean_t has_log_revprops;
529 SVN_ERR(svn_ra_has_capability(ra_session, &has_log_revprops,
530 SVN_RA_CAPABILITY_LOG_REVPROPS, pool));
531 if (!has_log_revprops)
532 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL,
533 _("Server does not support custom revprops"
534 " via log"));
537 if (paths)
539 int i;
540 for (i = 0; i < paths->nelts; i++)
542 svn_ra_serf__add_tag_buckets(buckets,
543 "S:path", APR_ARRAY_IDX(paths, i,
544 const char*),
545 session->bkt_alloc);
549 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("</S:log-report>",
550 sizeof("</S:log-report>")-1,
551 session->bkt_alloc);
552 serf_bucket_aggregate_append(buckets, tmp);
554 /* At this point, we may have a deleted file. So, we'll match ra_neon's
555 * behavior and use the larger of start or end as our 'peg' rev.
557 peg_rev = (start > end) ? start : end;
559 SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url,
560 session, NULL, peg_rev, pool));
562 req_url = svn_path_url_add_component(basecoll_url, relative_url, pool);
564 handler = apr_pcalloc(pool, sizeof(*handler));
566 handler->method = "REPORT";
567 handler->path = req_url;
568 handler->body_buckets = buckets;
569 handler->body_type = "text/xml";
570 handler->conn = session->conns[0];
571 handler->session = session;
573 parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
575 parser_ctx->pool = pool;
576 parser_ctx->user_data = log_ctx;
577 parser_ctx->start = start_log;
578 parser_ctx->end = end_log;
579 parser_ctx->cdata = cdata_log;
580 parser_ctx->done = &log_ctx->done;
581 parser_ctx->status_code = &log_ctx->status_code;
583 handler->response_handler = svn_ra_serf__handle_xml_parser;
584 handler->response_baton = parser_ctx;
586 svn_ra_serf__request_create(handler);
588 err = svn_ra_serf__context_run_wait(&log_ctx->done, session, pool);
590 if (parser_ctx->error)
592 svn_error_clear(err);
593 SVN_ERR(parser_ctx->error);
596 return err;