log.c revision 266731
138494Sobrien/*
2174294Sobrien * log.c :  entry point for log RA functions for ra_serf
338494Sobrien *
438494Sobrien * ====================================================================
538494Sobrien *    Licensed to the Apache Software Foundation (ASF) under one
638494Sobrien *    or more contributor license agreements.  See the NOTICE file
738494Sobrien *    distributed with this work for additional information
838494Sobrien *    regarding copyright ownership.  The ASF licenses this file
938494Sobrien *    to you under the Apache License, Version 2.0 (the
1038494Sobrien *    "License"); you may not use this file except in compliance
1138494Sobrien *    with the License.  You may obtain a copy of the License at
1238494Sobrien *
1338494Sobrien *      http://www.apache.org/licenses/LICENSE-2.0
1438494Sobrien *
1538494Sobrien *    Unless required by applicable law or agreed to in writing,
1638494Sobrien *    software distributed under the License is distributed on an
1738494Sobrien *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
1838494Sobrien *    KIND, either express or implied.  See the License for the
1938494Sobrien *    specific language governing permissions and limitations
2042629Sobrien *    under the License.
2138494Sobrien * ====================================================================
2238494Sobrien */
2338494Sobrien
2438494Sobrien
2538494Sobrien
2638494Sobrien#include <apr_uri.h>
2738494Sobrien#include <serf.h>
2838494Sobrien
2938494Sobrien#include "svn_hash.h"
3038494Sobrien#include "svn_pools.h"
3138494Sobrien#include "svn_ra.h"
3238494Sobrien#include "svn_dav.h"
3338494Sobrien#include "svn_base64.h"
3438494Sobrien#include "svn_xml.h"
3538494Sobrien#include "svn_config.h"
3638494Sobrien#include "svn_path.h"
3738494Sobrien#include "svn_props.h"
3838494Sobrien
3938494Sobrien#include "private/svn_dav_protocol.h"
40174294Sobrien#include "private/svn_string_private.h"
4138494Sobrien#include "private/svn_subr_private.h"
4238494Sobrien#include "svn_private_config.h"
4338494Sobrien
4438494Sobrien#include "ra_serf.h"
4538494Sobrien#include "../libsvn_ra/ra_loader.h"
4638494Sobrien
4738494Sobrien
4838494Sobrien/*
4938494Sobrien * This enum represents the current state of our XML parsing for a REPORT.
5038494Sobrien */
5138494Sobrienenum {
5238494Sobrien  INITIAL = 0,
5338494Sobrien  REPORT,
5438494Sobrien  ITEM,
5538494Sobrien  VERSION,
5638494Sobrien  CREATOR,
5738494Sobrien  DATE,
5838494Sobrien  COMMENT,
5938494Sobrien  REVPROP,
6038494Sobrien  HAS_CHILDREN,
6138494Sobrien  ADDED_PATH,
6238494Sobrien  REPLACED_PATH,
6338494Sobrien  DELETED_PATH,
6438494Sobrien  MODIFIED_PATH,
6538494Sobrien  SUBTRACTIVE_MERGE
6638494Sobrien};
6738494Sobrien
6838494Sobrientypedef struct log_context_t {
6938494Sobrien  apr_pool_t *pool;
7038494Sobrien
7138494Sobrien  /* parameters set by our caller */
7238494Sobrien  const apr_array_header_t *paths;
7338494Sobrien  svn_revnum_t start;
7438494Sobrien  svn_revnum_t end;
7538494Sobrien  int limit;
7638494Sobrien  svn_boolean_t changed_paths;
7738494Sobrien  svn_boolean_t strict_node_history;
78174294Sobrien  svn_boolean_t include_merged_revisions;
7938494Sobrien  const apr_array_header_t *revprops;
80174294Sobrien  int nest_level; /* used to track mergeinfo nesting levels */
8138494Sobrien  int count; /* only incremented when nest_level == 0 */
8238494Sobrien
8338494Sobrien  /* Collect information for storage into a log entry. Most of the entry
84174294Sobrien     members are collected by individual states. revprops and paths are
8538494Sobrien     N datapoints per entry.  */
8638494Sobrien  apr_hash_t *collect_revprops;
87174294Sobrien  apr_hash_t *collect_paths;
88174294Sobrien
89174294Sobrien  /* log receiver function and baton */
9052894Sobrien  svn_log_entry_receiver_t receiver;
9138494Sobrien  void *receiver_baton;
9238494Sobrien
9338494Sobrien  /* pre-1.5 compatibility */
9438494Sobrien  svn_boolean_t want_author;
9538494Sobrien  svn_boolean_t want_date;
9638494Sobrien  svn_boolean_t want_message;
9738494Sobrien} log_context_t;
98174294Sobrien
9938494Sobrien#define D_ "DAV:"
100174294Sobrien#define S_ SVN_XML_NAMESPACE
10138494Sobrienstatic const svn_ra_serf__xml_transition_t log_ttable[] = {
10238494Sobrien  { INITIAL, S_, "log-report", REPORT,
103174294Sobrien    FALSE, { NULL }, FALSE },
10438494Sobrien
105174294Sobrien  /* Note that we have an opener here. We need to construct a new LOG_ENTRY
10638494Sobrien     to record multiple paths.  */
10738494Sobrien  { REPORT, S_, "log-item", ITEM,
10838494Sobrien    FALSE, { NULL }, TRUE },
10938494Sobrien
110174294Sobrien  { ITEM, D_, SVN_DAV__VERSION_NAME, VERSION,
111174294Sobrien    TRUE, { NULL }, TRUE },
112131702Smbr
11382794Sobrien  { ITEM, D_, "creator-displayname", CREATOR,
11438494Sobrien    TRUE, { "?encoding", NULL }, TRUE },
115174294Sobrien
116174294Sobrien  { ITEM, S_, "date", DATE,
117174294Sobrien    TRUE, { "?encoding", NULL }, TRUE },
11838494Sobrien
119174294Sobrien  { ITEM, D_, "comment", COMMENT,
120174294Sobrien    TRUE, { "?encoding", NULL }, TRUE },
121174294Sobrien
12282794Sobrien  { ITEM, S_, "revprop", REVPROP,
12338494Sobrien    TRUE, { "name", "?encoding", NULL }, TRUE },
12438494Sobrien
125174294Sobrien  { ITEM, S_, "has-children", HAS_CHILDREN,
12638494Sobrien    FALSE, { NULL }, TRUE },
12738494Sobrien
12838494Sobrien  { ITEM, S_, "subtractive-merge", SUBTRACTIVE_MERGE,
12938494Sobrien    FALSE, { NULL }, TRUE },
13038494Sobrien
13138494Sobrien  { ITEM, S_, "added-path", ADDED_PATH,
13238494Sobrien    TRUE, { "?node-kind", "?text-mods", "?prop-mods",
13382794Sobrien            "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
13438494Sobrien
135174294Sobrien  { ITEM, S_, "replaced-path", REPLACED_PATH,
13638494Sobrien    TRUE, { "?node-kind", "?text-mods", "?prop-mods",
137174294Sobrien            "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
13852894Sobrien
13938494Sobrien  { ITEM, S_, "deleted-path", DELETED_PATH,
140174294Sobrien    TRUE, { "?node-kind", "?text-mods", "?prop-mods", NULL }, TRUE },
14138494Sobrien
14238494Sobrien  { ITEM, S_, "modified-path", MODIFIED_PATH,
14338494Sobrien    TRUE, { "?node-kind", "?text-mods", "?prop-mods", NULL }, TRUE },
144174294Sobrien
14538494Sobrien  { 0 }
14638494Sobrien};
14738494Sobrien
14838494Sobrien
14938494Sobrien/* Store CDATA into REVPROPS, associated with PROPNAME. If ENCODING is not
150174294Sobrien   NULL, then it must base "base64" and CDATA will be decoded first.
15138494Sobrien
15238494Sobrien   NOTE: PROPNAME must live longer than REVPROPS.  */
15338494Sobrienstatic svn_error_t *
15438494Sobriencollect_revprop(apr_hash_t *revprops,
15538494Sobrien                const char *propname,
156174294Sobrien                const svn_string_t *cdata,
157174294Sobrien                const char *encoding)
15838494Sobrien{
15938494Sobrien  apr_pool_t *result_pool = apr_hash_pool_get(revprops);
160174294Sobrien  const svn_string_t *decoded;
16138494Sobrien
162174294Sobrien  if (encoding)
16338494Sobrien    {
16438494Sobrien      /* Check for a known encoding type.  This is easy -- there's
16538494Sobrien         only one.  */
166174294Sobrien      if (strcmp(encoding, "base64") != 0)
16738494Sobrien        {
16838494Sobrien          return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
169174294Sobrien                                   _("Unsupported encoding '%s'"),
170174294Sobrien                                   encoding);
171174294Sobrien        }
17238494Sobrien
17352894Sobrien      decoded = svn_base64_decode_string(cdata, result_pool);
17438494Sobrien    }
17538494Sobrien  else
17638494Sobrien    {
17738494Sobrien      decoded = svn_string_dup(cdata, result_pool);
17838494Sobrien    }
17938494Sobrien
180174294Sobrien  /* Caller has ensured PROPNAME has sufficient lifetime.  */
18138494Sobrien  svn_hash_sets(revprops, propname, decoded);
182174294Sobrien
18338494Sobrien  return SVN_NO_ERROR;
18438494Sobrien}
185174294Sobrien
18638494Sobrien
187174294Sobrien/* Record ACTION on the path in CDATA into PATHS. Other properties about
18838494Sobrien   the action are pulled from ATTRS.  */
18938494Sobrienstatic svn_error_t *
19038494Sobriencollect_path(apr_hash_t *paths,
19138494Sobrien             char action,
192174294Sobrien             const svn_string_t *cdata,
193174294Sobrien             apr_hash_t *attrs)
194131702Smbr{
19582794Sobrien  apr_pool_t *result_pool = apr_hash_pool_get(paths);
19638494Sobrien  svn_log_changed_path2_t *lcp;
197174294Sobrien  const char *copyfrom_path;
198174294Sobrien  const char *copyfrom_rev;
199174294Sobrien  const char *path;
20038494Sobrien
201174294Sobrien  lcp = svn_log_changed_path2_create(result_pool);
202174294Sobrien  lcp->action = action;
203174294Sobrien  lcp->copyfrom_rev = SVN_INVALID_REVNUM;
20482794Sobrien
20538494Sobrien  /* COPYFROM_* are only recorded for ADDED_PATH and REPLACED_PATH.  */
20638494Sobrien  copyfrom_path = svn_hash_gets(attrs, "copyfrom-path");
207174294Sobrien  copyfrom_rev = svn_hash_gets(attrs, "copyfrom-rev");
20838494Sobrien  if (copyfrom_path && copyfrom_rev)
20938494Sobrien    {
21038494Sobrien      svn_revnum_t rev = SVN_STR_TO_REV(copyfrom_rev);
21138494Sobrien
21238494Sobrien      if (SVN_IS_VALID_REVNUM(rev))
21338494Sobrien        {
21438494Sobrien          lcp->copyfrom_path = apr_pstrdup(result_pool, copyfrom_path);
21582794Sobrien          lcp->copyfrom_rev = rev;
21682794Sobrien        }
21738494Sobrien    }
218174294Sobrien
21938494Sobrien  lcp->node_kind = svn_node_kind_from_word(svn_hash_gets(attrs, "node-kind"));
220174294Sobrien  lcp->text_modified = svn_tristate__from_word(svn_hash_gets(attrs,
22152894Sobrien                                                             "text-mods"));
22238494Sobrien  lcp->props_modified = svn_tristate__from_word(svn_hash_gets(attrs,
22338494Sobrien                                                              "prop-mods"));
22438494Sobrien
22538494Sobrien  path = apr_pstrmemdup(result_pool, cdata->data, cdata->len);
22638494Sobrien  svn_hash_sets(paths, path, lcp);
227174294Sobrien
22838494Sobrien  return SVN_NO_ERROR;
22938494Sobrien}
230174294Sobrien
23138494Sobrien
23238494Sobrien/* Conforms to svn_ra_serf__xml_opened_t  */
23338494Sobrienstatic svn_error_t *
23438494Sobrienlog_opened(svn_ra_serf__xml_estate_t *xes,
23538494Sobrien           void *baton,
236174294Sobrien           int entered_state,
23738494Sobrien           const svn_ra_serf__dav_props_t *tag,
23838494Sobrien           apr_pool_t *scratch_pool)
23938494Sobrien{
24038494Sobrien  log_context_t *log_ctx = baton;
24138494Sobrien
242174294Sobrien  if (entered_state == ITEM)
24338494Sobrien    {
24438494Sobrien      apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes);
24538494Sobrien
24638494Sobrien      log_ctx->collect_revprops = apr_hash_make(state_pool);
24738494Sobrien      log_ctx->collect_paths = apr_hash_make(state_pool);
248174294Sobrien    }
249174294Sobrien
250174294Sobrien  return SVN_NO_ERROR;
25138494Sobrien}
25238494Sobrien
25338494Sobrien
25438494Sobrien/* Conforms to svn_ra_serf__xml_closed_t  */
25538494Sobrienstatic svn_error_t *
25638494Sobrienlog_closed(svn_ra_serf__xml_estate_t *xes,
25738494Sobrien           void *baton,
25838494Sobrien           int leaving_state,
25938494Sobrien           const svn_string_t *cdata,
26038494Sobrien           apr_hash_t *attrs,
26138494Sobrien           apr_pool_t *scratch_pool)
26238494Sobrien{
26382794Sobrien  log_context_t *log_ctx = baton;
26438494Sobrien
26538494Sobrien  if (leaving_state == ITEM)
26638494Sobrien    {
26738494Sobrien      svn_log_entry_t *log_entry;
268174294Sobrien      const char *rev_str;
26938494Sobrien
27038494Sobrien      if (log_ctx->limit && (log_ctx->nest_level == 0)
27138494Sobrien          && (++log_ctx->count > log_ctx->limit))
27238494Sobrien        {
27338494Sobrien          return SVN_NO_ERROR;
27438494Sobrien        }
27538494Sobrien
27638494Sobrien      log_entry = svn_log_entry_create(scratch_pool);
277174294Sobrien
27838494Sobrien      /* Pick up the paths from the context. These have the same lifetime
27938494Sobrien         as this state. That is long enough for us to pass the paths to
28038494Sobrien         the receiver callback.  */
28138494Sobrien      if (apr_hash_count(log_ctx->collect_paths) > 0)
282174294Sobrien        {
28338494Sobrien          log_entry->changed_paths = log_ctx->collect_paths;
28438494Sobrien          log_entry->changed_paths2 = log_ctx->collect_paths;
28538494Sobrien        }
28638494Sobrien
287174294Sobrien      /* ... and same story for the collected revprops.  */
28838494Sobrien      log_entry->revprops = log_ctx->collect_revprops;
289174294Sobrien
290174294Sobrien      log_entry->has_children = svn_hash__get_bool(attrs,
291174294Sobrien                                                   "has-children",
292174294Sobrien                                                   FALSE);
293174294Sobrien      log_entry->subtractive_merge = svn_hash__get_bool(attrs,
294174294Sobrien                                                        "subtractive-merge",
295174294Sobrien                                                        FALSE);
29638494Sobrien
29738494Sobrien      rev_str = svn_hash_gets(attrs, "revision");
29838494Sobrien      if (rev_str)
29938494Sobrien        log_entry->revision = SVN_STR_TO_REV(rev_str);
30038494Sobrien      else
30138494Sobrien        log_entry->revision = SVN_INVALID_REVNUM;
30238494Sobrien
30338494Sobrien      /* Give the info to the reporter */
30438494Sobrien      SVN_ERR(log_ctx->receiver(log_ctx->receiver_baton,
30538494Sobrien                                log_entry,
306174294Sobrien                                scratch_pool));
30738494Sobrien
30838494Sobrien      if (log_entry->has_children)
309174294Sobrien        {
310174294Sobrien          log_ctx->nest_level++;
311174294Sobrien        }
312174294Sobrien      if (! SVN_IS_VALID_REVNUM(log_entry->revision))
313174294Sobrien        {
314174294Sobrien          SVN_ERR_ASSERT(log_ctx->nest_level);
315174294Sobrien          log_ctx->nest_level--;
316174294Sobrien        }
317174294Sobrien
318174294Sobrien      /* These hash tables are going to be unusable once this state's
31938494Sobrien         pool is destroyed. But let's not leave stale pointers in
32038494Sobrien         structures that have a longer life.  */
321174294Sobrien      log_ctx->collect_revprops = NULL;
322174294Sobrien      log_ctx->collect_paths = NULL;
323174294Sobrien    }
324174294Sobrien  else if (leaving_state == VERSION)
325174294Sobrien    {
326174294Sobrien      svn_ra_serf__xml_note(xes, ITEM, "revision", cdata->data);
327174294Sobrien    }
328174294Sobrien  else if (leaving_state == CREATOR)
329174294Sobrien    {
330174294Sobrien      if (log_ctx->want_author)
331174294Sobrien        {
332174294Sobrien          SVN_ERR(collect_revprop(log_ctx->collect_revprops,
333174294Sobrien                                  SVN_PROP_REVISION_AUTHOR,
334174294Sobrien                                  cdata,
335174294Sobrien                                  svn_hash_gets(attrs, "encoding")));
33638494Sobrien        }
337174294Sobrien    }
33838494Sobrien  else if (leaving_state == DATE)
33938494Sobrien    {
34038494Sobrien      if (log_ctx->want_date)
34138494Sobrien        {
34238494Sobrien          SVN_ERR(collect_revprop(log_ctx->collect_revprops,
34338494Sobrien                                  SVN_PROP_REVISION_DATE,
34438494Sobrien                                  cdata,
34538494Sobrien                                  svn_hash_gets(attrs, "encoding")));
34638494Sobrien        }
34738494Sobrien    }
34838494Sobrien  else if (leaving_state == COMMENT)
34938494Sobrien    {
35038494Sobrien      if (log_ctx->want_message)
35138494Sobrien        {
35238494Sobrien          SVN_ERR(collect_revprop(log_ctx->collect_revprops,
35338494Sobrien                                  SVN_PROP_REVISION_LOG,
35438494Sobrien                                  cdata,
35538494Sobrien                                  svn_hash_gets(attrs, "encoding")));
35638494Sobrien        }
35738494Sobrien    }
35838494Sobrien  else if (leaving_state == REVPROP)
35938494Sobrien    {
36038494Sobrien      apr_pool_t *result_pool = apr_hash_pool_get(log_ctx->collect_revprops);
36138494Sobrien
36238494Sobrien      SVN_ERR(collect_revprop(
36338494Sobrien                log_ctx->collect_revprops,
36438494Sobrien                apr_pstrdup(result_pool,
36538494Sobrien                            svn_hash_gets(attrs, "name")),
36638494Sobrien                cdata,
36738494Sobrien                svn_hash_gets(attrs, "encoding")
36838494Sobrien                ));
36938494Sobrien    }
37038494Sobrien  else if (leaving_state == HAS_CHILDREN)
37138494Sobrien    {
37238494Sobrien      svn_ra_serf__xml_note(xes, ITEM, "has-children", "yes");
37338494Sobrien    }
37438494Sobrien  else if (leaving_state == SUBTRACTIVE_MERGE)
375174294Sobrien    {
376174294Sobrien      svn_ra_serf__xml_note(xes, ITEM, "subtractive-merge", "yes");
377174294Sobrien    }
378174294Sobrien  else
379174294Sobrien    {
380174294Sobrien      char action;
381174294Sobrien
382174294Sobrien      if (leaving_state == ADDED_PATH)
383174294Sobrien        action = 'A';
384174294Sobrien      else if (leaving_state == REPLACED_PATH)
385174294Sobrien        action = 'R';
386174294Sobrien      else if (leaving_state == DELETED_PATH)
38738494Sobrien        action = 'D';
38838494Sobrien      else
38938494Sobrien        {
39038494Sobrien          SVN_ERR_ASSERT(leaving_state == MODIFIED_PATH);
39138494Sobrien          action = 'M';
39238494Sobrien        }
39338494Sobrien
39438494Sobrien      SVN_ERR(collect_path(log_ctx->collect_paths, action, cdata, attrs));
395174294Sobrien    }
396174294Sobrien
397174294Sobrien  return SVN_NO_ERROR;
398174294Sobrien}
399174294Sobrien
400174294Sobrien
401174294Sobrienstatic svn_error_t *
402174294Sobriencreate_log_body(serf_bucket_t **body_bkt,
403174294Sobrien                void *baton,
404174294Sobrien                serf_bucket_alloc_t *alloc,
405174294Sobrien                apr_pool_t *pool)
406174294Sobrien{
407174294Sobrien  serf_bucket_t *buckets;
408174294Sobrien  log_context_t *log_ctx = baton;
409174294Sobrien
410174294Sobrien  buckets = serf_bucket_aggregate_create(alloc);
41138494Sobrien
41238494Sobrien  svn_ra_serf__add_open_tag_buckets(buckets, alloc,
41338494Sobrien                                    "S:log-report",
41438494Sobrien                                    "xmlns:S", SVN_XML_NAMESPACE,
41538494Sobrien                                    NULL);
41638494Sobrien
41738494Sobrien  svn_ra_serf__add_tag_buckets(buckets,
41838494Sobrien                               "S:start-revision",
41938494Sobrien                               apr_ltoa(pool, log_ctx->start),
42038494Sobrien                               alloc);
42138494Sobrien  svn_ra_serf__add_tag_buckets(buckets,
42238494Sobrien                               "S:end-revision",
42338494Sobrien                               apr_ltoa(pool, log_ctx->end),
42438494Sobrien                               alloc);
42538494Sobrien
42638494Sobrien  if (log_ctx->limit)
42738494Sobrien    {
42838494Sobrien      svn_ra_serf__add_tag_buckets(buckets,
42938494Sobrien                                   "S:limit", apr_ltoa(pool, log_ctx->limit),
43038494Sobrien                                   alloc);
43138494Sobrien    }
43238494Sobrien
43338494Sobrien  if (log_ctx->changed_paths)
43438494Sobrien    {
43538494Sobrien      svn_ra_serf__add_tag_buckets(buckets,
43638494Sobrien                                   "S:discover-changed-paths", NULL,
43738494Sobrien                                   alloc);
43838494Sobrien    }
43938494Sobrien
44038494Sobrien  if (log_ctx->strict_node_history)
44138494Sobrien    {
44238494Sobrien      svn_ra_serf__add_tag_buckets(buckets,
44338494Sobrien                                   "S:strict-node-history", NULL,
44438494Sobrien                                   alloc);
44538494Sobrien    }
44638494Sobrien
44738494Sobrien  if (log_ctx->include_merged_revisions)
448174294Sobrien    {
449174294Sobrien      svn_ra_serf__add_tag_buckets(buckets,
450174294Sobrien                                   "S:include-merged-revisions", NULL,
451174294Sobrien                                   alloc);
452174294Sobrien    }
453174294Sobrien
454174294Sobrien  if (log_ctx->revprops)
455174294Sobrien    {
45638494Sobrien      int i;
45738494Sobrien      for (i = 0; i < log_ctx->revprops->nelts; i++)
45838494Sobrien        {
459174294Sobrien          char *name = APR_ARRAY_IDX(log_ctx->revprops, i, char *);
46038494Sobrien          svn_ra_serf__add_tag_buckets(buckets,
46138494Sobrien                                       "S:revprop", name,
46238494Sobrien                                       alloc);
46342629Sobrien        }
46438494Sobrien      if (log_ctx->revprops->nelts == 0)
46538494Sobrien        {
46638494Sobrien          svn_ra_serf__add_tag_buckets(buckets,
46738494Sobrien                                       "S:no-revprops", NULL,
46838494Sobrien                                       alloc);
46938494Sobrien        }
47038494Sobrien    }
47138494Sobrien  else
47238494Sobrien    {
47338494Sobrien      svn_ra_serf__add_tag_buckets(buckets,
47438494Sobrien                                   "S:all-revprops", NULL,
47538494Sobrien                                   alloc);
47638494Sobrien    }
47738494Sobrien
47838494Sobrien  if (log_ctx->paths)
47938494Sobrien    {
480174294Sobrien      int i;
481174294Sobrien      for (i = 0; i < log_ctx->paths->nelts; i++)
482174294Sobrien        {
483174294Sobrien          svn_ra_serf__add_tag_buckets(buckets,
484174294Sobrien                                       "S:path", APR_ARRAY_IDX(log_ctx->paths, i,
485174294Sobrien                                                               const char*),
486174294Sobrien                                       alloc);
487174294Sobrien        }
488174294Sobrien    }
489174294Sobrien
490174294Sobrien  svn_ra_serf__add_tag_buckets(buckets,
491174294Sobrien                               "S:encode-binary-props", NULL,
492174294Sobrien                               alloc);
493174294Sobrien
494174294Sobrien  svn_ra_serf__add_close_tag_buckets(buckets, alloc,
495174294Sobrien                                     "S:log-report");
496174294Sobrien
497174294Sobrien  *body_bkt = buckets;
498174294Sobrien  return SVN_NO_ERROR;
499174294Sobrien}
500174294Sobrien
501174294Sobriensvn_error_t *
502174294Sobriensvn_ra_serf__get_log(svn_ra_session_t *ra_session,
503174294Sobrien                     const apr_array_header_t *paths,
504174294Sobrien                     svn_revnum_t start,
505174294Sobrien                     svn_revnum_t end,
506174294Sobrien                     int limit,
507174294Sobrien                     svn_boolean_t discover_changed_paths,
508174294Sobrien                     svn_boolean_t strict_node_history,
509174294Sobrien                     svn_boolean_t include_merged_revisions,
510174294Sobrien                     const apr_array_header_t *revprops,
511174294Sobrien                     svn_log_entry_receiver_t receiver,
512174294Sobrien                     void *receiver_baton,
513174294Sobrien                     apr_pool_t *pool)
514174294Sobrien{
515174294Sobrien  log_context_t *log_ctx;
516174294Sobrien  svn_ra_serf__session_t *session = ra_session->priv;
517174294Sobrien  svn_ra_serf__handler_t *handler;
518174294Sobrien  svn_ra_serf__xml_context_t *xmlctx;
519174294Sobrien  svn_boolean_t want_custom_revprops;
520174294Sobrien  svn_revnum_t peg_rev;
521174294Sobrien  svn_error_t *err;
522174294Sobrien  const char *req_url;
523174294Sobrien
524174294Sobrien  log_ctx = apr_pcalloc(pool, sizeof(*log_ctx));
525174294Sobrien  log_ctx->pool = pool;
526174294Sobrien  log_ctx->receiver = receiver;
527174294Sobrien  log_ctx->receiver_baton = receiver_baton;
528174294Sobrien  log_ctx->paths = paths;
529174294Sobrien  log_ctx->start = start;
530174294Sobrien  log_ctx->end = end;
531174294Sobrien  log_ctx->limit = limit;
532174294Sobrien  log_ctx->changed_paths = discover_changed_paths;
533174294Sobrien  log_ctx->strict_node_history = strict_node_history;
534174294Sobrien  log_ctx->include_merged_revisions = include_merged_revisions;
535174294Sobrien  log_ctx->revprops = revprops;
536174294Sobrien  log_ctx->nest_level = 0;
537174294Sobrien
538174294Sobrien  want_custom_revprops = FALSE;
539174294Sobrien  if (revprops)
540174294Sobrien    {
541174294Sobrien      int i;
542174294Sobrien      for (i = 0; i < revprops->nelts; i++)
543174294Sobrien        {
544174294Sobrien          char *name = APR_ARRAY_IDX(revprops, i, char *);
545174294Sobrien          if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0)
546174294Sobrien            log_ctx->want_author = TRUE;
547174294Sobrien          else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0)
54852894Sobrien            log_ctx->want_date = TRUE;
54952894Sobrien          else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0)
55052894Sobrien            log_ctx->want_message = TRUE;
55152894Sobrien          else
55252894Sobrien            want_custom_revprops = TRUE;
55352894Sobrien        }
55452894Sobrien    }
55552894Sobrien  else
55638494Sobrien    {
55738494Sobrien      log_ctx->want_author = log_ctx->want_date = log_ctx->want_message = TRUE;
55838494Sobrien      want_custom_revprops = TRUE;
55938494Sobrien    }
56038494Sobrien
56138494Sobrien  if (want_custom_revprops)
56238494Sobrien    {
56338494Sobrien      svn_boolean_t has_log_revprops;
56438494Sobrien      SVN_ERR(svn_ra_serf__has_capability(ra_session, &has_log_revprops,
56538494Sobrien                                          SVN_RA_CAPABILITY_LOG_REVPROPS, pool));
56638494Sobrien      if (!has_log_revprops)
56738494Sobrien        return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL,
56838494Sobrien                                _("Server does not support custom revprops"
56938494Sobrien                                  " via log"));
57038494Sobrien    }
57138494Sobrien  /* At this point, we may have a deleted file.  So, we'll match ra_neon's
57238494Sobrien   * behavior and use the larger of start or end as our 'peg' rev.
57338494Sobrien   */
57438494Sobrien  peg_rev = (start == SVN_INVALID_REVNUM || start > end) ? start : end;
57538494Sobrien
57638494Sobrien  SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
57738494Sobrien                                      session, NULL /* conn */,
57838494Sobrien                                      NULL /* url */, peg_rev,
57938494Sobrien                                      pool, pool));
58038494Sobrien
58138494Sobrien  xmlctx = svn_ra_serf__xml_context_create(log_ttable,
58238494Sobrien                                           log_opened, log_closed, NULL,
58338494Sobrien                                           log_ctx,
58438494Sobrien                                           pool);
58538494Sobrien  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
58638494Sobrien
58738494Sobrien  handler->method = "REPORT";
58838494Sobrien  handler->path = req_url;
58938494Sobrien  handler->body_delegate = create_log_body;
59038494Sobrien  handler->body_delegate_baton = log_ctx;
59138494Sobrien  handler->body_type = "text/xml";
59238494Sobrien  handler->conn = session->conns[0];
59338494Sobrien  handler->session = session;
59438494Sobrien
59538494Sobrien  err = svn_ra_serf__context_run_one(handler, pool);
59638494Sobrien
59738494Sobrien  SVN_ERR(svn_error_compose_create(
59838494Sobrien              svn_ra_serf__error_on_status(handler->sline,
59938494Sobrien                                           req_url,
60038494Sobrien                                           handler->location),
60138494Sobrien              err));
60238494Sobrien
60338494Sobrien  return SVN_NO_ERROR;
60438494Sobrien}
60538494Sobrien