source: valtobtest/subversion-1.6.2/subversion/libsvn_ra_neon/fetch.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

File size: 100.4 KB
Line 
1/*
2 * fetch.c :  routines for fetching updates and checkouts
3 *
4 * ====================================================================
5 * Copyright (c) 2000-2008 CollabNet.  All rights reserved.
6 *
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution.  The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
12 *
13 * This software consists of voluntary contributions made by many
14 * individuals.  For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
17 */
18
19
20
21#include <stdlib.h> /* for free() */
22
23#define APR_WANT_STRFUNC
24#include <apr_want.h> /* for strcmp() */
25
26#include <apr_pools.h>
27#include <apr_tables.h>
28#include <apr_strings.h>
29#include <apr_xml.h>
30
31#include <ne_basic.h>
32
33#include "svn_error.h"
34#include "svn_pools.h"
35#include "svn_delta.h"
36#include "svn_io.h"
37#include "svn_base64.h"
38#include "svn_ra.h"
39#include "../libsvn_ra/ra_loader.h"
40#include "svn_path.h"
41#include "svn_xml.h"
42#include "svn_dav.h"
43#include "svn_time.h"
44#include "svn_props.h"
45
46#include "private/svn_dav_protocol.h"
47#include "svn_private_config.h"
48
49#include "ra_neon.h"
50
51
52typedef struct {
53  /* the information for this subdir. if rsrc==NULL, then this is a sentinel
54     record in fetch_ctx_t.subdirs to close the directory implied by the
55     parent_baton member. */
56  svn_ra_neon__resource_t *rsrc;
57
58  /* the directory containing this subdirectory. */
59  void *parent_baton;
60
61} subdir_t;
62
63typedef struct {
64  apr_pool_t *pool;
65
66  /* these two are the handler that the editor gave us */
67  svn_txdelta_window_handler_t handler;
68  void *handler_baton;
69
70  /* if we're receiving an svndiff, this is a parser which places the
71     resulting windows into the above handler/baton. */
72  svn_stream_t *stream;
73
74} file_read_ctx_t;
75
76typedef struct {
77  svn_boolean_t do_checksum;  /* only accumulate checksum if set */
78  svn_checksum_ctx_t *checksum_ctx; /* accumulating checksum of file contents */
79  svn_stream_t *stream;       /* stream to write file contents to */
80} file_write_ctx_t;
81
82typedef struct {
83  svn_ra_neon__request_t *req;  /* Used to propagate errors out of the reader */
84  int checked_type;             /* have we processed ctype yet? */
85
86  void *subctx;
87} custom_get_ctx_t;
88
89#define POP_SUBDIR(sds) (APR_ARRAY_IDX((sds), --(sds)->nelts, subdir_t *))
90#define PUSH_SUBDIR(sds,s) (APR_ARRAY_PUSH((sds), subdir_t *) = (s))
91
92typedef svn_error_t * (*prop_setter_t)(void *baton,
93                                       const char *name,
94                                       const svn_string_t *value,
95                                       apr_pool_t *pool);
96
97typedef struct {
98  /* The baton returned by the editor's open_root/open_dir */
99  void *baton;
100
101  /* Should we fetch properties for this directory when the close tag
102     is found? */
103  svn_boolean_t fetch_props;
104
105  /* The version resource URL for this directory. */
106  const char *vsn_url;
107
108  /* A buffer which stores the relative directory name. We also use this
109     for temporary construction of relative file names. */
110  svn_stringbuf_t *pathbuf;
111
112  /* If a directory, this may contain a hash of prophashes returned
113     from doing a depth 1 PROPFIND. */
114  apr_hash_t *children;
115
116  /* A subpool.  It's about memory.  Ya dig? */
117  apr_pool_t *pool;
118
119} dir_item_t;
120
121typedef struct {
122  svn_ra_neon__session_t *ras;
123
124  apr_file_t *tmpfile;
125
126  /* The pool of the report baton; used for things that must live during the
127     whole editing operation. */
128  apr_pool_t *pool;
129  /* Pool initialized when the report_baton is created, and meant for
130     quick scratchwork.  This is like a loop pool, but since the loop
131     that drives ra_neon callbacks is in the wrong scope for us to use
132     the normal loop pool idiom, we must resort to this.  Always clear
133     this pool right after using it; only YOU can prevent forest fires. */
134  apr_pool_t *scratch_pool;
135
136  svn_boolean_t fetch_content;
137  svn_boolean_t fetch_props;
138
139  const svn_delta_editor_t *editor;
140  void *edit_baton;
141
142  /* Stack of directory batons/vsn_urls. */
143  apr_array_header_t *dirs;
144
145#define TOP_DIR(rb)      (APR_ARRAY_IDX((rb)->dirs, (rb)->dirs->nelts - 1, \
146                          dir_item_t))
147#define DIR_DEPTH(rb)    ((rb)->dirs->nelts)
148#define PUSH_BATON(rb,b) (APR_ARRAY_PUSH((rb)->dirs, void *) = (b))
149
150  /* These items are only valid inside add- and open-file tags! */
151  void *file_baton;
152  apr_pool_t *file_pool;
153  const char *result_checksum; /* hex md5 digest of result; may be null */
154
155  svn_stringbuf_t *namestr;
156  svn_stringbuf_t *cpathstr;
157  svn_stringbuf_t *href;
158
159  /* Empty string means no encoding, "base64" means base64. */
160  svn_stringbuf_t *encoding;
161
162  /* These are used when receiving an inline txdelta, and null at all
163     other times. */
164  svn_txdelta_window_handler_t whandler;
165  void *whandler_baton;
166  svn_stream_t *svndiff_decoder;
167  svn_stream_t *base64_decoder;
168
169  /* A generic accumulator for elements that have small bits of cdata,
170     like md5_checksum, href, etc.  Uh, or where our own API gives us
171     no choice about holding them in memory, as with prop values, ahem.
172     This is always the empty stringbuf when not in use. */
173  svn_stringbuf_t *cdata_accum;
174
175  /* Are we inside a resource element? */
176  svn_boolean_t in_resource;
177  /* Valid if in_resource is true. */
178  svn_stringbuf_t *current_wcprop_path;
179  svn_boolean_t is_switch;
180
181  /* Named target, or NULL if none.  For example, in 'svn up wc/foo',
182     this is "wc/foo", but in 'svn up' it is "".
183
184     The target helps us determine whether a response received from
185     the server should be acted on.  Take 'svn up wc/foo': the server
186     may send back a new vsn-rsrc-url wcprop for 'wc' (because the
187     report had to be anchored there just in case the update deletes
188     wc/foo).  While this is correct behavior for the server, the
189     client should ignore the new wcprop, because the client knows
190     it's not really updating the top level directory. */
191  const char *target;
192
193  /* Whether the server should try to send copyfrom arguments. */
194  svn_boolean_t send_copyfrom_args;
195
196  /* Use an intermediate tmpfile for the REPORT response. */
197  svn_boolean_t spool_response;
198
199  /* A modern server will understand our "send-all" attribute on the
200     update report request, and will put a "send-all" attribute on
201     its response.  If we see that attribute, we set this to true,
202     otherwise, it stays false (i.e., it's not a modern server). */
203  svn_boolean_t receiving_all;
204
205  /* Hash mapping 'const char *' paths -> 'const char *' lock tokens. */
206  apr_hash_t *lock_tokens;
207
208} report_baton_t;
209
210static const svn_ra_neon__xml_elm_t report_elements[] =
211{
212  { SVN_XML_NAMESPACE, "update-report", ELEM_update_report, 0 },
213  { SVN_XML_NAMESPACE, "resource-walk", ELEM_resource_walk, 0 },
214  { SVN_XML_NAMESPACE, "resource", ELEM_resource, 0 },
215  { SVN_XML_NAMESPACE, "target-revision", ELEM_target_revision, 0 },
216  { SVN_XML_NAMESPACE, "open-directory", ELEM_open_directory, 0 },
217  { SVN_XML_NAMESPACE, "add-directory", ELEM_add_directory, 0 },
218  { SVN_XML_NAMESPACE, "absent-directory", ELEM_absent_directory, 0 },
219  { SVN_XML_NAMESPACE, "open-file", ELEM_open_file, 0 },
220  { SVN_XML_NAMESPACE, "add-file", ELEM_add_file, 0 },
221  { SVN_XML_NAMESPACE, "txdelta", ELEM_txdelta, 0 },
222  { SVN_XML_NAMESPACE, "absent-file", ELEM_absent_file, 0 },
223  { SVN_XML_NAMESPACE, "delete-entry", ELEM_delete_entry, 0 },
224  { SVN_XML_NAMESPACE, "fetch-props", ELEM_fetch_props, 0 },
225  { SVN_XML_NAMESPACE, "set-prop", ELEM_set_prop, 0 },
226  { SVN_XML_NAMESPACE, "remove-prop", ELEM_remove_prop, 0 },
227  { SVN_XML_NAMESPACE, "fetch-file", ELEM_fetch_file, 0 },
228  { SVN_XML_NAMESPACE, "prop", ELEM_SVN_prop, 0 },
229  { SVN_DAV_PROP_NS_DAV, "repository-uuid",
230    ELEM_repository_uuid, SVN_RA_NEON__XML_CDATA },
231
232  { SVN_DAV_PROP_NS_DAV, "md5-checksum", ELEM_md5_checksum,
233    SVN_RA_NEON__XML_CDATA },
234
235  { "DAV:", "version-name", ELEM_version_name, SVN_RA_NEON__XML_CDATA },
236  { "DAV:", SVN_DAV__CREATIONDATE, ELEM_creationdate, SVN_RA_NEON__XML_CDATA },
237  { "DAV:", "creator-displayname", ELEM_creator_displayname,
238     SVN_RA_NEON__XML_CDATA },
239
240  { "DAV:", "checked-in", ELEM_checked_in, 0 },
241  { "DAV:", "href", ELEM_href, SVN_RA_NEON__XML_CDATA },
242
243  { NULL }
244};
245
246static svn_error_t *simple_store_vsn_url(const char *vsn_url,
247                                         void *baton,
248                                         prop_setter_t setter,
249                                         apr_pool_t *pool)
250{
251  /* store the version URL as a property */
252  SVN_ERR_W((*setter)(baton, SVN_RA_NEON__LP_VSN_URL,
253                      svn_string_create(vsn_url, pool), pool),
254            _("Could not save the URL of the version resource"));
255
256  return NULL;
257}
258
259static svn_error_t *get_delta_base(const char **delta_base,
260                                   const char *relpath,
261                                   svn_ra_get_wc_prop_func_t get_wc_prop,
262                                   void *cb_baton,
263                                   apr_pool_t *pool)
264{
265  const svn_string_t *value;
266
267  if (relpath == NULL || get_wc_prop == NULL)
268    {
269      *delta_base = NULL;
270      return SVN_NO_ERROR;
271    }
272
273  SVN_ERR((*get_wc_prop)(cb_baton, relpath, SVN_RA_NEON__LP_VSN_URL,
274                         &value, pool));
275
276  *delta_base = value ? value->data : NULL;
277  return SVN_NO_ERROR;
278}
279
280/* helper func which maps certain DAV: properties to svn:wc:
281   properties.  Used during checkouts and updates.  */
282static svn_error_t *set_special_wc_prop(const char *key,
283                                        const svn_string_t *val,
284                                        prop_setter_t setter,
285                                        void *baton,
286                                        apr_pool_t *pool)
287{
288  const char *name = NULL;
289
290  if (strcmp(key, SVN_RA_NEON__PROP_VERSION_NAME) == 0)
291    name = SVN_PROP_ENTRY_COMMITTED_REV;
292  else if (strcmp(key, SVN_RA_NEON__PROP_CREATIONDATE) == 0)
293    name = SVN_PROP_ENTRY_COMMITTED_DATE;
294  else if (strcmp(key, SVN_RA_NEON__PROP_CREATOR_DISPLAYNAME) == 0)
295    name = SVN_PROP_ENTRY_LAST_AUTHOR;
296  else if (strcmp(key, SVN_RA_NEON__PROP_REPOSITORY_UUID) == 0)
297    name = SVN_PROP_ENTRY_UUID;
298
299  /* If we got a name we care about it, call the setter function. */
300  if (name)
301    SVN_ERR((*setter)(baton, name, val, pool));
302
303  return SVN_NO_ERROR;
304}
305
306
307static svn_error_t *add_props(apr_hash_t *props,
308                              prop_setter_t setter,
309                              void *baton,
310                              apr_pool_t *pool)
311{
312  apr_hash_index_t *hi;
313
314  for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
315    {
316      const void *vkey;
317      void *vval;
318      const char *key;
319      const svn_string_t *val;
320
321      apr_hash_this(hi, &vkey, NULL, &vval);
322      key = vkey;
323      val = vval;
324
325#define NSLEN (sizeof(SVN_DAV_PROP_NS_CUSTOM) - 1)
326      if (strncmp(key, SVN_DAV_PROP_NS_CUSTOM, NSLEN) == 0)
327        {
328          /* for props in the 'custom' namespace, we strip the
329             namespace and just use whatever name the user gave the
330             property. */
331          SVN_ERR((*setter)(baton, key + NSLEN, val, pool));
332          continue;
333        }
334#undef NSLEN
335
336#define NSLEN (sizeof(SVN_DAV_PROP_NS_SVN) - 1)
337      if (strncmp(key, SVN_DAV_PROP_NS_SVN, NSLEN) == 0)
338        {
339          /* This property is an 'svn:' prop, recognized by client, or
340             server, or both.  Convert the URI namespace into normal
341             'svn:' prefix again before pushing it at the wc. */
342          SVN_ERR((*setter)(baton, apr_pstrcat(pool, SVN_PROP_PREFIX,
343                                               key + NSLEN, NULL),
344                            val, pool));
345        }
346#undef NSLEN
347
348      else
349        {
350          /* If we get here, then we have a property that is neither
351             in the 'custom' space, nor in the 'svn' space.  So it
352             must be either in the 'network' space or 'DAV:' space.
353             The following routine converts a handful of DAV: props
354             into 'svn:wc:' or 'svn:entry:' props that libsvn_wc
355             wants. */
356          SVN_ERR(set_special_wc_prop(key, val, setter, baton, pool));
357        }
358    }
359  return SVN_NO_ERROR;
360}
361
362
363static svn_error_t *custom_get_request(svn_ra_neon__session_t *ras,
364                                       const char *url,
365                                       const char *relpath,
366                                       svn_ra_neon__block_reader reader,
367                                       void *subctx,
368                                       svn_ra_get_wc_prop_func_t get_wc_prop,
369                                       void *cb_baton,
370                                       svn_boolean_t use_base,
371                                       apr_pool_t *pool)
372{
373  custom_get_ctx_t cgc = { 0 };
374  const char *delta_base;
375  svn_ra_neon__request_t *request;
376  svn_error_t *err;
377
378  if (use_base)
379    {
380      /* See if we can get a version URL for this resource. This will
381         refer to what we already have in the working copy, thus we
382         can get a diff against this particular resource. */
383      SVN_ERR(get_delta_base(&delta_base, relpath,
384                             get_wc_prop, cb_baton, pool));
385    }
386  else
387    {
388      delta_base = NULL;
389    }
390
391  request = svn_ra_neon__request_create(ras, "GET", url, pool);
392
393  if (delta_base)
394    {
395      /* The HTTP delta draft uses an If-None-Match header holding an
396         entity tag corresponding to the copy we have. It is much more
397         natural for us to use a version URL to specify what we have.
398         Thus, we want to use the If: header to specify the URL. But
399         mod_dav sees all "State-token" items as lock tokens. When we
400         get mod_dav updated and the backend APIs expanded, then we
401         can switch to using the If: header. For now, use a custom
402         header to specify the version resource to use as the base. */
403      ne_add_request_header(request->ne_req,
404                            SVN_DAV_DELTA_BASE_HEADER, delta_base);
405    }
406
407  svn_ra_neon__add_response_body_reader(request, ne_accept_2xx, reader, &cgc);
408
409  /* complete initialization of the body reading context */
410  cgc.req = request;
411  cgc.subctx = subctx;
412
413  /* run the request */
414  err = svn_ra_neon__request_dispatch(NULL, request, NULL, NULL,
415                                      200 /* OK */,
416                                      226 /* IM Used */,
417                                      pool);
418  svn_ra_neon__request_destroy(request);
419
420  /* The request runner raises internal errors before Neon errors,
421     pass a returned error to our callers */
422
423  return err;
424}
425
426/* This implements the svn_ra_neon__block_reader() callback interface. */
427static svn_error_t *
428fetch_file_reader(void *userdata, const char *buf, size_t len)
429{
430  custom_get_ctx_t *cgc = userdata;
431  file_read_ctx_t *frc = cgc->subctx;
432
433  if (len == 0)
434    {
435      /* file is complete. */
436      return 0;
437    }
438
439  if (!cgc->checked_type)
440    {
441      ne_content_type ctype = { 0 };
442      int rv = ne_get_content_type(cgc->req->ne_req, &ctype);
443
444      if (rv != 0)
445        return
446          svn_error_create(SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS, NULL,
447                           _("Could not get content-type from response"));
448
449      /* Neon guarantees non-NULL values when rv==0 */
450      if (!strcmp(ctype.type, "application")
451          && !strcmp(ctype.subtype, "vnd.svn-svndiff"))
452        {
453          /* we are receiving an svndiff. set things up. */
454          frc->stream = svn_txdelta_parse_svndiff(frc->handler,
455                                                  frc->handler_baton,
456                                                  TRUE,
457                                                  frc->pool);
458        }
459
460      if (ctype.value)
461        free(ctype.value);
462
463      cgc->checked_type = 1;
464    }
465
466  if (frc->stream == NULL)
467    {
468      /* receiving plain text. construct a window for it. */
469
470      svn_txdelta_window_t window = { 0 };
471      svn_txdelta_op_t op;
472      svn_string_t data;
473
474      data.data = buf;
475      data.len = len;
476
477      op.action_code = svn_txdelta_new;
478      op.offset = 0;
479      op.length = len;
480
481      window.tview_len = len;       /* result will be this long */
482      window.num_ops = 1;
483      window.ops = &op;
484      window.new_data = &data;
485
486      /* We can't really do anything useful if we get an error here.  Pass
487         it off to someone who can. */
488      SVN_RA_NEON__REQ_ERR
489        (cgc->req,
490         (*frc->handler)(&window, frc->handler_baton));
491    }
492  else
493    {
494      /* receiving svndiff. feed it to the svndiff parser. */
495
496      apr_size_t written = len;
497
498      SVN_ERR(svn_stream_write(frc->stream, buf, &written));
499
500      /* ### the svndiff stream parser does not obey svn_stream semantics
501         ### in its write handler. it does not output the number of bytes
502         ### consumed by the handler. specifically, it may decrement the
503         ### number by 4 for the header, then never touch it again. that
504         ### makes it appear like an incomplete write.
505         ### disable this check for now. the svndiff parser actually does
506         ### consume all bytes, all the time.
507      */
508#if 0
509      if (written != len && cgc->err == NULL)
510        cgc->err = svn_error_createf(SVN_ERR_INCOMPLETE_DATA, NULL,
511                                     "Unable to completely write the svndiff "
512                                     "data to the parser stream "
513                                     "(wrote " APR_SIZE_T_FMT " "
514                                     "of " APR_SIZE_T_FMT " bytes)",
515                                     written, len);
516#endif
517    }
518
519  return 0;
520}
521
522static svn_error_t *simple_fetch_file(svn_ra_neon__session_t *ras,
523                                      const char *url,
524                                      const char *relpath,
525                                      svn_boolean_t text_deltas,
526                                      void *file_baton,
527                                      const char *base_checksum,
528                                      const svn_delta_editor_t *editor,
529                                      svn_ra_get_wc_prop_func_t get_wc_prop,
530                                      void *cb_baton,
531                                      apr_pool_t *pool)
532{
533  file_read_ctx_t frc = { 0 };
534
535  SVN_ERR_W((*editor->apply_textdelta)(file_baton,
536                                       base_checksum,
537                                       pool,
538                                       &frc.handler,
539                                       &frc.handler_baton),
540            _("Could not save file"));
541
542  /* Only bother with text-deltas if our caller cares. */
543  if (! text_deltas)
544    return (*frc.handler)(NULL, frc.handler_baton);
545
546  frc.pool = pool;
547
548  SVN_ERR(custom_get_request(ras, url, relpath,
549                             fetch_file_reader, &frc,
550                             get_wc_prop, cb_baton,
551                             TRUE, pool));
552
553  /* close the handler, since the file reading completed successfully. */
554  return (*frc.handler)(NULL, frc.handler_baton);
555}
556
557/* Helper for svn_ra_neon__get_file.  This implements
558   the svn_ra_neon__block_reader() callback interface. */
559static svn_error_t *
560get_file_reader(void *userdata, const char *buf, size_t len)
561{
562  custom_get_ctx_t *cgc = userdata;
563
564  /* The stream we want to push data at. */
565  file_write_ctx_t *fwc = cgc->subctx;
566  svn_stream_t *stream = fwc->stream;
567
568  if (fwc->do_checksum)
569    SVN_ERR(svn_checksum_update(fwc->checksum_ctx, buf, len));
570
571  /* Write however many bytes were passed in by neon. */
572  return svn_stream_write(stream, buf, &len);
573}
574
575
576/* minor helper for svn_ra_neon__get_file, of type prop_setter_t */
577static svn_error_t *
578add_prop_to_hash(void *baton,
579                 const char *name,
580                 const svn_string_t *value,
581                 apr_pool_t *pool)
582{
583  apr_hash_t *ht = (apr_hash_t *) baton;
584  apr_hash_set(ht, name, APR_HASH_KEY_STRING, value);
585  return SVN_NO_ERROR;
586}
587
588
589/* Helper for svn_ra_neon__get_file(), svn_ra_neon__get_dir(), and
590   svn_ra_neon__rev_proplist().
591
592   Loop over the properties in RSRC->propset, examining namespaces and
593   such to filter Subversion, custom, etc. properties.
594
595   User-visible props get added to the PROPS hash (alloced in POOL).
596
597   If ADD_ENTRY_PROPS is true, then "special" working copy entry-props
598   are added to the hash by set_special_wc_prop().
599*/
600static svn_error_t *
601filter_props(apr_hash_t *props,
602             svn_ra_neon__resource_t *rsrc,
603             svn_boolean_t add_entry_props,
604             apr_pool_t *pool)
605{
606  apr_hash_index_t *hi;
607
608  for (hi = apr_hash_first(pool, rsrc->propset); hi; hi = apr_hash_next(hi))
609    {
610      const void *key;
611      const char *name;
612      void *val;
613      const svn_string_t *value;
614
615      apr_hash_this(hi, &key, NULL, &val);
616      name = key;
617      value = svn_string_dup(val, pool);
618
619      /* If the property is in the 'custom' namespace, then it's a
620         normal user-controlled property coming from the fs.  Just
621         strip off this prefix and add to the hash. */
622#define NSLEN (sizeof(SVN_DAV_PROP_NS_CUSTOM) - 1)
623      if (strncmp(name, SVN_DAV_PROP_NS_CUSTOM, NSLEN) == 0)
624        {
625          apr_hash_set(props, name + NSLEN, APR_HASH_KEY_STRING, value);
626          continue;
627        }
628#undef NSLEN
629
630      /* If the property is in the 'svn' namespace, then it's a
631         normal user-controlled property coming from the fs.  Just
632         strip off the URI prefix, add an 'svn:', and add to the hash. */
633#define NSLEN (sizeof(SVN_DAV_PROP_NS_SVN) - 1)
634      if (strncmp(name, SVN_DAV_PROP_NS_SVN, NSLEN) == 0)
635        {
636          apr_hash_set(props,
637                       apr_pstrcat(pool, SVN_PROP_PREFIX, name + NSLEN, NULL),
638                       APR_HASH_KEY_STRING,
639                       value);
640          continue;
641        }
642#undef NSLEN
643      else if (strcmp(name, SVN_RA_NEON__PROP_CHECKED_IN) == 0)
644        {
645          /* For files, we currently only have one 'wc' prop. */
646          apr_hash_set(props, SVN_RA_NEON__LP_VSN_URL,
647                       APR_HASH_KEY_STRING, value);
648        }
649      else
650        {
651          /* If it's one of the 'entry' props, this func will
652             recognize the DAV: name & add it to the hash mapped to a
653             new name recognized by libsvn_wc. */
654          if (add_entry_props)
655            SVN_ERR(set_special_wc_prop(name, value, add_prop_to_hash,
656                                        props, pool));
657        }
658    }
659
660  return SVN_NO_ERROR;
661}
662
663svn_error_t *svn_ra_neon__get_file(svn_ra_session_t *session,
664                                   const char *path,
665                                   svn_revnum_t revision,
666                                   svn_stream_t *stream,
667                                   svn_revnum_t *fetched_rev,
668                                   apr_hash_t **props,
669                                   apr_pool_t *pool)
670{
671  svn_ra_neon__resource_t *rsrc;
672  const char *final_url;
673  svn_ra_neon__session_t *ras = session->priv;
674  const char *url = svn_path_url_add_component(ras->url->data, path, pool);
675
676  /* If the revision is invalid (head), then we're done.  Just fetch
677     the public URL, because that will always get HEAD. */
678  if ((! SVN_IS_VALID_REVNUM(revision)) && (fetched_rev == NULL))
679    final_url = url;
680
681  /* If the revision is something specific, we need to create a bc_url. */
682  else
683    {
684      svn_revnum_t got_rev;
685      svn_string_t bc_url, bc_relative;
686
687      SVN_ERR(svn_ra_neon__get_baseline_info(NULL,
688                                             &bc_url, &bc_relative,
689                                             &got_rev,
690                                             ras,
691                                             url, revision,
692                                             pool));
693      final_url = svn_path_url_add_component(bc_url.data,
694                                             bc_relative.data,
695                                             pool);
696      if (fetched_rev != NULL)
697        *fetched_rev = got_rev;
698    }
699
700  if (stream)
701    {
702      svn_error_t *err;
703      const svn_string_t *expected_checksum = NULL;
704      file_write_ctx_t fwc;
705      ne_propname md5_propname = { SVN_DAV_PROP_NS_DAV, "md5-checksum" };
706      const char *hex_digest;
707
708      /* Only request a checksum if we're getting the file contents. */
709      /* ### We should arrange for the checksum to be returned in the
710         svn_ra_neon__get_baseline_info() call above; that will prevent
711         the extra round trip, at least some of the time. */
712      err = svn_ra_neon__get_one_prop(&expected_checksum,
713                                      ras,
714                                      final_url,
715                                      NULL,
716                                      &md5_propname,
717                                      pool);
718
719      /* Older servers don't serve this prop, but that's okay. */
720      /* ### temporary hack for 0.17. if the server doesn't have the prop,
721         ### then __get_one_prop returns an empty string. deal with it.  */
722      if ((err && (err->apr_err == SVN_ERR_RA_DAV_PROPS_NOT_FOUND))
723          || (expected_checksum && (*expected_checksum->data == '\0')))
724        {
725          fwc.do_checksum = FALSE;
726          svn_error_clear(err);
727        }
728      else if (err)
729        return err;
730      else
731        fwc.do_checksum = TRUE;
732
733      fwc.stream = stream;
734
735      if (fwc.do_checksum)
736        fwc.checksum_ctx = svn_checksum_ctx_create(svn_checksum_md5, pool);
737
738      /* Fetch the file, shoving it at the provided stream. */
739      SVN_ERR(custom_get_request(ras, final_url, path,
740                                 get_file_reader, &fwc,
741                                 ras->callbacks->get_wc_prop,
742                                 ras->callback_baton,
743                                 FALSE, pool));
744
745      if (fwc.do_checksum)
746        {
747          svn_checksum_t *checksum;
748
749          SVN_ERR(svn_checksum_final(&checksum, fwc.checksum_ctx, pool));
750          hex_digest = svn_checksum_to_cstring_display(checksum, pool);
751
752          if (strcmp(hex_digest, expected_checksum->data) != 0)
753            return svn_error_createf
754              (SVN_ERR_CHECKSUM_MISMATCH, NULL,
755               _("Checksum mismatch for '%s':\n"
756                 "   expected checksum:  %s\n"
757                 "   actual checksum:    %s\n"),
758               path, expected_checksum->data, hex_digest);
759        }
760    }
761
762  if (props)
763    {
764      SVN_ERR(svn_ra_neon__get_props_resource(&rsrc, ras, final_url,
765                                              NULL, NULL /* all props */,
766                                              pool));
767      *props = apr_hash_make(pool);
768      SVN_ERR(filter_props(*props, rsrc, TRUE, pool));
769    }
770
771  return SVN_NO_ERROR;
772}
773
774/* The property we need to fetch to see whether the server we are
775   connected to supports the deadprop-count property. */
776static const ne_propname deadprop_count_support_props[] =
777{
778  { SVN_DAV_PROP_NS_DAV, "deadprop-count" },
779  { NULL }
780};
781
782svn_error_t *svn_ra_neon__get_dir(svn_ra_session_t *session,
783                                  apr_hash_t **dirents,
784                                  svn_revnum_t *fetched_rev,
785                                  apr_hash_t **props,
786                                  const char *path,
787                                  svn_revnum_t revision,
788                                  apr_uint32_t dirent_fields,
789                                  apr_pool_t *pool)
790{
791  svn_ra_neon__resource_t *rsrc;
792  apr_hash_index_t *hi;
793  apr_hash_t *resources;
794  const char *final_url;
795  apr_size_t final_url_n_components;
796  svn_boolean_t supports_deadprop_count;
797  svn_ra_neon__session_t *ras = session->priv;
798  const char *url = svn_path_url_add_component(ras->url->data, path, pool);
799
800  /* If the revision is invalid (head), then we're done.  Just fetch
801     the public URL, because that will always get HEAD. */
802  if ((! SVN_IS_VALID_REVNUM(revision)) && (fetched_rev == NULL))
803    final_url = url;
804
805  /* If the revision is something specific, we need to create a bc_url. */
806  else
807    {
808      svn_revnum_t got_rev;
809      svn_string_t bc_url, bc_relative;
810
811      SVN_ERR(svn_ra_neon__get_baseline_info(NULL,
812                                             &bc_url, &bc_relative,
813                                             &got_rev,
814                                             ras,
815                                             url, revision,
816                                             pool));
817      final_url = svn_path_url_add_component(bc_url.data,
818                                             bc_relative.data,
819                                             pool);
820      if (fetched_rev != NULL)
821        *fetched_rev = got_rev;
822    }
823
824  /* For issue 2151: See if we are dealing with a server that
825     understands the deadprop-count property.  If it doesn't, we'll
826     need to do an allprop PROPFIND.  If it does, we'll execute a more
827     targeted PROPFIND. */
828  {
829    const svn_string_t *deadprop_count;
830
831    SVN_ERR(svn_ra_neon__get_props_resource(&rsrc, ras,
832                                            final_url, NULL,
833                                            deadprop_count_support_props,
834                                            pool));
835
836    deadprop_count = apr_hash_get(rsrc->propset,
837                                  SVN_RA_NEON__PROP_DEADPROP_COUNT,
838                                  APR_HASH_KEY_STRING);
839
840    supports_deadprop_count = (deadprop_count != NULL);
841  }
842
843  if (dirents)
844    {
845      ne_propname *which_props;
846
847      /* if we didn't ask for the has_props field, we can get individual
848         properties. */
849      if ((SVN_DIRENT_HAS_PROPS & dirent_fields) == 0
850          || supports_deadprop_count)
851        {
852          int num_props = 1; /* start with one for the final NULL */
853
854          if (dirent_fields & SVN_DIRENT_KIND)
855            ++num_props;
856
857          if (dirent_fields & SVN_DIRENT_SIZE)
858            ++num_props;
859
860          if (dirent_fields & SVN_DIRENT_HAS_PROPS)
861            ++num_props;
862
863          if (dirent_fields & SVN_DIRENT_CREATED_REV)
864            ++num_props;
865
866          if (dirent_fields & SVN_DIRENT_TIME)
867            ++num_props;
868
869          if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
870            ++num_props;
871
872          which_props = apr_pcalloc(pool, num_props * sizeof(ne_propname));
873
874          --num_props; /* damn zero based arrays... */
875
876          /* first, null out the end... */
877          which_props[num_props].nspace = NULL;
878          which_props[num_props--].name = NULL;
879
880          /* Now, go through and fill in the ones we care about, moving along
881             the array as we go. */
882
883          if (dirent_fields & SVN_DIRENT_KIND)
884            {
885              which_props[num_props].nspace = "DAV:";
886              which_props[num_props--].name = "resourcetype";
887            }
888
889          if (dirent_fields & SVN_DIRENT_SIZE)
890            {
891              which_props[num_props].nspace = "DAV:";
892              which_props[num_props--].name = "getcontentlength";
893            }
894
895          if (dirent_fields & SVN_DIRENT_HAS_PROPS)
896            {
897              which_props[num_props].nspace = SVN_DAV_PROP_NS_DAV;
898              which_props[num_props--].name = "deadprop-count";
899            }
900
901          if (dirent_fields & SVN_DIRENT_CREATED_REV)
902            {
903              which_props[num_props].nspace = "DAV:";
904              which_props[num_props--].name = "version-name";
905            }
906
907          if (dirent_fields & SVN_DIRENT_TIME)
908            {
909              which_props[num_props].nspace = "DAV:";
910              which_props[num_props--].name = SVN_DAV__CREATIONDATE;
911            }
912
913          if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
914            {
915              which_props[num_props].nspace = "DAV:";
916              which_props[num_props--].name = "creator-displayname";
917            }
918
919          SVN_ERR_ASSERT(num_props == -1);
920        }
921      else
922        {
923          /* get all props, since we need them all to do has_props */
924          which_props = NULL;
925        }
926
927      /* Just like Nautilus, Cadaver, or any other browser, we do a
928         PROPFIND on the directory of depth 1. */
929      SVN_ERR(svn_ra_neon__get_props(&resources, ras,
930                                     final_url, SVN_RA_NEON__DEPTH_ONE,
931                                     NULL, which_props, pool));
932
933      /* Count the number of path components in final_url. */
934      final_url_n_components = svn_path_component_count(final_url);
935
936      /* Now we have a hash that maps a bunch of url children to resource
937         objects.  Each resource object contains the properties of the
938         child.   Parse these resources into svn_dirent_t structs. */
939      *dirents = apr_hash_make(pool);
940      for (hi = apr_hash_first(pool, resources);
941           hi;
942           hi = apr_hash_next(hi))
943        {
944          const void *key;
945          void *val;
946          const char *childname;
947          svn_ra_neon__resource_t *resource;
948          const svn_string_t *propval;
949          apr_hash_index_t *h;
950          svn_dirent_t *entry;
951
952          apr_hash_this(hi, &key, NULL, &val);
953          childname =  key;
954          resource = val;
955
956          /* Skip the effective '.' entry that comes back from
957             SVN_RA_NEON__DEPTH_ONE. The children must have one more
958             component then final_url.
959             Note that we can't just strcmp the URLs because of URL encoding
960             differences (i.e. %3c vs. %3C etc.) */
961          if (svn_path_component_count(childname) == final_url_n_components)
962            continue;
963
964          entry = apr_pcalloc(pool, sizeof(*entry));
965
966          if (dirent_fields & SVN_DIRENT_KIND)
967            {
968              /* node kind */
969              entry->kind = resource->is_collection ? svn_node_dir
970                                                    : svn_node_file;
971            }
972
973          if (dirent_fields & SVN_DIRENT_SIZE)
974            {
975              /* size */
976              propval = apr_hash_get(resource->propset,
977                                     SVN_RA_NEON__PROP_GETCONTENTLENGTH,
978                                     APR_HASH_KEY_STRING);
979              if (propval == NULL)
980                entry->size = 0;
981              else
982                entry->size = svn__atoui64(propval->data);
983            }
984
985          if (dirent_fields & SVN_DIRENT_HAS_PROPS)
986            {
987              /* Does this resource contain any 'svn' or 'custom'
988                 properties (e.g. ones actually created and set by the
989                 user)? */
990              if (supports_deadprop_count)
991                {
992                  propval = apr_hash_get(resource->propset,
993                                         SVN_RA_NEON__PROP_DEADPROP_COUNT,
994                                         APR_HASH_KEY_STRING);
995
996                  if (propval == NULL)
997                    {
998                      /* we thought that the server supported the
999                         deadprop-count property.  apparently not. */
1000                      return svn_error_create(SVN_ERR_INCOMPLETE_DATA, NULL,
1001                                              _("Server response missing the "
1002                                                "expected deadprop-count "
1003                                                "property"));
1004                    }
1005                  else
1006                    {
1007                      apr_int64_t prop_count = svn__atoui64(propval->data);
1008                      entry->has_props = (prop_count > 0);
1009                    }
1010                }
1011              else
1012                {
1013                   /* The server doesn't support the deadprop_count prop,
1014                      fallback */
1015                  for (h = apr_hash_first(pool, resource->propset);
1016                       h; h = apr_hash_next(h))
1017                    {
1018                      const void *kkey;
1019                      apr_hash_this(h, &kkey, NULL, NULL);
1020
1021                      if (strncmp((const char *) kkey, SVN_DAV_PROP_NS_CUSTOM,
1022                                  sizeof(SVN_DAV_PROP_NS_CUSTOM) - 1) == 0
1023                          || strncmp((const char *) kkey, SVN_DAV_PROP_NS_SVN,
1024                                     sizeof(SVN_DAV_PROP_NS_SVN) - 1) == 0)
1025                        entry->has_props = TRUE;
1026                    }
1027                }
1028            }
1029
1030          if (dirent_fields & SVN_DIRENT_CREATED_REV)
1031            {
1032              /* created_rev & friends */
1033              propval = apr_hash_get(resource->propset,
1034                                     SVN_RA_NEON__PROP_VERSION_NAME,
1035                                     APR_HASH_KEY_STRING);
1036              if (propval != NULL)
1037                entry->created_rev = SVN_STR_TO_REV(propval->data);
1038            }
1039
1040          if (dirent_fields & SVN_DIRENT_TIME)
1041            {
1042              propval = apr_hash_get(resource->propset,
1043                                     SVN_RA_NEON__PROP_CREATIONDATE,
1044                                     APR_HASH_KEY_STRING);
1045              if (propval != NULL)
1046                SVN_ERR(svn_time_from_cstring(&(entry->time),
1047                                              propval->data, pool));
1048            }
1049
1050          if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
1051            {
1052              propval = apr_hash_get(resource->propset,
1053                                     SVN_RA_NEON__PROP_CREATOR_DISPLAYNAME,
1054                                     APR_HASH_KEY_STRING);
1055              if (propval != NULL)
1056                entry->last_author = propval->data;
1057            }
1058
1059          apr_hash_set(*dirents,
1060                       svn_path_uri_decode(svn_path_basename(childname, pool),
1061                                           pool),
1062                       APR_HASH_KEY_STRING, entry);
1063        }
1064    }
1065
1066  if (props)
1067    {
1068      SVN_ERR(svn_ra_neon__get_props_resource(&rsrc, ras, final_url,
1069                                              NULL, NULL /* all props */,
1070                                              pool));
1071
1072      *props = apr_hash_make(pool);
1073      SVN_ERR(filter_props(*props, rsrc, TRUE, pool));
1074    }
1075
1076  return SVN_NO_ERROR;
1077}
1078
1079
1080/* ------------------------------------------------------------------------- */
1081
1082svn_error_t *svn_ra_neon__get_latest_revnum(svn_ra_session_t *session,
1083                                            svn_revnum_t *latest_revnum,
1084                                            apr_pool_t *pool)
1085{
1086  svn_ra_neon__session_t *ras = session->priv;
1087
1088  /* ### should we perform an OPTIONS to validate the server we're about
1089     ### to talk to? */
1090
1091  /* we don't need any of the baseline URLs and stuff, but this does
1092     give us the latest revision number */
1093  SVN_ERR(svn_ra_neon__get_baseline_info(NULL, NULL, NULL, latest_revnum,
1094                                         ras, ras->root.path,
1095                                         SVN_INVALID_REVNUM, pool));
1096
1097  SVN_ERR(svn_ra_neon__maybe_store_auth_info(ras, pool));
1098
1099  return NULL;
1100}
1101
1102/* ------------------------------------------------------------------------- */
1103
1104
1105svn_error_t *svn_ra_neon__change_rev_prop(svn_ra_session_t *session,
1106                                          svn_revnum_t rev,
1107                                          const char *name,
1108                                          const svn_string_t *value,
1109                                          apr_pool_t *pool)
1110{
1111  svn_ra_neon__session_t *ras = session->priv;
1112  svn_ra_neon__resource_t *baseline;
1113  svn_error_t *err;
1114  apr_hash_t *prop_changes = NULL;
1115  apr_array_header_t *prop_deletes = NULL;
1116  static const ne_propname wanted_props[] =
1117    {
1118      { "DAV:", "auto-version" },
1119      { NULL }
1120    };
1121
1122  /* Main objective: do a PROPPATCH (allprops) on a baseline object */
1123
1124  /* ### A Word From Our Sponsor:  see issue #916.
1125
1126     Be it heretofore known that this Subversion behavior is
1127     officially in violation of WebDAV/DeltaV.  DeltaV has *no*
1128     concept of unversioned properties, anywhere.  If you proppatch
1129     something, some new version of *something* is created.
1130
1131     In particular, we've decided that a 'baseline' maps to an svn
1132     revision; if we attempted to proppatch a baseline, a *normal*
1133     DeltaV server would do an auto-checkout, patch the working
1134     baseline, auto-checkin, and create a new baseline.  But
1135     mod_dav_svn just changes the baseline destructively.
1136  */
1137
1138  /* Get the baseline resource. */
1139  SVN_ERR(svn_ra_neon__get_baseline_props(NULL, &baseline,
1140                                          ras,
1141                                          ras->url->data,
1142                                          rev,
1143                                          wanted_props, /* DAV:auto-version */
1144                                          pool));
1145
1146  /* ### TODO: if we got back some value for the baseline's
1147         'DAV:auto-version' property, interpret it.  We *don't* want
1148         to attempt the PROPPATCH if the deltaV server is going to do
1149         auto-versioning and create a new baseline! */
1150
1151  if (value)
1152    {
1153      prop_changes = apr_hash_make(pool);
1154      apr_hash_set(prop_changes, name, APR_HASH_KEY_STRING, value);
1155    }
1156  else
1157    {
1158      prop_deletes = apr_array_make(pool, 1, sizeof(const char *));
1159      APR_ARRAY_PUSH(prop_deletes, const char *) = name;
1160    }
1161
1162  err = svn_ra_neon__do_proppatch(ras, baseline->url, prop_changes,
1163                                  prop_deletes, NULL, pool);
1164  if (err)
1165    return
1166      svn_error_create
1167      (SVN_ERR_RA_DAV_REQUEST_FAILED, err,
1168       _("DAV request failed; it's possible that the repository's "
1169         "pre-revprop-change hook either failed or is non-existent"));
1170
1171  return SVN_NO_ERROR;
1172}
1173
1174
1175svn_error_t *svn_ra_neon__rev_proplist(svn_ra_session_t *session,
1176                                       svn_revnum_t rev,
1177                                       apr_hash_t **props,
1178                                       apr_pool_t *pool)
1179{
1180  svn_ra_neon__session_t *ras = session->priv;
1181  svn_ra_neon__resource_t *baseline;
1182
1183  *props = apr_hash_make(pool);
1184
1185  /* Main objective: do a PROPFIND (allprops) on a baseline object */
1186  SVN_ERR(svn_ra_neon__get_baseline_props(NULL, &baseline,
1187                                          ras,
1188                                          ras->url->data,
1189                                          rev,
1190                                          NULL, /* get ALL properties */
1191                                          pool));
1192
1193  /* Build a new property hash, based on the one in the baseline
1194     resource.  In particular, convert the xml-property-namespaces
1195     into ones that the client understands.  Strip away the DAV:
1196     liveprops as well. */
1197  return filter_props(*props, baseline, FALSE, pool);
1198}
1199
1200
1201svn_error_t *svn_ra_neon__rev_prop(svn_ra_session_t *session,
1202                                   svn_revnum_t rev,
1203                                   const char *name,
1204                                   svn_string_t **value,
1205                                   apr_pool_t *pool)
1206{
1207  apr_hash_t *props;
1208
1209  /* We just call svn_ra_neon__rev_proplist() and filter its results here
1210   * because sending the property name to the server may create an error
1211   * if it has a colon in its name.  While more costly this allows DAV
1212   * clients to still gain access to all the allowed property names.
1213   * See Issue #1807 for more details. */
1214  SVN_ERR(svn_ra_neon__rev_proplist(session, rev, &props, pool));
1215
1216  *value = apr_hash_get(props, name, APR_HASH_KEY_STRING);
1217
1218  return SVN_NO_ERROR;
1219}
1220
1221
1222
1223
1224/* -------------------------------------------------------------------------
1225**
1226** UPDATE HANDLING
1227**
1228** ### docco...
1229**
1230** DTD of the update report:
1231** ### open/add file/dir. first child is always checked-in/href (vsn_url).
1232** ### next are subdir elems, possibly fetch-file, then fetch-prop.
1233*/
1234
1235/* Determine whether we're receiving the expected XML response.
1236   Return CHILD when interested in receiving the child's contents
1237   or one of SVN_RA_NEON__XML_INVALID and SVN_RA_NEON__XML_DECLINE
1238   when respectively this is the incorrect response or
1239   the element (and its children) are uninteresting */
1240static int validate_element(svn_ra_neon__xml_elmid parent,
1241                            svn_ra_neon__xml_elmid child)
1242{
1243  /* We're being very strict with the validity of XML elements here. If
1244     something exists that we don't know about, then we might not update
1245     the client properly. We also make various assumptions in the element
1246     processing functions, and the strong validation enables those
1247     assumptions. */
1248
1249  switch (parent)
1250    {
1251    case ELEM_root:
1252      if (child == ELEM_update_report)
1253        return child;
1254      else
1255        return SVN_RA_NEON__XML_INVALID;
1256
1257    case ELEM_update_report:
1258      if (child == ELEM_target_revision
1259          || child == ELEM_open_directory
1260          || child == ELEM_resource_walk)
1261        return child;
1262      else
1263        return SVN_RA_NEON__XML_INVALID;
1264
1265    case ELEM_resource_walk:
1266      if (child == ELEM_resource)
1267        return child;
1268      else
1269        return SVN_RA_NEON__XML_INVALID;
1270
1271    case ELEM_resource:
1272      if (child == ELEM_checked_in)
1273        return child;
1274      else
1275        return SVN_RA_NEON__XML_INVALID;
1276
1277    case ELEM_open_directory:
1278      if (child == ELEM_absent_directory
1279          || child == ELEM_open_directory
1280          || child == ELEM_add_directory
1281          || child == ELEM_absent_file
1282          || child == ELEM_open_file
1283          || child == ELEM_add_file
1284          || child == ELEM_fetch_props
1285          || child == ELEM_set_prop
1286          || child == ELEM_remove_prop
1287          || child == ELEM_delete_entry
1288          || child == ELEM_SVN_prop
1289          || child == ELEM_checked_in)
1290        return child;
1291      else
1292        return SVN_RA_NEON__XML_INVALID;
1293
1294    case ELEM_add_directory:
1295      if (child == ELEM_absent_directory
1296          || child == ELEM_add_directory
1297          || child == ELEM_absent_file
1298          || child == ELEM_add_file
1299          || child == ELEM_remove_prop
1300          || child == ELEM_set_prop
1301          || child == ELEM_SVN_prop
1302          || child == ELEM_checked_in)
1303        return child;
1304      else
1305        return SVN_RA_NEON__XML_INVALID;
1306
1307    case ELEM_open_file:
1308      if (child == ELEM_checked_in
1309          || child == ELEM_fetch_file
1310          || child == ELEM_SVN_prop
1311          || child == ELEM_txdelta
1312          || child == ELEM_fetch_props
1313          || child == ELEM_set_prop
1314          || child == ELEM_remove_prop)
1315        return child;
1316      else
1317        return SVN_RA_NEON__XML_INVALID;
1318
1319    case ELEM_add_file:
1320      if (child == ELEM_checked_in
1321          || child == ELEM_txdelta
1322          || child == ELEM_set_prop
1323          || child == ELEM_remove_prop
1324          || child == ELEM_SVN_prop)
1325        return child;
1326      else
1327        return SVN_RA_NEON__XML_INVALID;
1328
1329    case ELEM_checked_in:
1330      if (child == ELEM_href)
1331        return child;
1332      else
1333        return SVN_RA_NEON__XML_INVALID;
1334
1335    case ELEM_set_prop:
1336      /* Prop name is an attribute, prop value is CDATA, so no child elts. */
1337      return child;
1338
1339    case ELEM_SVN_prop:
1340      /*      if (child == ELEM_version_name
1341              || child == ELEM_creationdate
1342              || child == ELEM_creator_displayname
1343              || child == ELEM_md5_checksum
1344              || child == ELEM_repository_uuid
1345              || child == ELEM_remove_prop)
1346              return child;
1347              else
1348              return SVN_RA_NEON__XML_DECLINE;
1349      */
1350      /* ### TODO:  someday uncomment the block above, and make the
1351         else clause return NE_XML_IGNORE.  But first, neon needs to
1352         define that value.  :-) */
1353      return child;
1354
1355    default:
1356      return SVN_RA_NEON__XML_DECLINE;
1357    }
1358
1359  /* NOTREACHED */
1360}
1361
1362static void push_dir(report_baton_t *rb,
1363                     void *baton,
1364                     svn_stringbuf_t *pathbuf,
1365                     apr_pool_t *pool)
1366{
1367  dir_item_t *di = apr_array_push(rb->dirs);
1368
1369  memset(di, 0, sizeof(*di));
1370  di->baton = baton;
1371  di->pathbuf = pathbuf;
1372  di->pool = pool;
1373}
1374
1375/* This implements the `ne_xml_startelm_cb' prototype. */
1376static svn_error_t *
1377start_element(int *elem, void *userdata, int parent, const char *nspace,
1378              const char *elt_name, const char **atts)
1379{
1380  report_baton_t *rb = userdata;
1381  const char *att;
1382  svn_revnum_t base;
1383  const char *name;
1384  const char *bc_url;
1385  svn_stringbuf_t *cpath = NULL;
1386  svn_revnum_t crev = SVN_INVALID_REVNUM;
1387  dir_item_t *parent_dir;
1388  void *new_dir_baton;
1389  svn_stringbuf_t *pathbuf;
1390  apr_pool_t *subpool;
1391  const char *base_checksum = NULL;
1392  const svn_ra_neon__xml_elm_t *elm;
1393
1394  elm = svn_ra_neon__lookup_xml_elem(report_elements, nspace, elt_name);
1395  *elem = elm ? validate_element(parent, elm->id) : SVN_RA_NEON__XML_DECLINE;
1396  if (*elem < 1) /* not a valid element */
1397    return SVN_NO_ERROR;
1398
1399  switch (elm->id)
1400    {
1401    case ELEM_update_report:
1402      att = svn_xml_get_attr_value("send-all", atts);
1403      if (att && (strcmp(att, "true") == 0))
1404        rb->receiving_all = TRUE;
1405      break;
1406
1407    case ELEM_target_revision:
1408      att = svn_xml_get_attr_value("rev", atts);
1409      if (att == NULL)
1410        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1411                                 _("Missing rev attr in target-revision"
1412                                   " element"));
1413      SVN_ERR((*rb->editor->set_target_revision)(rb->edit_baton,
1414                                                 SVN_STR_TO_REV(att),
1415                                                 rb->pool));
1416      break;
1417
1418    case ELEM_absent_directory:
1419      name = svn_xml_get_attr_value("name", atts);
1420      if (name == NULL)
1421        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1422                                 _("Missing name attr in absent-directory"
1423                                   " element"));
1424
1425      parent_dir = &TOP_DIR(rb);
1426      pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, parent_dir->pool);
1427      svn_path_add_component(pathbuf, name);
1428
1429      SVN_ERR((*rb->editor->absent_directory)(pathbuf->data,
1430                                              parent_dir->baton,
1431                                              parent_dir->pool));
1432      break;
1433
1434    case ELEM_absent_file:
1435      name = svn_xml_get_attr_value("name", atts);
1436      if (name == NULL)
1437        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1438                                 _("Missing name attr in absent-file"
1439                                   " element"));
1440      parent_dir = &TOP_DIR(rb);
1441      pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, parent_dir->pool);
1442      svn_path_add_component(pathbuf, name);
1443
1444      SVN_ERR((*rb->editor->absent_file)(pathbuf->data,
1445                                         parent_dir->baton,
1446                                         parent_dir->pool));
1447      break;
1448
1449    case ELEM_resource:
1450      att = svn_xml_get_attr_value("path", atts);
1451      if (att == NULL)
1452        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1453                                 _("Missing path attr in resource element"));
1454      svn_stringbuf_set(rb->current_wcprop_path, att);
1455      rb->in_resource = TRUE;
1456      break;
1457
1458    case ELEM_open_directory:
1459      att = svn_xml_get_attr_value("rev", atts);
1460      if (att == NULL)
1461        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1462                                 _("Missing rev attr in open-directory"
1463                                   " element"));
1464      base = SVN_STR_TO_REV(att);
1465
1466      if (DIR_DEPTH(rb) == 0)
1467        {
1468          /* pathbuf has to live for the whole edit! */
1469          pathbuf = svn_stringbuf_create("", rb->pool);
1470
1471          /* During switch operations, we need to invalidate the
1472             tree's version resource URLs in case something goes
1473             wrong. */
1474          if (rb->is_switch && rb->ras->callbacks->invalidate_wc_props)
1475            {
1476              SVN_ERR(rb->ras->callbacks->invalidate_wc_props
1477                      (rb->ras->callback_baton, rb->target,
1478                       SVN_RA_NEON__LP_VSN_URL, rb->pool));
1479            }
1480
1481          subpool = svn_pool_create(rb->pool);
1482          SVN_ERR((*rb->editor->open_root)(rb->edit_baton, base,
1483                                           subpool, &new_dir_baton));
1484
1485          /* push the new baton onto the directory baton stack */
1486          push_dir(rb, new_dir_baton, pathbuf, subpool);
1487        }
1488      else
1489        {
1490          name = svn_xml_get_attr_value("name", atts);
1491          if (name == NULL)
1492            return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1493                                     _("Missing name attr in open-directory"
1494                                       " element"));
1495          svn_stringbuf_set(rb->namestr, name);
1496
1497          parent_dir = &TOP_DIR(rb);
1498          subpool = svn_pool_create(parent_dir->pool);
1499
1500          pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, subpool);
1501          svn_path_add_component(pathbuf, rb->namestr->data);
1502
1503          SVN_ERR((*rb->editor->open_directory)(pathbuf->data,
1504                                                parent_dir->baton, base,
1505                                                subpool,
1506                                                &new_dir_baton));
1507
1508          /* push the new baton onto the directory baton stack */
1509          push_dir(rb, new_dir_baton, pathbuf, subpool);
1510        }
1511
1512      /* Property fetching is NOT implied in replacement. */
1513      TOP_DIR(rb).fetch_props = FALSE;
1514      break;
1515
1516    case ELEM_add_directory:
1517      name = svn_xml_get_attr_value("name", atts);
1518      if (name == NULL)
1519        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1520                                 _("Missing name attr in add-directory"
1521                                   " element"));
1522      svn_stringbuf_set(rb->namestr, name);
1523
1524      att = svn_xml_get_attr_value("copyfrom-path", atts);
1525      if (att != NULL)
1526        {
1527          cpath = rb->cpathstr;
1528          svn_stringbuf_set(cpath, att);
1529
1530          att = svn_xml_get_attr_value("copyfrom-rev", atts);
1531          if (att == NULL)
1532            return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1533                                     _("Missing copyfrom-rev attr in"
1534                                       " add-directory element"));
1535          crev = SVN_STR_TO_REV(att);
1536        }
1537
1538      parent_dir = &TOP_DIR(rb);
1539      subpool = svn_pool_create(parent_dir->pool);
1540
1541      pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, subpool);
1542      svn_path_add_component(pathbuf, rb->namestr->data);
1543
1544      SVN_ERR((*rb->editor->add_directory)(pathbuf->data, parent_dir->baton,
1545                                           cpath ? cpath->data : NULL,
1546                                           crev, subpool,
1547                                           &new_dir_baton));
1548
1549      /* push the new baton onto the directory baton stack */
1550      push_dir(rb, new_dir_baton, pathbuf, subpool);
1551
1552      /* Property fetching is implied in addition.  This flag is only
1553         for parsing old-style reports; it is ignored when talking to
1554         a modern server. */
1555      TOP_DIR(rb).fetch_props = TRUE;
1556
1557      bc_url = svn_xml_get_attr_value("bc-url", atts);
1558
1559      /* In non-modern report responses, we're just told to fetch the
1560         props later.  In that case, we can at least do a pre-emptive
1561         depth-1 propfind on the directory right now; this prevents
1562         individual propfinds on added-files later on, thus reducing
1563         the number of network turnarounds (though not by as much as
1564         simply getting a modern report response!).  */
1565      if ((! rb->receiving_all) && bc_url)
1566        {
1567          apr_hash_t *bc_children;
1568          SVN_ERR(svn_ra_neon__get_props(&bc_children,
1569                                         rb->ras,
1570                                         bc_url,
1571                                         SVN_RA_NEON__DEPTH_ONE,
1572                                         NULL, NULL /* allprops */,
1573                                         TOP_DIR(rb).pool));
1574
1575          /* re-index the results into a more usable hash.
1576             bc_children maps bc-url->resource_t, but we want the
1577             dir_item_t's hash to map vc-url->resource_t. */
1578          if (bc_children)
1579            {
1580              apr_hash_index_t *hi;
1581              TOP_DIR(rb).children = apr_hash_make(TOP_DIR(rb).pool);
1582
1583              for (hi = apr_hash_first(TOP_DIR(rb).pool, bc_children);
1584                   hi; hi = apr_hash_next(hi))
1585                {
1586                  void *val;
1587                  svn_ra_neon__resource_t *rsrc;
1588                  const svn_string_t *vc_url;
1589
1590                  apr_hash_this(hi, NULL, NULL, &val);
1591                  rsrc = val;
1592
1593                  vc_url = apr_hash_get(rsrc->propset,
1594                                        SVN_RA_NEON__PROP_CHECKED_IN,
1595                                        APR_HASH_KEY_STRING);
1596                  if (vc_url)
1597                    apr_hash_set(TOP_DIR(rb).children,
1598                                 vc_url->data, vc_url->len,
1599                                 rsrc->propset);
1600                }
1601            }
1602        }
1603
1604      break;
1605
1606    case ELEM_open_file:
1607      att = svn_xml_get_attr_value("rev", atts);
1608      if (att == NULL)
1609        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1610                                 _("Missing rev attr in open-file"
1611                                   " element"));
1612      base = SVN_STR_TO_REV(att);
1613
1614      name = svn_xml_get_attr_value("name", atts);
1615      if (name == NULL)
1616        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1617                                 _("Missing name attr in open-file"
1618                                   " element"));
1619      svn_stringbuf_set(rb->namestr, name);
1620
1621      parent_dir = &TOP_DIR(rb);
1622      rb->file_pool = svn_pool_create(parent_dir->pool);
1623      rb->result_checksum = NULL;
1624
1625      /* Add this file's name into the directory's path buffer. It will be
1626         removed in end_element() */
1627      svn_path_add_component(parent_dir->pathbuf, rb->namestr->data);
1628
1629      SVN_ERR((*rb->editor->open_file)(parent_dir->pathbuf->data,
1630                                       parent_dir->baton, base,
1631                                       rb->file_pool,
1632                                       &rb->file_baton));
1633
1634      /* Property fetching is NOT implied in replacement. */
1635      rb->fetch_props = FALSE;
1636
1637      break;
1638
1639    case ELEM_add_file:
1640      name = svn_xml_get_attr_value("name", atts);
1641      if (name == NULL)
1642        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1643                                 _("Missing name attr in add-file"
1644                                   " element"));
1645      svn_stringbuf_set(rb->namestr, name);
1646
1647      att = svn_xml_get_attr_value("copyfrom-path", atts);
1648      if (att != NULL)
1649        {
1650          cpath = rb->cpathstr;
1651          svn_stringbuf_set(cpath, att);
1652
1653          att = svn_xml_get_attr_value("copyfrom-rev", atts);
1654          if (att == NULL)
1655            return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1656                                     _("Missing copyfrom-rev attr in add-file"
1657                                       " element"));
1658          crev = SVN_STR_TO_REV(att);
1659        }
1660
1661      parent_dir = &TOP_DIR(rb);
1662      rb->file_pool = svn_pool_create(parent_dir->pool);
1663      rb->result_checksum = NULL;
1664
1665      /* Add this file's name into the directory's path buffer. It will be
1666         removed in end_element() */
1667      svn_path_add_component(parent_dir->pathbuf, rb->namestr->data);
1668
1669      SVN_ERR((*rb->editor->add_file)(parent_dir->pathbuf->data,
1670                                      parent_dir->baton,
1671                                      cpath ? cpath->data : NULL,
1672                                      crev, rb->file_pool,
1673                                      &rb->file_baton));
1674
1675      /* Property fetching is implied in addition.  This flag is only
1676         for parsing old-style reports; it is ignored when talking to
1677         a modern server. */
1678      rb->fetch_props = TRUE;
1679
1680      break;
1681
1682    case ELEM_txdelta:
1683      /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in addition to
1684         <fetch-file>s and such) when *not* in "send-all" mode.  As a
1685         client, we're smart enough to know that's wrong, so when not
1686         in "receiving-all" mode, we'll ignore <txdelta> tags
1687         altogether. */
1688      if (! rb->receiving_all)
1689        break;
1690
1691      SVN_ERR((*rb->editor->apply_textdelta)(rb->file_baton,
1692                                             NULL, /* ### base_checksum */
1693                                             rb->file_pool,
1694                                             &(rb->whandler),
1695                                             &(rb->whandler_baton)));
1696
1697      rb->svndiff_decoder = svn_txdelta_parse_svndiff(rb->whandler,
1698                                                      rb->whandler_baton,
1699                                                      TRUE, rb->file_pool);
1700
1701      rb->base64_decoder = svn_base64_decode(rb->svndiff_decoder,
1702                                             rb->file_pool);
1703      break;
1704
1705    case ELEM_set_prop:
1706      {
1707        const char *encoding = svn_xml_get_attr_value("encoding", atts);
1708        name = svn_xml_get_attr_value("name", atts);
1709        if (name == NULL)
1710          return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1711                                   _("Missing name attr in set-prop element"));
1712        svn_stringbuf_set(rb->namestr, name);
1713        if (encoding)
1714          svn_stringbuf_set(rb->encoding, encoding);
1715        else
1716          svn_stringbuf_setempty(rb->encoding);
1717      }
1718
1719      break;
1720
1721    case ELEM_remove_prop:
1722      name = svn_xml_get_attr_value("name", atts);
1723      if (name == NULL)
1724        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1725                                 _("Missing name attr in remove-prop element"));
1726      svn_stringbuf_set(rb->namestr, name);
1727
1728      /* Removing a prop.  */
1729      if (rb->file_baton == NULL)
1730        SVN_ERR(rb->editor->change_dir_prop(TOP_DIR(rb).baton,
1731                                            rb->namestr->data,
1732                                            NULL, TOP_DIR(rb).pool));
1733      else
1734        SVN_ERR(rb->editor->change_file_prop(rb->file_baton, rb->namestr->data,
1735                                             NULL, rb->file_pool));
1736      break;
1737
1738    case ELEM_fetch_props:
1739      if (!rb->fetch_content)
1740        {
1741          /* If this is just a status check, the specifics of the
1742             property change are uninteresting.  Simply call our
1743             editor function with bogus data so it registers a
1744             property mod. */
1745          svn_stringbuf_set(rb->namestr, SVN_PROP_PREFIX "BOGOSITY");
1746
1747          if (rb->file_baton == NULL)
1748            SVN_ERR(rb->editor->change_dir_prop(TOP_DIR(rb).baton,
1749                                                rb->namestr->data,
1750                                                NULL, TOP_DIR(rb).pool));
1751          else
1752            SVN_ERR(rb->editor->change_file_prop(rb->file_baton,
1753                                                 rb->namestr->data,
1754                                                 NULL, rb->file_pool));
1755        }
1756      else
1757        {
1758          /* Note that we need to fetch props for this... */
1759          if (rb->file_baton == NULL)
1760            TOP_DIR(rb).fetch_props = TRUE; /* ...directory. */
1761          else
1762            rb->fetch_props = TRUE; /* ...file. */
1763        }
1764      break;
1765
1766    case ELEM_fetch_file:
1767      base_checksum = svn_xml_get_attr_value("base-checksum", atts);
1768      rb->result_checksum = NULL;
1769
1770      /* If we aren't expecting to see the file contents inline, we
1771         should ignore server requests to fetch them.
1772
1773         ### This conditional was added to counteract a little bug in
1774         Subversion 0.33.0's mod_dav_svn whereby both the <txdelta>
1775         and <fetch-file> tags were being transmitted.  Someday, we
1776         should remove the conditional again to give the server the
1777         option of sending inline text-deltas for some files while
1778         telling the client to fetch others. */
1779      if (! rb->receiving_all)
1780        {
1781          /* assert: rb->href->len > 0 */
1782          SVN_ERR(simple_fetch_file(rb->ras,
1783                                    rb->href->data,
1784                                    TOP_DIR(rb).pathbuf->data,
1785                                    rb->fetch_content,
1786                                    rb->file_baton,
1787                                    base_checksum,
1788                                    rb->editor,
1789                                    rb->ras->callbacks->get_wc_prop,
1790                                    rb->ras->callback_baton,
1791                                    rb->file_pool));
1792        }
1793      break;
1794
1795    case ELEM_delete_entry:
1796      name = svn_xml_get_attr_value("name", atts);
1797      if (name == NULL)
1798        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1799                                 _("Missing name attr in delete-entry"
1800                                   " element"));
1801      svn_stringbuf_set(rb->namestr, name);
1802
1803      parent_dir = &TOP_DIR(rb);
1804
1805      /* Pool use is a little non-standard here.  When lots of items in the
1806         same directory get deleted each one will trigger a call to
1807         editor->delete_entry, but we don't have a pool that readily fits
1808         the usual iteration pattern and so memory use could grow without
1809         bound (see issue 1635).  To avoid such growth we use a temporary,
1810         short-lived, pool. */
1811      subpool = svn_pool_create(parent_dir->pool);
1812
1813      pathbuf = svn_stringbuf_dup(parent_dir->pathbuf, subpool);
1814      svn_path_add_component(pathbuf, rb->namestr->data);
1815
1816      SVN_ERR((*rb->editor->delete_entry)(pathbuf->data,
1817                                          SVN_INVALID_REVNUM,
1818                                          TOP_DIR(rb).baton,
1819                                          subpool));
1820      svn_pool_destroy(subpool);
1821      break;
1822
1823    default:
1824      break;
1825    }
1826
1827  *elem = elm->id;
1828
1829  return SVN_NO_ERROR;
1830}
1831
1832
1833static svn_error_t *
1834add_node_props(report_baton_t *rb, apr_pool_t *pool)
1835{
1836  svn_ra_neon__resource_t *rsrc = NULL;
1837  apr_hash_t *props = NULL;
1838
1839  /* Do nothing if parsing a send-all-style report, because the properties
1840     already come inline. */
1841  if (rb->receiving_all)
1842    return SVN_NO_ERROR;
1843
1844  /* Do nothing (else) if we aren't fetching content.  */
1845  if (!rb->fetch_content)
1846    return SVN_NO_ERROR;
1847
1848  if (rb->file_baton)
1849    {
1850      const char *lock_token = apr_hash_get(rb->lock_tokens,
1851                                            TOP_DIR(rb).pathbuf->data,
1852                                            TOP_DIR(rb).pathbuf->len);
1853
1854      /* Workaround a buglet in older versions of mod_dav_svn in that it
1855         will not send remove-prop in the update report when a lock
1856         property disappears when send-all is false.  */
1857      if (lock_token)
1858        {
1859          svn_lock_t *lock;
1860          SVN_ERR(svn_ra_neon__get_lock_internal(rb->ras, &lock,
1861                                                 TOP_DIR(rb).pathbuf->data,
1862                                                 pool));
1863          if (! (lock
1864                 && lock->token
1865                 && (strcmp(lock->token, lock_token) == 0)))
1866            SVN_ERR(rb->editor->change_file_prop(rb->file_baton,
1867                                                 SVN_PROP_ENTRY_LOCK_TOKEN,
1868                                                 NULL, pool));
1869        }
1870
1871      /* If we aren't supposed to be fetching props, don't. */
1872      if (! rb->fetch_props)
1873        return SVN_NO_ERROR;
1874
1875      /* Check to see if your parent directory already has your props
1876         stored, possibly from a depth-1 propfind.   Otherwise just do
1877         a propfind directly on the file url. */
1878      if ( ! ((TOP_DIR(rb).children)
1879              && (props = apr_hash_get(TOP_DIR(rb).children, rb->href->data,
1880                                       APR_HASH_KEY_STRING))) )
1881        {
1882          SVN_ERR(svn_ra_neon__get_props_resource(&rsrc,
1883                                                  rb->ras,
1884                                                  rb->href->data,
1885                                                  NULL,
1886                                                  NULL,
1887                                                  pool));
1888          props = rsrc->propset;
1889        }
1890
1891      SVN_ERR(add_props(props,
1892                        rb->editor->change_file_prop,
1893                        rb->file_baton,
1894                        pool));
1895    }
1896  else
1897    {
1898      if (! TOP_DIR(rb).fetch_props)
1899        return SVN_NO_ERROR;
1900
1901      /* Check to see if your props are already stored, possibly from
1902         a depth-1 propfind.  Otherwise just do a propfind directly on
1903         the directory url. */
1904      if ( ! ((TOP_DIR(rb).children)
1905              && (props = apr_hash_get(TOP_DIR(rb).children,
1906                                       TOP_DIR(rb).vsn_url,
1907                                       APR_HASH_KEY_STRING))) )
1908        {
1909          SVN_ERR(svn_ra_neon__get_props_resource(&rsrc,
1910                                                  rb->ras,
1911                                                  TOP_DIR(rb).vsn_url,
1912                                                  NULL,
1913                                                  NULL,
1914                                                  pool));
1915          props = rsrc->propset;
1916        }
1917
1918      SVN_ERR(add_props(props,
1919                        rb->editor->change_dir_prop,
1920                        TOP_DIR(rb).baton,
1921                        pool));
1922    }
1923
1924  return SVN_NO_ERROR;
1925}
1926
1927/* This implements the `svn_ra_neon__cdata_cb_t' prototype. */
1928static svn_error_t *
1929cdata_handler(void *userdata, int state, const char *cdata, size_t len)
1930{
1931  report_baton_t *rb = userdata;
1932
1933  switch(state)
1934    {
1935    case ELEM_href:
1936    case ELEM_set_prop:
1937    case ELEM_md5_checksum:
1938    case ELEM_version_name:
1939    case ELEM_creationdate:
1940    case ELEM_creator_displayname:
1941      svn_stringbuf_appendbytes(rb->cdata_accum, cdata, len);
1942      break;
1943
1944    case ELEM_txdelta:
1945      {
1946        apr_size_t nlen = len;
1947
1948        /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in addition to
1949           <fetch-file>s and such) when *not* in "send-all" mode.  As a
1950           client, we're smart enough to know that's wrong, so when not
1951           in "receiving-all" mode, we'll ignore <txdelta> tags
1952           altogether. */
1953        if (! rb->receiving_all)
1954          break;
1955
1956        SVN_ERR(svn_stream_write(rb->base64_decoder, cdata, &nlen));
1957        if (nlen != len)
1958          {
1959            /* Short write without associated error?  "Can't happen." */
1960            return svn_error_createf(SVN_ERR_STREAM_UNEXPECTED_EOF, NULL,
1961                                     _("Error writing to '%s': unexpected EOF"),
1962                                     svn_path_local_style(rb->namestr->data,
1963                                                          rb->pool));
1964          }
1965      }
1966      break;
1967    }
1968
1969  return 0; /* no error */
1970}
1971
1972/* This implements the `svn_ra_neon_endelm_cb_t' prototype. */
1973static svn_error_t *
1974end_element(void *userdata, int state,
1975            const char *nspace, const char *elt_name)
1976{
1977  report_baton_t *rb = userdata;
1978  const svn_delta_editor_t *editor = rb->editor;
1979  const svn_ra_neon__xml_elm_t *elm;
1980
1981  elm = svn_ra_neon__lookup_xml_elem(report_elements, nspace, elt_name);
1982
1983  if (elm == NULL)
1984    return SVN_NO_ERROR;
1985
1986  switch (elm->id)
1987    {
1988    case ELEM_resource:
1989      rb->in_resource = FALSE;
1990      break;
1991
1992    case ELEM_update_report:
1993      /* End of report; close up the editor. */
1994      SVN_ERR((*rb->editor->close_edit)(rb->edit_baton, rb->pool));
1995      rb->edit_baton = NULL;
1996      break;
1997
1998    case ELEM_add_directory:
1999    case ELEM_open_directory:
2000
2001      /* fetch node props, unless this is the top dir and the real
2002         target of the operation is not the top dir. */
2003      if (! ((DIR_DEPTH(rb) == 1) && *rb->target))
2004        SVN_ERR(add_node_props(rb, TOP_DIR(rb).pool));
2005
2006      /* Close the directory on top of the stack, and pop it.  Also,
2007         destroy the subpool used exclusive by this directory and its
2008         children.  */
2009      SVN_ERR((*rb->editor->close_directory)(TOP_DIR(rb).baton,
2010                                             TOP_DIR(rb).pool));
2011      svn_pool_destroy(TOP_DIR(rb).pool);
2012      apr_array_pop(rb->dirs);
2013      break;
2014
2015    case ELEM_add_file:
2016      /* we wait until the close element to do the work. this allows us to
2017         retrieve the href before fetching. */
2018
2019      /* fetch file */
2020      if (! rb->receiving_all)
2021        {
2022          SVN_ERR(simple_fetch_file(rb->ras,
2023                                    rb->href->data,
2024                                    TOP_DIR(rb).pathbuf->data,
2025                                    rb->fetch_content,
2026                                    rb->file_baton,
2027                                    NULL,  /* no base checksum in an add */
2028                                    rb->editor,
2029                                    rb->ras->callbacks->get_wc_prop,
2030                                    rb->ras->callback_baton,
2031                                    rb->file_pool));
2032
2033          /* fetch node props as necessary. */
2034          SVN_ERR(add_node_props(rb, rb->file_pool));
2035        }
2036
2037      /* close the file and mark that we are no longer operating on a file */
2038      SVN_ERR((*rb->editor->close_file)(rb->file_baton,
2039                                        rb->result_checksum,
2040                                        rb->file_pool));
2041      rb->file_baton = NULL;
2042
2043      /* Yank this file out of the directory's path buffer. */
2044      svn_path_remove_component(TOP_DIR(rb).pathbuf);
2045      svn_pool_destroy(rb->file_pool);
2046      rb->file_pool = NULL;
2047      break;
2048
2049    case ELEM_txdelta:
2050      /* Pre 1.2, mod_dav_svn was using <txdelta> tags (in addition to
2051         <fetch-file>s and such) when *not* in "send-all" mode.  As a
2052         client, we're smart enough to know that's wrong, so when not
2053         in "receiving-all" mode, we'll ignore <txdelta> tags
2054         altogether. */
2055      if (! rb->receiving_all)
2056        break;
2057
2058      SVN_ERR(svn_stream_close(rb->base64_decoder));
2059      rb->whandler = NULL;
2060      rb->whandler_baton = NULL;
2061      rb->svndiff_decoder = NULL;
2062      rb->base64_decoder = NULL;
2063      break;
2064
2065    case ELEM_open_file:
2066      /* fetch node props as necessary. */
2067      SVN_ERR(add_node_props(rb, rb->file_pool));
2068
2069      /* close the file and mark that we are no longer operating on a file */
2070      SVN_ERR((*rb->editor->close_file)(rb->file_baton,
2071                                        rb->result_checksum,
2072                                        rb->file_pool));
2073      rb->file_baton = NULL;
2074
2075      /* Yank this file out of the directory's path buffer. */
2076      svn_path_remove_component(TOP_DIR(rb).pathbuf);
2077      svn_pool_destroy(rb->file_pool);
2078      rb->file_pool = NULL;
2079      break;
2080
2081    case ELEM_set_prop:
2082      {
2083        svn_string_t decoded_value;
2084        const svn_string_t *decoded_value_p;
2085        apr_pool_t *pool;
2086
2087        if (rb->file_baton)
2088          pool = rb->file_pool;
2089        else
2090          pool = TOP_DIR(rb).pool;
2091
2092        decoded_value.data = rb->cdata_accum->data;
2093        decoded_value.len = rb->cdata_accum->len;
2094
2095        /* Determine the cdata encoding, if any. */
2096        if (svn_stringbuf_isempty(rb->encoding))
2097          {
2098            decoded_value_p = &decoded_value;
2099          }
2100        else if (strcmp(rb->encoding->data, "base64") == 0)
2101          {
2102            decoded_value_p = svn_base64_decode_string(&decoded_value, pool);
2103            svn_stringbuf_setempty(rb->encoding);
2104          }
2105        else
2106          {
2107            return svn_error_createf(SVN_ERR_XML_UNKNOWN_ENCODING, NULL,
2108                                     _("Unknown XML encoding: '%s'"),
2109                                     rb->encoding->data);
2110          }
2111
2112        /* Set the prop. */
2113        if (rb->file_baton)
2114          {
2115            SVN_ERR(rb->editor->change_file_prop(rb->file_baton,
2116                                                 rb->namestr->data,
2117                                                 decoded_value_p, pool));
2118          }
2119        else
2120          {
2121            SVN_ERR(rb->editor->change_dir_prop(TOP_DIR(rb).baton,
2122                                                rb->namestr->data,
2123                                                decoded_value_p, pool));
2124          }
2125      }
2126
2127      svn_stringbuf_setempty(rb->cdata_accum);
2128      break;
2129
2130    case ELEM_href:
2131      if (rb->fetch_content)
2132        /* record the href that we just found */
2133        SVN_ERR(svn_ra_neon__copy_href(rb->href, rb->cdata_accum->data,
2134                                       rb->scratch_pool));
2135
2136      svn_stringbuf_setempty(rb->cdata_accum);
2137
2138      /* do nothing if we aren't fetching content. */
2139      if (!rb->fetch_content)
2140        break;
2141
2142      /* if we're within a <resource> tag, then just call the generic
2143         RA set_wcprop_callback directly;  no need to use the
2144         update-editor.  */
2145      if (rb->in_resource)
2146        {
2147          svn_string_t href_val;
2148          href_val.data = rb->href->data;
2149          href_val.len = rb->href->len;
2150
2151          if (rb->ras->callbacks->set_wc_prop != NULL)
2152            SVN_ERR(rb->ras->callbacks->set_wc_prop
2153                    (rb->ras->callback_baton,
2154                     rb->current_wcprop_path->data,
2155                     SVN_RA_NEON__LP_VSN_URL,
2156                     &href_val,
2157                     rb->scratch_pool));
2158          svn_pool_clear(rb->scratch_pool);
2159        }
2160      /* else we're setting a wcprop in the context of an editor drive. */
2161      else if (rb->file_baton == NULL)
2162        {
2163          /* Update the wcprop here, unless this is the top directory
2164             and the real target of this operation is something other
2165             than the top directory. */
2166          if (! ((DIR_DEPTH(rb) == 1) && *rb->target))
2167            {
2168              SVN_ERR(simple_store_vsn_url(rb->href->data, TOP_DIR(rb).baton,
2169                                           rb->editor->change_dir_prop,
2170                                           TOP_DIR(rb).pool));
2171
2172              /* save away the URL in case a fetch-props arrives after all of
2173                 the subdir processing. we will need this copy of the URL to
2174                 fetch the properties (i.e. rb->href will be toast by then). */
2175              TOP_DIR(rb).vsn_url = apr_pmemdup(TOP_DIR(rb).pool,
2176                                                rb->href->data,
2177                                                rb->href->len + 1);
2178            }
2179        }
2180      else
2181        {
2182          SVN_ERR(simple_store_vsn_url(rb->href->data, rb->file_baton,
2183                                       rb->editor->change_file_prop,
2184                                       rb->file_pool));
2185        }
2186      break;
2187
2188    case ELEM_md5_checksum:
2189      /* We only care about file checksums. */
2190      if (rb->file_baton)
2191        {
2192          rb->result_checksum = apr_pstrdup(rb->file_pool,
2193                                            rb->cdata_accum->data);
2194        }
2195      svn_stringbuf_setempty(rb->cdata_accum);
2196      break;
2197
2198    case ELEM_version_name:
2199    case ELEM_creationdate:
2200    case ELEM_creator_displayname:
2201      {
2202        /* The name of the xml tag is the property that we want to set. */
2203        apr_pool_t *pool =
2204          rb->file_baton ? rb->file_pool : TOP_DIR(rb).pool;
2205        prop_setter_t setter =
2206          rb->file_baton ? editor->change_file_prop : editor->change_dir_prop;
2207        const char *name = apr_pstrcat(pool, elm->nspace, elm->name, NULL);
2208        void *baton = rb->file_baton ? rb->file_baton : TOP_DIR(rb).baton;
2209        svn_string_t valstr;
2210
2211        valstr.data = rb->cdata_accum->data;
2212        valstr.len = rb->cdata_accum->len;
2213        SVN_ERR(set_special_wc_prop(name, &valstr, setter, baton, pool));
2214        svn_stringbuf_setempty(rb->cdata_accum);
2215      }
2216      break;
2217
2218    default:
2219      break;
2220    }
2221
2222  return SVN_NO_ERROR;
2223}
2224
2225
2226static svn_error_t * reporter_set_path(void *report_baton,
2227                                       const char *path,
2228                                       svn_revnum_t revision,
2229                                       svn_depth_t depth,
2230                                       svn_boolean_t start_empty,
2231                                       const char *lock_token,
2232                                       apr_pool_t *pool)
2233{
2234  report_baton_t *rb = report_baton;
2235  const char *entry;
2236  svn_stringbuf_t *qpath = NULL;
2237  const char *tokenstring = "";
2238  const char *depthstring = apr_psprintf(pool, "depth=\"%s\"",
2239                                         svn_depth_to_word(depth));
2240
2241  if (lock_token)
2242    {
2243      tokenstring = apr_psprintf(pool, "lock-token=\"%s\"", lock_token);
2244      apr_hash_set(rb->lock_tokens,
2245                   apr_pstrdup(apr_hash_pool_get(rb->lock_tokens), path),
2246                   APR_HASH_KEY_STRING,
2247                   apr_pstrdup(apr_hash_pool_get(rb->lock_tokens), lock_token));
2248    }
2249
2250  svn_xml_escape_cdata_cstring(&qpath, path, pool);
2251  if (start_empty)
2252    entry = apr_psprintf(pool,
2253                         "<S:entry rev=\"%ld\" %s %s"
2254                         " start-empty=\"true\">%s</S:entry>" DEBUG_CR,
2255                         revision, depthstring, tokenstring, qpath->data);
2256  else
2257    entry = apr_psprintf(pool,
2258                         "<S:entry rev=\"%ld\" %s %s>"
2259                         "%s</S:entry>" DEBUG_CR,
2260                         revision, depthstring, tokenstring, qpath->data);
2261
2262  return svn_io_file_write_full(rb->tmpfile, entry, strlen(entry), NULL, pool);
2263}
2264
2265
2266static svn_error_t * reporter_link_path(void *report_baton,
2267                                        const char *path,
2268                                        const char *url,
2269                                        svn_revnum_t revision,
2270                                        svn_depth_t depth,
2271                                        svn_boolean_t start_empty,
2272                                        const char *lock_token,
2273                                        apr_pool_t *pool)
2274{
2275  report_baton_t *rb = report_baton;
2276  const char *entry;
2277  svn_stringbuf_t *qpath = NULL, *qlinkpath = NULL;
2278  svn_string_t bc_relative;
2279  const char *tokenstring = "";
2280  const char *depthstring = apr_psprintf(pool, "depth=\"%s\"",
2281                                         svn_depth_to_word(depth));
2282
2283  if (lock_token)
2284    {
2285      tokenstring = apr_psprintf(pool, "lock-token=\"%s\"", lock_token);
2286      apr_hash_set(rb->lock_tokens,
2287                   apr_pstrdup(apr_hash_pool_get(rb->lock_tokens), path),
2288                   APR_HASH_KEY_STRING,
2289                   apr_pstrdup(apr_hash_pool_get(rb->lock_tokens), lock_token));
2290    }
2291
2292  /* Convert the copyfrom_* url/rev "public" pair into a Baseline
2293     Collection (BC) URL that represents the revision -- and a
2294     relative path under that BC.  */
2295  SVN_ERR(svn_ra_neon__get_baseline_info(NULL, NULL, &bc_relative, NULL,
2296                                         rb->ras,
2297                                         url, revision,
2298                                         pool));
2299
2300
2301  svn_xml_escape_cdata_cstring(&qpath, path, pool);
2302  svn_xml_escape_attr_cstring(&qlinkpath, bc_relative.data, pool);
2303  if (start_empty)
2304    entry = apr_psprintf(pool,
2305                         "<S:entry rev=\"%ld\" %s %s"
2306                         " linkpath=\"/%s\" start-empty=\"true\""
2307                         ">%s</S:entry>" DEBUG_CR,
2308                         revision, depthstring, tokenstring,
2309                         qlinkpath->data, qpath->data);
2310  else
2311    entry = apr_psprintf(pool,
2312                         "<S:entry rev=\"%ld\" %s %s"
2313                         " linkpath=\"/%s\">%s</S:entry>" DEBUG_CR,
2314                         revision, depthstring, tokenstring,
2315                         qlinkpath->data, qpath->data);
2316
2317  return svn_io_file_write_full(rb->tmpfile, entry, strlen(entry), NULL, pool);
2318}
2319
2320
2321static svn_error_t * reporter_delete_path(void *report_baton,
2322                                          const char *path,
2323                                          apr_pool_t *pool)
2324{
2325  report_baton_t *rb = report_baton;
2326  const char *s;
2327  svn_stringbuf_t *qpath = NULL;
2328
2329  svn_xml_escape_cdata_cstring(&qpath, path, pool);
2330  s = apr_psprintf(pool,
2331                   "<S:missing>%s</S:missing>" DEBUG_CR,
2332                   qpath->data);
2333
2334  return svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool);
2335}
2336
2337
2338static svn_error_t * reporter_abort_report(void *report_baton,
2339                                           apr_pool_t *pool)
2340{
2341  report_baton_t *rb = report_baton;
2342
2343  (void) apr_file_close(rb->tmpfile);
2344
2345  return SVN_NO_ERROR;
2346}
2347
2348
2349static svn_error_t * reporter_finish_report(void *report_baton,
2350                                            apr_pool_t *pool)
2351{
2352  report_baton_t *rb = report_baton;
2353  svn_error_t *err;
2354  const char *vcc;
2355  apr_hash_t *request_headers = apr_hash_make(pool);
2356  apr_hash_set(request_headers, "Accept-Encoding", APR_HASH_KEY_STRING,
2357               "svndiff1;q=0.9,svndiff;q=0.8");
2358
2359
2360#define SVN_RA_NEON__REPORT_TAIL  "</S:update-report>" DEBUG_CR
2361  /* write the final closing gunk to our request body. */
2362  SVN_ERR(svn_io_file_write_full(rb->tmpfile,
2363                                 SVN_RA_NEON__REPORT_TAIL,
2364                                 sizeof(SVN_RA_NEON__REPORT_TAIL) - 1,
2365                                 NULL, pool));
2366#undef SVN_RA_NEON__REPORT_TAIL
2367
2368  /* get the editor process prepped */
2369  rb->dirs = apr_array_make(rb->pool, 5, sizeof(dir_item_t));
2370  rb->namestr = MAKE_BUFFER(rb->pool);
2371  rb->cpathstr = MAKE_BUFFER(rb->pool);
2372  rb->encoding = MAKE_BUFFER(rb->pool);
2373  rb->href = MAKE_BUFFER(rb->pool);
2374
2375  /* get the VCC.  if this doesn't work out for us, don't forget to
2376     remove the tmpfile before returning the error. */
2377  if ((err = svn_ra_neon__get_vcc(&vcc, rb->ras,
2378                                  rb->ras->url->data, pool)))
2379    {
2380      /* We're done with the file.  this should delete it. Note: it
2381         isn't a big deal if this line is never executed -- the pool
2382         will eventually get it. We're just being proactive here. */
2383      (void) apr_file_close(rb->tmpfile);
2384      return err;
2385    }
2386
2387  /* dispatch the REPORT. */
2388  err = svn_ra_neon__parsed_request(rb->ras, "REPORT", vcc,
2389                                    NULL, rb->tmpfile, NULL,
2390                                    start_element,
2391                                    cdata_handler,
2392                                    end_element,
2393                                    rb,
2394                                    request_headers, NULL,
2395                                    rb->spool_response, pool);
2396
2397  /* We're done with the file. Proactively close/delete the thing. */
2398  (void) apr_file_close(rb->tmpfile);
2399
2400  SVN_ERR(err);
2401
2402  /* We got the whole HTTP response thing done.  *Whew*.  Our edit
2403     baton should have been closed by now, so return a failure if it
2404     hasn't been. */
2405  if (rb->edit_baton)
2406    {
2407      return svn_error_createf
2408        (SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
2409         _("REPORT response handling failed to complete the editor drive"));
2410    }
2411
2412  /* store auth info if we can. */
2413  return svn_ra_neon__maybe_store_auth_info(rb->ras, pool);
2414}
2415
2416static const svn_ra_reporter3_t ra_neon_reporter = {
2417  reporter_set_path,
2418  reporter_delete_path,
2419  reporter_link_path,
2420  reporter_finish_report,
2421  reporter_abort_report
2422};
2423
2424
2425/* Make a generic REPORTER / REPORT_BATON for reporting the state of
2426   the working copy against REVISION during updates or status checks.
2427   The server will drive EDITOR / EDIT_BATON to indicate how to
2428   transform the working copy into the requested target.
2429
2430   SESSION is the RA session in use.  TARGET is an optional single
2431   path component will restrict the scope of the operation to an entry
2432   in the directory represented by the SESSION's URL, or empty if the
2433   entire directory is meant to be the target.
2434
2435   DEPTH is the requested depth of the operation.  It will be
2436   transmitted to the server, which (if it understands depths) can use
2437   the information to limit the information it sends back.  Also store
2438   DEPTH in the REPORT_BATON: that way, if the server is old and does
2439   not understand depth requests, the client can notice this when the
2440   response starts streaming in, and adjust accordingly (as of this
2441   writnig, by wrapping REPORTER->editor and REPORTER->edit_baton in a
2442   filtering editor that simply tosses out the data the client doesn't
2443   want).
2444
2445   If SEND_COPYFROM_ARGS is set, then ask the server to transmit
2446   copyfrom args in add_file() in add_directory() calls.
2447
2448   If IGNORE_ANCESTRY is set, the server will transmit real diffs
2449   between the working copy and the target even if those objects are
2450   not historically related.  Otherwise, the response will generally
2451   look like a giant delete followed by a giant add.
2452
2453   RESOURCE_WALK controls whether to ask the DAV server to supply an
2454   entire tree's worth of version-resource-URL working copy cache
2455   updates.
2456
2457   FETCH_CONTENT is used by the REPORT response parser to determine
2458   whether it should bother getting the contents of files represented
2459   in the delta response (of if a directory delta is all that is of
2460   interest).
2461
2462   If SEND_ALL is set, the server will be asked to embed contents into
2463   the main response.
2464
2465   If SPOOL_RESPONSE is set, the REPORT response will be cached to
2466   disk in a tmpfile (in full), then read back and parsed.
2467
2468   Oh, and do all this junk in POOL.  */
2469static svn_error_t *
2470make_reporter(svn_ra_session_t *session,
2471              const svn_ra_reporter3_t **reporter,
2472              void **report_baton,
2473              svn_revnum_t revision,
2474              const char *target,
2475              const char *dst_path,
2476              svn_depth_t depth,
2477              svn_boolean_t send_copyfrom_args,
2478              svn_boolean_t ignore_ancestry,
2479              svn_boolean_t resource_walk,
2480              const svn_delta_editor_t *editor,
2481              void *edit_baton,
2482              svn_boolean_t fetch_content,
2483              svn_boolean_t send_all,
2484              svn_boolean_t spool_response,
2485              apr_pool_t *pool)
2486{
2487  svn_ra_neon__session_t *ras = session->priv;
2488  report_baton_t *rb;
2489  const char *s;
2490  svn_stringbuf_t *xml_s;
2491  const svn_delta_editor_t *filter_editor;
2492  void *filter_baton;
2493  svn_boolean_t has_target = *target != '\0';
2494  svn_boolean_t server_supports_depth;
2495
2496  SVN_ERR(svn_ra_neon__has_capability(session, &server_supports_depth,
2497                                      SVN_RA_CAPABILITY_DEPTH, pool));
2498  /* We can skip the depth filtering when the user requested
2499     depth_files or depth_infinity because the server will
2500     transmit the right stuff anyway. */
2501  if ((depth != svn_depth_files)
2502      && (depth != svn_depth_infinity)
2503      && ! server_supports_depth)
2504    {
2505      SVN_ERR(svn_delta_depth_filter_editor(&filter_editor,
2506                                            &filter_baton,
2507                                            editor,
2508                                            edit_baton,
2509                                            depth,
2510                                            has_target,
2511                                            pool));
2512      editor = filter_editor;
2513      edit_baton = filter_baton;
2514    }
2515
2516  rb = apr_pcalloc(pool, sizeof(*rb));
2517  rb->ras = ras;
2518  rb->pool = pool;
2519  rb->scratch_pool = svn_pool_create(pool);
2520  rb->editor = editor;
2521  rb->edit_baton = edit_baton;
2522  rb->fetch_content = fetch_content;
2523  rb->in_resource = FALSE;
2524  rb->current_wcprop_path = svn_stringbuf_create("", pool);
2525  rb->is_switch = dst_path != NULL;
2526  rb->target = target;
2527  rb->receiving_all = FALSE;
2528  rb->spool_response = spool_response;
2529  rb->whandler = NULL;
2530  rb->whandler_baton = NULL;
2531  rb->svndiff_decoder = NULL;
2532  rb->base64_decoder = NULL;
2533  rb->cdata_accum = svn_stringbuf_create("", pool);
2534  rb->send_copyfrom_args = send_copyfrom_args;
2535  rb->lock_tokens = apr_hash_make(pool);
2536
2537  /* Neon "pulls" request body content from the caller. The reporter is
2538     organized where data is "pushed" into self. To match these up, we use
2539     an intermediate file -- push data into the file, then let Neon pull
2540     from the file.
2541
2542     Note: one day we could spin up a thread and use a pipe between this
2543     code and Neon. We write to a pipe, Neon reads from the pipe. Each
2544     thread can block on the pipe, waiting for the other to complete its
2545     work.
2546  */
2547
2548  /* Create a temp file in the system area to hold the contents. Note that
2549     we need a file since we will be rewinding it. The file will be closed
2550     and deleted when the pool is cleaned up. */
2551  SVN_ERR(svn_io_open_unique_file3(&rb->tmpfile, NULL, NULL,
2552                                   svn_io_file_del_on_pool_cleanup,
2553                                   pool, pool));
2554
2555  /* prep the file */
2556  s = apr_psprintf(pool, "<S:update-report send-all=\"%s\" xmlns:S=\""
2557                   SVN_XML_NAMESPACE "\">" DEBUG_CR,
2558                   send_all ? "true" : "false");
2559  SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
2560
2561  /* always write the original source path.  this is part of the "new
2562     style" update-report syntax.  if the tmpfile is used in an "old
2563     style' update-report request, older servers will just ignore this
2564     unknown xml element. */
2565  xml_s = NULL;
2566  svn_xml_escape_cdata_cstring(&xml_s, ras->url->data, pool);
2567  s = apr_psprintf(pool, "<S:src-path>%s</S:src-path>" DEBUG_CR, xml_s->data);
2568  SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
2569
2570  /* an invalid revnum means "latest". we can just omit the target-revision
2571     element in that case. */
2572  if (SVN_IS_VALID_REVNUM(revision))
2573    {
2574      s = apr_psprintf(pool,
2575                       "<S:target-revision>%ld</S:target-revision>" DEBUG_CR,
2576                       revision);
2577      SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
2578    }
2579
2580  /* Pre-0.36 servers don't like to see an empty target string.  */
2581  if (*target)
2582    {
2583      xml_s = NULL;
2584      svn_xml_escape_cdata_cstring(&xml_s, target, pool);
2585      s = apr_psprintf(pool, "<S:update-target>%s</S:update-target>" DEBUG_CR,
2586                       xml_s->data);
2587      SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
2588    }
2589
2590
2591  /* A NULL dst_path is also no problem;  this is only passed during a
2592     'switch' operation.  If NULL, we don't mention it in the custom
2593     report, and mod_dav_svn automatically runs dir_delta() on two
2594     identical paths. */
2595  if (dst_path)
2596    {
2597      xml_s = NULL;
2598      svn_xml_escape_cdata_cstring(&xml_s, dst_path, pool);
2599      s = apr_psprintf(pool, "<S:dst-path>%s</S:dst-path>" DEBUG_CR,
2600                       xml_s->data);
2601      SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
2602    }
2603
2604  /* Old servers know "recursive" but not "depth"; help them DTRT. */
2605  if (depth == svn_depth_files || depth == svn_depth_empty)
2606    {
2607      const char *data = "<S:recursive>no</S:recursive>" DEBUG_CR;
2608      SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
2609                                     NULL, pool));
2610    }
2611
2612  /* mod_dav_svn defaults to svn_depth_infinity, but we always send anyway. */
2613  {
2614    s = apr_psprintf(pool, "<S:depth>%s</S:depth>" DEBUG_CR,
2615                     svn_depth_to_word(depth));
2616    SVN_ERR(svn_io_file_write_full(rb->tmpfile, s, strlen(s), NULL, pool));
2617  }
2618
2619  /* mod_dav_svn will use ancestry in diffs unless it finds this element. */
2620  if (ignore_ancestry)
2621    {
2622      const char *data = "<S:ignore-ancestry>yes</S:ignore-ancestry>" DEBUG_CR;
2623      SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
2624                                     NULL, pool));
2625    }
2626
2627  /* mod_dav_svn 1.5 and later won't send copyfrom args unless it
2628     finds this element.  older mod_dav_svn modules should just
2629     ignore the unknown element. */
2630  if (send_copyfrom_args)
2631    {
2632      const char *data =
2633        "<S:send-copyfrom-args>yes</S:send-copyfrom-args>" DEBUG_CR;
2634      SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
2635                                     NULL, pool));
2636    }
2637
2638  /* If we want a resource walk to occur, note that now. */
2639  if (resource_walk)
2640    {
2641      const char *data = "<S:resource-walk>yes</S:resource-walk>" DEBUG_CR;
2642      SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
2643                                     NULL, pool));
2644    }
2645
2646  /* When in 'send-all' mode, mod_dav_svn will assume that it should
2647     calculate and transmit real text-deltas (instead of empty windows
2648     that merely indicate "text is changed") unless it finds this
2649     element.  When not in 'send-all' mode, mod_dav_svn will never
2650     send text-deltas at all.
2651
2652     NOTE: Do NOT count on servers actually obeying this, as some exist
2653     which obey send-all, but do not check for this directive at all! */
2654  if (send_all && (! fetch_content))
2655    {
2656      const char *data = "<S:text-deltas>no</S:text-deltas>" DEBUG_CR;
2657      SVN_ERR(svn_io_file_write_full(rb->tmpfile, data, strlen(data),
2658                                     NULL, pool));
2659    }
2660
2661  *reporter = &ra_neon_reporter;
2662  *report_baton = rb;
2663
2664  return SVN_NO_ERROR;
2665}
2666
2667
2668svn_error_t * svn_ra_neon__do_update(svn_ra_session_t *session,
2669                                     const svn_ra_reporter3_t **reporter,
2670                                     void **report_baton,
2671                                     svn_revnum_t revision_to_update_to,
2672                                     const char *update_target,
2673                                     svn_depth_t depth,
2674                                     svn_boolean_t send_copyfrom_args,
2675                                     const svn_delta_editor_t *wc_update,
2676                                     void *wc_update_baton,
2677                                     apr_pool_t *pool)
2678{
2679  return make_reporter(session,
2680                       reporter,
2681                       report_baton,
2682                       revision_to_update_to,
2683                       update_target,
2684                       NULL,
2685                       depth,
2686                       send_copyfrom_args,
2687                       FALSE,
2688                       FALSE,
2689                       wc_update,
2690                       wc_update_baton,
2691                       TRUE, /* fetch_content */
2692                       TRUE, /* send_all */
2693                       FALSE, /* spool_response */
2694                       pool);
2695}
2696
2697
2698svn_error_t * svn_ra_neon__do_status(svn_ra_session_t *session,
2699                                     const svn_ra_reporter3_t **reporter,
2700                                     void **report_baton,
2701                                     const char *status_target,
2702                                     svn_revnum_t revision,
2703                                     svn_depth_t depth,
2704                                     const svn_delta_editor_t *wc_status,
2705                                     void *wc_status_baton,
2706                                     apr_pool_t *pool)
2707{
2708  return make_reporter(session,
2709                       reporter,
2710                       report_baton,
2711                       revision,
2712                       status_target,
2713                       NULL,
2714                       depth,
2715                       FALSE,
2716                       FALSE,
2717                       FALSE,
2718                       wc_status,
2719                       wc_status_baton,
2720                       FALSE, /* fetch_content */
2721                       TRUE, /* send_all */
2722                       FALSE, /* spool_response */
2723                       pool);
2724}
2725
2726
2727svn_error_t * svn_ra_neon__do_switch(svn_ra_session_t *session,
2728                                     const svn_ra_reporter3_t **reporter,
2729                                     void **report_baton,
2730                                     svn_revnum_t revision_to_update_to,
2731                                     const char *update_target,
2732                                     svn_depth_t depth,
2733                                     const char *switch_url,
2734                                     const svn_delta_editor_t *wc_update,
2735                                     void *wc_update_baton,
2736                                     apr_pool_t *pool)
2737{
2738  return make_reporter(session,
2739                       reporter,
2740                       report_baton,
2741                       revision_to_update_to,
2742                       update_target,
2743                       switch_url,
2744                       depth,
2745                       FALSE,  /* ### TODO(sussman): no copyfrom args */
2746                       TRUE,
2747                       FALSE, /* ### Disabled, pre-1.2 servers sometimes
2748                                 return incorrect resource-walk data */
2749                       wc_update,
2750                       wc_update_baton,
2751                       TRUE, /* fetch_content */
2752                       TRUE, /* send_all */
2753                       FALSE, /* spool_response */
2754                       pool);
2755}
2756
2757
2758svn_error_t * svn_ra_neon__do_diff(svn_ra_session_t *session,
2759                                   const svn_ra_reporter3_t **reporter,
2760                                   void **report_baton,
2761                                   svn_revnum_t revision,
2762                                   const char *diff_target,
2763                                   svn_depth_t depth,
2764                                   svn_boolean_t ignore_ancestry,
2765                                   svn_boolean_t text_deltas,
2766                                   const char *versus_url,
2767                                   const svn_delta_editor_t *wc_diff,
2768                                   void *wc_diff_baton,
2769                                   apr_pool_t *pool)
2770{
2771  return make_reporter(session,
2772                       reporter,
2773                       report_baton,
2774                       revision,
2775                       diff_target,
2776                       versus_url,
2777                       depth,
2778                       FALSE,
2779                       ignore_ancestry,
2780                       FALSE,
2781                       wc_diff,
2782                       wc_diff_baton,
2783                       text_deltas, /* fetch_content */
2784                       FALSE, /* send_all */
2785                       TRUE, /* spool_response */
2786                       pool);
2787}
Note: See TracBrowser for help on using the repository browser.