1251881Speter/*
2251881Speter * update.c :  entry point for update 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#define APR_WANT_STRFUNC
27251881Speter#include <apr_version.h>
28251881Speter#include <apr_want.h>
29251881Speter
30251881Speter#include <apr_uri.h>
31251881Speter
32251881Speter#include <serf.h>
33251881Speter
34251881Speter#include "svn_hash.h"
35251881Speter#include "svn_pools.h"
36251881Speter#include "svn_ra.h"
37251881Speter#include "svn_dav.h"
38251881Speter#include "svn_xml.h"
39251881Speter#include "svn_delta.h"
40251881Speter#include "svn_path.h"
41251881Speter#include "svn_base64.h"
42251881Speter#include "svn_props.h"
43251881Speter
44251881Speter#include "svn_private_config.h"
45251881Speter#include "private/svn_dep_compat.h"
46251881Speter#include "private/svn_fspath.h"
47251881Speter#include "private/svn_string_private.h"
48251881Speter
49251881Speter#include "ra_serf.h"
50251881Speter#include "../libsvn_ra/ra_loader.h"
51251881Speter
52299742Sdim
53251881Speter
54251881Speter/*
55251881Speter * This enum represents the current state of our XML parsing for a REPORT.
56251881Speter *
57251881Speter * A little explanation of how the parsing works.  Every time we see
58251881Speter * an open-directory tag, we enter the OPEN_DIR state.  Likewise, for
59251881Speter * add-directory, open-file, etc.  When we see the closing variant of the
60251881Speter * open-directory tag, we'll 'pop' out of that state.
61251881Speter *
62251881Speter * Each state has a pool associated with it that can have temporary
63251881Speter * allocations that will live as long as the tag is opened.  Once
64251881Speter * the tag is 'closed', the pool will be reused.
65251881Speter */
66251881Spetertypedef enum report_state_e {
67299742Sdim  INITIAL = XML_STATE_INITIAL /* = 0 */,
68299742Sdim  UPDATE_REPORT,
69299742Sdim  TARGET_REVISION,
70299742Sdim
71299742Sdim  OPEN_DIR,
72299742Sdim  ADD_DIR,
73299742Sdim
74299742Sdim  OPEN_FILE,
75299742Sdim  ADD_FILE,
76299742Sdim
77299742Sdim  DELETE_ENTRY,
78299742Sdim  ABSENT_DIR,
79299742Sdim  ABSENT_FILE,
80299742Sdim
81299742Sdim  SET_PROP,
82299742Sdim  REMOVE_PROP,
83299742Sdim
84299742Sdim  PROP,
85299742Sdim
86299742Sdim  FETCH_FILE,
87299742Sdim  FETCH_PROPS,
88299742Sdim  TXDELTA,
89299742Sdim
90299742Sdim  CHECKED_IN,
91299742Sdim  CHECKED_IN_HREF,
92299742Sdim
93299742Sdim  MD5_CHECKSUM,
94299742Sdim
95299742Sdim  VERSION_NAME,
96299742Sdim  CREATIONDATE,
97299742Sdim  CREATOR_DISPLAYNAME
98251881Speter} report_state_e;
99251881Speter
100251881Speter
101299742Sdim#define D_ "DAV:"
102299742Sdim#define S_ SVN_XML_NAMESPACE
103299742Sdim#define V_ SVN_DAV_PROP_NS_DAV
104299742Sdimstatic const svn_ra_serf__xml_transition_t update_ttable[] = {
105299742Sdim  { INITIAL, S_, "update-report", UPDATE_REPORT,
106299742Sdim    FALSE, { "?inline-props", "?send-all", NULL }, TRUE },
107299742Sdim
108299742Sdim  { UPDATE_REPORT, S_, "target-revision", TARGET_REVISION,
109299742Sdim    FALSE, { "rev", NULL }, TRUE },
110299742Sdim
111299742Sdim  { UPDATE_REPORT, S_, "open-directory", OPEN_DIR,
112299742Sdim    FALSE, { "rev", NULL }, TRUE },
113299742Sdim
114299742Sdim  { OPEN_DIR, S_, "open-directory", OPEN_DIR,
115299742Sdim    FALSE, { "rev", "name", NULL }, TRUE },
116299742Sdim
117299742Sdim  { ADD_DIR, S_, "open-directory", OPEN_DIR,
118299742Sdim    FALSE, { "rev", "name", NULL }, TRUE },
119299742Sdim
120299742Sdim  { OPEN_DIR, S_, "add-directory", ADD_DIR,
121299742Sdim    FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/
122299742Sdim              NULL }, TRUE },
123299742Sdim
124299742Sdim  { ADD_DIR, S_, "add-directory", ADD_DIR,
125299742Sdim    FALSE, { "name", "?copyfrom-path", "?copyfrom-rev", /*"?bc-url",*/
126299742Sdim              NULL }, TRUE },
127299742Sdim
128299742Sdim  { OPEN_DIR, S_, "open-file", OPEN_FILE,
129299742Sdim    FALSE, { "rev", "name", NULL }, TRUE },
130299742Sdim
131299742Sdim  { ADD_DIR, S_, "open-file", OPEN_FILE,
132299742Sdim    FALSE, { "rev", "name", NULL }, TRUE },
133299742Sdim
134299742Sdim  { OPEN_DIR, S_, "add-file", ADD_FILE,
135299742Sdim    FALSE, { "name", "?copyfrom-path", "?copyfrom-rev",
136299742Sdim             "?sha1-checksum", NULL }, TRUE },
137299742Sdim
138299742Sdim  { ADD_DIR, S_, "add-file", ADD_FILE,
139299742Sdim    FALSE, { "name", "?copyfrom-path", "?copyfrom-rev",
140299742Sdim             "?sha1-checksum", NULL }, TRUE },
141299742Sdim
142299742Sdim  { OPEN_DIR, S_, "delete-entry", DELETE_ENTRY,
143299742Sdim    FALSE, { "?rev", "name", NULL }, TRUE },
144299742Sdim
145299742Sdim  { ADD_DIR, S_, "delete-entry", DELETE_ENTRY,
146299742Sdim    FALSE, { "?rev", "name", NULL }, TRUE },
147299742Sdim
148299742Sdim  { OPEN_DIR, S_, "absent-directory", ABSENT_DIR,
149299742Sdim    FALSE, { "name", NULL }, TRUE },
150299742Sdim
151299742Sdim  { ADD_DIR, S_, "absent-directory", ABSENT_DIR,
152299742Sdim    FALSE, { "name", NULL }, TRUE },
153299742Sdim
154299742Sdim  { OPEN_DIR, S_, "absent-file", ABSENT_FILE,
155299742Sdim    FALSE, { "name", NULL }, TRUE },
156299742Sdim
157299742Sdim  { ADD_DIR, S_, "absent-file", ABSENT_FILE,
158299742Sdim    FALSE, { "name", NULL }, TRUE },
159299742Sdim
160299742Sdim
161299742Sdim  { OPEN_DIR, D_, "checked-in", CHECKED_IN,
162299742Sdim    FALSE, { NULL }, FALSE },
163299742Sdim
164299742Sdim  { ADD_DIR, D_, "checked-in", CHECKED_IN,
165299742Sdim    FALSE, { NULL }, FALSE },
166299742Sdim
167299742Sdim  { OPEN_FILE, D_, "checked-in", CHECKED_IN,
168299742Sdim    FALSE, { NULL }, FALSE },
169299742Sdim
170299742Sdim  { ADD_FILE, D_, "checked-in", CHECKED_IN,
171299742Sdim    FALSE, { NULL }, FALSE },
172299742Sdim
173299742Sdim
174299742Sdim  { OPEN_DIR, S_, "set-prop", SET_PROP,
175299742Sdim    TRUE, { "name", "?encoding", NULL }, TRUE },
176299742Sdim
177299742Sdim  { ADD_DIR, S_, "set-prop", SET_PROP,
178299742Sdim    TRUE, { "name", "?encoding", NULL }, TRUE },
179299742Sdim
180299742Sdim  { OPEN_FILE, S_, "set-prop", SET_PROP,
181299742Sdim    TRUE, { "name", "?encoding", NULL }, TRUE },
182299742Sdim
183299742Sdim  { ADD_FILE, S_, "set-prop", SET_PROP,
184299742Sdim    TRUE, { "name", "?encoding", NULL }, TRUE },
185299742Sdim
186299742Sdim
187299742Sdim  { OPEN_DIR, S_, "remove-prop", REMOVE_PROP,
188299742Sdim    TRUE, { "name", NULL }, TRUE },
189299742Sdim
190299742Sdim  { ADD_DIR, S_, "remove-prop", REMOVE_PROP,
191299742Sdim    TRUE, { "name", NULL }, TRUE },
192299742Sdim
193299742Sdim  { OPEN_FILE, S_, "remove-prop", REMOVE_PROP,
194299742Sdim    TRUE, { "name", NULL }, TRUE },
195299742Sdim
196299742Sdim  { ADD_FILE, S_, "remove-prop", REMOVE_PROP,
197299742Sdim    TRUE, { "name", NULL }, TRUE },
198299742Sdim
199299742Sdim  { OPEN_FILE, S_, "prop", PROP,
200299742Sdim    FALSE, { NULL }, FALSE },
201299742Sdim  { OPEN_DIR, S_, "prop", PROP,
202299742Sdim    FALSE, { NULL }, FALSE },
203299742Sdim  { ADD_FILE, S_, "prop", PROP,
204299742Sdim    FALSE, { NULL }, FALSE },
205299742Sdim  { ADD_DIR, S_, "prop", PROP,
206299742Sdim    FALSE, { NULL }, FALSE },
207299742Sdim
208299742Sdim  { OPEN_FILE, S_, "txdelta", TXDELTA,
209299742Sdim    FALSE, { "?base-checksum" }, TRUE },
210299742Sdim
211299742Sdim  { ADD_FILE, S_, "txdelta", TXDELTA,
212299742Sdim    FALSE, { "?base-checksum" }, TRUE },
213299742Sdim
214299742Sdim  { OPEN_FILE, S_, "fetch-file", FETCH_FILE,
215299742Sdim    FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE},
216299742Sdim
217299742Sdim  { ADD_FILE, S_, "fetch-file", FETCH_FILE,
218299742Sdim    FALSE, { "?base-checksum", "?sha1-checksum", NULL }, TRUE },
219299742Sdim
220299742Sdim  { CHECKED_IN, D_, "href", CHECKED_IN_HREF,
221299742Sdim    TRUE, { NULL }, TRUE },
222299742Sdim
223299742Sdim  { PROP, V_, "md5-checksum", MD5_CHECKSUM,
224299742Sdim    TRUE, { NULL }, TRUE },
225299742Sdim
226299742Sdim  /* These are only reported for <= 1.6.x mod_dav_svn */
227299742Sdim  { OPEN_DIR, S_, "fetch-props", FETCH_PROPS,
228299742Sdim    FALSE, { NULL }, FALSE },
229299742Sdim  { OPEN_FILE, S_, "fetch-props", FETCH_PROPS,
230299742Sdim    FALSE, { NULL }, FALSE },
231299742Sdim
232299742Sdim  { PROP, D_, "version-name", VERSION_NAME,
233299742Sdim    TRUE, { NULL }, TRUE },
234299742Sdim  { PROP, D_, "creationdate", CREATIONDATE,
235299742Sdim    TRUE, { NULL }, TRUE },
236299742Sdim  { PROP, D_, "creator-displayname", CREATOR_DISPLAYNAME,
237299742Sdim    TRUE, { NULL }, TRUE },
238299742Sdim  { 0 }
239299742Sdim};
240299742Sdim
241251881Speter/* While we process the REPORT response, we will queue up GET and PROPFIND
242251881Speter   requests. For a very large checkout, it is very easy to queue requests
243251881Speter   faster than they are resolved. Thus, we need to pause the XML processing
244251881Speter   (which queues more requests) to avoid queueing too many, with their
245251881Speter   attendant memory costs. When the queue count drops low enough, we will
246251881Speter   resume XML processing.
247251881Speter
248251881Speter   Note that we don't want the count to drop to zero. We have multiple
249251881Speter   connections that we want to keep busy. These are also heuristic numbers
250251881Speter   since network and parsing behavior (ie. it doesn't pause immediately)
251251881Speter   can make the measurements quite imprecise.
252251881Speter
253251881Speter   We measure outstanding requests as the sum of NUM_ACTIVE_FETCHES and
254251881Speter   NUM_ACTIVE_PROPFINDS in the report_context_t structure.  */
255251881Speter#define REQUEST_COUNT_TO_PAUSE 50
256251881Speter#define REQUEST_COUNT_TO_RESUME 40
257251881Speter
258299742Sdim#define SPILLBUF_BLOCKSIZE 4096
259299742Sdim#define SPILLBUF_MAXBUFFSIZE 131072
260251881Speter
261299742Sdim#define PARSE_CHUNK_SIZE 8000 /* Copied from xml.c ### Needs tuning */
262299742Sdim
263251881Speter/* Forward-declare our report context. */
264251881Spetertypedef struct report_context_t report_context_t;
265299742Sdimtypedef struct body_create_baton_t body_create_baton_t;
266251881Speter/*
267251881Speter * This structure represents the information for a directory.
268251881Speter */
269299742Sdimtypedef struct dir_baton_t
270251881Speter{
271299742Sdim  struct dir_baton_t *parent_dir;       /* NULL when root */
272251881Speter
273299742Sdim  apr_pool_t *pool;                     /* Subpool for this directory */
274251881Speter
275251881Speter  /* Pointer back to our original report context. */
276299742Sdim  report_context_t *ctx;
277251881Speter
278299742Sdim  const char *relpath;                  /* session relative path */
279299742Sdim  const char *base_name;                /* Name of item "" for root */
280251881Speter
281251881Speter  /* the canonical url for this directory after updating. (received) */
282251881Speter  const char *url;
283251881Speter
284299742Sdim  /* The original repos_relpath of this url (via the reporter)
285299742Sdim  directly, or via an ancestor. */
286251881Speter  const char *repos_relpath;
287251881Speter
288299742Sdim  svn_revnum_t base_rev;                /* base revision or NULL for Add */
289251881Speter
290299742Sdim  const char *copyfrom_path;            /* NULL for open */
291299742Sdim  svn_revnum_t copyfrom_rev;            /* SVN_INVALID_REVNUM for open */
292299742Sdim
293251881Speter  /* controlling dir baton - this is only created in ensure_dir_opened() */
294299742Sdim  svn_boolean_t dir_opened;
295251881Speter  void *dir_baton;
296251881Speter
297251881Speter  /* How many references to this directory do we still have open? */
298251881Speter  apr_size_t ref_count;
299251881Speter
300299742Sdim  svn_boolean_t fetch_props;                 /* Use PROPFIND request? */
301251881Speter  svn_ra_serf__handler_t *propfind_handler;
302299742Sdim  apr_hash_t *remove_props;
303251881Speter
304299742Sdim} dir_baton_t;
305251881Speter
306251881Speter/*
307299742Sdim* This structure represents the information for a file.
308299742Sdim*
309299742Sdim* This structure is created as we parse the REPORT response and
310299742Sdim* once the element is completed, we may create a fetch_ctx_t structure
311299742Sdim* to give to serf to retrieve this file.
312299742Sdim*/
313299742Sdimtypedef struct file_baton_t
314251881Speter{
315299742Sdim  dir_baton_t *parent_dir;              /* The parent */
316299742Sdim  apr_pool_t *pool;                     /* Subpool for this file*/
317251881Speter
318299742Sdim  const char *relpath;                  /* session relative path */
319251881Speter  const char *base_name;
320251881Speter
321299742Sdim  /* the canonical url for this directory after updating. (received) */
322251881Speter  const char *url;
323251881Speter
324299742Sdim  /* The original repos_relpath of this url as reported. */
325299742Sdim  const char *repos_relpath;
326299742Sdim
327251881Speter  /* lock token, if we had one to start off with. */
328251881Speter  const char *lock_token;
329251881Speter
330299742Sdim  svn_revnum_t base_rev;                /* SVN_INVALID_REVNUM for Add */
331251881Speter
332299742Sdim  const char *copyfrom_path;            /* NULL for open */
333299742Sdim  svn_revnum_t copyfrom_rev;            /* SVN_INVALID_REVNUM for open */
334251881Speter
335299742Sdim  /* controlling dir baton - this is only created in ensure_file_opened() */
336299742Sdim  svn_boolean_t file_opened;
337299742Sdim  void *file_baton;
338251881Speter
339299742Sdim  svn_boolean_t fetch_props;            /* Use PROPFIND request? */
340251881Speter  svn_ra_serf__handler_t *propfind_handler;
341299742Sdim  svn_boolean_t found_lock_prop;
342299742Sdim  apr_hash_t *remove_props;
343251881Speter
344251881Speter  /* Has the server told us to go fetch - only valid if we had it already */
345251881Speter  svn_boolean_t fetch_file;
346251881Speter
347251881Speter  /* controlling file_baton and textdelta handler */
348299742Sdim  svn_txdelta_window_handler_t txdelta;
349299742Sdim  void *txdelta_baton;
350251881Speter
351299742Sdim  svn_checksum_t *base_md5_checksum;
352299742Sdim  svn_checksum_t *final_md5_checksum;
353299742Sdim  svn_checksum_t *final_sha1_checksum;
354251881Speter
355299742Sdim  svn_stream_t *txdelta_stream;         /* Stream that feeds windows when
356299742Sdim                                           written to within txdelta*/
357299742Sdim} file_baton_t;
358251881Speter
359251881Speter/*
360251881Speter * This structure represents a single request to GET (fetch) a file with
361251881Speter * its associated Serf session/connection.
362251881Speter */
363299742Sdimtypedef struct fetch_ctx_t {
364251881Speter
365251881Speter  /* The handler representing this particular fetch.  */
366251881Speter  svn_ra_serf__handler_t *handler;
367251881Speter
368299742Sdim  svn_boolean_t using_compression;
369251881Speter
370251881Speter  /* Stores the information for the file we want to fetch. */
371299742Sdim  file_baton_t *file;
372251881Speter
373251881Speter  /* Have we read our response headers yet? */
374251881Speter  svn_boolean_t read_headers;
375251881Speter
376251881Speter  /* This flag is set when our response is aborted before we reach the
377251881Speter   * end and we decide to requeue this request.
378251881Speter   */
379251881Speter  svn_boolean_t aborted_read;
380251881Speter  apr_off_t aborted_read_size;
381251881Speter
382251881Speter  /* This is the amount of data that we have read so far. */
383251881Speter  apr_off_t read_size;
384251881Speter
385251881Speter  /* If we're writing this file to a stream, this will be non-NULL. */
386299742Sdim  svn_stream_t *result_stream;
387251881Speter
388299742Sdim  /* The base-rev header  */
389299742Sdim  const char *delta_base;
390251881Speter
391299742Sdim} fetch_ctx_t;
392251881Speter
393251881Speter/*
394251881Speter * The master structure for a REPORT request and response.
395251881Speter */
396251881Speterstruct report_context_t {
397251881Speter  apr_pool_t *pool;
398251881Speter
399251881Speter  svn_ra_serf__session_t *sess;
400251881Speter
401251881Speter  /* Source path and destination path */
402251881Speter  const char *source;
403251881Speter  const char *destination;
404251881Speter
405251881Speter  /* Our update target. */
406251881Speter  const char *update_target;
407251881Speter
408251881Speter  /* What is the target revision that we want for this REPORT? */
409251881Speter  svn_revnum_t target_rev;
410251881Speter
411299742Sdim  /* Where are we (used while parsing) */
412299742Sdim  dir_baton_t *cur_dir;
413299742Sdim  file_baton_t *cur_file;
414299742Sdim
415251881Speter  /* Have we been asked to ignore ancestry or textdeltas? */
416251881Speter  svn_boolean_t ignore_ancestry;
417251881Speter  svn_boolean_t text_deltas;
418251881Speter
419251881Speter  /* Do we want the server to send copyfrom args or not? */
420251881Speter  svn_boolean_t send_copyfrom_args;
421251881Speter
422251881Speter  /* Is the server sending everything in one response? */
423251881Speter  svn_boolean_t send_all_mode;
424251881Speter
425251881Speter  /* Is the server including properties inline for newly added
426251881Speter     files/dirs? */
427251881Speter  svn_boolean_t add_props_included;
428251881Speter
429251881Speter  /* Path -> const char *repos_relpath mapping */
430251881Speter  apr_hash_t *switched_paths;
431251881Speter
432251881Speter  /* Our master update editor and baton. */
433299742Sdim  const svn_delta_editor_t *editor;
434299742Sdim  void *editor_baton;
435251881Speter
436251881Speter  /* The file holding request body for the REPORT.
437251881Speter   *
438251881Speter   * ### todo: It will be better for performance to store small
439251881Speter   * request bodies (like 4k) in memory and bigger bodies on disk.
440251881Speter   */
441299742Sdim  svn_stream_t *body_template;
442299742Sdim  body_create_baton_t *body;
443251881Speter
444251881Speter  /* number of pending GET requests */
445251881Speter  unsigned int num_active_fetches;
446251881Speter
447251881Speter  /* number of pending PROPFIND requests */
448251881Speter  unsigned int num_active_propfinds;
449251881Speter
450251881Speter  /* Are we done parsing the REPORT response? */
451251881Speter  svn_boolean_t done;
452251881Speter
453251881Speter  /* Did we receive all data from the network? */
454251881Speter  svn_boolean_t report_received;
455251881Speter
456251881Speter  /* Did we close the root directory? */
457251881Speter  svn_boolean_t closed_root;
458251881Speter};
459251881Speter
460299742Sdim/* Baton for collecting REPORT body. Depending on the size this
461299742Sdim   work is backed by a memory buffer (via serf buckets) or by
462299742Sdim   a file */
463299742Sdimstruct body_create_baton_t
464299742Sdim{
465299742Sdim  apr_pool_t *result_pool;
466299742Sdim  apr_size_t total_bytes;
467251881Speter
468299742Sdim  apr_pool_t *scratch_pool;
469251881Speter
470299742Sdim  serf_bucket_alloc_t *alloc;
471299742Sdim  serf_bucket_t *collect_bucket;
472251881Speter
473299742Sdim  const void *all_data;
474299742Sdim  apr_file_t *file;
475299742Sdim};
476251881Speter
477251881Speter
478299742Sdim#define MAX_BODY_IN_RAM (256*1024)
479251881Speter
480299742Sdim/* Fold all previously collected data in a single buffer allocated in
481299742Sdim   RESULT_POOL and clear all intermediate state */
482299742Sdimstatic const char *
483299742Sdimbody_allocate_all(body_create_baton_t *body,
484299742Sdim                  apr_pool_t *result_pool)
485299742Sdim{
486299742Sdim  char *buffer = apr_pcalloc(result_pool, body->total_bytes);
487299742Sdim  const char *data;
488299742Sdim  apr_size_t sz;
489299742Sdim  apr_status_t s;
490299742Sdim  apr_size_t remaining = body->total_bytes;
491299742Sdim  char *next = buffer;
492251881Speter
493299742Sdim  while (!(s = serf_bucket_read(body->collect_bucket, remaining, &data, &sz)))
494299742Sdim    {
495299742Sdim      memcpy(next, data, sz);
496299742Sdim      remaining -= sz;
497299742Sdim      next += sz;
498251881Speter
499299742Sdim      if (! remaining)
500299742Sdim        break;
501299742Sdim    }
502251881Speter
503299742Sdim  if (!SERF_BUCKET_READ_ERROR(s))
504299742Sdim    {
505299742Sdim      memcpy(next, data, sz);
506299742Sdim    }
507251881Speter
508299742Sdim  serf_bucket_destroy(body->collect_bucket);
509299742Sdim  body->collect_bucket = NULL;
510251881Speter
511299742Sdim  return (s != APR_EOF) ? NULL : buffer;
512299742Sdim}
513251881Speter
514299742Sdim/* Noop function. Make serf take care of freeing in error situations */
515299742Sdimstatic void serf_free_no_error(void *unfreed_baton, void *block) {}
516251881Speter
517299742Sdim/* Stream write function for body creation */
518299742Sdimstatic svn_error_t *
519299742Sdimbody_write_fn(void *baton,
520299742Sdim              const char *data,
521299742Sdim              apr_size_t *len)
522299742Sdim{
523299742Sdim  body_create_baton_t *bcb = baton;
524251881Speter
525299742Sdim  if (!bcb->scratch_pool)
526299742Sdim    bcb->scratch_pool = svn_pool_create(bcb->result_pool);
527251881Speter
528299742Sdim  if (bcb->file)
529299742Sdim    {
530299742Sdim      SVN_ERR(svn_io_file_write_full(bcb->file, data, *len, NULL,
531299742Sdim                                     bcb->scratch_pool));
532299742Sdim      svn_pool_clear(bcb->scratch_pool);
533251881Speter
534299742Sdim      bcb->total_bytes += *len;
535299742Sdim    }
536299742Sdim  else if (*len + bcb->total_bytes > MAX_BODY_IN_RAM)
537299742Sdim    {
538299742Sdim      SVN_ERR(svn_io_open_unique_file3(&bcb->file, NULL, NULL,
539299742Sdim                                       svn_io_file_del_on_pool_cleanup,
540299742Sdim                                       bcb->result_pool, bcb->scratch_pool));
541251881Speter
542299742Sdim      if (bcb->total_bytes)
543299742Sdim        {
544299742Sdim          const char *all = body_allocate_all(bcb, bcb->scratch_pool);
545251881Speter
546299742Sdim          SVN_ERR(svn_io_file_write_full(bcb->file, all, bcb->total_bytes,
547299742Sdim                                         NULL, bcb->scratch_pool));
548299742Sdim        }
549251881Speter
550299742Sdim      SVN_ERR(svn_io_file_write_full(bcb->file, data, *len, NULL,
551299742Sdim                                     bcb->scratch_pool));
552299742Sdim      bcb->total_bytes += *len;
553299742Sdim    }
554299742Sdim  else
555299742Sdim    {
556299742Sdim      if (!bcb->alloc)
557299742Sdim        bcb->alloc = serf_bucket_allocator_create(bcb->scratch_pool,
558299742Sdim                                                  serf_free_no_error, NULL);
559299742Sdim
560299742Sdim      if (!bcb->collect_bucket)
561299742Sdim        bcb->collect_bucket = serf_bucket_aggregate_create(bcb->alloc);
562299742Sdim
563299742Sdim      serf_bucket_aggregate_append(bcb->collect_bucket,
564299742Sdim                                   serf_bucket_simple_copy_create(data, *len,
565299742Sdim                                                                  bcb->alloc));
566299742Sdim
567299742Sdim      bcb->total_bytes += *len;
568299742Sdim    }
569299742Sdim
570299742Sdim  return SVN_NO_ERROR;
571299742Sdim}
572299742Sdim
573299742Sdim/* Stream close function for collecting body */
574251881Speterstatic svn_error_t *
575299742Sdimbody_done_fn(void *baton)
576251881Speter{
577299742Sdim  body_create_baton_t *bcb = baton;
578299742Sdim  if (bcb->file)
579299742Sdim    {
580299742Sdim      /* We need to flush the file, make it unbuffered (so that it can be
581299742Sdim        * zero-copied via mmap), and reset the position before attempting
582299742Sdim        * to deliver the file.
583299742Sdim        *
584299742Sdim        * N.B. If we have APR 1.3+, we can unbuffer the file to let us use
585299742Sdim        * mmap and zero-copy the PUT body.  However, on older APR versions,
586299742Sdim        * we can't check the buffer status; but serf will fall through and
587299742Sdim        * create a file bucket for us on the buffered handle.
588299742Sdim        */
589251881Speter
590299742Sdim      SVN_ERR(svn_io_file_flush(bcb->file, bcb->scratch_pool));
591299742Sdim      apr_file_buffer_set(bcb->file, NULL, 0);
592299742Sdim    }
593299742Sdim  else if (bcb->collect_bucket)
594299742Sdim    bcb->all_data = body_allocate_all(bcb, bcb->result_pool);
595299742Sdim
596299742Sdim  if (bcb->scratch_pool)
597299742Sdim    svn_pool_destroy(bcb->scratch_pool);
598299742Sdim
599251881Speter  return SVN_NO_ERROR;
600251881Speter}
601251881Speter
602251881Speterstatic svn_error_t *
603299742Sdimcreate_dir_baton(dir_baton_t **new_dir,
604299742Sdim                 report_context_t *ctx,
605299742Sdim                 const char *name,
606299742Sdim                 apr_pool_t *scratch_pool)
607251881Speter{
608299742Sdim  dir_baton_t *parent = ctx->cur_dir;
609299742Sdim  apr_pool_t *dir_pool;
610299742Sdim  dir_baton_t *dir;
611251881Speter
612299742Sdim  if (parent)
613299742Sdim    dir_pool = svn_pool_create(parent->pool);
614299742Sdim  else
615299742Sdim    dir_pool = svn_pool_create(ctx->pool);
616299742Sdim
617299742Sdim  dir = apr_pcalloc(dir_pool, sizeof(*dir));
618299742Sdim  dir->pool = dir_pool;
619299742Sdim  dir->ctx = ctx;
620299742Sdim
621299742Sdim  if (parent)
622251881Speter    {
623299742Sdim      dir->parent_dir = parent;
624299742Sdim      parent->ref_count++;
625299742Sdim    }
626251881Speter
627299742Sdim  dir->relpath = parent ? svn_relpath_join(parent->relpath, name, dir_pool)
628299742Sdim                        : apr_pstrdup(dir_pool, name);
629299742Sdim  dir->base_name = svn_relpath_basename(dir->relpath, NULL);
630299742Sdim
631299742Sdim  dir->repos_relpath = svn_hash_gets(ctx->switched_paths, dir->relpath);
632299742Sdim  if (!dir->repos_relpath)
633299742Sdim    {
634299742Sdim      if (parent)
635299742Sdim        dir->repos_relpath = svn_relpath_join(parent->repos_relpath, name,
636299742Sdim                                              dir_pool);
637299742Sdim      else
638299742Sdim        dir->repos_relpath = svn_uri_skip_ancestor(ctx->sess->repos_root_str,
639299742Sdim                                                   ctx->sess->session_url_str,
640299742Sdim                                                   dir_pool);
641251881Speter    }
642251881Speter
643299742Sdim  dir->base_rev = SVN_INVALID_REVNUM;
644299742Sdim  dir->copyfrom_rev = SVN_INVALID_REVNUM;
645299742Sdim
646299742Sdim  dir->ref_count = 1;
647299742Sdim
648299742Sdim  ctx->cur_dir = dir;
649299742Sdim
650299742Sdim  *new_dir = dir;
651251881Speter  return SVN_NO_ERROR;
652251881Speter}
653251881Speter
654251881Speterstatic svn_error_t *
655299742Sdimcreate_file_baton(file_baton_t **new_file,
656299742Sdim                  report_context_t *ctx,
657299742Sdim                  const char *name,
658299742Sdim                  apr_pool_t *scratch_pool)
659251881Speter{
660299742Sdim  dir_baton_t *parent = ctx->cur_dir;
661299742Sdim  apr_pool_t *file_pool;
662299742Sdim  file_baton_t *file;
663251881Speter
664299742Sdim  file_pool = svn_pool_create(parent->pool);
665299742Sdim
666299742Sdim  file = apr_pcalloc(file_pool, sizeof(*file));
667299742Sdim  file->pool = file_pool;
668299742Sdim
669299742Sdim  file->parent_dir = parent;
670299742Sdim  parent->ref_count++;
671299742Sdim
672299742Sdim  file->relpath = svn_relpath_join(parent->relpath, name, file_pool);
673299742Sdim  file->base_name = svn_relpath_basename(file->relpath, NULL);
674299742Sdim
675299742Sdim  file->repos_relpath = svn_hash_gets(ctx->switched_paths, file->relpath);
676299742Sdim  if (!file->repos_relpath)
677299742Sdim    file->repos_relpath = svn_relpath_join(parent->repos_relpath, name,
678299742Sdim                                           file_pool);
679299742Sdim
680299742Sdim  /* Sane defaults */
681299742Sdim  file->base_rev = SVN_INVALID_REVNUM;
682299742Sdim  file->copyfrom_rev = SVN_INVALID_REVNUM;
683299742Sdim
684299742Sdim  *new_file = file;
685299742Sdim
686299742Sdim  ctx->cur_file = file;
687299742Sdim
688251881Speter  return SVN_NO_ERROR;
689251881Speter}
690251881Speter
691299742Sdim/** Minimum nr. of outstanding requests needed before a new connection is
692299742Sdim *  opened. */
693299742Sdim#define REQS_PER_CONN 8
694251881Speter
695299742Sdim/** This function creates a new connection for this serf session, but only
696299742Sdim * if the number of NUM_ACTIVE_REQS > REQS_PER_CONN or if there currently is
697299742Sdim * only one main connection open.
698299742Sdim */
699299742Sdimstatic svn_error_t *
700299742Sdimopen_connection_if_needed(svn_ra_serf__session_t *sess, int num_active_reqs)
701299742Sdim{
702299742Sdim  /* For each REQS_PER_CONN outstanding requests open a new connection, with
703299742Sdim   * a minimum of 1 extra connection. */
704299742Sdim  if (sess->num_conns == 1 ||
705299742Sdim      ((num_active_reqs / REQS_PER_CONN) > sess->num_conns))
706299742Sdim    {
707299742Sdim      int cur = sess->num_conns;
708299742Sdim      apr_status_t status;
709251881Speter
710299742Sdim      sess->conns[cur] = apr_pcalloc(sess->pool, sizeof(*sess->conns[cur]));
711299742Sdim      sess->conns[cur]->bkt_alloc = serf_bucket_allocator_create(sess->pool,
712299742Sdim                                                                 NULL, NULL);
713299742Sdim      sess->conns[cur]->last_status_code = -1;
714299742Sdim      sess->conns[cur]->session = sess;
715299742Sdim      status = serf_connection_create2(&sess->conns[cur]->conn,
716299742Sdim                                       sess->context,
717299742Sdim                                       sess->session_url,
718299742Sdim                                       svn_ra_serf__conn_setup,
719299742Sdim                                       sess->conns[cur],
720299742Sdim                                       svn_ra_serf__conn_closed,
721299742Sdim                                       sess->conns[cur],
722299742Sdim                                       sess->pool);
723299742Sdim      if (status)
724299742Sdim        return svn_ra_serf__wrap_err(status, NULL);
725299742Sdim
726299742Sdim      sess->num_conns++;
727299742Sdim    }
728299742Sdim
729299742Sdim  return SVN_NO_ERROR;
730299742Sdim}
731299742Sdim
732251881Speter/* Returns best connection for fetching files/properties. */
733251881Speterstatic svn_ra_serf__connection_t *
734251881Speterget_best_connection(report_context_t *ctx)
735251881Speter{
736251881Speter  svn_ra_serf__connection_t *conn;
737251881Speter  int first_conn = 1;
738251881Speter
739251881Speter  /* Skip the first connection if the REPORT response hasn't been completely
740251881Speter     received yet or if we're being told to limit our connections to
741251881Speter     2 (because this could be an attempt to ensure that we do all our
742251881Speter     auxiliary GETs/PROPFINDs on a single connection).
743251881Speter
744251881Speter     ### FIXME: This latter requirement (max_connections > 2) is
745251881Speter     ### really just a hack to work around the fact that some update
746251881Speter     ### editor implementations (such as svnrdump's dump editor)
747251881Speter     ### simply can't handle the way ra_serf violates the editor v1
748251881Speter     ### drive ordering requirements.
749251881Speter     ###
750251881Speter     ### See http://subversion.tigris.org/issues/show_bug.cgi?id=4116.
751251881Speter  */
752251881Speter  if (ctx->report_received && (ctx->sess->max_connections > 2))
753251881Speter    first_conn = 0;
754251881Speter
755299742Sdim  /* If there's only one available auxiliary connection to use, don't bother
756299742Sdim     doing all the cur_conn math -- just return that one connection.  */
757251881Speter  if (ctx->sess->num_conns - first_conn == 1)
758251881Speter    {
759251881Speter      conn = ctx->sess->conns[first_conn];
760251881Speter    }
761251881Speter  else
762251881Speter    {
763299742Sdim#if SERF_VERSION_AT_LEAST(1, 4, 0)
764299742Sdim      /* Often one connection is slower than others, e.g. because the server
765299742Sdim         process/thread has to do more work for the particular set of requests.
766299742Sdim         In the worst case, when REQUEST_COUNT_TO_RESUME requests are queued
767299742Sdim         on such a slow connection, ra_serf will completely stop sending
768299742Sdim         requests.
769299742Sdim
770299742Sdim         The method used here selects the connection with the least amount of
771299742Sdim         pending requests, thereby giving more work to lightly loaded server
772299742Sdim         processes.
773299742Sdim       */
774299742Sdim      int i, best_conn = first_conn;
775299742Sdim      unsigned int min = INT_MAX;
776299742Sdim      for (i = first_conn; i < ctx->sess->num_conns; i++)
777299742Sdim        {
778299742Sdim          serf_connection_t *sc = ctx->sess->conns[i]->conn;
779299742Sdim          unsigned int pending = serf_connection_pending_requests(sc);
780299742Sdim          if (pending < min)
781299742Sdim            {
782299742Sdim              min = pending;
783299742Sdim              best_conn = i;
784299742Sdim            }
785299742Sdim        }
786299742Sdim      conn = ctx->sess->conns[best_conn];
787299742Sdim#else
788299742Sdim    /* We don't know how many requests are pending per connection, so just
789299742Sdim       cycle them. */
790251881Speter      conn = ctx->sess->conns[ctx->sess->cur_conn];
791251881Speter      ctx->sess->cur_conn++;
792251881Speter      if (ctx->sess->cur_conn >= ctx->sess->num_conns)
793251881Speter        ctx->sess->cur_conn = first_conn;
794299742Sdim#endif
795251881Speter    }
796251881Speter  return conn;
797251881Speter}
798251881Speter
799251881Speter/** Helpers to open and close directories */
800251881Speter
801251881Speterstatic svn_error_t*
802299742Sdimensure_dir_opened(dir_baton_t *dir,
803299742Sdim                  apr_pool_t *scratch_pool)
804251881Speter{
805299742Sdim  report_context_t *ctx = dir->ctx;
806251881Speter
807299742Sdim  if (dir->dir_opened)
808299742Sdim    return SVN_NO_ERROR;
809251881Speter
810251881Speter  if (dir->base_name[0] == '\0')
811251881Speter    {
812251881Speter      if (ctx->destination
813251881Speter          && ctx->sess->wc_callbacks->invalidate_wc_props)
814251881Speter        {
815251881Speter          SVN_ERR(ctx->sess->wc_callbacks->invalidate_wc_props(
816251881Speter                      ctx->sess->wc_callback_baton,
817251881Speter                      ctx->update_target,
818299742Sdim                      SVN_RA_SERF__WC_CHECKED_IN_URL, scratch_pool));
819251881Speter        }
820251881Speter
821299742Sdim      SVN_ERR(ctx->editor->open_root(ctx->editor_baton, dir->base_rev,
822299742Sdim                                     dir->pool,
823299742Sdim                                     &dir->dir_baton));
824251881Speter    }
825251881Speter  else
826251881Speter    {
827299742Sdim      SVN_ERR(ensure_dir_opened(dir->parent_dir, scratch_pool));
828251881Speter
829251881Speter      if (SVN_IS_VALID_REVNUM(dir->base_rev))
830251881Speter        {
831299742Sdim          SVN_ERR(ctx->editor->open_directory(dir->relpath,
832299742Sdim                                              dir->parent_dir->dir_baton,
833299742Sdim                                              dir->base_rev,
834299742Sdim                                              dir->pool,
835299742Sdim                                              &dir->dir_baton));
836251881Speter        }
837251881Speter      else
838251881Speter        {
839299742Sdim          SVN_ERR(ctx->editor->add_directory(dir->relpath,
840299742Sdim                                             dir->parent_dir->dir_baton,
841299742Sdim                                             dir->copyfrom_path,
842299742Sdim                                             dir->copyfrom_rev,
843299742Sdim                                             dir->pool,
844299742Sdim                                             &dir->dir_baton));
845251881Speter        }
846251881Speter    }
847251881Speter
848299742Sdim  dir->dir_opened = TRUE;
849299742Sdim
850251881Speter  return SVN_NO_ERROR;
851251881Speter}
852251881Speter
853251881Speterstatic svn_error_t *
854299742Sdimmaybe_close_dir(dir_baton_t *dir)
855251881Speter{
856299742Sdim  apr_pool_t *scratch_pool = dir->pool;
857299742Sdim  dir_baton_t *parent = dir->parent_dir;
858299742Sdim  report_context_t *ctx = dir->ctx;
859251881Speter
860299742Sdim  if (--dir->ref_count)
861251881Speter    {
862299742Sdim      return SVN_NO_ERROR;
863251881Speter    }
864251881Speter
865299742Sdim  SVN_ERR(ensure_dir_opened(dir, dir->pool));
866251881Speter
867299742Sdim  if (dir->remove_props)
868251881Speter    {
869299742Sdim      apr_hash_index_t *hi;
870251881Speter
871299742Sdim      for (hi = apr_hash_first(scratch_pool, dir->remove_props);
872299742Sdim           hi;
873299742Sdim           hi = apr_hash_next(hi))
874251881Speter        {
875299742Sdim          SVN_ERR(ctx->editor->change_file_prop(dir->dir_baton,
876299742Sdim                                                apr_hash_this_key(hi),
877299742Sdim                                                NULL /* value */,
878299742Sdim                                                scratch_pool));
879251881Speter        }
880251881Speter    }
881251881Speter
882299742Sdim  SVN_ERR(dir->ctx->editor->close_directory(dir->dir_baton, scratch_pool));
883251881Speter
884299742Sdim  svn_pool_destroy(dir->pool /* scratch_pool */);
885299742Sdim
886299742Sdim  if (parent)
887299742Sdim    return svn_error_trace(maybe_close_dir(parent));
888299742Sdim  else
889299742Sdim    return SVN_NO_ERROR;
890251881Speter}
891251881Speter
892299742Sdimstatic svn_error_t *
893299742Sdimensure_file_opened(file_baton_t *file,
894299742Sdim                   apr_pool_t *scratch_pool)
895251881Speter{
896299742Sdim  const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
897251881Speter
898299742Sdim  if (file->file_opened)
899299742Sdim    return SVN_NO_ERROR;
900251881Speter
901299742Sdim  /* Ensure our parent is open. */
902299742Sdim  SVN_ERR(ensure_dir_opened(file->parent_dir, scratch_pool));
903251881Speter
904299742Sdim  /* Open (or add) the file. */
905299742Sdim  if (SVN_IS_VALID_REVNUM(file->base_rev))
906251881Speter    {
907299742Sdim      SVN_ERR(editor->open_file(file->relpath,
908299742Sdim                                file->parent_dir->dir_baton,
909299742Sdim                                file->base_rev,
910299742Sdim                                file->pool,
911299742Sdim                                &file->file_baton));
912251881Speter    }
913299742Sdim  else
914251881Speter    {
915299742Sdim      SVN_ERR(editor->add_file(file->relpath,
916299742Sdim                               file->parent_dir->dir_baton,
917299742Sdim                               file->copyfrom_path,
918299742Sdim                               file->copyfrom_rev,
919299742Sdim                               file->pool,
920299742Sdim                               &file->file_baton));
921299742Sdim    }
922251881Speter
923299742Sdim  file->file_opened = TRUE;
924251881Speter
925299742Sdim  return SVN_NO_ERROR;
926251881Speter}
927251881Speter
928299742Sdim
929299742Sdim/** Routines called when we are fetching a file */
930299742Sdim
931251881Speterstatic svn_error_t *
932251881Speterheaders_fetch(serf_bucket_t *headers,
933251881Speter              void *baton,
934299742Sdim              apr_pool_t *pool /* request pool */,
935299742Sdim              apr_pool_t *scratch_pool)
936251881Speter{
937299742Sdim  fetch_ctx_t *fetch_ctx = baton;
938251881Speter
939251881Speter  /* note that we have old VC URL */
940299742Sdim  if (fetch_ctx->delta_base)
941251881Speter    {
942251881Speter      serf_bucket_headers_setn(headers, SVN_DAV_DELTA_BASE_HEADER,
943299742Sdim                               fetch_ctx->delta_base);
944251881Speter      serf_bucket_headers_setn(headers, "Accept-Encoding",
945251881Speter                               "svndiff1;q=0.9,svndiff;q=0.8");
946251881Speter    }
947299742Sdim  else if (fetch_ctx->using_compression)
948251881Speter    {
949251881Speter      serf_bucket_headers_setn(headers, "Accept-Encoding", "gzip");
950251881Speter    }
951251881Speter
952251881Speter  return SVN_NO_ERROR;
953251881Speter}
954251881Speter
955251881Speterstatic svn_error_t *
956251881Spetercancel_fetch(serf_request_t *request,
957251881Speter             serf_bucket_t *response,
958251881Speter             int status_code,
959251881Speter             void *baton)
960251881Speter{
961299742Sdim  fetch_ctx_t *fetch_ctx = baton;
962251881Speter
963251881Speter  /* Uh-oh.  Our connection died on us.
964251881Speter   *
965251881Speter   * The core ra_serf layer will requeue our request - we just need to note
966251881Speter   * that we got cut off in the middle of our song.
967251881Speter   */
968251881Speter  if (!response)
969251881Speter    {
970251881Speter      /* If we already started the fetch and opened the file handle, we need
971251881Speter       * to hold subsequent read() ops until we get back to where we were
972251881Speter       * before the close and we can then resume the textdelta() calls.
973251881Speter       */
974251881Speter      if (fetch_ctx->read_headers)
975251881Speter        {
976251881Speter          if (!fetch_ctx->aborted_read && fetch_ctx->read_size)
977251881Speter            {
978251881Speter              fetch_ctx->aborted_read = TRUE;
979251881Speter              fetch_ctx->aborted_read_size = fetch_ctx->read_size;
980251881Speter            }
981251881Speter          fetch_ctx->read_size = 0;
982251881Speter        }
983251881Speter
984251881Speter      return SVN_NO_ERROR;
985251881Speter    }
986251881Speter
987251881Speter  /* We have no idea what went wrong. */
988251881Speter  SVN_ERR_MALFUNCTION();
989251881Speter}
990251881Speter
991251881Speter/* Wield the editor referenced by INFO to open (or add) the file
992251881Speter   file also associated with INFO, setting properties on the file and
993251881Speter   calling the editor's apply_textdelta() function on it if necessary
994251881Speter   (or if FORCE_APPLY_TEXTDELTA is set).
995251881Speter
996251881Speter   Callers will probably want to also see the function that serves
997251881Speter   the opposite purpose of this one, close_updated_file().  */
998251881Speterstatic svn_error_t *
999299742Sdimopen_file_txdelta(file_baton_t *file,
1000251881Speter                  apr_pool_t *scratch_pool)
1001251881Speter{
1002299742Sdim  const svn_delta_editor_t *editor = file->parent_dir->ctx->editor;
1003251881Speter
1004299742Sdim  SVN_ERR_ASSERT(file->txdelta == NULL);
1005251881Speter
1006299742Sdim  SVN_ERR(ensure_file_opened(file, scratch_pool));
1007251881Speter
1008251881Speter  /* Get (maybe) a textdelta window handler for transmitting file
1009251881Speter     content changes. */
1010299742Sdim  SVN_ERR(editor->apply_textdelta(file->file_baton,
1011299742Sdim                                  svn_checksum_to_cstring(
1012299742Sdim                                                  file->base_md5_checksum,
1013299742Sdim                                                  scratch_pool),
1014299742Sdim                                  file->pool,
1015299742Sdim                                  &file->txdelta,
1016299742Sdim                                  &file->txdelta_baton));
1017251881Speter
1018251881Speter  return SVN_NO_ERROR;
1019251881Speter}
1020251881Speter
1021299742Sdim/* Close the file, handling loose ends and cleanup */
1022251881Speterstatic svn_error_t *
1023299742Sdimclose_file(file_baton_t *file,
1024299742Sdim           apr_pool_t *scratch_pool)
1025251881Speter{
1026299742Sdim  dir_baton_t *parent_dir = file->parent_dir;
1027299742Sdim  report_context_t *ctx = parent_dir->ctx;
1028251881Speter
1029299742Sdim  SVN_ERR(ensure_file_opened(file, scratch_pool));
1030299742Sdim
1031251881Speter  /* Set all of the properties we received */
1032299742Sdim  if (file->remove_props)
1033251881Speter    {
1034299742Sdim      apr_hash_index_t *hi;
1035299742Sdim
1036299742Sdim      for (hi = apr_hash_first(scratch_pool, file->remove_props);
1037299742Sdim           hi;
1038299742Sdim           hi = apr_hash_next(hi))
1039299742Sdim        {
1040299742Sdim          SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
1041299742Sdim                                                apr_hash_this_key(hi),
1042299742Sdim                                                NULL /* value */,
1043299742Sdim                                                scratch_pool));
1044299742Sdim        }
1045251881Speter    }
1046251881Speter
1047299742Sdim  /* Check for lock information. */
1048299742Sdim
1049299742Sdim  /* This works around a bug in some older versions of mod_dav_svn in that it
1050299742Sdim   * will not send remove-prop in the update report when a lock property
1051299742Sdim   * disappears when send-all is false.
1052299742Sdim
1053299742Sdim   ### Given that we only fetch props on additions, is this really necessary?
1054299742Sdim       Or is it covering up old local copy bugs where we copied locks to other
1055299742Sdim       paths? */
1056299742Sdim  if (!ctx->add_props_included
1057299742Sdim      && file->lock_token && !file->found_lock_prop
1058299742Sdim      && SVN_IS_VALID_REVNUM(file->base_rev) /* file_is_added */)
1059299742Sdim    {
1060299742Sdim      SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
1061299742Sdim                                            SVN_PROP_ENTRY_LOCK_TOKEN,
1062299742Sdim                                            NULL,
1063299742Sdim                                            scratch_pool));
1064299742Sdim    }
1065299742Sdim
1066299742Sdim  if (file->url)
1067299742Sdim    {
1068299742Sdim      SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
1069299742Sdim                                            SVN_RA_SERF__WC_CHECKED_IN_URL,
1070299742Sdim                                            svn_string_create(file->url,
1071299742Sdim                                                              scratch_pool),
1072299742Sdim                                            scratch_pool));
1073299742Sdim    }
1074299742Sdim
1075251881Speter  /* Close the file via the editor. */
1076299742Sdim  SVN_ERR(ctx->editor->close_file(file->file_baton,
1077299742Sdim                                  svn_checksum_to_cstring(
1078299742Sdim                                        file->final_md5_checksum,
1079299742Sdim                                        scratch_pool),
1080299742Sdim                                  scratch_pool));
1081251881Speter
1082299742Sdim  svn_pool_destroy(file->pool);
1083251881Speter
1084299742Sdim  SVN_ERR(maybe_close_dir(parent_dir)); /* Remove reference */
1085299742Sdim
1086251881Speter  return SVN_NO_ERROR;
1087251881Speter}
1088251881Speter
1089251881Speter/* Implements svn_ra_serf__response_handler_t */
1090251881Speterstatic svn_error_t *
1091251881Speterhandle_fetch(serf_request_t *request,
1092251881Speter             serf_bucket_t *response,
1093251881Speter             void *handler_baton,
1094251881Speter             apr_pool_t *pool)
1095251881Speter{
1096251881Speter  const char *data;
1097251881Speter  apr_size_t len;
1098251881Speter  apr_status_t status;
1099299742Sdim  fetch_ctx_t *fetch_ctx = handler_baton;
1100299742Sdim  file_baton_t *file = fetch_ctx->file;
1101251881Speter
1102251881Speter  /* ### new field. make sure we didn't miss some initialization.  */
1103251881Speter  SVN_ERR_ASSERT(fetch_ctx->handler != NULL);
1104251881Speter
1105251881Speter  if (!fetch_ctx->read_headers)
1106251881Speter    {
1107251881Speter      serf_bucket_t *hdrs;
1108251881Speter      const char *val;
1109251881Speter
1110299742Sdim      /* If the error code wasn't 200, something went wrong. Don't use the
1111299742Sdim       * returned data as its probably an error message. Just bail out instead.
1112299742Sdim       */
1113299742Sdim      if (fetch_ctx->handler->sline.code != 200)
1114299742Sdim        {
1115299742Sdim          fetch_ctx->handler->discard_body = TRUE;
1116299742Sdim          return SVN_NO_ERROR; /* Will return an error in the DONE handler */
1117299742Sdim        }
1118299742Sdim
1119251881Speter      hdrs = serf_bucket_response_get_headers(response);
1120251881Speter      val = serf_bucket_headers_get(hdrs, "Content-Type");
1121251881Speter
1122251881Speter      if (val && svn_cstring_casecmp(val, SVN_SVNDIFF_MIME_TYPE) == 0)
1123251881Speter        {
1124299742Sdim          fetch_ctx->result_stream =
1125299742Sdim              svn_txdelta_parse_svndiff(file->txdelta,
1126299742Sdim                                        file->txdelta_baton,
1127299742Sdim                                        TRUE, file->pool);
1128251881Speter
1129251881Speter          /* Validate the delta base claimed by the server matches
1130251881Speter             what we asked for! */
1131251881Speter          val = serf_bucket_headers_get(hdrs, SVN_DAV_DELTA_BASE_HEADER);
1132299742Sdim          if (val && fetch_ctx->delta_base == NULL)
1133251881Speter            {
1134299742Sdim              /* We recieved response with delta base header while we didn't
1135299742Sdim                 requested it -- report it as error. */
1136299742Sdim              return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
1137299742Sdim                                       _("GET request returned unexpected "
1138299742Sdim                                         "delta base: %s"), val);
1139251881Speter            }
1140299742Sdim          else if (val && (strcmp(val, fetch_ctx->delta_base) != 0))
1141299742Sdim            {
1142299742Sdim              return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
1143299742Sdim                                       _("GET request returned unexpected "
1144299742Sdim                                         "delta base: %s"), val);
1145299742Sdim            }
1146251881Speter        }
1147251881Speter      else
1148251881Speter        {
1149299742Sdim          fetch_ctx->result_stream = NULL;
1150251881Speter        }
1151251881Speter
1152251881Speter      fetch_ctx->read_headers = TRUE;
1153251881Speter    }
1154251881Speter
1155299742Sdim  while (TRUE)
1156251881Speter    {
1157251881Speter      svn_txdelta_window_t delta_window = { 0 };
1158251881Speter      svn_txdelta_op_t delta_op;
1159251881Speter      svn_string_t window_data;
1160251881Speter
1161251881Speter      status = serf_bucket_read(response, 8000, &data, &len);
1162251881Speter      if (SERF_BUCKET_READ_ERROR(status))
1163251881Speter        {
1164251881Speter          return svn_ra_serf__wrap_err(status, NULL);
1165251881Speter        }
1166251881Speter
1167251881Speter      fetch_ctx->read_size += len;
1168251881Speter
1169251881Speter      if (fetch_ctx->aborted_read)
1170251881Speter        {
1171251881Speter          apr_off_t skip;
1172251881Speter          /* We haven't caught up to where we were before. */
1173251881Speter          if (fetch_ctx->read_size < fetch_ctx->aborted_read_size)
1174251881Speter            {
1175251881Speter              /* Eek.  What did the file shrink or something? */
1176251881Speter              if (APR_STATUS_IS_EOF(status))
1177251881Speter                {
1178251881Speter                  SVN_ERR_MALFUNCTION();
1179251881Speter                }
1180251881Speter
1181251881Speter              /* Skip on to the next iteration of this loop. */
1182299742Sdim              if (status /* includes EAGAIN */)
1183299742Sdim                return svn_ra_serf__wrap_err(status, NULL);
1184299742Sdim
1185251881Speter              continue;
1186251881Speter            }
1187251881Speter
1188251881Speter          /* Woo-hoo.  We're back. */
1189251881Speter          fetch_ctx->aborted_read = FALSE;
1190251881Speter
1191251881Speter          /* Update data and len to just provide the new data. */
1192251881Speter          skip = len - (fetch_ctx->read_size - fetch_ctx->aborted_read_size);
1193251881Speter          data += skip;
1194299742Sdim          len -= (apr_size_t)skip;
1195251881Speter        }
1196251881Speter
1197299742Sdim      if (fetch_ctx->result_stream)
1198299742Sdim        SVN_ERR(svn_stream_write(fetch_ctx->result_stream, data, &len));
1199299742Sdim
1200251881Speter      /* otherwise, manually construct the text delta window. */
1201251881Speter      else if (len)
1202251881Speter        {
1203251881Speter          window_data.data = data;
1204251881Speter          window_data.len = len;
1205251881Speter
1206251881Speter          delta_op.action_code = svn_txdelta_new;
1207251881Speter          delta_op.offset = 0;
1208251881Speter          delta_op.length = len;
1209251881Speter
1210251881Speter          delta_window.tview_len = len;
1211251881Speter          delta_window.num_ops = 1;
1212251881Speter          delta_window.ops = &delta_op;
1213251881Speter          delta_window.new_data = &window_data;
1214251881Speter
1215251881Speter          /* write to the file located in the info. */
1216299742Sdim          SVN_ERR(file->txdelta(&delta_window, file->txdelta_baton));
1217251881Speter        }
1218251881Speter
1219251881Speter      if (APR_STATUS_IS_EOF(status))
1220251881Speter        {
1221299742Sdim          if (fetch_ctx->result_stream)
1222299742Sdim            SVN_ERR(svn_stream_close(fetch_ctx->result_stream));
1223251881Speter          else
1224299742Sdim            SVN_ERR(file->txdelta(NULL, file->txdelta_baton));
1225299742Sdim        }
1226251881Speter
1227299742Sdim      /* Report EOF, EEAGAIN and other special errors to serf */
1228299742Sdim      if (status)
1229299742Sdim        return svn_ra_serf__wrap_err(status, NULL);
1230299742Sdim    }
1231299742Sdim}
1232251881Speter
1233299742Sdim/* --------------------------------------------------------- */
1234251881Speter
1235299742Sdim/** Wrappers around our various property walkers **/
1236251881Speter
1237299742Sdim/* Implements svn_ra_serf__prop_func */
1238251881Speterstatic svn_error_t *
1239299742Sdimset_file_props(void *baton,
1240299742Sdim               const char *path,
1241299742Sdim               const char *ns,
1242299742Sdim               const char *name,
1243299742Sdim               const svn_string_t *val,
1244299742Sdim               apr_pool_t *scratch_pool)
1245251881Speter{
1246299742Sdim  file_baton_t *file = baton;
1247299742Sdim  report_context_t *ctx = file->parent_dir->ctx;
1248299742Sdim  const char *prop_name;
1249251881Speter
1250299742Sdim  prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
1251251881Speter
1252299742Sdim  if (!prop_name)
1253251881Speter    {
1254299742Sdim      /* This works around a bug in some older versions of
1255299742Sdim       * mod_dav_svn in that it will not send remove-prop in the update
1256299742Sdim       * report when a lock property disappears when send-all is false.
1257299742Sdim       *
1258299742Sdim       * Therefore, we'll try to look at our properties and see if there's
1259299742Sdim       * an active lock.  If not, then we'll assume there isn't a lock
1260299742Sdim       * anymore.
1261299742Sdim       */
1262299742Sdim      /* assert(!ctx->add_props_included); // Or we wouldn't be here */
1263299742Sdim      if (file->lock_token
1264299742Sdim          && !file->found_lock_prop
1265299742Sdim          && val
1266299742Sdim          && strcmp(ns, "DAV:") == 0
1267299742Sdim          && strcmp(name, "lockdiscovery") == 0)
1268251881Speter        {
1269299742Sdim          char *new_lock;
1270299742Sdim          new_lock = apr_pstrdup(scratch_pool, val->data);
1271299742Sdim          apr_collapse_spaces(new_lock, new_lock);
1272251881Speter
1273299742Sdim          if (new_lock[0] != '\0')
1274299742Sdim            file->found_lock_prop = TRUE;
1275251881Speter        }
1276251881Speter
1277299742Sdim      return SVN_NO_ERROR;
1278299742Sdim    }
1279251881Speter
1280299742Sdim  SVN_ERR(ensure_file_opened(file, scratch_pool));
1281251881Speter
1282299742Sdim  SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
1283299742Sdim                                        prop_name, val,
1284299742Sdim                                        scratch_pool));
1285251881Speter
1286299742Sdim  return SVN_NO_ERROR;
1287251881Speter}
1288251881Speter
1289299742Sdim/* Implements svn_ra_serf__response_done_delegate_t */
1290251881Speterstatic svn_error_t *
1291299742Sdimfile_props_done(serf_request_t *request,
1292299742Sdim                void *baton,
1293299742Sdim                apr_pool_t *scratch_pool)
1294251881Speter{
1295299742Sdim  file_baton_t *file = baton;
1296299742Sdim  svn_ra_serf__handler_t *handler = file->propfind_handler;
1297251881Speter
1298299742Sdim  if (handler->server_error)
1299299742Sdim      return svn_error_trace(svn_ra_serf__server_error_create(handler,
1300299742Sdim                                                              scratch_pool));
1301251881Speter
1302299742Sdim  if (handler->sline.code != 207)
1303299742Sdim    return svn_error_trace(svn_ra_serf__unexpected_status(handler));
1304251881Speter
1305299742Sdim  file->parent_dir->ctx->num_active_propfinds--;
1306251881Speter
1307299742Sdim  file->fetch_props = FALSE;
1308251881Speter
1309299742Sdim  if (file->fetch_file)
1310299742Sdim    return SVN_NO_ERROR; /* Still processing file request */
1311299742Sdim
1312299742Sdim  /* Closing the file will automatically deliver the propfind props.
1313299742Sdim   *
1314299742Sdim   * Note that closing the directory may dispose the pool containing the
1315299742Sdim   * handler, which is only a valid operation in this callback, as only
1316299742Sdim   * after this callback our serf plumbing assumes the request is done. */
1317299742Sdim
1318299742Sdim  return svn_error_trace(close_file(file, scratch_pool));
1319251881Speter}
1320251881Speter
1321251881Speterstatic svn_error_t *
1322299742Sdimfile_fetch_done(serf_request_t *request,
1323299742Sdim                void *baton,
1324299742Sdim                apr_pool_t *scratch_pool)
1325251881Speter{
1326299742Sdim  fetch_ctx_t *fetch_ctx = baton;
1327299742Sdim  file_baton_t *file = fetch_ctx->file;
1328299742Sdim  svn_ra_serf__handler_t *handler = fetch_ctx->handler;
1329251881Speter
1330299742Sdim  if (handler->server_error)
1331299742Sdim      return svn_error_trace(svn_ra_serf__server_error_create(handler,
1332299742Sdim                                                              scratch_pool));
1333251881Speter
1334299742Sdim  if (handler->sline.code != 200)
1335299742Sdim    return svn_error_trace(svn_ra_serf__unexpected_status(handler));
1336251881Speter
1337299742Sdim  file->parent_dir->ctx->num_active_fetches--;
1338251881Speter
1339299742Sdim  file->fetch_file = FALSE;
1340251881Speter
1341299742Sdim  if (file->fetch_props)
1342299742Sdim    return SVN_NO_ERROR; /* Still processing PROPFIND request */
1343251881Speter
1344299742Sdim  /* Closing the file will automatically deliver the propfind props.
1345299742Sdim   *
1346299742Sdim   * Note that closing the directory may dispose the pool containing the
1347299742Sdim   * handler, fetch_ctx, etc. which is only a valid operation in this
1348299742Sdim   * callback, as only after this callback our serf plumbing assumes the
1349299742Sdim   * request is done. */
1350299742Sdim  return svn_error_trace(close_file(file, scratch_pool));
1351251881Speter}
1352251881Speter
1353299742Sdim/* Initiates additional requests needed for a file when not in "send-all" mode.
1354299742Sdim */
1355251881Speterstatic svn_error_t *
1356299742Sdimfetch_for_file(file_baton_t *file,
1357299742Sdim               apr_pool_t *scratch_pool)
1358251881Speter{
1359299742Sdim  report_context_t *ctx = file->parent_dir->ctx;
1360251881Speter  svn_ra_serf__connection_t *conn;
1361251881Speter  svn_ra_serf__handler_t *handler;
1362251881Speter
1363299742Sdim  /* Open extra connections if we have enough requests to send. */
1364299742Sdim  if (ctx->sess->num_conns < ctx->sess->max_connections)
1365299742Sdim    SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches +
1366299742Sdim                                                 ctx->num_active_propfinds));
1367299742Sdim
1368251881Speter  /* What connection should we go on? */
1369251881Speter  conn = get_best_connection(ctx);
1370251881Speter
1371299742Sdim  /* Note that we (still) use conn for both requests.. Should we send
1372299742Sdim     them out on different connections? */
1373251881Speter
1374299742Sdim  if (file->fetch_file)
1375251881Speter    {
1376299742Sdim      SVN_ERR(open_file_txdelta(file, scratch_pool));
1377251881Speter
1378299742Sdim      if (!ctx->text_deltas
1379299742Sdim          || file->txdelta == svn_delta_noop_window_handler)
1380251881Speter        {
1381299742Sdim          SVN_ERR(file->txdelta(NULL, file->txdelta_baton));
1382299742Sdim          file->fetch_file = FALSE;
1383299742Sdim        }
1384251881Speter
1385299742Sdim      if (file->fetch_file
1386299742Sdim          && file->final_sha1_checksum
1387299742Sdim          && ctx->sess->wc_callbacks->get_wc_contents)
1388251881Speter        {
1389299742Sdim          svn_error_t *err;
1390299742Sdim          svn_stream_t *cached_contents = NULL;
1391251881Speter
1392299742Sdim          err = ctx->sess->wc_callbacks->get_wc_contents(
1393299742Sdim                                                ctx->sess->wc_callback_baton,
1394299742Sdim                                                &cached_contents,
1395299742Sdim                                                file->final_sha1_checksum,
1396299742Sdim                                                scratch_pool);
1397251881Speter
1398299742Sdim          if (err || !cached_contents)
1399299742Sdim            svn_error_clear(err); /* ### Can we return some/most errors? */
1400299742Sdim          else
1401251881Speter            {
1402299742Sdim              /* ### For debugging purposes we could validate the md5 here,
1403299742Sdim                     but our implementations in libsvn_client already do that
1404299742Sdim                     for us... */
1405299742Sdim              SVN_ERR(svn_txdelta_send_stream(cached_contents,
1406299742Sdim                                              file->txdelta,
1407299742Sdim                                              file->txdelta_baton,
1408299742Sdim                                              NULL, scratch_pool));
1409299742Sdim              SVN_ERR(svn_stream_close(cached_contents));
1410299742Sdim              file->fetch_file = FALSE;
1411251881Speter            }
1412251881Speter        }
1413251881Speter
1414299742Sdim      if (file->fetch_file)
1415251881Speter        {
1416299742Sdim          fetch_ctx_t *fetch_ctx;
1417299742Sdim
1418299742Sdim          /* Let's fetch the file with a GET request... */
1419299742Sdim          SVN_ERR_ASSERT(file->url && file->repos_relpath);
1420299742Sdim
1421299742Sdim          /* Otherwise, we use a GET request for the file's contents. */
1422299742Sdim
1423299742Sdim          fetch_ctx = apr_pcalloc(file->pool, sizeof(*fetch_ctx));
1424299742Sdim          fetch_ctx->file = file;
1425299742Sdim          fetch_ctx->using_compression = ctx->sess->using_compression;
1426299742Sdim
1427299742Sdim          /* Can we somehow get away with just obtaining a DIFF? */
1428299742Sdim          if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(ctx->sess))
1429251881Speter            {
1430299742Sdim              /* If this file is switched vs the editor root we should provide
1431299742Sdim                 its real url instead of the one calculated from the session root.
1432299742Sdim              */
1433299742Sdim              if (SVN_IS_VALID_REVNUM(file->base_rev))
1434299742Sdim                {
1435299742Sdim                  fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s",
1436299742Sdim                                                       ctx->sess->rev_root_stub,
1437299742Sdim                                                       file->base_rev,
1438299742Sdim                                                       svn_path_uri_encode(
1439299742Sdim                                                          file->repos_relpath,
1440299742Sdim                                                          scratch_pool));
1441299742Sdim                }
1442299742Sdim              else if (file->copyfrom_path)
1443299742Sdim                {
1444299742Sdim                  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(file->copyfrom_rev));
1445251881Speter
1446299742Sdim                  fetch_ctx->delta_base = apr_psprintf(file->pool, "%s/%ld/%s",
1447299742Sdim                                                       ctx->sess->rev_root_stub,
1448299742Sdim                                                       file->copyfrom_rev,
1449299742Sdim                                                       svn_path_uri_encode(
1450299742Sdim                                                          file->copyfrom_path+1,
1451299742Sdim                                                          scratch_pool));
1452299742Sdim                }
1453251881Speter            }
1454299742Sdim          else if (ctx->sess->wc_callbacks->get_wc_prop)
1455251881Speter            {
1456299742Sdim              /* If we have a WC, we might be able to dive all the way into the WC
1457299742Sdim              * to get the previous URL so we can do a differential GET with the
1458299742Sdim              * base URL.
1459299742Sdim              */
1460299742Sdim              const svn_string_t *value = NULL;
1461299742Sdim              SVN_ERR(ctx->sess->wc_callbacks->get_wc_prop(
1462299742Sdim                                                ctx->sess->wc_callback_baton,
1463299742Sdim                                                file->relpath,
1464299742Sdim                                                SVN_RA_SERF__WC_CHECKED_IN_URL,
1465299742Sdim                                                &value, scratch_pool));
1466299742Sdim
1467299742Sdim              fetch_ctx->delta_base = value
1468299742Sdim                                        ? apr_pstrdup(file->pool, value->data)
1469299742Sdim                                        : NULL;
1470251881Speter            }
1471251881Speter
1472299742Sdim          handler = svn_ra_serf__create_handler(ctx->sess, file->pool);
1473251881Speter
1474251881Speter          handler->method = "GET";
1475299742Sdim          handler->path = file->url;
1476251881Speter
1477299742Sdim          handler->conn = conn; /* Explicit scheduling */
1478251881Speter
1479251881Speter          handler->custom_accept_encoding = TRUE;
1480299742Sdim          handler->no_dav_headers = TRUE;
1481251881Speter          handler->header_delegate = headers_fetch;
1482251881Speter          handler->header_delegate_baton = fetch_ctx;
1483251881Speter
1484251881Speter          handler->response_handler = handle_fetch;
1485251881Speter          handler->response_baton = fetch_ctx;
1486251881Speter
1487251881Speter          handler->response_error = cancel_fetch;
1488251881Speter          handler->response_error_baton = fetch_ctx;
1489251881Speter
1490299742Sdim          handler->done_delegate = file_fetch_done;
1491299742Sdim          handler->done_delegate_baton = fetch_ctx;
1492299742Sdim
1493251881Speter          fetch_ctx->handler = handler;
1494251881Speter
1495251881Speter          svn_ra_serf__request_create(handler);
1496251881Speter
1497251881Speter          ctx->num_active_fetches++;
1498251881Speter        }
1499251881Speter    }
1500299742Sdim
1501299742Sdim  /* If needed, create the PROPFIND to retrieve the file's properties. */
1502299742Sdim  if (file->fetch_props)
1503251881Speter    {
1504299742Sdim      SVN_ERR(svn_ra_serf__create_propfind_handler(&file->propfind_handler,
1505299742Sdim                                                   ctx->sess, file->url,
1506299742Sdim                                                   ctx->target_rev, "0",
1507299742Sdim                                                   all_props,
1508299742Sdim                                                   set_file_props, file,
1509299742Sdim                                                   file->pool));
1510299742Sdim      file->propfind_handler->conn = conn; /* Explicit scheduling */
1511251881Speter
1512299742Sdim      file->propfind_handler->done_delegate = file_props_done;
1513299742Sdim      file->propfind_handler->done_delegate_baton = file;
1514299742Sdim
1515299742Sdim      /* Create a serf request for the PROPFIND.  */
1516299742Sdim      svn_ra_serf__request_create(file->propfind_handler);
1517299742Sdim
1518299742Sdim      ctx->num_active_propfinds++;
1519251881Speter    }
1520251881Speter
1521299742Sdim  if (file->fetch_props || file->fetch_file)
1522299742Sdim      return SVN_NO_ERROR;
1523251881Speter
1524299742Sdim
1525299742Sdim  /* Somehow we are done; probably via the local cache.
1526299742Sdim     Close the file and release memory, etc. */
1527299742Sdim
1528299742Sdim  return svn_error_trace(close_file(file, scratch_pool));
1529251881Speter}
1530251881Speter
1531299742Sdim/* Implements svn_ra_serf__prop_func */
1532251881Speterstatic svn_error_t *
1533299742Sdimset_dir_prop(void *baton,
1534299742Sdim             const char *path,
1535299742Sdim             const char *ns,
1536299742Sdim             const char *name,
1537299742Sdim             const svn_string_t *val,
1538251881Speter             apr_pool_t *scratch_pool)
1539251881Speter{
1540299742Sdim  dir_baton_t *dir = baton;
1541299742Sdim  report_context_t *ctx = dir->ctx;
1542299742Sdim  const char *prop_name;
1543251881Speter
1544299742Sdim  prop_name = svn_ra_serf__svnname_from_wirename(ns, name, scratch_pool);
1545299742Sdim  if (prop_name == NULL)
1546299742Sdim    return SVN_NO_ERROR;
1547251881Speter
1548299742Sdim  SVN_ERR(ensure_dir_opened(dir, scratch_pool));
1549251881Speter
1550299742Sdim  SVN_ERR(ctx->editor->change_dir_prop(dir->dir_baton,
1551299742Sdim                                       prop_name, val,
1552299742Sdim                                       scratch_pool));
1553299742Sdim  return SVN_NO_ERROR;
1554299742Sdim}
1555251881Speter
1556299742Sdim/* Implements svn_ra_serf__response_done_delegate_t */
1557299742Sdimstatic svn_error_t *
1558299742Sdimdir_props_done(serf_request_t *request,
1559299742Sdim               void *baton,
1560299742Sdim               apr_pool_t *scratch_pool)
1561299742Sdim{
1562299742Sdim  dir_baton_t *dir = baton;
1563299742Sdim  svn_ra_serf__handler_t *handler = dir->propfind_handler;
1564251881Speter
1565299742Sdim  if (handler->server_error)
1566299742Sdim    return svn_ra_serf__server_error_create(handler, scratch_pool);
1567251881Speter
1568299742Sdim  if (handler->sline.code != 207)
1569299742Sdim    return svn_error_trace(svn_ra_serf__unexpected_status(handler));
1570251881Speter
1571299742Sdim  dir->ctx->num_active_propfinds--;
1572251881Speter
1573299742Sdim  /* Closing the directory will automatically deliver the propfind props.
1574299742Sdim   *
1575299742Sdim   * Note that closing the directory may dispose the pool containing the
1576299742Sdim   * handler, which is only a valid operation in this callback, as after
1577299742Sdim   * this callback serf assumes the request is done. */
1578251881Speter
1579299742Sdim  return svn_error_trace(maybe_close_dir(dir));
1580299742Sdim}
1581251881Speter
1582299742Sdim/* Initiates additional requests needed for a directory when not in "send-all"
1583299742Sdim * mode */
1584299742Sdimstatic svn_error_t *
1585299742Sdimfetch_for_dir(dir_baton_t *dir,
1586299742Sdim              apr_pool_t *scratch)
1587299742Sdim{
1588299742Sdim  report_context_t *ctx = dir->ctx;
1589299742Sdim  svn_ra_serf__connection_t *conn;
1590251881Speter
1591299742Sdim  /* Open extra connections if we have enough requests to send. */
1592299742Sdim  if (ctx->sess->num_conns < ctx->sess->max_connections)
1593299742Sdim    SVN_ERR(open_connection_if_needed(ctx->sess, ctx->num_active_fetches +
1594299742Sdim                                                 ctx->num_active_propfinds));
1595251881Speter
1596299742Sdim  /* What connection should we go on? */
1597299742Sdim  conn = get_best_connection(ctx);
1598251881Speter
1599299742Sdim  /* If needed, create the PROPFIND to retrieve the file's properties. */
1600299742Sdim  if (dir->fetch_props)
1601299742Sdim    {
1602299742Sdim      SVN_ERR(svn_ra_serf__create_propfind_handler(&dir->propfind_handler,
1603299742Sdim                                                   ctx->sess, dir->url,
1604299742Sdim                                                   ctx->target_rev, "0",
1605299742Sdim                                                   all_props,
1606299742Sdim                                                   set_dir_prop, dir,
1607299742Sdim                                                   dir->pool));
1608251881Speter
1609299742Sdim      dir->propfind_handler->conn = conn;
1610299742Sdim      dir->propfind_handler->done_delegate = dir_props_done;
1611299742Sdim      dir->propfind_handler->done_delegate_baton = dir;
1612251881Speter
1613299742Sdim      /* Create a serf request for the PROPFIND.  */
1614299742Sdim      svn_ra_serf__request_create(dir->propfind_handler);
1615251881Speter
1616299742Sdim      ctx->num_active_propfinds++;
1617251881Speter    }
1618299742Sdim  else
1619299742Sdim    SVN_ERR_MALFUNCTION();
1620251881Speter
1621299742Sdim  return SVN_NO_ERROR;
1622299742Sdim}
1623251881Speter
1624299742Sdim
1625299742Sdim/** XML callbacks for our update-report response parsing */
1626251881Speter
1627299742Sdim/* Conforms to svn_ra_serf__xml_opened_t  */
1628299742Sdimstatic svn_error_t *
1629299742Sdimupdate_opened(svn_ra_serf__xml_estate_t *xes,
1630299742Sdim              void *baton,
1631299742Sdim              int entered_state,
1632299742Sdim              const svn_ra_serf__dav_props_t *tag,
1633299742Sdim              apr_pool_t *scratch_pool)
1634299742Sdim{
1635299742Sdim  report_context_t *ctx = baton;
1636299742Sdim  apr_hash_t *attrs;
1637251881Speter
1638299742Sdim  switch (entered_state)
1639299742Sdim    {
1640299742Sdim      case UPDATE_REPORT:
1641251881Speter        {
1642299742Sdim          const char *val;
1643251881Speter
1644299742Sdim          attrs = svn_ra_serf__xml_gather_since(xes, UPDATE_REPORT);
1645299742Sdim          val = svn_hash_gets(attrs, "inline-props");
1646251881Speter
1647299742Sdim          if (val && (strcmp(val, "true") == 0))
1648299742Sdim            ctx->add_props_included = TRUE;
1649251881Speter
1650299742Sdim          val = svn_hash_gets(attrs, "send-all");
1651251881Speter
1652299742Sdim          if (val && (strcmp(val, "true") == 0))
1653299742Sdim            {
1654299742Sdim              ctx->send_all_mode = TRUE;
1655251881Speter
1656299742Sdim              /* All properties are included in send-all mode. */
1657299742Sdim              ctx->add_props_included = TRUE;
1658299742Sdim            }
1659299742Sdim        }
1660299742Sdim        break;
1661251881Speter
1662299742Sdim      case OPEN_DIR:
1663299742Sdim      case ADD_DIR:
1664251881Speter        {
1665299742Sdim          dir_baton_t *dir;
1666299742Sdim          const char *name;
1667299742Sdim          attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
1668251881Speter
1669299742Sdim          name = svn_hash_gets(attrs, "name");
1670299742Sdim          if (!name)
1671299742Sdim            name = "";
1672251881Speter
1673299742Sdim          SVN_ERR(create_dir_baton(&dir, ctx, name, scratch_pool));
1674251881Speter
1675299742Sdim          if (entered_state == OPEN_DIR)
1676299742Sdim            {
1677299742Sdim              apr_int64_t base_rev;
1678251881Speter
1679299742Sdim              SVN_ERR(svn_cstring_atoi64(&base_rev,
1680299742Sdim                                         svn_hash_gets(attrs, "rev")));
1681299742Sdim              dir->base_rev = (svn_revnum_t)base_rev;
1682299742Sdim            }
1683299742Sdim          else
1684299742Sdim            {
1685299742Sdim              dir->copyfrom_path = svn_hash_gets(attrs, "copyfrom-path");
1686251881Speter
1687299742Sdim              if (dir->copyfrom_path)
1688299742Sdim                {
1689299742Sdim                  apr_int64_t copyfrom_rev;
1690299742Sdim                  const char *copyfrom_rev_str;
1691299742Sdim                  dir->copyfrom_path = svn_fspath__canonicalize(
1692299742Sdim                                                        dir->copyfrom_path,
1693299742Sdim                                                        dir->pool);
1694251881Speter
1695299742Sdim                  copyfrom_rev_str = svn_hash_gets(attrs, "copyfrom-rev");
1696251881Speter
1697299742Sdim                  if (!copyfrom_rev_str)
1698299742Sdim                    return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
1699299742Sdim                                             NULL,
1700299742Sdim                                            _("Missing '%s' attribute"),
1701299742Sdim                                            "copyfrom-rev");
1702251881Speter
1703299742Sdim                  SVN_ERR(svn_cstring_atoi64(&copyfrom_rev, copyfrom_rev_str));
1704251881Speter
1705299742Sdim                  dir->copyfrom_rev = (svn_revnum_t)copyfrom_rev;
1706299742Sdim                }
1707251881Speter
1708299742Sdim              if (! ctx->add_props_included)
1709299742Sdim                dir->fetch_props = TRUE;
1710299742Sdim            }
1711251881Speter        }
1712299742Sdim        break;
1713299742Sdim      case OPEN_FILE:
1714299742Sdim      case ADD_FILE:
1715251881Speter        {
1716299742Sdim          file_baton_t *file;
1717251881Speter
1718299742Sdim          attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
1719251881Speter
1720299742Sdim          SVN_ERR(create_file_baton(&file, ctx, svn_hash_gets(attrs, "name"),
1721299742Sdim                                    scratch_pool));
1722251881Speter
1723299742Sdim          if (entered_state == OPEN_FILE)
1724299742Sdim            {
1725299742Sdim              apr_int64_t base_rev;
1726251881Speter
1727299742Sdim              SVN_ERR(svn_cstring_atoi64(&base_rev,
1728299742Sdim                                         svn_hash_gets(attrs, "rev")));
1729299742Sdim              file->base_rev = (svn_revnum_t)base_rev;
1730299742Sdim            }
1731299742Sdim          else
1732299742Sdim            {
1733299742Sdim              const char *sha1_checksum;
1734299742Sdim              file->copyfrom_path = svn_hash_gets(attrs, "copyfrom-path");
1735251881Speter
1736299742Sdim              if (file->copyfrom_path)
1737299742Sdim                {
1738299742Sdim                  apr_int64_t copyfrom_rev;
1739299742Sdim                  const char *copyfrom_rev_str;
1740251881Speter
1741299742Sdim                  file->copyfrom_path = svn_fspath__canonicalize(
1742299742Sdim                                                        file->copyfrom_path,
1743299742Sdim                                                        file->pool);
1744251881Speter
1745299742Sdim                  copyfrom_rev_str = svn_hash_gets(attrs, "copyfrom-rev");
1746251881Speter
1747299742Sdim                  if (!copyfrom_rev_str)
1748299742Sdim                    return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
1749299742Sdim                                             NULL,
1750299742Sdim                                            _("Missing '%s' attribute"),
1751299742Sdim                                            "copyfrom-rev");
1752251881Speter
1753299742Sdim                  SVN_ERR(svn_cstring_atoi64(&copyfrom_rev, copyfrom_rev_str));
1754251881Speter
1755299742Sdim                  file->copyfrom_rev = (svn_revnum_t)copyfrom_rev;
1756299742Sdim                }
1757251881Speter
1758299742Sdim              sha1_checksum = svn_hash_gets(attrs, "sha1-checksum");
1759299742Sdim              if (sha1_checksum)
1760299742Sdim                {
1761299742Sdim                  SVN_ERR(svn_checksum_parse_hex(&file->final_sha1_checksum,
1762299742Sdim                                                 svn_checksum_sha1,
1763299742Sdim                                                 sha1_checksum,
1764299742Sdim                                                 file->pool));
1765299742Sdim                }
1766251881Speter
1767299742Sdim              /* If the server isn't in "send-all" mode, we should expect to
1768299742Sdim                 fetch contents for added files. */
1769299742Sdim              if (! ctx->send_all_mode)
1770299742Sdim                file->fetch_file = TRUE;
1771251881Speter
1772299742Sdim              /* If the server isn't included properties for added items,
1773299742Sdim                 we'll need to fetch them ourselves. */
1774299742Sdim              if (! ctx->add_props_included)
1775299742Sdim                file->fetch_props = TRUE;
1776299742Sdim            }
1777299742Sdim        }
1778299742Sdim        break;
1779251881Speter
1780299742Sdim      case TXDELTA:
1781251881Speter        {
1782299742Sdim          file_baton_t *file = ctx->cur_file;
1783299742Sdim          const char *base_checksum;
1784251881Speter
1785299742Sdim          /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in
1786299742Sdim             addition to <fetch-file>s and such) when *not* in
1787299742Sdim             "send-all" mode.  As a client, we're smart enough to know
1788299742Sdim             that's wrong, so we'll just ignore these tags. */
1789299742Sdim          if (! ctx->send_all_mode)
1790299742Sdim            break;
1791251881Speter
1792299742Sdim          file->fetch_file = FALSE;
1793251881Speter
1794299742Sdim          attrs = svn_ra_serf__xml_gather_since(xes, entered_state);
1795299742Sdim          base_checksum = svn_hash_gets(attrs, "base-checksum");
1796251881Speter
1797299742Sdim          if (base_checksum)
1798299742Sdim            SVN_ERR(svn_checksum_parse_hex(&file->base_md5_checksum,
1799299742Sdim                                           svn_checksum_md5, base_checksum,
1800299742Sdim                                           file->pool));
1801251881Speter
1802299742Sdim          SVN_ERR(open_file_txdelta(ctx->cur_file, scratch_pool));
1803251881Speter
1804299742Sdim          if (ctx->cur_file->txdelta != svn_delta_noop_window_handler)
1805299742Sdim            {
1806299742Sdim              svn_stream_t *decoder;
1807251881Speter
1808299742Sdim              decoder = svn_txdelta_parse_svndiff(file->txdelta,
1809299742Sdim                                                  file->txdelta_baton,
1810299742Sdim                                                  TRUE /* error early close*/,
1811299742Sdim                                                  file->pool);
1812251881Speter
1813299742Sdim              file->txdelta_stream = svn_base64_decode(decoder, file->pool);
1814299742Sdim            }
1815299742Sdim        }
1816299742Sdim        break;
1817251881Speter
1818299742Sdim      case FETCH_PROPS:
1819251881Speter        {
1820299742Sdim          /* Subversion <= 1.6 servers will return a fetch-props element on
1821299742Sdim             open-file and open-dir when non entry props were changed in
1822299742Sdim             !send-all mode. In turn we fetch the full set of properties
1823299742Sdim             and send all of those as *changes* to the editor. So these
1824299742Sdim             editors have to be aware that they receive-non property changes.
1825299742Sdim             (In case of incomplete directories they have to be aware anyway)
1826251881Speter
1827299742Sdim             In r1063337 this behavior was changed in mod_dav_svn to always
1828299742Sdim             send property changes inline in these cases. (See issue #3657)
1829251881Speter
1830299742Sdim             Note that before that change the property changes to the last_*
1831299742Sdim             entry props were already inlined via specific xml elements. */
1832299742Sdim          if (ctx->cur_file)
1833299742Sdim            ctx->cur_file->fetch_props = TRUE;
1834299742Sdim          else if (ctx->cur_dir)
1835299742Sdim            ctx->cur_dir->fetch_props = TRUE;
1836299742Sdim        }
1837299742Sdim        break;
1838251881Speter    }
1839251881Speter
1840299742Sdim  return SVN_NO_ERROR;
1841299742Sdim}
1842251881Speter
1843251881Speter
1844251881Speter
1845299742Sdim/* Conforms to svn_ra_serf__xml_closed_t  */
1846299742Sdimstatic svn_error_t *
1847299742Sdimupdate_closed(svn_ra_serf__xml_estate_t *xes,
1848299742Sdim              void *baton,
1849299742Sdim              int leaving_state,
1850299742Sdim              const svn_string_t *cdata,
1851299742Sdim              apr_hash_t *attrs,
1852299742Sdim              apr_pool_t *scratch_pool)
1853299742Sdim{
1854299742Sdim  report_context_t *ctx = baton;
1855251881Speter
1856299742Sdim  switch (leaving_state)
1857251881Speter    {
1858299742Sdim      case UPDATE_REPORT:
1859299742Sdim        ctx->done = TRUE;
1860299742Sdim        break;
1861299742Sdim      case TARGET_REVISION:
1862251881Speter        {
1863299742Sdim          const char *revstr = svn_hash_gets(attrs, "rev");
1864299742Sdim          apr_int64_t rev;
1865251881Speter
1866299742Sdim          SVN_ERR(svn_cstring_atoi64(&rev, revstr));
1867251881Speter
1868299742Sdim          SVN_ERR(ctx->editor->set_target_revision(ctx->editor_baton,
1869299742Sdim                                                   (svn_revnum_t)rev,
1870299742Sdim                                                   scratch_pool));
1871251881Speter        }
1872299742Sdim        break;
1873251881Speter
1874299742Sdim      case CHECKED_IN_HREF:
1875299742Sdim        if (ctx->cur_file)
1876299742Sdim          ctx->cur_file->url = apr_pstrdup(ctx->cur_file->pool, cdata->data);
1877299742Sdim        else
1878299742Sdim          ctx->cur_dir->url = apr_pstrdup(ctx->cur_dir->pool, cdata->data);
1879299742Sdim        break;
1880251881Speter
1881299742Sdim      case SET_PROP:
1882299742Sdim      case REMOVE_PROP:
1883251881Speter        {
1884299742Sdim          const char *name = svn_hash_gets(attrs, "name");
1885299742Sdim          const char *encoding;
1886299742Sdim          const svn_string_t *value;
1887251881Speter
1888299742Sdim          if (leaving_state == REMOVE_PROP)
1889299742Sdim            value = NULL;
1890299742Sdim          else if ((encoding = svn_hash_gets(attrs, "encoding")))
1891299742Sdim            {
1892299742Sdim              if (strcmp(encoding, "base64") != 0)
1893299742Sdim                return svn_error_createf(SVN_ERR_XML_UNKNOWN_ENCODING, NULL,
1894299742Sdim                                         _("Got unrecognized encoding '%s'"),
1895299742Sdim                                         encoding);
1896251881Speter
1897299742Sdim              value = svn_base64_decode_string(cdata, scratch_pool);
1898299742Sdim            }
1899299742Sdim          else
1900299742Sdim            value = cdata;
1901251881Speter
1902299742Sdim          if (ctx->cur_file)
1903299742Sdim            {
1904299742Sdim              file_baton_t *file = ctx->cur_file;
1905251881Speter
1906299742Sdim              if (value
1907299742Sdim                  || ctx->add_props_included
1908299742Sdim                  || SVN_IS_VALID_REVNUM(file->base_rev))
1909299742Sdim                {
1910299742Sdim                  SVN_ERR(ensure_file_opened(file, scratch_pool));
1911251881Speter
1912299742Sdim                  SVN_ERR(ctx->editor->change_file_prop(file->file_baton,
1913299742Sdim                                                        name,
1914299742Sdim                                                        value,
1915299742Sdim                                                        scratch_pool));
1916299742Sdim                }
1917299742Sdim              else
1918299742Sdim                {
1919299742Sdim                  if (!file->remove_props)
1920299742Sdim                    file->remove_props = apr_hash_make(file->pool);
1921251881Speter
1922299742Sdim                  svn_hash_sets(file->remove_props,
1923299742Sdim                                apr_pstrdup(file->pool, name),
1924299742Sdim                                "");
1925299742Sdim                }
1926251881Speter            }
1927251881Speter          else
1928251881Speter            {
1929299742Sdim              dir_baton_t *dir = ctx->cur_dir;
1930251881Speter
1931299742Sdim              if (value
1932299742Sdim                  || ctx->add_props_included
1933299742Sdim                  || SVN_IS_VALID_REVNUM(dir->base_rev))
1934299742Sdim                {
1935299742Sdim                  SVN_ERR(ensure_dir_opened(dir, scratch_pool));
1936251881Speter
1937299742Sdim                  SVN_ERR(ctx->editor->change_dir_prop(dir->dir_baton,
1938299742Sdim                                                       name,
1939299742Sdim                                                       value,
1940299742Sdim                                                       scratch_pool));
1941299742Sdim                }
1942299742Sdim              else
1943251881Speter                {
1944299742Sdim                  if (!dir->remove_props)
1945299742Sdim                    dir->remove_props = apr_hash_make(dir->pool);
1946299742Sdim
1947299742Sdim                  svn_hash_sets(dir->remove_props,
1948299742Sdim                                apr_pstrdup(dir->pool, name),
1949299742Sdim                                "");
1950251881Speter                }
1951251881Speter            }
1952251881Speter        }
1953299742Sdim        break;
1954299742Sdim
1955299742Sdim      case OPEN_DIR:
1956299742Sdim      case ADD_DIR:
1957251881Speter        {
1958299742Sdim          dir_baton_t *dir = ctx->cur_dir;
1959299742Sdim          ctx->cur_dir = ctx->cur_dir->parent_dir;
1960251881Speter
1961299742Sdim          if (dir->fetch_props && ! dir->url)
1962299742Sdim            {
1963299742Sdim              return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1964299742Sdim                                      _("The REPORT response did not "
1965299742Sdim                                        "include the requested checked-in "
1966299742Sdim                                        "value"));
1967299742Sdim            }
1968251881Speter
1969299742Sdim          if (!dir->fetch_props)
1970299742Sdim            {
1971299742Sdim              SVN_ERR(maybe_close_dir(dir));
1972299742Sdim              break; /* dir potentially no longer valid */
1973299742Sdim            }
1974299742Sdim          else
1975299742Sdim            {
1976299742Sdim              /* Otherwise, if the server is *not* in "send-all" mode, we
1977299742Sdim                 are at a point where we can queue up the PROPFIND request */
1978299742Sdim              SVN_ERR(fetch_for_dir(dir, scratch_pool));
1979299742Sdim            }
1980299742Sdim        }
1981299742Sdim        break;
1982251881Speter
1983299742Sdim      case OPEN_FILE:
1984299742Sdim      case ADD_FILE:
1985251881Speter        {
1986299742Sdim          file_baton_t *file = ctx->cur_file;
1987251881Speter
1988299742Sdim          ctx->cur_file = NULL;
1989299742Sdim          /* go fetch info->name from DAV:checked-in */
1990251881Speter
1991299742Sdim          if ((file->fetch_file || file->fetch_props) && ! file->url)
1992299742Sdim            {
1993299742Sdim              return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1994299742Sdim                                      _("The REPORT response did not "
1995299742Sdim                                        "include the requested checked-in "
1996299742Sdim                                        "value"));
1997299742Sdim            }
1998251881Speter
1999299742Sdim          /* If the server is in "send-all" mode or didn't get further work,
2000299742Sdim             we can now close the file */
2001299742Sdim          if (! file->fetch_file && ! file->fetch_props)
2002299742Sdim            {
2003299742Sdim              SVN_ERR(close_file(file, scratch_pool));
2004299742Sdim              break; /* file is no longer valid */
2005299742Sdim            }
2006299742Sdim          else
2007299742Sdim            {
2008299742Sdim              /* Otherwise, if the server is *not* in "send-all" mode, we
2009299742Sdim                 should be at a point where we can queue up any auxiliary
2010299742Sdim                 content-fetching requests. */
2011299742Sdim              SVN_ERR(fetch_for_file(file, scratch_pool));
2012299742Sdim            }
2013251881Speter        }
2014299742Sdim        break;
2015251881Speter
2016299742Sdim      case MD5_CHECKSUM:
2017299742Sdim        SVN_ERR(svn_checksum_parse_hex(&ctx->cur_file->final_md5_checksum,
2018299742Sdim                                       svn_checksum_md5,
2019299742Sdim                                       cdata->data,
2020299742Sdim                                       ctx->cur_file->pool));
2021299742Sdim        break;
2022251881Speter
2023299742Sdim      case FETCH_FILE:
2024251881Speter        {
2025299742Sdim          file_baton_t *file = ctx->cur_file;
2026299742Sdim          const char *base_checksum = svn_hash_gets(attrs, "base-checksum");
2027299742Sdim          const char *sha1_checksum = svn_hash_gets(attrs, "sha1-checksum");
2028251881Speter
2029299742Sdim          if (base_checksum)
2030299742Sdim            SVN_ERR(svn_checksum_parse_hex(&file->base_md5_checksum,
2031299742Sdim                                           svn_checksum_md5, base_checksum,
2032299742Sdim                                           file->pool));
2033251881Speter
2034299742Sdim          /* Property is duplicated between add-file and fetch-file */
2035299742Sdim          if (sha1_checksum && !file->final_sha1_checksum)
2036299742Sdim            SVN_ERR(svn_checksum_parse_hex(&file->final_sha1_checksum,
2037299742Sdim                                           svn_checksum_sha1,
2038299742Sdim                                           sha1_checksum,
2039299742Sdim                                           file->pool));
2040251881Speter
2041299742Sdim          /* Some 0.3x mod_dav_svn wrote both txdelta and fetch-file
2042299742Sdim             elements in send-all mode. (See neon for history) */
2043299742Sdim          if (! ctx->send_all_mode)
2044299742Sdim            file->fetch_file = TRUE;
2045251881Speter        }
2046299742Sdim        break;
2047251881Speter
2048299742Sdim      case DELETE_ENTRY:
2049251881Speter        {
2050299742Sdim          const char *name = svn_hash_gets(attrs, "name");
2051299742Sdim          const char *revstr;
2052299742Sdim          apr_int64_t delete_rev;
2053251881Speter
2054299742Sdim          SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool));
2055251881Speter
2056299742Sdim          revstr = svn_hash_gets(attrs, "rev");
2057251881Speter
2058299742Sdim          if (revstr)
2059299742Sdim            SVN_ERR(svn_cstring_atoi64(&delete_rev, revstr));
2060299742Sdim          else
2061299742Sdim            delete_rev = SVN_INVALID_REVNUM;
2062251881Speter
2063299742Sdim          SVN_ERR(ctx->editor->delete_entry(
2064299742Sdim                                    svn_relpath_join(ctx->cur_dir->relpath,
2065299742Sdim                                                     name,
2066299742Sdim                                                     scratch_pool),
2067299742Sdim                                    (svn_revnum_t)delete_rev,
2068299742Sdim                                    ctx->cur_dir->dir_baton,
2069299742Sdim                                    scratch_pool));
2070299742Sdim        }
2071299742Sdim        break;
2072251881Speter
2073299742Sdim      case ABSENT_DIR:
2074251881Speter        {
2075299742Sdim          const char *name = svn_hash_gets(attrs, "name");
2076251881Speter
2077299742Sdim          SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool));
2078251881Speter
2079299742Sdim          SVN_ERR(ctx->editor->absent_directory(
2080299742Sdim                                    svn_relpath_join(ctx->cur_dir->relpath,
2081299742Sdim                                                     name, scratch_pool),
2082299742Sdim                                    ctx->cur_dir->dir_baton,
2083299742Sdim                                    scratch_pool));
2084251881Speter        }
2085299742Sdim        break;
2086299742Sdim     case ABSENT_FILE:
2087251881Speter        {
2088299742Sdim          const char *name = svn_hash_gets(attrs, "name");
2089251881Speter
2090299742Sdim          SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool));
2091251881Speter
2092299742Sdim          SVN_ERR(ctx->editor->absent_file(
2093299742Sdim                                    svn_relpath_join(ctx->cur_dir->relpath,
2094299742Sdim                                                     name, scratch_pool),
2095299742Sdim                                    ctx->cur_dir->dir_baton,
2096299742Sdim                                    scratch_pool));
2097251881Speter        }
2098299742Sdim        break;
2099251881Speter
2100299742Sdim      case TXDELTA:
2101251881Speter        {
2102299742Sdim          file_baton_t *file = ctx->cur_file;
2103299742Sdim
2104299742Sdim          if (file->txdelta_stream)
2105251881Speter            {
2106299742Sdim              SVN_ERR(svn_stream_close(file->txdelta_stream));
2107299742Sdim              file->txdelta_stream = NULL;
2108251881Speter            }
2109251881Speter        }
2110299742Sdim        break;
2111251881Speter
2112299742Sdim      case VERSION_NAME:
2113299742Sdim      case CREATIONDATE:
2114299742Sdim      case CREATOR_DISPLAYNAME:
2115251881Speter        {
2116299742Sdim          /* Subversion <= 1.6 servers would return a fetch-props element on
2117299742Sdim             open-file and open-dir when non entry props were changed in
2118299742Sdim             !send-all mode. In turn we fetch the full set of properties and
2119299742Sdim             send those as *changes* to the editor. So these editors have to
2120299742Sdim             be aware that they receive non property changes.
2121299742Sdim             (In case of incomplete directories they have to be aware anyway)
2122251881Speter
2123299742Sdim             In that case the last_* entry props are posted as 3 specific xml
2124299742Sdim             elements, which we handle here.
2125251881Speter
2126299742Sdim             In r1063337 this behavior was changed in mod_dav_svn to always
2127299742Sdim             send property changes inline in these cases. (See issue #3657)
2128299742Sdim           */
2129251881Speter
2130299742Sdim          const char *propname;
2131251881Speter
2132299742Sdim          if (ctx->cur_file)
2133299742Sdim            SVN_ERR(ensure_file_opened(ctx->cur_file, scratch_pool));
2134299742Sdim          else if (ctx->cur_dir)
2135299742Sdim            SVN_ERR(ensure_dir_opened(ctx->cur_dir, scratch_pool));
2136251881Speter          else
2137299742Sdim            break;
2138299742Sdim
2139299742Sdim          switch (leaving_state)
2140251881Speter            {
2141299742Sdim              case VERSION_NAME:
2142299742Sdim                propname = SVN_PROP_ENTRY_COMMITTED_REV;
2143299742Sdim                break;
2144299742Sdim              case CREATIONDATE:
2145299742Sdim                propname = SVN_PROP_ENTRY_COMMITTED_DATE;
2146299742Sdim                break;
2147299742Sdim              case CREATOR_DISPLAYNAME:
2148299742Sdim                propname = SVN_PROP_ENTRY_LAST_AUTHOR;
2149299742Sdim                break;
2150299742Sdim              default:
2151299742Sdim                SVN_ERR_MALFUNCTION(); /* Impossible to reach */
2152251881Speter            }
2153251881Speter
2154299742Sdim          if (ctx->cur_file)
2155299742Sdim            SVN_ERR(ctx->editor->change_file_prop(ctx->cur_file->file_baton,
2156299742Sdim                                                  propname, cdata,
2157299742Sdim                                                  scratch_pool));
2158251881Speter          else
2159299742Sdim            SVN_ERR(ctx->editor->change_dir_prop(ctx->cur_dir->dir_baton,
2160299742Sdim                                                  propname, cdata,
2161299742Sdim                                                  scratch_pool));
2162251881Speter        }
2163299742Sdim        break;
2164251881Speter    }
2165251881Speter
2166251881Speter  return SVN_NO_ERROR;
2167251881Speter}
2168251881Speter
2169299742Sdim
2170299742Sdim/* Conforms to svn_ra_serf__xml_cdata_t  */
2171251881Speterstatic svn_error_t *
2172299742Sdimupdate_cdata(svn_ra_serf__xml_estate_t *xes,
2173299742Sdim             void *baton,
2174299742Sdim             int current_state,
2175251881Speter             const char *data,
2176251881Speter             apr_size_t len,
2177251881Speter             apr_pool_t *scratch_pool)
2178251881Speter{
2179299742Sdim  report_context_t *ctx = baton;
2180251881Speter
2181299742Sdim  if (current_state == TXDELTA && ctx->cur_file
2182299742Sdim      && ctx->cur_file->txdelta_stream)
2183251881Speter    {
2184299742Sdim      SVN_ERR(svn_stream_write(ctx->cur_file->txdelta_stream, data, &len));
2185251881Speter    }
2186251881Speter
2187251881Speter  return SVN_NO_ERROR;
2188251881Speter}
2189251881Speter
2190251881Speter
2191251881Speter/** Editor callbacks given to callers to create request body */
2192251881Speter
2193251881Speter/* Helper to create simple xml tag without attributes. */
2194251881Speterstatic void
2195251881Spetermake_simple_xml_tag(svn_stringbuf_t **buf_p,
2196251881Speter                    const char *tagname,
2197251881Speter                    const char *cdata,
2198251881Speter                    apr_pool_t *pool)
2199251881Speter{
2200299742Sdim  svn_xml_make_open_tag(buf_p, pool, svn_xml_protect_pcdata, tagname,
2201299742Sdim                        SVN_VA_NULL);
2202251881Speter  svn_xml_escape_cdata_cstring(buf_p, cdata, pool);
2203251881Speter  svn_xml_make_close_tag(buf_p, pool, tagname);
2204251881Speter}
2205251881Speter
2206251881Speterstatic svn_error_t *
2207251881Speterset_path(void *report_baton,
2208251881Speter         const char *path,
2209251881Speter         svn_revnum_t revision,
2210251881Speter         svn_depth_t depth,
2211251881Speter         svn_boolean_t start_empty,
2212251881Speter         const char *lock_token,
2213251881Speter         apr_pool_t *pool)
2214251881Speter{
2215251881Speter  report_context_t *report = report_baton;
2216251881Speter  svn_stringbuf_t *buf = NULL;
2217251881Speter
2218251881Speter  svn_xml_make_open_tag(&buf, pool, svn_xml_protect_pcdata, "S:entry",
2219251881Speter                        "rev", apr_ltoa(pool, revision),
2220251881Speter                        "lock-token", lock_token,
2221251881Speter                        "depth", svn_depth_to_word(depth),
2222251881Speter                        "start-empty", start_empty ? "true" : NULL,
2223299742Sdim                        SVN_VA_NULL);
2224251881Speter  svn_xml_escape_cdata_cstring(&buf, path, pool);
2225251881Speter  svn_xml_make_close_tag(&buf, pool, "S:entry");
2226251881Speter
2227299742Sdim  SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
2228251881Speter
2229251881Speter  return SVN_NO_ERROR;
2230251881Speter}
2231251881Speter
2232251881Speterstatic svn_error_t *
2233251881Speterdelete_path(void *report_baton,
2234251881Speter            const char *path,
2235251881Speter            apr_pool_t *pool)
2236251881Speter{
2237251881Speter  report_context_t *report = report_baton;
2238251881Speter  svn_stringbuf_t *buf = NULL;
2239251881Speter
2240251881Speter  make_simple_xml_tag(&buf, "S:missing", path, pool);
2241251881Speter
2242299742Sdim  SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
2243251881Speter
2244251881Speter  return SVN_NO_ERROR;
2245251881Speter}
2246251881Speter
2247251881Speterstatic svn_error_t *
2248251881Speterlink_path(void *report_baton,
2249251881Speter          const char *path,
2250251881Speter          const char *url,
2251251881Speter          svn_revnum_t revision,
2252251881Speter          svn_depth_t depth,
2253251881Speter          svn_boolean_t start_empty,
2254251881Speter          const char *lock_token,
2255251881Speter          apr_pool_t *pool)
2256251881Speter{
2257251881Speter  report_context_t *report = report_baton;
2258251881Speter  const char *link, *report_target;
2259251881Speter  apr_uri_t uri;
2260251881Speter  apr_status_t status;
2261251881Speter  svn_stringbuf_t *buf = NULL;
2262251881Speter
2263251881Speter  /* We need to pass in the baseline relative path.
2264251881Speter   *
2265251881Speter   * TODO Confirm that it's on the same server?
2266251881Speter   */
2267251881Speter  status = apr_uri_parse(pool, url, &uri);
2268251881Speter  if (status)
2269251881Speter    {
2270251881Speter      return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
2271251881Speter                               _("Unable to parse URL '%s'"), url);
2272251881Speter    }
2273251881Speter
2274299742Sdim  SVN_ERR(svn_ra_serf__report_resource(&report_target, report->sess, pool));
2275299742Sdim  SVN_ERR(svn_ra_serf__get_relative_path(&link, uri.path, report->sess, pool));
2276251881Speter
2277299742Sdim  link = apr_pstrcat(pool, "/", link, SVN_VA_NULL);
2278251881Speter
2279251881Speter  svn_xml_make_open_tag(&buf, pool, svn_xml_protect_pcdata, "S:entry",
2280251881Speter                        "rev", apr_ltoa(pool, revision),
2281251881Speter                        "lock-token", lock_token,
2282251881Speter                        "depth", svn_depth_to_word(depth),
2283251881Speter                        "linkpath", link,
2284251881Speter                        "start-empty", start_empty ? "true" : NULL,
2285299742Sdim                        SVN_VA_NULL);
2286251881Speter  svn_xml_escape_cdata_cstring(&buf, path, pool);
2287251881Speter  svn_xml_make_close_tag(&buf, pool, "S:entry");
2288251881Speter
2289299742Sdim  SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
2290251881Speter
2291251881Speter  /* Store the switch roots to allow generating repos_relpaths from just
2292251881Speter     the working copy paths. (Needed for HTTPv2) */
2293251881Speter  path = apr_pstrdup(report->pool, path);
2294299742Sdim  link = apr_pstrdup(report->pool, link + 1);
2295299742Sdim  svn_hash_sets(report->switched_paths, path, link);
2296251881Speter
2297299742Sdim  if (!path[0] && report->update_target[0])
2298251881Speter    {
2299299742Sdim      /* The update root is switched. Make sure we store it the way
2300299742Sdim         we expect it to find */
2301299742Sdim      svn_hash_sets(report->switched_paths, report->update_target, link);
2302251881Speter    }
2303251881Speter
2304299742Sdim  return APR_SUCCESS;
2305251881Speter}
2306251881Speter
2307299742Sdim/* Serf callback to create update request body bucket.
2308299742Sdim   Implements svn_ra_serf__request_body_delegate_t */
2309251881Speterstatic svn_error_t *
2310251881Spetercreate_update_report_body(serf_bucket_t **body_bkt,
2311251881Speter                          void *baton,
2312251881Speter                          serf_bucket_alloc_t *alloc,
2313299742Sdim                          apr_pool_t *pool /* request pool */,
2314299742Sdim                          apr_pool_t *scratch_pool)
2315251881Speter{
2316251881Speter  report_context_t *report = baton;
2317299742Sdim  body_create_baton_t *body = report->body;
2318251881Speter
2319299742Sdim  if (body->file)
2320299742Sdim    {
2321299742Sdim      apr_off_t offset;
2322251881Speter
2323299742Sdim      offset = 0;
2324299742Sdim      SVN_ERR(svn_io_file_seek(body->file, APR_SET, &offset, pool));
2325251881Speter
2326299742Sdim      *body_bkt = serf_bucket_file_create(report->body->file, alloc);
2327299742Sdim    }
2328299742Sdim  else
2329299742Sdim    {
2330299742Sdim      *body_bkt = serf_bucket_simple_create(body->all_data,
2331299742Sdim                                            body->total_bytes,
2332299742Sdim                                            NULL, NULL, alloc);
2333299742Sdim    }
2334299742Sdim
2335251881Speter  return SVN_NO_ERROR;
2336251881Speter}
2337251881Speter
2338251881Speter/* Serf callback to setup update request headers. */
2339251881Speterstatic svn_error_t *
2340251881Spetersetup_update_report_headers(serf_bucket_t *headers,
2341251881Speter                            void *baton,
2342299742Sdim                            apr_pool_t *pool /* request pool */,
2343299742Sdim                            apr_pool_t *scratch_pool)
2344251881Speter{
2345251881Speter  report_context_t *report = baton;
2346251881Speter
2347251881Speter  if (report->sess->using_compression)
2348251881Speter    {
2349251881Speter      serf_bucket_headers_setn(headers, "Accept-Encoding",
2350253734Speter                               "gzip,svndiff1;q=0.9,svndiff;q=0.8");
2351251881Speter    }
2352251881Speter  else
2353251881Speter    {
2354251881Speter      serf_bucket_headers_setn(headers, "Accept-Encoding",
2355251881Speter                               "svndiff1;q=0.9,svndiff;q=0.8");
2356251881Speter    }
2357251881Speter
2358251881Speter  return SVN_NO_ERROR;
2359251881Speter}
2360251881Speter
2361299742Sdim/* Baton for update_delay_handler */
2362299742Sdimtypedef struct update_delay_baton_t
2363299742Sdim{
2364299742Sdim  report_context_t *report;
2365299742Sdim  svn_spillbuf_t *spillbuf;
2366299742Sdim  svn_ra_serf__response_handler_t inner_handler;
2367299742Sdim  void *inner_handler_baton;
2368299742Sdim} update_delay_baton_t;
2369299742Sdim
2370299742Sdim/* Helper for update_delay_handler() and process_pending() to
2371299742Sdim   call UDB->INNER_HANDLER with buffer pointed by DATA. */
2372251881Speterstatic svn_error_t *
2373299742Sdimprocess_buffer(update_delay_baton_t *udb,
2374299742Sdim               serf_request_t *request,
2375299742Sdim               const void *data,
2376299742Sdim               apr_size_t len,
2377299742Sdim               svn_boolean_t at_eof,
2378299742Sdim               serf_bucket_alloc_t *alloc,
2379299742Sdim               apr_pool_t *pool)
2380251881Speter{
2381299742Sdim  serf_bucket_t *tmp_bucket;
2382251881Speter  svn_error_t *err;
2383251881Speter
2384299742Sdim  /* ### This code (and the eagain bucket code) can probably be
2385299742Sdim      ### simplified by using a bit of aggregate bucket magic.
2386299742Sdim      ### See mail from Ivan to dev@s.a.o. */
2387299742Sdim  if (at_eof)
2388299742Sdim  {
2389299742Sdim      tmp_bucket = serf_bucket_simple_create(data, len, NULL, NULL,
2390299742Sdim                                             alloc);
2391299742Sdim  }
2392299742Sdim  else
2393299742Sdim  {
2394299742Sdim      tmp_bucket = svn_ra_serf__create_bucket_with_eagain(data, len,
2395299742Sdim                                                          alloc);
2396299742Sdim  }
2397251881Speter
2398299742Sdim  /* If not at EOF create a bucket that finishes with EAGAIN, otherwise
2399299742Sdim      use a standard bucket with default EOF handling */
2400299742Sdim  err = udb->inner_handler(request, tmp_bucket,
2401299742Sdim                           udb->inner_handler_baton, pool);
2402251881Speter
2403299742Sdim  /* And free the bucket explicitly to avoid growing request allocator
2404299742Sdim     storage (in a loop) */
2405299742Sdim  serf_bucket_destroy(tmp_bucket);
2406251881Speter
2407299742Sdim  return svn_error_trace(err);
2408299742Sdim}
2409251881Speter
2410251881Speter
2411299742Sdim/* Delaying wrapping reponse handler, to avoid creating too many
2412299742Sdim   requests to deliver efficiently */
2413299742Sdimstatic svn_error_t *
2414299742Sdimupdate_delay_handler(serf_request_t *request,
2415299742Sdim                     serf_bucket_t *response,
2416299742Sdim                     void *handler_baton,
2417299742Sdim                     apr_pool_t *scratch_pool)
2418299742Sdim{
2419299742Sdim  update_delay_baton_t *udb = handler_baton;
2420299742Sdim  apr_status_t status;
2421299742Sdim  apr_pool_t *iterpool = NULL;
2422251881Speter
2423299742Sdim  if (! udb->spillbuf)
2424251881Speter    {
2425299742Sdim      if (udb->report->send_all_mode)
2426299742Sdim        {
2427299742Sdim          /* Easy out... We only have one request, so avoid everything and just
2428299742Sdim             call the inner handler.
2429251881Speter
2430299742Sdim             We will always get in the loop (below) on the first chunk, as only
2431299742Sdim             the server can get us in true send-all mode */
2432251881Speter
2433299742Sdim          return svn_error_trace(udb->inner_handler(request, response,
2434299742Sdim                                                    udb->inner_handler_baton,
2435299742Sdim                                                    scratch_pool));
2436299742Sdim        }
2437251881Speter
2438299742Sdim      while ((udb->report->num_active_fetches + udb->report->num_active_propfinds)
2439299742Sdim                 < REQUEST_COUNT_TO_RESUME)
2440299742Sdim        {
2441299742Sdim          const char *data;
2442299742Sdim          apr_size_t len;
2443299742Sdim          svn_boolean_t at_eof = FALSE;
2444299742Sdim          svn_error_t *err;
2445251881Speter
2446299742Sdim          status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
2447299742Sdim          if (SERF_BUCKET_READ_ERROR(status))
2448299742Sdim            return svn_ra_serf__wrap_err(status, NULL);
2449299742Sdim          else if (APR_STATUS_IS_EOF(status))
2450299742Sdim            udb->report->report_received = at_eof = TRUE;
2451251881Speter
2452299742Sdim          if (!iterpool)
2453299742Sdim            iterpool = svn_pool_create(scratch_pool);
2454299742Sdim          else
2455299742Sdim            svn_pool_clear(iterpool);
2456251881Speter
2457299742Sdim          if (len == 0 && !at_eof)
2458299742Sdim            return svn_ra_serf__wrap_err(status, NULL);
2459251881Speter
2460299742Sdim          err = process_buffer(udb, request, data, len, at_eof,
2461299742Sdim                               serf_request_get_alloc(request),
2462299742Sdim                               iterpool);
2463251881Speter
2464299742Sdim          if (err && SERF_BUCKET_READ_ERROR(err->apr_err))
2465299742Sdim            return svn_error_trace(err);
2466299742Sdim          else if (err && APR_STATUS_IS_EAGAIN(err->apr_err))
2467251881Speter            {
2468299742Sdim              svn_error_clear(err); /* Throttling is working ok */
2469251881Speter            }
2470299742Sdim          else if (err && (APR_STATUS_IS_EOF(err->apr_err)))
2471299742Sdim            {
2472299742Sdim              svn_pool_destroy(iterpool);
2473299742Sdim              return svn_error_trace(err); /* No buffering was necessary */
2474299742Sdim            }
2475299742Sdim          else
2476299742Sdim            {
2477299742Sdim              /* SERF_ERROR_WAIT_CONN should be impossible? */
2478299742Sdim              return svn_error_trace(err);
2479299742Sdim            }
2480251881Speter        }
2481251881Speter
2482299742Sdim      /* Let's start using the spill infrastructure */
2483299742Sdim      udb->spillbuf = svn_spillbuf__create(SPILLBUF_BLOCKSIZE,
2484299742Sdim                                           SPILLBUF_MAXBUFFSIZE,
2485299742Sdim                                           udb->report->pool);
2486299742Sdim    }
2487251881Speter
2488299742Sdim  /* Read everything we can to a spillbuffer */
2489299742Sdim  do
2490299742Sdim    {
2491299742Sdim      const char *data;
2492299742Sdim      apr_size_t len;
2493251881Speter
2494299742Sdim      /* ### What blocksize should we pass? */
2495299742Sdim      status = serf_bucket_read(response, 8*PARSE_CHUNK_SIZE, &data, &len);
2496251881Speter
2497299742Sdim      if (!SERF_BUCKET_READ_ERROR(status))
2498299742Sdim        SVN_ERR(svn_spillbuf__write(udb->spillbuf, data, len, scratch_pool));
2499299742Sdim    }
2500299742Sdim  while (status == APR_SUCCESS);
2501251881Speter
2502299742Sdim  if (APR_STATUS_IS_EOF(status))
2503299742Sdim    udb->report->report_received = TRUE;
2504251881Speter
2505299742Sdim  /* We handle feeding the data from the main context loop, which will be right
2506299742Sdim     after processing the pending data */
2507251881Speter
2508299742Sdim  if (status)
2509299742Sdim    return svn_ra_serf__wrap_err(status, NULL);
2510299742Sdim  else
2511299742Sdim    return SVN_NO_ERROR;
2512299742Sdim}
2513251881Speter
2514299742Sdim/* Process pending data from the update report, if any */
2515299742Sdimstatic svn_error_t *
2516299742Sdimprocess_pending(update_delay_baton_t *udb,
2517299742Sdim                apr_pool_t *scratch_pool)
2518299742Sdim{
2519299742Sdim  apr_pool_t *iterpool = NULL;
2520299742Sdim  serf_bucket_alloc_t *alloc = NULL;
2521251881Speter
2522299742Sdim  while ((udb->report->num_active_fetches + udb->report->num_active_propfinds)
2523299742Sdim            < REQUEST_COUNT_TO_RESUME)
2524299742Sdim    {
2525299742Sdim      const char *data;
2526299742Sdim      apr_size_t len;
2527299742Sdim      svn_boolean_t at_eof;
2528299742Sdim      svn_error_t *err;
2529251881Speter
2530299742Sdim      if (!iterpool)
2531299742Sdim        {
2532299742Sdim          iterpool = svn_pool_create(scratch_pool);
2533299742Sdim          alloc = serf_bucket_allocator_create(scratch_pool, NULL, NULL);
2534299742Sdim        }
2535299742Sdim      else
2536299742Sdim        svn_pool_clear(iterpool);
2537251881Speter
2538299742Sdim      SVN_ERR(svn_spillbuf__read(&data, &len, udb->spillbuf, iterpool));
2539251881Speter
2540299742Sdim      if (data == NULL && !udb->report->report_received)
2541299742Sdim        break;
2542299742Sdim      else if (data == NULL)
2543299742Sdim        at_eof = TRUE;
2544299742Sdim      else
2545299742Sdim        at_eof = FALSE;
2546251881Speter
2547299742Sdim      err = process_buffer(udb, NULL /* allowed? */, data, len,
2548299742Sdim                           at_eof, alloc, iterpool);
2549251881Speter
2550299742Sdim      if (err && APR_STATUS_IS_EAGAIN(err->apr_err))
2551299742Sdim        {
2552299742Sdim          svn_error_clear(err); /* Throttling is working */
2553251881Speter        }
2554299742Sdim      else if (err && APR_STATUS_IS_EOF(err->apr_err))
2555251881Speter        {
2556299742Sdim          svn_error_clear(err);
2557251881Speter
2558299742Sdim          svn_pool_destroy(iterpool);
2559299742Sdim          udb->spillbuf = NULL;
2560299742Sdim          return SVN_NO_ERROR;
2561299742Sdim        }
2562299742Sdim      else if (err)
2563299742Sdim        return svn_error_trace(err);
2564299742Sdim    }
2565251881Speter
2566299742Sdim  if (iterpool)
2567299742Sdim    svn_pool_destroy(iterpool);
2568251881Speter
2569299742Sdim  return SVN_NO_ERROR;
2570299742Sdim}
2571251881Speter
2572299742Sdim/* Process the 'update' editor report */
2573299742Sdimstatic svn_error_t *
2574299742Sdimprocess_editor_report(report_context_t *ctx,
2575299742Sdim                      svn_ra_serf__handler_t *handler,
2576299742Sdim                      apr_pool_t *scratch_pool)
2577299742Sdim{
2578299742Sdim  svn_ra_serf__session_t *sess = ctx->sess;
2579299742Sdim  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2580299742Sdim  apr_interval_time_t waittime_left = sess->timeout;
2581299742Sdim  update_delay_baton_t *ud;
2582251881Speter
2583299742Sdim  /* Now wrap the response handler with delay support to avoid sending
2584299742Sdim     out too many requests at once */
2585299742Sdim  ud = apr_pcalloc(scratch_pool, sizeof(*ud));
2586299742Sdim  ud->report = ctx;
2587251881Speter
2588299742Sdim  ud->inner_handler = handler->response_handler;
2589299742Sdim  ud->inner_handler_baton = handler->response_baton;
2590251881Speter
2591299742Sdim  handler->response_handler = update_delay_handler;
2592299742Sdim  handler->response_baton = ud;
2593251881Speter
2594299742Sdim  /* Open the first extra connection. */
2595299742Sdim  SVN_ERR(open_connection_if_needed(sess, 0));
2596251881Speter
2597299742Sdim  sess->cur_conn = 1;
2598251881Speter
2599299742Sdim  /* Note that we may have no active GET or PROPFIND requests, yet the
2600299742Sdim     processing has not been completed. This could be from a delay on the
2601299742Sdim     network or because we've spooled the entire response into our "pending"
2602299742Sdim     content of the XML parser. The DONE flag will get set when all the
2603299742Sdim     XML content has been received *and* parsed.  */
2604299742Sdim  while (!handler->done
2605299742Sdim         || ctx->num_active_fetches
2606299742Sdim         || ctx->num_active_propfinds
2607299742Sdim         || !ctx->done)
2608299742Sdim    {
2609299742Sdim      svn_error_t *err;
2610299742Sdim      int i;
2611251881Speter
2612299742Sdim      svn_pool_clear(iterpool);
2613251881Speter
2614299742Sdim      err = svn_ra_serf__context_run(sess, &waittime_left, iterpool);
2615251881Speter
2616299742Sdim      if (handler->done && handler->server_error)
2617299742Sdim        {
2618299742Sdim          svn_error_clear(err);
2619299742Sdim          err = svn_ra_serf__server_error_create(handler, iterpool);
2620251881Speter
2621299742Sdim          SVN_ERR_ASSERT(err != NULL);
2622251881Speter        }
2623251881Speter
2624299742Sdim      SVN_ERR(err);
2625251881Speter
2626299742Sdim      /* If there is pending REPORT data, process it now. */
2627299742Sdim      if (ud->spillbuf)
2628299742Sdim        SVN_ERR(process_pending(ud, iterpool));
2629251881Speter
2630251881Speter      /* Debugging purposes only! */
2631251881Speter      for (i = 0; i < sess->num_conns; i++)
2632251881Speter        {
2633251881Speter          serf_debug__closed_conn(sess->conns[i]->bkt_alloc);
2634251881Speter        }
2635251881Speter    }
2636251881Speter
2637299742Sdim  svn_pool_clear(iterpool);
2638299742Sdim
2639251881Speter  /* If we got a complete report, close the edit.  Otherwise, abort it. */
2640299742Sdim  if (ctx->done)
2641299742Sdim    SVN_ERR(ctx->editor->close_edit(ctx->editor_baton, iterpool));
2642299742Sdim  else
2643299742Sdim    return svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
2644299742Sdim                            _("Missing update-report close tag"));
2645251881Speter
2646299742Sdim  svn_pool_destroy(iterpool);
2647299742Sdim  return SVN_NO_ERROR;
2648299742Sdim}
2649299742Sdim
2650299742Sdimstatic svn_error_t *
2651299742Sdimfinish_report(void *report_baton,
2652299742Sdim              apr_pool_t *pool)
2653299742Sdim{
2654299742Sdim  report_context_t *report = report_baton;
2655299742Sdim  svn_ra_serf__session_t *sess = report->sess;
2656299742Sdim  svn_ra_serf__handler_t *handler;
2657299742Sdim  svn_ra_serf__xml_context_t *xmlctx;
2658299742Sdim  const char *report_target;
2659299742Sdim  svn_stringbuf_t *buf = NULL;
2660299742Sdim  apr_pool_t *scratch_pool = svn_pool_create(pool);
2661299742Sdim  svn_error_t *err;
2662299742Sdim
2663299742Sdim  svn_xml_make_close_tag(&buf, scratch_pool, "S:update-report");
2664299742Sdim  SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
2665299742Sdim  SVN_ERR(svn_stream_close(report->body_template));
2666299742Sdim
2667299742Sdim  SVN_ERR(svn_ra_serf__report_resource(&report_target, sess,  scratch_pool));
2668299742Sdim
2669299742Sdim  xmlctx = svn_ra_serf__xml_context_create(update_ttable,
2670299742Sdim                                           update_opened, update_closed,
2671299742Sdim                                           update_cdata,
2672299742Sdim                                           report,
2673299742Sdim                                           scratch_pool);
2674299742Sdim  handler = svn_ra_serf__create_expat_handler(sess, xmlctx, NULL,
2675299742Sdim                                              scratch_pool);
2676299742Sdim
2677299742Sdim  handler->method = "REPORT";
2678299742Sdim  handler->path = report_target;
2679299742Sdim  handler->body_delegate = create_update_report_body;
2680299742Sdim  handler->body_delegate_baton = report;
2681299742Sdim  handler->body_type = "text/xml";
2682299742Sdim  handler->custom_accept_encoding = TRUE;
2683299742Sdim  handler->header_delegate = setup_update_report_headers;
2684299742Sdim  handler->header_delegate_baton = report;
2685299742Sdim
2686299742Sdim  svn_ra_serf__request_create(handler);
2687299742Sdim
2688299742Sdim  err = process_editor_report(report, handler, scratch_pool);
2689299742Sdim
2690299742Sdim  if (err)
2691251881Speter    {
2692299742Sdim      err = svn_error_trace(err);
2693299742Sdim      err = svn_error_compose_create(
2694299742Sdim                err,
2695299742Sdim                svn_error_trace(
2696299742Sdim                    report->editor->abort_edit(report->editor_baton,
2697299742Sdim                                               scratch_pool)));
2698251881Speter    }
2699251881Speter
2700299742Sdim  svn_pool_destroy(scratch_pool);
2701299742Sdim
2702251881Speter  return svn_error_trace(err);
2703251881Speter}
2704251881Speter
2705251881Speter
2706251881Speterstatic svn_error_t *
2707251881Speterabort_report(void *report_baton,
2708251881Speter             apr_pool_t *pool)
2709251881Speter{
2710251881Speter#if 0
2711251881Speter  report_context_t *report = report_baton;
2712251881Speter#endif
2713251881Speter
2714251881Speter  /* Should we perform some cleanup here? */
2715251881Speter
2716251881Speter  return SVN_NO_ERROR;
2717251881Speter}
2718251881Speter
2719251881Speterstatic const svn_ra_reporter3_t ra_serf_reporter = {
2720251881Speter  set_path,
2721251881Speter  delete_path,
2722251881Speter  link_path,
2723251881Speter  finish_report,
2724251881Speter  abort_report
2725251881Speter};
2726251881Speter
2727251881Speter
2728251881Speter/** RA function implementations and body */
2729251881Speter
2730251881Speterstatic svn_error_t *
2731251881Spetermake_update_reporter(svn_ra_session_t *ra_session,
2732251881Speter                     const svn_ra_reporter3_t **reporter,
2733251881Speter                     void **report_baton,
2734251881Speter                     svn_revnum_t revision,
2735251881Speter                     const char *src_path,
2736251881Speter                     const char *dest_path,
2737251881Speter                     const char *update_target,
2738251881Speter                     svn_depth_t depth,
2739251881Speter                     svn_boolean_t ignore_ancestry,
2740251881Speter                     svn_boolean_t text_deltas,
2741251881Speter                     svn_boolean_t send_copyfrom_args,
2742251881Speter                     const svn_delta_editor_t *update_editor,
2743251881Speter                     void *update_baton,
2744251881Speter                     apr_pool_t *result_pool,
2745251881Speter                     apr_pool_t *scratch_pool)
2746251881Speter{
2747251881Speter  report_context_t *report;
2748251881Speter  const svn_delta_editor_t *filter_editor;
2749251881Speter  void *filter_baton;
2750251881Speter  svn_boolean_t has_target = *update_target != '\0';
2751251881Speter  svn_boolean_t server_supports_depth;
2752251881Speter  svn_ra_serf__session_t *sess = ra_session->priv;
2753251881Speter  svn_stringbuf_t *buf = NULL;
2754251881Speter  svn_boolean_t use_bulk_updates;
2755251881Speter
2756251881Speter  SVN_ERR(svn_ra_serf__has_capability(ra_session, &server_supports_depth,
2757251881Speter                                      SVN_RA_CAPABILITY_DEPTH, scratch_pool));
2758251881Speter  /* We can skip the depth filtering when the user requested
2759251881Speter     depth_files or depth_infinity because the server will
2760251881Speter     transmit the right stuff anyway. */
2761251881Speter  if ((depth != svn_depth_files)
2762251881Speter      && (depth != svn_depth_infinity)
2763251881Speter      && ! server_supports_depth)
2764251881Speter    {
2765251881Speter      SVN_ERR(svn_delta_depth_filter_editor(&filter_editor,
2766251881Speter                                            &filter_baton,
2767251881Speter                                            update_editor,
2768251881Speter                                            update_baton,
2769251881Speter                                            depth, has_target,
2770299742Sdim                                            result_pool));
2771251881Speter      update_editor = filter_editor;
2772251881Speter      update_baton = filter_baton;
2773251881Speter    }
2774251881Speter
2775251881Speter  report = apr_pcalloc(result_pool, sizeof(*report));
2776251881Speter  report->pool = result_pool;
2777251881Speter  report->sess = sess;
2778251881Speter  report->target_rev = revision;
2779251881Speter  report->ignore_ancestry = ignore_ancestry;
2780251881Speter  report->send_copyfrom_args = send_copyfrom_args;
2781251881Speter  report->text_deltas = text_deltas;
2782251881Speter  report->switched_paths = apr_hash_make(report->pool);
2783251881Speter
2784251881Speter  report->source = src_path;
2785251881Speter  report->destination = dest_path;
2786251881Speter  report->update_target = update_target;
2787251881Speter
2788299742Sdim  report->editor = update_editor;
2789299742Sdim  report->editor_baton = update_baton;
2790251881Speter  report->done = FALSE;
2791251881Speter
2792251881Speter  *reporter = &ra_serf_reporter;
2793251881Speter  *report_baton = report;
2794251881Speter
2795299742Sdim  report->body = apr_pcalloc(report->pool, sizeof(*report->body));
2796299742Sdim  report->body->result_pool = report->pool;
2797299742Sdim  report->body_template = svn_stream_create(report->body, report->pool);
2798299742Sdim  svn_stream_set_write(report->body_template, body_write_fn);
2799299742Sdim  svn_stream_set_close(report->body_template, body_done_fn);
2800251881Speter
2801251881Speter  if (sess->bulk_updates == svn_tristate_true)
2802251881Speter    {
2803251881Speter      /* User would like to use bulk updates. */
2804251881Speter      use_bulk_updates = TRUE;
2805251881Speter    }
2806251881Speter  else if (sess->bulk_updates == svn_tristate_false)
2807251881Speter    {
2808251881Speter      /* User doesn't want bulk updates. */
2809251881Speter      use_bulk_updates = FALSE;
2810251881Speter    }
2811251881Speter  else
2812251881Speter    {
2813251881Speter      /* User doesn't have any preferences on bulk updates. Decide on server
2814251881Speter         preferences and capabilities. */
2815251881Speter      if (sess->server_allows_bulk)
2816251881Speter        {
2817251881Speter          if (apr_strnatcasecmp(sess->server_allows_bulk, "off") == 0)
2818251881Speter            {
2819251881Speter              /* Server doesn't want bulk updates */
2820251881Speter              use_bulk_updates = FALSE;
2821251881Speter            }
2822251881Speter          else if (apr_strnatcasecmp(sess->server_allows_bulk, "prefer") == 0)
2823251881Speter            {
2824251881Speter              /* Server prefers bulk updates, and we respect that */
2825251881Speter              use_bulk_updates = TRUE;
2826251881Speter            }
2827251881Speter          else
2828251881Speter            {
2829251881Speter              /* Server allows bulk updates, but doesn't dictate its use. Do
2830251881Speter                 whatever is the default. */
2831251881Speter              use_bulk_updates = FALSE;
2832251881Speter            }
2833251881Speter        }
2834251881Speter      else
2835251881Speter        {
2836251881Speter          /* Pre-1.8 server didn't send the bulk_updates header. Check if server
2837251881Speter             supports inlining properties in update editor report. */
2838251881Speter          if (sess->supports_inline_props)
2839251881Speter            {
2840299742Sdim              /* NOTE: both inlined properties and server->allows_bulk_update
2841299742Sdim                 (flag SVN_DAV_ALLOW_BULK_UPDATES) were added in 1.8.0, so
2842299742Sdim                 this code is never reached with a released version of
2843299742Sdim                 mod_dav_svn.
2844299742Sdim
2845299742Sdim                 Basically by default a 1.8.0 client connecting to a 1.7.x or
2846299742Sdim                 older server will always use bulk updates. */
2847299742Sdim
2848251881Speter              /* Inline props supported: do not use bulk updates. */
2849251881Speter              use_bulk_updates = FALSE;
2850251881Speter            }
2851251881Speter          else
2852251881Speter            {
2853251881Speter              /* Inline props are not supported: use bulk updates to avoid
2854251881Speter               * PROPFINDs for every added node. */
2855251881Speter              use_bulk_updates = TRUE;
2856251881Speter            }
2857251881Speter        }
2858251881Speter    }
2859251881Speter
2860251881Speter  if (use_bulk_updates)
2861251881Speter    {
2862251881Speter      svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal,
2863251881Speter                            "S:update-report",
2864251881Speter                            "xmlns:S", SVN_XML_NAMESPACE, "send-all", "true",
2865299742Sdim                            SVN_VA_NULL);
2866251881Speter    }
2867251881Speter  else
2868251881Speter    {
2869251881Speter      svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal,
2870251881Speter                            "S:update-report",
2871251881Speter                            "xmlns:S", SVN_XML_NAMESPACE,
2872299742Sdim                            SVN_VA_NULL);
2873251881Speter      /* Subversion 1.8+ servers can be told to send properties for newly
2874251881Speter         added items inline even when doing a skelta response. */
2875251881Speter      make_simple_xml_tag(&buf, "S:include-props", "yes", scratch_pool);
2876251881Speter    }
2877251881Speter
2878251881Speter  make_simple_xml_tag(&buf, "S:src-path", report->source, scratch_pool);
2879251881Speter
2880251881Speter  if (SVN_IS_VALID_REVNUM(report->target_rev))
2881251881Speter    {
2882251881Speter      make_simple_xml_tag(&buf, "S:target-revision",
2883251881Speter                          apr_ltoa(scratch_pool, report->target_rev),
2884251881Speter                          scratch_pool);
2885251881Speter    }
2886251881Speter
2887251881Speter  if (report->destination && *report->destination)
2888251881Speter    {
2889251881Speter      make_simple_xml_tag(&buf, "S:dst-path", report->destination,
2890251881Speter                          scratch_pool);
2891251881Speter    }
2892251881Speter
2893251881Speter  if (report->update_target && *report->update_target)
2894251881Speter    {
2895251881Speter      make_simple_xml_tag(&buf, "S:update-target", report->update_target,
2896251881Speter                          scratch_pool);
2897251881Speter    }
2898251881Speter
2899251881Speter  if (report->ignore_ancestry)
2900251881Speter    {
2901251881Speter      make_simple_xml_tag(&buf, "S:ignore-ancestry", "yes", scratch_pool);
2902251881Speter    }
2903251881Speter
2904251881Speter  if (report->send_copyfrom_args)
2905251881Speter    {
2906251881Speter      make_simple_xml_tag(&buf, "S:send-copyfrom-args", "yes", scratch_pool);
2907251881Speter    }
2908251881Speter
2909251881Speter  /* Old servers know "recursive" but not "depth"; help them DTRT. */
2910251881Speter  if (depth == svn_depth_files || depth == svn_depth_empty)
2911251881Speter    {
2912251881Speter      make_simple_xml_tag(&buf, "S:recursive", "no", scratch_pool);
2913251881Speter    }
2914251881Speter
2915251881Speter  /* When in 'send-all' mode, mod_dav_svn will assume that it should
2916251881Speter     calculate and transmit real text-deltas (instead of empty windows
2917251881Speter     that merely indicate "text is changed") unless it finds this
2918251881Speter     element.
2919251881Speter
2920251881Speter     NOTE: Do NOT count on servers actually obeying this, as some exist
2921251881Speter     which obey send-all, but do not check for this directive at all!
2922251881Speter
2923251881Speter     NOTE 2: When not in 'send-all' mode, mod_dav_svn can still be configured to
2924251881Speter     override our request and send text-deltas. */
2925251881Speter  if (! text_deltas)
2926251881Speter    {
2927251881Speter      make_simple_xml_tag(&buf, "S:text-deltas", "no", scratch_pool);
2928251881Speter    }
2929251881Speter
2930251881Speter  make_simple_xml_tag(&buf, "S:depth", svn_depth_to_word(depth), scratch_pool);
2931251881Speter
2932299742Sdim  SVN_ERR(svn_stream_write(report->body_template, buf->data, &buf->len));
2933251881Speter
2934251881Speter  return SVN_NO_ERROR;
2935251881Speter}
2936251881Speter
2937251881Spetersvn_error_t *
2938251881Spetersvn_ra_serf__do_update(svn_ra_session_t *ra_session,
2939251881Speter                       const svn_ra_reporter3_t **reporter,
2940251881Speter                       void **report_baton,
2941251881Speter                       svn_revnum_t revision_to_update_to,
2942251881Speter                       const char *update_target,
2943251881Speter                       svn_depth_t depth,
2944251881Speter                       svn_boolean_t send_copyfrom_args,
2945251881Speter                       svn_boolean_t ignore_ancestry,
2946251881Speter                       const svn_delta_editor_t *update_editor,
2947251881Speter                       void *update_baton,
2948251881Speter                       apr_pool_t *result_pool,
2949251881Speter                       apr_pool_t *scratch_pool)
2950251881Speter{
2951251881Speter  svn_ra_serf__session_t *session = ra_session->priv;
2952251881Speter
2953251881Speter  SVN_ERR(make_update_reporter(ra_session, reporter, report_baton,
2954251881Speter                               revision_to_update_to,
2955251881Speter                               session->session_url.path, NULL, update_target,
2956251881Speter                               depth, ignore_ancestry, TRUE /* text_deltas */,
2957251881Speter                               send_copyfrom_args,
2958251881Speter                               update_editor, update_baton,
2959251881Speter                               result_pool, scratch_pool));
2960251881Speter  return SVN_NO_ERROR;
2961251881Speter}
2962251881Speter
2963251881Spetersvn_error_t *
2964251881Spetersvn_ra_serf__do_diff(svn_ra_session_t *ra_session,
2965251881Speter                     const svn_ra_reporter3_t **reporter,
2966251881Speter                     void **report_baton,
2967251881Speter                     svn_revnum_t revision,
2968251881Speter                     const char *diff_target,
2969251881Speter                     svn_depth_t depth,
2970251881Speter                     svn_boolean_t ignore_ancestry,
2971251881Speter                     svn_boolean_t text_deltas,
2972251881Speter                     const char *versus_url,
2973251881Speter                     const svn_delta_editor_t *diff_editor,
2974251881Speter                     void *diff_baton,
2975251881Speter                     apr_pool_t *pool)
2976251881Speter{
2977251881Speter  svn_ra_serf__session_t *session = ra_session->priv;
2978251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
2979251881Speter
2980251881Speter  SVN_ERR(make_update_reporter(ra_session, reporter, report_baton,
2981251881Speter                               revision,
2982251881Speter                               session->session_url.path, versus_url, diff_target,
2983299742Sdim                               depth, ignore_ancestry, text_deltas,
2984299742Sdim                               FALSE /* send_copyfrom */,
2985251881Speter                               diff_editor, diff_baton,
2986251881Speter                               pool, scratch_pool));
2987251881Speter  svn_pool_destroy(scratch_pool);
2988251881Speter  return SVN_NO_ERROR;
2989251881Speter}
2990251881Speter
2991251881Spetersvn_error_t *
2992251881Spetersvn_ra_serf__do_status(svn_ra_session_t *ra_session,
2993251881Speter                       const svn_ra_reporter3_t **reporter,
2994251881Speter                       void **report_baton,
2995251881Speter                       const char *status_target,
2996251881Speter                       svn_revnum_t revision,
2997251881Speter                       svn_depth_t depth,
2998251881Speter                       const svn_delta_editor_t *status_editor,
2999251881Speter                       void *status_baton,
3000251881Speter                       apr_pool_t *pool)
3001251881Speter{
3002251881Speter  svn_ra_serf__session_t *session = ra_session->priv;
3003251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
3004251881Speter
3005251881Speter  SVN_ERR(make_update_reporter(ra_session, reporter, report_baton,
3006251881Speter                               revision,
3007251881Speter                               session->session_url.path, NULL, status_target,
3008251881Speter                               depth, FALSE, FALSE, FALSE,
3009251881Speter                               status_editor, status_baton,
3010251881Speter                               pool, scratch_pool));
3011251881Speter  svn_pool_destroy(scratch_pool);
3012251881Speter  return SVN_NO_ERROR;
3013251881Speter}
3014251881Speter
3015251881Spetersvn_error_t *
3016251881Spetersvn_ra_serf__do_switch(svn_ra_session_t *ra_session,
3017251881Speter                       const svn_ra_reporter3_t **reporter,
3018251881Speter                       void **report_baton,
3019251881Speter                       svn_revnum_t revision_to_switch_to,
3020251881Speter                       const char *switch_target,
3021251881Speter                       svn_depth_t depth,
3022251881Speter                       const char *switch_url,
3023251881Speter                       svn_boolean_t send_copyfrom_args,
3024251881Speter                       svn_boolean_t ignore_ancestry,
3025251881Speter                       const svn_delta_editor_t *switch_editor,
3026251881Speter                       void *switch_baton,
3027251881Speter                       apr_pool_t *result_pool,
3028251881Speter                       apr_pool_t *scratch_pool)
3029251881Speter{
3030251881Speter  svn_ra_serf__session_t *session = ra_session->priv;
3031251881Speter
3032251881Speter  return make_update_reporter(ra_session, reporter, report_baton,
3033251881Speter                              revision_to_switch_to,
3034251881Speter                              session->session_url.path,
3035251881Speter                              switch_url, switch_target,
3036251881Speter                              depth,
3037251881Speter                              ignore_ancestry,
3038251881Speter                              TRUE /* text_deltas */,
3039251881Speter                              send_copyfrom_args,
3040251881Speter                              switch_editor, switch_baton,
3041251881Speter                              result_pool, scratch_pool);
3042251881Speter}
3043