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 * ====================================================================
27 #include "svn_pools.h"
31 #include "../libsvn_ra/ra_loader.h"
32 #include "svn_config.h"
33 #include "svn_delta.h"
34 #include "svn_version.h"
37 #include "private/svn_dav_protocol.h"
38 #include "svn_private_config.h"
44 * This enum represents the current state of our XML parsing for a REPORT.
65 /* The currently collected value as we build it up */
69 /* Temporary change path - ultimately inserted into changed_paths hash. */
70 svn_log_changed_path_t
*tmp_path
;
73 svn_log_entry_t
*log_entry
;
75 /* Place to hold revprop name. */
76 const char *revprop_name
;
82 /* parameters set by our caller */
85 svn_boolean_t changed_paths
;
91 /* log receiver function and baton */
92 svn_log_entry_receiver_t receiver
;
95 /* pre-1.5 compatibility */
96 svn_boolean_t want_author
;
97 svn_boolean_t want_date
;
98 svn_boolean_t want_message
;
103 push_state(svn_ra_serf__xml_parser_t
*parser
,
104 log_context_t
*log_ctx
,
107 svn_ra_serf__xml_push_state(parser
, state
);
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
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;
151 start_log(svn_ra_serf__xml_parser_t
*parser
,
153 svn_ra_serf__dav_props_t name
,
156 log_context_t
*log_ctx
= userData
;
159 state
= parser
->state
->current_state
;
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)
170 if (log_ctx
->limit
&& log_ctx
->count
> log_ctx
->limit
)
175 push_state(parser
, log_ctx
, ITEM
);
177 else if (state
== ITEM
)
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
,
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
,
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';
269 end_log(svn_ra_serf__xml_parser_t
*parser
,
271 svn_ra_serf__dav_props_t name
)
273 log_context_t
*log_ctx
= userData
;
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
,
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
);
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
,
309 svn_string_ncreate(info
->tmp
, info
->tmp_len
,
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
,
322 svn_string_ncreate(info
->tmp
, info
->tmp_len
,
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
,
335 svn_string_ncreate(info
->tmp
, info
->tmp_len
,
339 svn_ra_serf__xml_pop_state(parser
);
341 else if (state
== REVPROP
)
343 apr_hash_set(info
->log_entry
->revprops
, info
->revprop_name
,
345 svn_string_ncreate(info
->tmp
, info
->tmp_len
, info
->pool
));
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))
366 path
= apr_pstrmemdup(info
->pool
, info
->tmp
, info
->tmp_len
);
369 apr_hash_set(info
->log_entry
->changed_paths
, path
, APR_HASH_KEY_STRING
,
371 svn_ra_serf__xml_pop_state(parser
);
378 cdata_log(svn_ra_serf__xml_parser_t
*parser
,
383 log_context_t
*log_ctx
= userData
;
389 state
= parser
->state
->current_state
;
390 info
= parser
->state
->private;
403 svn_ra_serf__expand_string(&info
->tmp
, &info
->tmp_len
,
404 data
, len
, parser
->state
->pool
);
414 svn_ra_serf__get_log(svn_ra_session_t
*ra_session
,
415 const apr_array_header_t
*paths
,
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
,
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
;
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,
450 serf_bucket_aggregate_append(buckets
, tmp
);
452 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(SVN_XML_NAMESPACE
,
453 sizeof(SVN_XML_NAMESPACE
)-1,
455 serf_bucket_aggregate_append(buckets
, tmp
);
457 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\">",
460 serf_bucket_aggregate_append(buckets
, tmp
);
462 svn_ra_serf__add_tag_buckets(buckets
,
463 "S:start-revision", apr_ltoa(pool
, start
),
465 svn_ra_serf__add_tag_buckets(buckets
,
466 "S:end-revision", apr_ltoa(pool
, end
),
471 svn_ra_serf__add_tag_buckets(buckets
,
472 "S:limit", apr_ltoa(pool
, limit
),
476 if (discover_changed_paths
)
478 svn_ra_serf__add_tag_buckets(buckets
,
479 "S:discover-changed-paths", NULL
,
483 if (strict_node_history
)
485 svn_ra_serf__add_tag_buckets(buckets
,
486 "S:strict-node-history", NULL
,
490 if (include_merged_revisions
)
492 svn_ra_serf__add_tag_buckets(buckets
,
493 "S:include-merged-revisions", NULL
,
497 want_custom_revprops
= FALSE
;
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
,
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
;
514 want_custom_revprops
= TRUE
;
519 svn_ra_serf__add_tag_buckets(buckets
,
520 "S:all-revprops", NULL
,
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"
540 for (i
= 0; i
< paths
->nelts
; i
++)
542 svn_ra_serf__add_tag_buckets(buckets
,
543 "S:path", APR_ARRAY_IDX(paths
, i
,
549 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("</S:log-report>",
550 sizeof("</S:log-report>")-1,
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
);