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