1251881Speter/*
2251881Speter * log.c :  entry point for log RA functions for ra_serf
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <apr_uri.h>
27251881Speter#include <serf.h>
28251881Speter
29251881Speter#include "svn_hash.h"
30251881Speter#include "svn_pools.h"
31251881Speter#include "svn_ra.h"
32251881Speter#include "svn_dav.h"
33251881Speter#include "svn_base64.h"
34251881Speter#include "svn_xml.h"
35251881Speter#include "svn_config.h"
36251881Speter#include "svn_path.h"
37251881Speter#include "svn_props.h"
38251881Speter
39251881Speter#include "private/svn_dav_protocol.h"
40251881Speter#include "private/svn_string_private.h"
41251881Speter#include "private/svn_subr_private.h"
42251881Speter#include "svn_private_config.h"
43251881Speter
44251881Speter#include "ra_serf.h"
45251881Speter#include "../libsvn_ra/ra_loader.h"
46251881Speter
47251881Speter
48251881Speter/*
49251881Speter * This enum represents the current state of our XML parsing for a REPORT.
50251881Speter */
51251881Speterenum {
52251881Speter  INITIAL = 0,
53251881Speter  REPORT,
54251881Speter  ITEM,
55251881Speter  VERSION,
56251881Speter  CREATOR,
57251881Speter  DATE,
58251881Speter  COMMENT,
59251881Speter  REVPROP,
60251881Speter  HAS_CHILDREN,
61251881Speter  ADDED_PATH,
62251881Speter  REPLACED_PATH,
63251881Speter  DELETED_PATH,
64251881Speter  MODIFIED_PATH,
65251881Speter  SUBTRACTIVE_MERGE
66251881Speter};
67251881Speter
68251881Spetertypedef struct log_context_t {
69251881Speter  apr_pool_t *pool;
70251881Speter
71251881Speter  /* parameters set by our caller */
72251881Speter  const apr_array_header_t *paths;
73251881Speter  svn_revnum_t start;
74251881Speter  svn_revnum_t end;
75251881Speter  int limit;
76251881Speter  svn_boolean_t changed_paths;
77251881Speter  svn_boolean_t strict_node_history;
78251881Speter  svn_boolean_t include_merged_revisions;
79251881Speter  const apr_array_header_t *revprops;
80251881Speter  int nest_level; /* used to track mergeinfo nesting levels */
81251881Speter  int count; /* only incremented when nest_level == 0 */
82251881Speter
83251881Speter  /* Collect information for storage into a log entry. Most of the entry
84251881Speter     members are collected by individual states. revprops and paths are
85251881Speter     N datapoints per entry.  */
86251881Speter  apr_hash_t *collect_revprops;
87251881Speter  apr_hash_t *collect_paths;
88251881Speter
89251881Speter  /* log receiver function and baton */
90251881Speter  svn_log_entry_receiver_t receiver;
91251881Speter  void *receiver_baton;
92251881Speter
93251881Speter  /* pre-1.5 compatibility */
94251881Speter  svn_boolean_t want_author;
95251881Speter  svn_boolean_t want_date;
96251881Speter  svn_boolean_t want_message;
97251881Speter} log_context_t;
98251881Speter
99251881Speter#define D_ "DAV:"
100251881Speter#define S_ SVN_XML_NAMESPACE
101251881Speterstatic const svn_ra_serf__xml_transition_t log_ttable[] = {
102251881Speter  { INITIAL, S_, "log-report", REPORT,
103251881Speter    FALSE, { NULL }, FALSE },
104251881Speter
105251881Speter  /* Note that we have an opener here. We need to construct a new LOG_ENTRY
106251881Speter     to record multiple paths.  */
107251881Speter  { REPORT, S_, "log-item", ITEM,
108251881Speter    FALSE, { NULL }, TRUE },
109251881Speter
110251881Speter  { ITEM, D_, SVN_DAV__VERSION_NAME, VERSION,
111251881Speter    TRUE, { NULL }, TRUE },
112251881Speter
113251881Speter  { ITEM, D_, "creator-displayname", CREATOR,
114251881Speter    TRUE, { "?encoding", NULL }, TRUE },
115251881Speter
116251881Speter  { ITEM, S_, "date", DATE,
117251881Speter    TRUE, { "?encoding", NULL }, TRUE },
118251881Speter
119251881Speter  { ITEM, D_, "comment", COMMENT,
120251881Speter    TRUE, { "?encoding", NULL }, TRUE },
121251881Speter
122251881Speter  { ITEM, S_, "revprop", REVPROP,
123251881Speter    TRUE, { "name", "?encoding", NULL }, TRUE },
124251881Speter
125251881Speter  { ITEM, S_, "has-children", HAS_CHILDREN,
126251881Speter    FALSE, { NULL }, TRUE },
127251881Speter
128251881Speter  { ITEM, S_, "subtractive-merge", SUBTRACTIVE_MERGE,
129251881Speter    FALSE, { NULL }, TRUE },
130251881Speter
131251881Speter  { ITEM, S_, "added-path", ADDED_PATH,
132251881Speter    TRUE, { "?node-kind", "?text-mods", "?prop-mods",
133251881Speter            "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
134251881Speter
135251881Speter  { ITEM, S_, "replaced-path", REPLACED_PATH,
136251881Speter    TRUE, { "?node-kind", "?text-mods", "?prop-mods",
137251881Speter            "?copyfrom-path", "?copyfrom-rev", NULL }, TRUE },
138251881Speter
139251881Speter  { ITEM, S_, "deleted-path", DELETED_PATH,
140251881Speter    TRUE, { "?node-kind", "?text-mods", "?prop-mods", NULL }, TRUE },
141251881Speter
142251881Speter  { ITEM, S_, "modified-path", MODIFIED_PATH,
143251881Speter    TRUE, { "?node-kind", "?text-mods", "?prop-mods", NULL }, TRUE },
144251881Speter
145251881Speter  { 0 }
146251881Speter};
147251881Speter
148251881Speter
149251881Speter/* Store CDATA into REVPROPS, associated with PROPNAME. If ENCODING is not
150251881Speter   NULL, then it must base "base64" and CDATA will be decoded first.
151251881Speter
152251881Speter   NOTE: PROPNAME must live longer than REVPROPS.  */
153251881Speterstatic svn_error_t *
154251881Spetercollect_revprop(apr_hash_t *revprops,
155251881Speter                const char *propname,
156251881Speter                const svn_string_t *cdata,
157251881Speter                const char *encoding)
158251881Speter{
159251881Speter  apr_pool_t *result_pool = apr_hash_pool_get(revprops);
160251881Speter  const svn_string_t *decoded;
161251881Speter
162251881Speter  if (encoding)
163251881Speter    {
164251881Speter      /* Check for a known encoding type.  This is easy -- there's
165251881Speter         only one.  */
166251881Speter      if (strcmp(encoding, "base64") != 0)
167251881Speter        {
168251881Speter          return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
169251881Speter                                   _("Unsupported encoding '%s'"),
170251881Speter                                   encoding);
171251881Speter        }
172251881Speter
173251881Speter      decoded = svn_base64_decode_string(cdata, result_pool);
174251881Speter    }
175251881Speter  else
176251881Speter    {
177251881Speter      decoded = svn_string_dup(cdata, result_pool);
178251881Speter    }
179251881Speter
180251881Speter  /* Caller has ensured PROPNAME has sufficient lifetime.  */
181251881Speter  svn_hash_sets(revprops, propname, decoded);
182251881Speter
183251881Speter  return SVN_NO_ERROR;
184251881Speter}
185251881Speter
186251881Speter
187251881Speter/* Record ACTION on the path in CDATA into PATHS. Other properties about
188251881Speter   the action are pulled from ATTRS.  */
189251881Speterstatic svn_error_t *
190251881Spetercollect_path(apr_hash_t *paths,
191251881Speter             char action,
192251881Speter             const svn_string_t *cdata,
193251881Speter             apr_hash_t *attrs)
194251881Speter{
195251881Speter  apr_pool_t *result_pool = apr_hash_pool_get(paths);
196251881Speter  svn_log_changed_path2_t *lcp;
197251881Speter  const char *copyfrom_path;
198251881Speter  const char *copyfrom_rev;
199251881Speter  const char *path;
200251881Speter
201251881Speter  lcp = svn_log_changed_path2_create(result_pool);
202251881Speter  lcp->action = action;
203251881Speter  lcp->copyfrom_rev = SVN_INVALID_REVNUM;
204251881Speter
205251881Speter  /* COPYFROM_* are only recorded for ADDED_PATH and REPLACED_PATH.  */
206251881Speter  copyfrom_path = svn_hash_gets(attrs, "copyfrom-path");
207251881Speter  copyfrom_rev = svn_hash_gets(attrs, "copyfrom-rev");
208251881Speter  if (copyfrom_path && copyfrom_rev)
209251881Speter    {
210251881Speter      svn_revnum_t rev = SVN_STR_TO_REV(copyfrom_rev);
211251881Speter
212251881Speter      if (SVN_IS_VALID_REVNUM(rev))
213251881Speter        {
214251881Speter          lcp->copyfrom_path = apr_pstrdup(result_pool, copyfrom_path);
215251881Speter          lcp->copyfrom_rev = rev;
216251881Speter        }
217251881Speter    }
218251881Speter
219251881Speter  lcp->node_kind = svn_node_kind_from_word(svn_hash_gets(attrs, "node-kind"));
220251881Speter  lcp->text_modified = svn_tristate__from_word(svn_hash_gets(attrs,
221251881Speter                                                             "text-mods"));
222251881Speter  lcp->props_modified = svn_tristate__from_word(svn_hash_gets(attrs,
223251881Speter                                                              "prop-mods"));
224251881Speter
225251881Speter  path = apr_pstrmemdup(result_pool, cdata->data, cdata->len);
226251881Speter  svn_hash_sets(paths, path, lcp);
227251881Speter
228251881Speter  return SVN_NO_ERROR;
229251881Speter}
230251881Speter
231251881Speter
232251881Speter/* Conforms to svn_ra_serf__xml_opened_t  */
233251881Speterstatic svn_error_t *
234251881Speterlog_opened(svn_ra_serf__xml_estate_t *xes,
235251881Speter           void *baton,
236251881Speter           int entered_state,
237251881Speter           const svn_ra_serf__dav_props_t *tag,
238251881Speter           apr_pool_t *scratch_pool)
239251881Speter{
240251881Speter  log_context_t *log_ctx = baton;
241251881Speter
242251881Speter  if (entered_state == ITEM)
243251881Speter    {
244251881Speter      apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes);
245251881Speter
246251881Speter      log_ctx->collect_revprops = apr_hash_make(state_pool);
247251881Speter      log_ctx->collect_paths = apr_hash_make(state_pool);
248251881Speter    }
249251881Speter
250251881Speter  return SVN_NO_ERROR;
251251881Speter}
252251881Speter
253251881Speter
254251881Speter/* Conforms to svn_ra_serf__xml_closed_t  */
255251881Speterstatic svn_error_t *
256251881Speterlog_closed(svn_ra_serf__xml_estate_t *xes,
257251881Speter           void *baton,
258251881Speter           int leaving_state,
259251881Speter           const svn_string_t *cdata,
260251881Speter           apr_hash_t *attrs,
261251881Speter           apr_pool_t *scratch_pool)
262251881Speter{
263251881Speter  log_context_t *log_ctx = baton;
264251881Speter
265251881Speter  if (leaving_state == ITEM)
266251881Speter    {
267251881Speter      svn_log_entry_t *log_entry;
268251881Speter      const char *rev_str;
269251881Speter
270251881Speter      if (log_ctx->limit && (log_ctx->nest_level == 0)
271251881Speter          && (++log_ctx->count > log_ctx->limit))
272251881Speter        {
273251881Speter          return SVN_NO_ERROR;
274251881Speter        }
275251881Speter
276251881Speter      log_entry = svn_log_entry_create(scratch_pool);
277251881Speter
278251881Speter      /* Pick up the paths from the context. These have the same lifetime
279251881Speter         as this state. That is long enough for us to pass the paths to
280251881Speter         the receiver callback.  */
281251881Speter      if (apr_hash_count(log_ctx->collect_paths) > 0)
282251881Speter        {
283251881Speter          log_entry->changed_paths = log_ctx->collect_paths;
284251881Speter          log_entry->changed_paths2 = log_ctx->collect_paths;
285251881Speter        }
286251881Speter
287251881Speter      /* ... and same story for the collected revprops.  */
288251881Speter      log_entry->revprops = log_ctx->collect_revprops;
289251881Speter
290251881Speter      log_entry->has_children = svn_hash__get_bool(attrs,
291251881Speter                                                   "has-children",
292251881Speter                                                   FALSE);
293251881Speter      log_entry->subtractive_merge = svn_hash__get_bool(attrs,
294251881Speter                                                        "subtractive-merge",
295251881Speter                                                        FALSE);
296251881Speter
297251881Speter      rev_str = svn_hash_gets(attrs, "revision");
298251881Speter      if (rev_str)
299251881Speter        log_entry->revision = SVN_STR_TO_REV(rev_str);
300251881Speter      else
301251881Speter        log_entry->revision = SVN_INVALID_REVNUM;
302251881Speter
303251881Speter      /* Give the info to the reporter */
304251881Speter      SVN_ERR(log_ctx->receiver(log_ctx->receiver_baton,
305251881Speter                                log_entry,
306251881Speter                                scratch_pool));
307251881Speter
308251881Speter      if (log_entry->has_children)
309251881Speter        {
310251881Speter          log_ctx->nest_level++;
311251881Speter        }
312251881Speter      if (! SVN_IS_VALID_REVNUM(log_entry->revision))
313251881Speter        {
314251881Speter          SVN_ERR_ASSERT(log_ctx->nest_level);
315251881Speter          log_ctx->nest_level--;
316251881Speter        }
317251881Speter
318251881Speter      /* These hash tables are going to be unusable once this state's
319251881Speter         pool is destroyed. But let's not leave stale pointers in
320251881Speter         structures that have a longer life.  */
321251881Speter      log_ctx->collect_revprops = NULL;
322251881Speter      log_ctx->collect_paths = NULL;
323251881Speter    }
324251881Speter  else if (leaving_state == VERSION)
325251881Speter    {
326251881Speter      svn_ra_serf__xml_note(xes, ITEM, "revision", cdata->data);
327251881Speter    }
328251881Speter  else if (leaving_state == CREATOR)
329251881Speter    {
330251881Speter      if (log_ctx->want_author)
331251881Speter        {
332251881Speter          SVN_ERR(collect_revprop(log_ctx->collect_revprops,
333251881Speter                                  SVN_PROP_REVISION_AUTHOR,
334251881Speter                                  cdata,
335251881Speter                                  svn_hash_gets(attrs, "encoding")));
336251881Speter        }
337251881Speter    }
338251881Speter  else if (leaving_state == DATE)
339251881Speter    {
340251881Speter      if (log_ctx->want_date)
341251881Speter        {
342251881Speter          SVN_ERR(collect_revprop(log_ctx->collect_revprops,
343251881Speter                                  SVN_PROP_REVISION_DATE,
344251881Speter                                  cdata,
345251881Speter                                  svn_hash_gets(attrs, "encoding")));
346251881Speter        }
347251881Speter    }
348251881Speter  else if (leaving_state == COMMENT)
349251881Speter    {
350251881Speter      if (log_ctx->want_message)
351251881Speter        {
352251881Speter          SVN_ERR(collect_revprop(log_ctx->collect_revprops,
353251881Speter                                  SVN_PROP_REVISION_LOG,
354251881Speter                                  cdata,
355251881Speter                                  svn_hash_gets(attrs, "encoding")));
356251881Speter        }
357251881Speter    }
358251881Speter  else if (leaving_state == REVPROP)
359251881Speter    {
360251881Speter      apr_pool_t *result_pool = apr_hash_pool_get(log_ctx->collect_revprops);
361251881Speter
362251881Speter      SVN_ERR(collect_revprop(
363251881Speter                log_ctx->collect_revprops,
364251881Speter                apr_pstrdup(result_pool,
365251881Speter                            svn_hash_gets(attrs, "name")),
366251881Speter                cdata,
367251881Speter                svn_hash_gets(attrs, "encoding")
368251881Speter                ));
369251881Speter    }
370251881Speter  else if (leaving_state == HAS_CHILDREN)
371251881Speter    {
372251881Speter      svn_ra_serf__xml_note(xes, ITEM, "has-children", "yes");
373251881Speter    }
374251881Speter  else if (leaving_state == SUBTRACTIVE_MERGE)
375251881Speter    {
376251881Speter      svn_ra_serf__xml_note(xes, ITEM, "subtractive-merge", "yes");
377251881Speter    }
378251881Speter  else
379251881Speter    {
380251881Speter      char action;
381251881Speter
382251881Speter      if (leaving_state == ADDED_PATH)
383251881Speter        action = 'A';
384251881Speter      else if (leaving_state == REPLACED_PATH)
385251881Speter        action = 'R';
386251881Speter      else if (leaving_state == DELETED_PATH)
387251881Speter        action = 'D';
388251881Speter      else
389251881Speter        {
390251881Speter          SVN_ERR_ASSERT(leaving_state == MODIFIED_PATH);
391251881Speter          action = 'M';
392251881Speter        }
393251881Speter
394251881Speter      SVN_ERR(collect_path(log_ctx->collect_paths, action, cdata, attrs));
395251881Speter    }
396251881Speter
397251881Speter  return SVN_NO_ERROR;
398251881Speter}
399251881Speter
400251881Speter
401251881Speterstatic svn_error_t *
402251881Spetercreate_log_body(serf_bucket_t **body_bkt,
403251881Speter                void *baton,
404251881Speter                serf_bucket_alloc_t *alloc,
405251881Speter                apr_pool_t *pool)
406251881Speter{
407251881Speter  serf_bucket_t *buckets;
408251881Speter  log_context_t *log_ctx = baton;
409251881Speter
410251881Speter  buckets = serf_bucket_aggregate_create(alloc);
411251881Speter
412251881Speter  svn_ra_serf__add_open_tag_buckets(buckets, alloc,
413251881Speter                                    "S:log-report",
414251881Speter                                    "xmlns:S", SVN_XML_NAMESPACE,
415251881Speter                                    NULL);
416251881Speter
417251881Speter  svn_ra_serf__add_tag_buckets(buckets,
418251881Speter                               "S:start-revision",
419251881Speter                               apr_ltoa(pool, log_ctx->start),
420251881Speter                               alloc);
421251881Speter  svn_ra_serf__add_tag_buckets(buckets,
422251881Speter                               "S:end-revision",
423251881Speter                               apr_ltoa(pool, log_ctx->end),
424251881Speter                               alloc);
425251881Speter
426251881Speter  if (log_ctx->limit)
427251881Speter    {
428251881Speter      svn_ra_serf__add_tag_buckets(buckets,
429251881Speter                                   "S:limit", apr_ltoa(pool, log_ctx->limit),
430251881Speter                                   alloc);
431251881Speter    }
432251881Speter
433251881Speter  if (log_ctx->changed_paths)
434251881Speter    {
435251881Speter      svn_ra_serf__add_tag_buckets(buckets,
436251881Speter                                   "S:discover-changed-paths", NULL,
437251881Speter                                   alloc);
438251881Speter    }
439251881Speter
440251881Speter  if (log_ctx->strict_node_history)
441251881Speter    {
442251881Speter      svn_ra_serf__add_tag_buckets(buckets,
443251881Speter                                   "S:strict-node-history", NULL,
444251881Speter                                   alloc);
445251881Speter    }
446251881Speter
447251881Speter  if (log_ctx->include_merged_revisions)
448251881Speter    {
449251881Speter      svn_ra_serf__add_tag_buckets(buckets,
450251881Speter                                   "S:include-merged-revisions", NULL,
451251881Speter                                   alloc);
452251881Speter    }
453251881Speter
454251881Speter  if (log_ctx->revprops)
455251881Speter    {
456251881Speter      int i;
457251881Speter      for (i = 0; i < log_ctx->revprops->nelts; i++)
458251881Speter        {
459251881Speter          char *name = APR_ARRAY_IDX(log_ctx->revprops, i, char *);
460251881Speter          svn_ra_serf__add_tag_buckets(buckets,
461251881Speter                                       "S:revprop", name,
462251881Speter                                       alloc);
463251881Speter        }
464251881Speter      if (log_ctx->revprops->nelts == 0)
465251881Speter        {
466251881Speter          svn_ra_serf__add_tag_buckets(buckets,
467251881Speter                                       "S:no-revprops", NULL,
468251881Speter                                       alloc);
469251881Speter        }
470251881Speter    }
471251881Speter  else
472251881Speter    {
473251881Speter      svn_ra_serf__add_tag_buckets(buckets,
474251881Speter                                   "S:all-revprops", NULL,
475251881Speter                                   alloc);
476251881Speter    }
477251881Speter
478251881Speter  if (log_ctx->paths)
479251881Speter    {
480251881Speter      int i;
481251881Speter      for (i = 0; i < log_ctx->paths->nelts; i++)
482251881Speter        {
483251881Speter          svn_ra_serf__add_tag_buckets(buckets,
484251881Speter                                       "S:path", APR_ARRAY_IDX(log_ctx->paths, i,
485251881Speter                                                               const char*),
486251881Speter                                       alloc);
487251881Speter        }
488251881Speter    }
489251881Speter
490251881Speter  svn_ra_serf__add_tag_buckets(buckets,
491251881Speter                               "S:encode-binary-props", NULL,
492251881Speter                               alloc);
493251881Speter
494251881Speter  svn_ra_serf__add_close_tag_buckets(buckets, alloc,
495251881Speter                                     "S:log-report");
496251881Speter
497251881Speter  *body_bkt = buckets;
498251881Speter  return SVN_NO_ERROR;
499251881Speter}
500251881Speter
501251881Spetersvn_error_t *
502251881Spetersvn_ra_serf__get_log(svn_ra_session_t *ra_session,
503251881Speter                     const apr_array_header_t *paths,
504251881Speter                     svn_revnum_t start,
505251881Speter                     svn_revnum_t end,
506251881Speter                     int limit,
507251881Speter                     svn_boolean_t discover_changed_paths,
508251881Speter                     svn_boolean_t strict_node_history,
509251881Speter                     svn_boolean_t include_merged_revisions,
510251881Speter                     const apr_array_header_t *revprops,
511251881Speter                     svn_log_entry_receiver_t receiver,
512251881Speter                     void *receiver_baton,
513251881Speter                     apr_pool_t *pool)
514251881Speter{
515251881Speter  log_context_t *log_ctx;
516251881Speter  svn_ra_serf__session_t *session = ra_session->priv;
517251881Speter  svn_ra_serf__handler_t *handler;
518251881Speter  svn_ra_serf__xml_context_t *xmlctx;
519251881Speter  svn_boolean_t want_custom_revprops;
520251881Speter  svn_revnum_t peg_rev;
521251881Speter  svn_error_t *err;
522251881Speter  const char *req_url;
523251881Speter
524251881Speter  log_ctx = apr_pcalloc(pool, sizeof(*log_ctx));
525251881Speter  log_ctx->pool = pool;
526251881Speter  log_ctx->receiver = receiver;
527251881Speter  log_ctx->receiver_baton = receiver_baton;
528251881Speter  log_ctx->paths = paths;
529251881Speter  log_ctx->start = start;
530251881Speter  log_ctx->end = end;
531251881Speter  log_ctx->limit = limit;
532251881Speter  log_ctx->changed_paths = discover_changed_paths;
533251881Speter  log_ctx->strict_node_history = strict_node_history;
534251881Speter  log_ctx->include_merged_revisions = include_merged_revisions;
535251881Speter  log_ctx->revprops = revprops;
536251881Speter  log_ctx->nest_level = 0;
537251881Speter
538251881Speter  want_custom_revprops = FALSE;
539251881Speter  if (revprops)
540251881Speter    {
541251881Speter      int i;
542251881Speter      for (i = 0; i < revprops->nelts; i++)
543251881Speter        {
544251881Speter          char *name = APR_ARRAY_IDX(revprops, i, char *);
545251881Speter          if (strcmp(name, SVN_PROP_REVISION_AUTHOR) == 0)
546251881Speter            log_ctx->want_author = TRUE;
547251881Speter          else if (strcmp(name, SVN_PROP_REVISION_DATE) == 0)
548251881Speter            log_ctx->want_date = TRUE;
549251881Speter          else if (strcmp(name, SVN_PROP_REVISION_LOG) == 0)
550251881Speter            log_ctx->want_message = TRUE;
551251881Speter          else
552251881Speter            want_custom_revprops = TRUE;
553251881Speter        }
554251881Speter    }
555251881Speter  else
556251881Speter    {
557251881Speter      log_ctx->want_author = log_ctx->want_date = log_ctx->want_message = TRUE;
558251881Speter      want_custom_revprops = TRUE;
559251881Speter    }
560251881Speter
561251881Speter  if (want_custom_revprops)
562251881Speter    {
563251881Speter      svn_boolean_t has_log_revprops;
564251881Speter      SVN_ERR(svn_ra_serf__has_capability(ra_session, &has_log_revprops,
565251881Speter                                          SVN_RA_CAPABILITY_LOG_REVPROPS, pool));
566251881Speter      if (!has_log_revprops)
567251881Speter        return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL,
568251881Speter                                _("Server does not support custom revprops"
569251881Speter                                  " via log"));
570251881Speter    }
571251881Speter  /* At this point, we may have a deleted file.  So, we'll match ra_neon's
572251881Speter   * behavior and use the larger of start or end as our 'peg' rev.
573251881Speter   */
574251881Speter  peg_rev = (start > end) ? start : end;
575251881Speter
576251881Speter  SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
577251881Speter                                      session, NULL /* conn */,
578251881Speter                                      NULL /* url */, peg_rev,
579251881Speter                                      pool, pool));
580251881Speter
581251881Speter  xmlctx = svn_ra_serf__xml_context_create(log_ttable,
582251881Speter                                           log_opened, log_closed, NULL,
583251881Speter                                           log_ctx,
584251881Speter                                           pool);
585251881Speter  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
586251881Speter
587251881Speter  handler->method = "REPORT";
588251881Speter  handler->path = req_url;
589251881Speter  handler->body_delegate = create_log_body;
590251881Speter  handler->body_delegate_baton = log_ctx;
591251881Speter  handler->body_type = "text/xml";
592251881Speter  handler->conn = session->conns[0];
593251881Speter  handler->session = session;
594251881Speter
595251881Speter  err = svn_ra_serf__context_run_one(handler, pool);
596251881Speter
597251881Speter  SVN_ERR(svn_error_compose_create(
598253734Speter              svn_ra_serf__error_on_status(handler->sline,
599251881Speter                                           req_url,
600251881Speter                                           handler->location),
601251881Speter              err));
602251881Speter
603251881Speter  return SVN_NO_ERROR;
604251881Speter}
605