2 * update.c : entry point for update RA functions for ra_serf
4 * ====================================================================
5 * Copyright (c) 2006 CollabNet. All rights reserved.
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.
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 * ====================================================================
21 #define APR_WANT_STRFUNC
30 #include "svn_pools.h"
34 #include "../libsvn_ra/ra_loader.h"
35 #include "svn_config.h"
36 #include "svn_delta.h"
37 #include "svn_version.h"
39 #include "svn_base64.h"
40 #include "svn_private_config.h"
46 * This enum represents the current state of our XML parsing for a REPORT.
48 * A little explanation of how the parsing works. Every time we see
49 * an open-directory tag, we enter the OPEN_DIR state. Likewise, for
50 * add-directory, open-file, etc. When we see the closing variant of the
51 * open-directory tag, we'll 'pop' out of that state.
53 * Each state has a pool associated with it that can have temporary
54 * allocations that will live as long as the tag is opened. Once
55 * the tag is 'closed', the pool will be reused.
68 /* Forward-declare our report context. */
69 typedef struct report_context_t report_context_t
;
72 * This structure represents the information for a directory.
74 typedef struct report_dir_t
76 /* Our parent directory.
78 * This value is NULL when we are the root.
80 struct report_dir_t
*parent_dir
;
84 /* Pointer back to our original report context. */
85 report_context_t
*report_context
;
87 /* Our name sans any parents. */
88 const char *base_name
;
90 /* the expanded directory name (including all parent names) */
93 /* temporary path buffer for this directory. */
94 svn_stringbuf_t
*name_buf
;
96 /* the canonical url for this directory. */
99 /* Our base revision - SVN_INVALID_REVNUM if we're adding this dir. */
100 svn_revnum_t base_rev
;
102 /* The target revision we're retrieving. */
103 svn_revnum_t target_rev
;
105 /* controlling dir baton - this is only created in open_dir() */
107 apr_pool_t
*dir_baton_pool
;
109 /* Our master update editor and baton. */
110 const svn_delta_editor_t
*update_editor
;
113 /* How many references to this directory do we still have open? */
114 apr_size_t ref_count
;
116 /* Namespace list allocated out of this ->pool. */
117 svn_ra_serf__ns_t
*ns_list
;
119 /* hashtable for all of the properties (shared within a dir) */
122 /* hashtable for all to-be-removed properties (shared within a dir) */
123 apr_hash_t
*removed_props
;
125 /* The propfind request for our current directory */
126 svn_ra_serf__propfind_context_t
*propfind
;
128 /* Has the server told us to fetch the dir props? */
129 svn_boolean_t fetch_props
;
131 /* Have we closed the directory tag (meaning no more additions)? */
132 svn_boolean_t tag_closed
;
134 /* The children of this directory */
135 struct report_dir_t
*children
;
137 /* The next sibling of this directory */
138 struct report_dir_t
*sibling
;
142 * This structure represents the information for a file.
144 * A directory may have a report_info_t associated with it as well.
146 * This structure is created as we parse the REPORT response and
147 * once the element is completed, we create a report_fetch_t structure
148 * to give to serf to retrieve this file.
150 typedef struct report_info_t
154 /* The enclosing directory.
156 * If this structure refers to a directory, the dir it points to will be
161 /* Our name sans any directory info. */
162 const char *base_name
;
164 /* the expanded file name (including all parent directory names) */
167 /* file name buffer */
168 svn_stringbuf_t
*name_buf
;
170 /* the canonical url for this file. */
173 /* lock token, if we had one to start off with. */
174 const char *lock_token
;
176 /* Our base revision - SVN_INVALID_REVNUM if we're adding this file. */
177 svn_revnum_t base_rev
;
179 /* The target revision we're retrieving. */
180 svn_revnum_t target_rev
;
182 /* our delta base, if present (NULL if we're adding the file) */
183 const svn_string_t
*delta_base
;
185 /* Path of original item if add with history */
186 const char *copyfrom_path
;
188 /* Revision of original item if add with history */
189 svn_revnum_t copyfrom_rev
;
191 /* The propfind request for our current file (if present) */
192 svn_ra_serf__propfind_context_t
*propfind
;
194 /* Has the server told us to fetch the file props? */
195 svn_boolean_t fetch_props
;
197 /* Has the server told us to go fetch - only valid if we had it already */
198 svn_boolean_t fetch_file
;
200 /* The properties for this file */
203 /* pool passed to update->add_file, etc. */
204 apr_pool_t
*editor_pool
;
206 /* controlling file_baton and textdelta handler */
208 svn_txdelta_window_handler_t textdelta
;
209 void *textdelta_baton
;
211 /* temporary property for this file which is currently being parsed
212 * It will eventually be stored in our parent directory's property hash.
215 const char *prop_name
;
216 const char *prop_val
;
217 apr_size_t prop_val_len
;
218 const char *prop_encoding
;
222 * This structure represents a single request to GET (fetch) a file with
223 * its associated Serf session/connection.
225 typedef struct report_fetch_t
{
229 /* Non-NULL if we received an error during processing. */
232 /* The session we should use to fetch the file. */
233 svn_ra_serf__session_t
*sess
;
235 /* The connection we should use to fetch file. */
236 svn_ra_serf__connection_t
*conn
;
238 /* Stores the information for the file we want to fetch. */
241 /* Have we read our response headers yet? */
242 svn_boolean_t read_headers
;
244 /* This flag is set when our response is aborted before we reach the
245 * end and we decide to requeue this request.
247 svn_boolean_t aborted_read
;
248 apr_off_t aborted_read_size
;
250 /* This is the amount of data that we have read so far. */
253 /* If we're receiving an svndiff, this will be non-NULL. */
254 svn_stream_t
*delta_stream
;
256 /* If we're writing this file to a stream, this will be non-NULL. */
257 svn_stream_t
*target_stream
;
259 /* Are we done fetching this file? */
261 svn_ra_serf__list_t
**done_list
;
262 svn_ra_serf__list_t done_item
;
267 * The master structure for a REPORT request and response.
269 struct report_context_t
{
272 svn_ra_serf__session_t
*sess
;
273 svn_ra_serf__connection_t
*conn
;
275 /* Source path and destination path */
277 const char *destination
;
279 /* Our update target. */
280 const char *update_target
;
282 /* What is the target revision that we want for this REPORT? */
283 svn_revnum_t target_rev
;
285 /* Have we been asked to ignore ancestry or textdeltas? */
286 svn_boolean_t ignore_ancestry
;
287 svn_boolean_t text_deltas
;
289 /* Do we want the server to send copyfrom args or not? */
290 svn_boolean_t send_copyfrom_args
;
292 /* Path -> lock token mapping. */
293 apr_hash_t
*lock_path_tokens
;
295 /* Our master update editor and baton. */
296 const svn_delta_editor_t
*update_editor
;
299 /* The request body for the REPORT. */
300 serf_bucket_t
*buckets
;
302 /* root directory object */
303 report_dir_t
*root_dir
;
305 /* number of pending GET requests */
306 unsigned int active_fetches
;
308 /* completed fetches (contains report_fetch_t) */
309 svn_ra_serf__list_t
*done_fetches
;
311 /* number of pending PROPFIND requests */
312 unsigned int active_propfinds
;
314 /* completed PROPFIND requests (contains propfind_context_t) */
315 svn_ra_serf__list_t
*done_propfinds
;
317 /* list of files that only have prop changes (contains report_info_t) */
318 svn_ra_serf__list_t
*file_propchanges_only
;
320 /* The path to the REPORT request */
323 /* Are we done parsing the REPORT response? */
329 /** Report state management helper **/
331 static report_info_t
*
332 push_state(svn_ra_serf__xml_parser_t
*parser
,
333 report_context_t
*ctx
,
334 report_state_e state
)
337 apr_pool_t
*info_parent_pool
;
339 svn_ra_serf__xml_push_state(parser
, state
);
341 info
= parser
->state
->private;
343 /* Our private pool needs to be disjoint from the state pool. */
346 info_parent_pool
= ctx
->pool
;
350 info_parent_pool
= info
->pool
;
353 if (state
== OPEN_DIR
|| state
== ADD_DIR
)
355 report_info_t
*new_info
;
357 new_info
= apr_palloc(info_parent_pool
, sizeof(*new_info
));
358 apr_pool_create(&new_info
->pool
, info_parent_pool
);
359 new_info
->lock_token
= NULL
;
361 new_info
->dir
= apr_pcalloc(new_info
->pool
, sizeof(*new_info
->dir
));
362 new_info
->dir
->pool
= new_info
->pool
;
364 /* Create the root property tree. */
365 new_info
->dir
->props
= apr_hash_make(new_info
->pool
);
366 new_info
->props
= new_info
->dir
->props
;
367 new_info
->dir
->removed_props
= apr_hash_make(new_info
->pool
);
369 /* Point to the update_editor */
370 new_info
->dir
->update_editor
= ctx
->update_editor
;
371 new_info
->dir
->update_baton
= ctx
->update_baton
;
372 new_info
->dir
->report_context
= ctx
;
376 info
->dir
->ref_count
++;
378 new_info
->dir
->parent_dir
= info
->dir
;
380 /* Point our ns_list at our parents to try to reuse it. */
381 new_info
->dir
->ns_list
= info
->dir
->ns_list
;
383 /* Add ourselves to our parent's list */
384 new_info
->dir
->sibling
= info
->dir
->children
;
385 info
->dir
->children
= new_info
->dir
;
389 /* Allow us to be found later. */
390 ctx
->root_dir
= new_info
->dir
;
393 parser
->state
->private = new_info
;
395 else if (state
== OPEN_FILE
|| state
== ADD_FILE
)
397 report_info_t
*new_info
;
399 new_info
= apr_palloc(info_parent_pool
, sizeof(*new_info
));
400 apr_pool_create(&new_info
->pool
, info_parent_pool
);
401 new_info
->file_baton
= NULL
;
402 new_info
->lock_token
= NULL
;
403 new_info
->fetch_file
= FALSE
;
405 /* Point at our parent's directory state. */
406 new_info
->dir
= info
->dir
;
407 info
->dir
->ref_count
++;
409 new_info
->props
= apr_hash_make(new_info
->pool
);
411 parser
->state
->private = new_info
;
414 return parser
->state
->private;
418 /** Wrappers around our various property walkers **/
421 set_file_props(void *baton
,
422 const char *ns
, apr_ssize_t ns_len
,
423 const char *name
, apr_ssize_t name_len
,
424 const svn_string_t
*val
,
427 report_info_t
*info
= baton
;
428 const svn_delta_editor_t
*editor
= info
->dir
->update_editor
;
430 return svn_ra_serf__set_baton_props(editor
->change_file_prop
,
432 ns
, ns_len
, name
, name_len
, val
, pool
);
436 set_dir_props(void *baton
,
437 const char *ns
, apr_ssize_t ns_len
,
438 const char *name
, apr_ssize_t name_len
,
439 const svn_string_t
*val
,
442 report_dir_t
*dir
= baton
;
443 return svn_ra_serf__set_baton_props(dir
->update_editor
->change_dir_prop
,
445 ns
, ns_len
, name
, name_len
, val
, pool
);
449 remove_file_props(void *baton
,
450 const char *ns
, apr_ssize_t ns_len
,
451 const char *name
, apr_ssize_t name_len
,
452 const svn_string_t
*val
,
455 report_info_t
*info
= baton
;
456 const svn_delta_editor_t
*editor
= info
->dir
->update_editor
;
458 return svn_ra_serf__set_baton_props(editor
->change_file_prop
,
460 ns
, ns_len
, name
, name_len
, NULL
, pool
);
464 remove_dir_props(void *baton
,
465 const char *ns
, apr_ssize_t ns_len
,
466 const char *name
, apr_ssize_t name_len
,
467 const svn_string_t
*val
,
470 report_dir_t
*dir
= baton
;
471 return svn_ra_serf__set_baton_props(dir
->update_editor
->change_dir_prop
,
473 ns
, ns_len
, name
, name_len
, NULL
, pool
);
477 /** Helpers to open and close directories */
480 open_dir(report_dir_t
*dir
)
482 /* if we're already open, return now */
488 if (dir
->base_name
[0] == '\0')
490 apr_pool_create(&dir
->dir_baton_pool
, dir
->pool
);
492 if (dir
->report_context
->destination
&&
493 dir
->report_context
->sess
->wc_callbacks
->invalidate_wc_props
)
495 SVN_ERR(dir
->report_context
->sess
->wc_callbacks
->invalidate_wc_props(
496 dir
->report_context
->sess
->wc_callback_baton
,
497 dir
->report_context
->update_target
,
498 SVN_RA_SERF__WC_CHECKED_IN_URL
, dir
->pool
));
501 SVN_ERR(dir
->update_editor
->open_root(dir
->update_baton
, dir
->base_rev
,
507 SVN_ERR(open_dir(dir
->parent_dir
));
509 apr_pool_create(&dir
->dir_baton_pool
, dir
->parent_dir
->dir_baton_pool
);
511 if (SVN_IS_VALID_REVNUM(dir
->base_rev
))
513 SVN_ERR(dir
->update_editor
->open_directory(dir
->name
,
514 dir
->parent_dir
->dir_baton
,
521 SVN_ERR(dir
->update_editor
->add_directory(dir
->name
,
522 dir
->parent_dir
->dir_baton
,
523 NULL
, SVN_INVALID_REVNUM
,
533 close_dir(report_dir_t
*dir
)
535 report_dir_t
*prev
, *sibling
;
542 svn_ra_serf__walk_all_props(dir
->props
, dir
->base_name
, dir
->base_rev
,
543 set_dir_props
, dir
, dir
->dir_baton_pool
);
545 svn_ra_serf__walk_all_props(dir
->removed_props
, dir
->base_name
,
546 dir
->base_rev
, remove_dir_props
, dir
,
547 dir
->dir_baton_pool
);
549 if (dir
->fetch_props
)
551 svn_ra_serf__walk_all_props(dir
->props
, dir
->url
, dir
->target_rev
,
552 set_dir_props
, dir
, dir
->dir_baton_pool
);
555 SVN_ERR(dir
->update_editor
->close_directory(dir
->dir_baton
,
556 dir
->dir_baton_pool
));
558 /* remove us from our parent's children list */
562 sibling
= dir
->parent_dir
->children
;
564 while (sibling
!= dir
)
567 sibling
= sibling
->sibling
;
574 dir
->parent_dir
->children
= dir
->sibling
;
578 prev
->sibling
= dir
->sibling
;
582 svn_pool_destroy(dir
->dir_baton_pool
);
583 svn_pool_destroy(dir
->pool
);
588 static svn_error_t
*close_all_dirs(report_dir_t
*dir
)
590 while (dir
->children
)
592 SVN_ERR(close_all_dirs(dir
->children
));
601 SVN_ERR(open_dir(dir
));
603 return close_dir(dir
);
607 /** Routines called when we are fetching a file */
609 /* This function works around a bug in mod_dav_svn in that it will not
610 * send remove-prop in the update report when a lock property disappears
611 * when send-all is false.
613 * Therefore, we'll try to look at our properties and see if there's
614 * an active lock. If not, then we'll assume there isn't a lock
618 check_lock(report_info_t
*info
)
620 const char *lock_val
;
622 lock_val
= svn_ra_serf__get_ver_prop(info
->props
, info
->url
,
624 "DAV:", "lockdiscovery");
629 new_lock
= apr_pstrdup(info
->editor_pool
, lock_val
);
630 apr_collapse_spaces(new_lock
, new_lock
);
634 if (!lock_val
|| lock_val
[0] == '\0')
638 str
= svn_string_ncreate("", 1, info
->editor_pool
);
640 svn_ra_serf__set_ver_prop(info
->dir
->removed_props
, info
->base_name
,
641 info
->base_rev
, "DAV:", "lock-token",
642 str
, info
->dir
->pool
);
647 headers_fetch(serf_bucket_t
*headers
,
651 report_fetch_t
*fetch_ctx
= baton
;
653 /* note that we have old VC URL */
654 if (SVN_IS_VALID_REVNUM(fetch_ctx
->info
->base_rev
) &&
655 fetch_ctx
->info
->delta_base
)
657 serf_bucket_headers_setn(headers
, SVN_DAV_DELTA_BASE_HEADER
,
658 fetch_ctx
->info
->delta_base
->data
);
659 serf_bucket_headers_setn(headers
, "Accept-Encoding",
660 "svndiff1;q=0.9,svndiff;q=0.8");
662 else if (fetch_ctx
->conn
->using_compression
== TRUE
)
664 serf_bucket_headers_setn(headers
, "Accept-Encoding", "gzip");
671 cancel_fetch(serf_request_t
*request
,
672 serf_bucket_t
*response
,
676 report_fetch_t
*fetch_ctx
= baton
;
678 /* Uh-oh. Our connection died on us.
680 * The core ra_serf layer will requeue our request - we just need to note
681 * that we got cut off in the middle of our song.
685 /* If we already started the fetch and opened the file handle, we need
686 * to hold subsequent read() ops until we get back to where we were
687 * before the close and we can then resume the textdelta() calls.
689 if (fetch_ctx
->read_headers
== TRUE
)
691 if (fetch_ctx
->aborted_read
== FALSE
&& fetch_ctx
->read_size
)
693 fetch_ctx
->aborted_read
= TRUE
;
694 fetch_ctx
->aborted_read_size
= fetch_ctx
->read_size
;
696 fetch_ctx
->read_size
= 0;
702 /* We have no idea what went wrong. */
707 error_fetch(serf_request_t
*request
,
708 report_fetch_t
*fetch_ctx
,
711 fetch_ctx
->err
= err
;
713 fetch_ctx
->done
= TRUE
;
715 fetch_ctx
->done_item
.data
= fetch_ctx
;
716 fetch_ctx
->done_item
.next
= *fetch_ctx
->done_list
;
717 *fetch_ctx
->done_list
= &fetch_ctx
->done_item
;
719 serf_request_set_handler(request
, svn_ra_serf__handle_discard_body
, NULL
);
725 handle_fetch(serf_request_t
*request
,
726 serf_bucket_t
*response
,
733 report_fetch_t
*fetch_ctx
= handler_baton
;
737 if (fetch_ctx
->read_headers
== FALSE
)
743 hdrs
= serf_bucket_response_get_headers(response
);
744 val
= serf_bucket_headers_get(hdrs
, "Content-Type");
745 info
= fetch_ctx
->info
;
747 err
= open_dir(info
->dir
);
750 return error_fetch(request
, fetch_ctx
, err
);
753 apr_pool_create(&info
->editor_pool
, info
->dir
->dir_baton_pool
);
755 /* Expand our full name now if we haven't done so yet. */
758 info
->name_buf
= svn_stringbuf_dup(info
->dir
->name_buf
,
760 svn_path_add_component(info
->name_buf
, info
->base_name
);
761 info
->name
= info
->name_buf
->data
;
764 if (SVN_IS_VALID_REVNUM(info
->base_rev
))
766 err
= info
->dir
->update_editor
->open_file(info
->name
,
767 info
->dir
->dir_baton
,
774 err
= info
->dir
->update_editor
->add_file(info
->name
,
775 info
->dir
->dir_baton
,
784 return error_fetch(request
, fetch_ctx
, err
);
787 err
= info
->dir
->update_editor
->apply_textdelta(info
->file_baton
,
791 &info
->textdelta_baton
);
795 return error_fetch(request
, fetch_ctx
, err
);
798 if (val
&& svn_cstring_casecmp(val
, "application/vnd.svn-svndiff") == 0)
800 fetch_ctx
->delta_stream
=
801 svn_txdelta_parse_svndiff(info
->textdelta
,
802 info
->textdelta_baton
,
803 TRUE
, info
->editor_pool
);
807 fetch_ctx
->delta_stream
= NULL
;
810 fetch_ctx
->read_headers
= TRUE
;
813 /* If the error code wasn't 200, something went wrong. Don't use the returned
814 data as its probably an error message. Just bail out instead. */
815 status
= serf_bucket_response_status(response
, &sl
);
816 if (SERF_BUCKET_READ_ERROR(status
))
822 err
= svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED
, NULL
,
823 _("GET request failed: %d %s"),
825 return error_fetch(request
, fetch_ctx
, err
);
830 svn_txdelta_window_t delta_window
= { 0 };
831 svn_txdelta_op_t delta_op
;
832 svn_string_t window_data
;
834 status
= serf_bucket_read(response
, 8000, &data
, &len
);
835 if (SERF_BUCKET_READ_ERROR(status
))
840 fetch_ctx
->read_size
+= len
;
842 if (fetch_ctx
->aborted_read
== TRUE
)
844 /* We haven't caught up to where we were before. */
845 if (fetch_ctx
->read_size
< fetch_ctx
->aborted_read_size
)
847 /* Eek. What did the file shrink or something? */
848 if (APR_STATUS_IS_EOF(status
))
853 /* Skip on to the next iteration of this loop. */
854 if (APR_STATUS_IS_EAGAIN(status
))
861 /* Woo-hoo. We're back. */
862 fetch_ctx
->aborted_read
= FALSE
;
864 /* Increment data and len by the difference. */
865 data
+= fetch_ctx
->read_size
- fetch_ctx
->aborted_read_size
;
866 len
= fetch_ctx
->read_size
- fetch_ctx
->aborted_read_size
;
869 if (fetch_ctx
->delta_stream
)
871 err
= svn_stream_write(fetch_ctx
->delta_stream
, data
, &len
);
874 return error_fetch(request
, fetch_ctx
, err
);
877 /* otherwise, manually construct the text delta window. */
880 window_data
.data
= data
;
881 window_data
.len
= len
;
883 delta_op
.action_code
= svn_txdelta_new
;
885 delta_op
.length
= len
;
887 delta_window
.tview_len
= len
;
888 delta_window
.num_ops
= 1;
889 delta_window
.ops
= &delta_op
;
890 delta_window
.new_data
= &window_data
;
892 /* write to the file located in the info. */
893 err
= fetch_ctx
->info
->textdelta(&delta_window
,
894 fetch_ctx
->info
->textdelta_baton
);
897 return error_fetch(request
, fetch_ctx
, err
);
901 if (APR_STATUS_IS_EOF(status
))
903 report_info_t
*info
= fetch_ctx
->info
;
905 err
= info
->textdelta(NULL
, info
->textdelta_baton
);
908 return error_fetch(request
, fetch_ctx
, err
);
911 if (info
->lock_token
)
914 /* set all of the properties we received */
915 svn_ra_serf__walk_all_props(info
->props
,
919 info
, info
->editor_pool
);
920 svn_ra_serf__walk_all_props(info
->dir
->removed_props
,
924 info
, info
->editor_pool
);
925 if (info
->fetch_props
)
927 svn_ra_serf__walk_all_props(info
->props
,
931 info
, info
->editor_pool
);
934 err
= info
->dir
->update_editor
->close_file(info
->file_baton
, NULL
,
939 return error_fetch(request
, fetch_ctx
, err
);
942 fetch_ctx
->done
= TRUE
;
944 fetch_ctx
->done_item
.data
= fetch_ctx
;
945 fetch_ctx
->done_item
.next
= *fetch_ctx
->done_list
;
946 *fetch_ctx
->done_list
= &fetch_ctx
->done_item
;
948 /* We're done with our pools. */
949 svn_pool_destroy(info
->editor_pool
);
950 svn_pool_destroy(info
->pool
);
954 if (APR_STATUS_IS_EAGAIN(status
))
963 handle_stream(serf_request_t
*request
,
964 serf_bucket_t
*response
,
968 report_fetch_t
*fetch_ctx
= handler_baton
;
971 serf_bucket_response_status(response
, &sl
);
973 /* Woo-hoo. Nothing here to see. */
976 fetch_ctx
->done
= TRUE
;
977 fetch_ctx
->err
= svn_error_createf(SVN_ERR_RA_DAV_PATH_NOT_FOUND
, NULL
,
978 "'%s' path not found",
979 fetch_ctx
->info
->name
);
980 return svn_ra_serf__handle_discard_body(request
, response
, NULL
, pool
);
989 status
= serf_bucket_read(response
, 8000, &data
, &len
);
990 if (SERF_BUCKET_READ_ERROR(status
))
995 fetch_ctx
->read_size
+= len
;
997 if (fetch_ctx
->aborted_read
== TRUE
)
999 /* We haven't caught up to where we were before. */
1000 if (fetch_ctx
->read_size
< fetch_ctx
->aborted_read_size
)
1002 /* Eek. What did the file shrink or something? */
1003 if (APR_STATUS_IS_EOF(status
))
1008 /* Skip on to the next iteration of this loop. */
1009 if (APR_STATUS_IS_EAGAIN(status
))
1016 /* Woo-hoo. We're back. */
1017 fetch_ctx
->aborted_read
= FALSE
;
1019 /* Increment data and len by the difference. */
1020 data
+= fetch_ctx
->read_size
- fetch_ctx
->aborted_read_size
;
1021 len
+= fetch_ctx
->read_size
- fetch_ctx
->aborted_read_size
;
1026 apr_size_t written_len
;
1030 svn_stream_write(fetch_ctx
->target_stream
, data
, &written_len
);
1033 if (APR_STATUS_IS_EOF(status
))
1035 fetch_ctx
->done
= TRUE
;
1046 static svn_error_t
*
1047 handle_propchange_only(report_info_t
*info
)
1049 /* Ensure our parent is open. */
1050 SVN_ERR(open_dir(info
->dir
));
1052 apr_pool_create(&info
->editor_pool
, info
->dir
->dir_baton_pool
);
1054 /* Expand our full name now if we haven't done so yet. */
1057 info
->name_buf
= svn_stringbuf_dup(info
->dir
->name_buf
,
1059 svn_path_add_component(info
->name_buf
, info
->base_name
);
1060 info
->name
= info
->name_buf
->data
;
1063 if (SVN_IS_VALID_REVNUM(info
->base_rev
))
1065 SVN_ERR(info
->dir
->update_editor
->open_file(info
->name
,
1066 info
->dir
->dir_baton
,
1069 &info
->file_baton
));
1073 SVN_ERR(info
->dir
->update_editor
->add_file(info
->name
,
1074 info
->dir
->dir_baton
,
1075 info
->copyfrom_path
,
1078 &info
->file_baton
));
1081 if (info
->fetch_file
)
1083 SVN_ERR(info
->dir
->update_editor
->apply_textdelta(info
->file_baton
,
1087 &info
->textdelta_baton
));
1090 if (info
->lock_token
)
1093 /* set all of the properties we received */
1094 svn_ra_serf__walk_all_props(info
->props
,
1095 info
->base_name
, info
->base_rev
,
1096 set_file_props
, info
, info
->editor_pool
);
1097 svn_ra_serf__walk_all_props(info
->dir
->removed_props
,
1098 info
->base_name
, info
->base_rev
,
1099 remove_file_props
, info
, info
->editor_pool
);
1100 if (info
->fetch_props
)
1102 svn_ra_serf__walk_all_props(info
->props
, info
->url
, info
->target_rev
,
1103 set_file_props
, info
, info
->editor_pool
);
1106 SVN_ERR(info
->dir
->update_editor
->close_file(info
->file_baton
, NULL
,
1107 info
->editor_pool
));
1109 /* We're done with our pools. */
1110 svn_pool_destroy(info
->editor_pool
);
1111 svn_pool_destroy(info
->pool
);
1113 info
->dir
->ref_count
--;
1115 return SVN_NO_ERROR
;
1118 static svn_error_t
*
1119 fetch_file(report_context_t
*ctx
, report_info_t
*info
)
1121 svn_ra_serf__connection_t
*conn
;
1122 svn_ra_serf__handler_t
*handler
;
1124 /* What connection should we go on? */
1125 conn
= ctx
->sess
->conns
[ctx
->sess
->cur_conn
];
1127 /* go fetch info->name from DAV:checked-in */
1129 svn_ra_serf__get_ver_prop(info
->props
, info
->base_name
,
1130 info
->base_rev
, "DAV:", "checked-in");
1134 return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED
, NULL
,
1135 _("The OPTIONS response did not include the "
1136 "requested checked-in value"));
1139 /* If needed, create the PROPFIND to retrieve the file's properties. */
1140 info
->propfind
= NULL
;
1141 if (info
->fetch_props
)
1143 svn_ra_serf__deliver_props(&info
->propfind
, info
->props
,
1145 info
->url
, info
->target_rev
, "0", all_props
,
1146 FALSE
, &ctx
->done_propfinds
, info
->dir
->pool
);
1147 if (!info
->propfind
)
1152 ctx
->active_propfinds
++;
1155 /* If we've been asked to fetch the file or its an add, do so.
1156 * Otherwise, handle the case where only the properties changed.
1158 if (info
->fetch_file
&& ctx
->text_deltas
== TRUE
)
1160 report_fetch_t
*fetch_ctx
;
1162 fetch_ctx
= apr_pcalloc(info
->dir
->pool
, sizeof(*fetch_ctx
));
1163 fetch_ctx
->pool
= info
->pool
;
1164 fetch_ctx
->info
= info
;
1165 fetch_ctx
->done_list
= &ctx
->done_fetches
;
1166 fetch_ctx
->sess
= ctx
->sess
;
1167 fetch_ctx
->conn
= conn
;
1169 handler
= apr_pcalloc(info
->pool
, sizeof(*handler
));
1171 handler
->method
= "GET";
1172 handler
->path
= fetch_ctx
->info
->url
;
1174 handler
->conn
= conn
;
1175 handler
->session
= ctx
->sess
;
1177 handler
->header_delegate
= headers_fetch
;
1178 handler
->header_delegate_baton
= fetch_ctx
;
1180 handler
->response_handler
= handle_fetch
;
1181 handler
->response_baton
= fetch_ctx
;
1183 handler
->response_error
= cancel_fetch
;
1184 handler
->response_error_baton
= fetch_ctx
;
1186 svn_ra_serf__request_create(handler
);
1188 ctx
->active_fetches
++;
1190 else if (info
->propfind
)
1192 svn_ra_serf__list_t
*list_item
;
1194 list_item
= apr_pcalloc(info
->dir
->pool
, sizeof(*list_item
));
1195 list_item
->data
= info
;
1196 list_item
->next
= ctx
->file_propchanges_only
;
1197 ctx
->file_propchanges_only
= list_item
;
1201 /* No propfind or GET request. Just handle the prop changes now. */
1202 SVN_ERR(handle_propchange_only(info
));
1205 return SVN_NO_ERROR
;
1209 /** XML callbacks for our update-report response parsing */
1211 static svn_error_t
*
1212 start_report(svn_ra_serf__xml_parser_t
*parser
,
1214 svn_ra_serf__dav_props_t name
,
1217 report_context_t
*ctx
= userData
;
1218 report_state_e state
;
1220 state
= parser
->state
->current_state
;
1222 if (state
== NONE
&& strcmp(name
.name
, "target-revision") == 0)
1226 rev
= svn_xml_get_attr_value("rev", attrs
);
1230 return svn_error_create
1231 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1232 _("Missing revision attr in target-revision element"));
1235 ctx
->update_editor
->set_target_revision(ctx
->update_baton
,
1236 SVN_STR_TO_REV(rev
),
1239 else if (state
== NONE
&& strcmp(name
.name
, "open-directory") == 0)
1242 report_info_t
*info
;
1244 rev
= svn_xml_get_attr_value("rev", attrs
);
1248 return svn_error_create
1249 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1250 _("Missing revision attr in open-directory element"));
1253 info
= push_state(parser
, ctx
, OPEN_DIR
);
1255 info
->base_rev
= apr_atoi64(rev
);
1256 info
->dir
->base_rev
= info
->base_rev
;
1257 info
->dir
->target_rev
= ctx
->target_rev
;
1258 info
->fetch_props
= TRUE
;
1260 info
->dir
->base_name
= "";
1261 /* Create empty stringbuf with estimated max. path size. */
1262 info
->dir
->name_buf
= svn_stringbuf_create_ensure(256, info
->pool
);
1263 info
->dir
->name
= info
->dir
->name_buf
->data
;
1265 info
->base_name
= info
->dir
->base_name
;
1266 info
->name
= info
->dir
->name
;
1267 info
->name_buf
= info
->dir
->name_buf
;
1269 else if (state
== NONE
)
1271 /* do nothing as we haven't seen our valid start tag yet. */
1273 else if ((state
== OPEN_DIR
|| state
== ADD_DIR
) &&
1274 strcmp(name
.name
, "open-directory") == 0)
1276 const char *rev
, *dirname
;
1278 report_info_t
*info
;
1280 rev
= svn_xml_get_attr_value("rev", attrs
);
1284 return svn_error_create
1285 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1286 _("Missing revision attr in open-directory element"));
1289 dirname
= svn_xml_get_attr_value("name", attrs
);
1293 return svn_error_create
1294 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1295 _("Missing name attr in open-directory element"));
1298 info
= push_state(parser
, ctx
, OPEN_DIR
);
1302 info
->base_rev
= apr_atoi64(rev
);
1303 dir
->base_rev
= info
->base_rev
;
1304 dir
->target_rev
= ctx
->target_rev
;
1306 info
->fetch_props
= FALSE
;
1308 dir
->base_name
= apr_pstrdup(dir
->pool
, dirname
);
1309 info
->base_name
= dir
->base_name
;
1311 /* Expand our name. */
1312 dir
->name_buf
= svn_stringbuf_dup(dir
->parent_dir
->name_buf
, dir
->pool
);
1313 svn_path_add_component(dir
->name_buf
, dir
->base_name
);
1315 dir
->name
= dir
->name_buf
->data
;
1316 info
->name
= dir
->name
;
1318 else if ((state
== OPEN_DIR
|| state
== ADD_DIR
) &&
1319 strcmp(name
.name
, "add-directory") == 0)
1321 const char *dir_name
, *cf
, *cr
;
1323 report_info_t
*info
;
1325 dir_name
= svn_xml_get_attr_value("name", attrs
);
1328 return svn_error_create
1329 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1330 _("Missing name attr in add-directory element"));
1332 cf
= svn_xml_get_attr_value("copyfrom-path", attrs
);
1333 cr
= svn_xml_get_attr_value("copyfrom-rev", attrs
);
1335 info
= push_state(parser
, ctx
, ADD_DIR
);
1339 dir
->base_name
= apr_pstrdup(dir
->pool
, dir_name
);
1340 info
->base_name
= dir
->base_name
;
1342 /* Expand our name. */
1343 dir
->name_buf
= svn_stringbuf_dup(dir
->parent_dir
->name_buf
, dir
->pool
);
1344 svn_path_add_component(dir
->name_buf
, dir
->base_name
);
1346 dir
->name
= dir
->name_buf
->data
;
1347 info
->name
= dir
->name
;
1349 info
->copyfrom_path
= cf
? apr_pstrdup(info
->pool
, cf
) : NULL
;
1350 info
->copyfrom_rev
= cr
? apr_atoi64(cr
) : SVN_INVALID_REVNUM
;
1352 /* Mark that we don't have a base. */
1353 info
->base_rev
= SVN_INVALID_REVNUM
;
1354 dir
->base_rev
= info
->base_rev
;
1355 dir
->target_rev
= ctx
->target_rev
;
1356 dir
->fetch_props
= TRUE
;
1358 else if ((state
== OPEN_DIR
|| state
== ADD_DIR
) &&
1359 strcmp(name
.name
, "open-file") == 0)
1361 const char *file_name
, *rev
;
1362 report_info_t
*info
;
1364 file_name
= svn_xml_get_attr_value("name", attrs
);
1368 return svn_error_create
1369 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1370 _("Missing name attr in open-file element"));
1373 rev
= svn_xml_get_attr_value("rev", attrs
);
1377 return svn_error_create
1378 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1379 _("Missing revision attr in open-file element"));
1382 info
= push_state(parser
, ctx
, OPEN_FILE
);
1384 info
->base_rev
= apr_atoi64(rev
);
1385 info
->target_rev
= ctx
->target_rev
;
1386 info
->fetch_props
= FALSE
;
1388 info
->base_name
= apr_pstrdup(info
->pool
, file_name
);
1391 else if ((state
== OPEN_DIR
|| state
== ADD_DIR
) &&
1392 strcmp(name
.name
, "add-file") == 0)
1394 const char *file_name
, *cf
, *cr
;
1395 report_info_t
*info
;
1397 file_name
= svn_xml_get_attr_value("name", attrs
);
1398 cf
= svn_xml_get_attr_value("copyfrom-path", attrs
);
1399 cr
= svn_xml_get_attr_value("copyfrom-rev", attrs
);
1403 return svn_error_create
1404 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1405 _("Missing name attr in add-file element"));
1408 info
= push_state(parser
, ctx
, ADD_FILE
);
1410 info
->base_rev
= SVN_INVALID_REVNUM
;
1411 info
->target_rev
= ctx
->target_rev
;
1412 info
->fetch_props
= TRUE
;
1413 info
->fetch_file
= TRUE
;
1415 info
->base_name
= apr_pstrdup(info
->pool
, file_name
);
1418 info
->copyfrom_path
= cf
? apr_pstrdup(info
->pool
, cf
) : NULL
;
1419 info
->copyfrom_rev
= cr
? apr_atoi64(cr
) : SVN_INVALID_REVNUM
;
1421 else if ((state
== OPEN_DIR
|| state
== ADD_DIR
) &&
1422 strcmp(name
.name
, "delete-entry") == 0)
1424 const char *file_name
;
1425 svn_stringbuf_t
*name_buf
;
1426 report_info_t
*info
;
1427 apr_pool_t
*tmppool
;
1429 file_name
= svn_xml_get_attr_value("name", attrs
);
1433 return svn_error_create
1434 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1435 _("Missing name attr in delete-entry element"));
1438 info
= parser
->state
->private;
1440 SVN_ERR(open_dir(info
->dir
));
1442 apr_pool_create(&tmppool
, info
->dir
->dir_baton_pool
);
1444 name_buf
= svn_stringbuf_dup(info
->dir
->name_buf
, tmppool
);
1445 svn_path_add_component(name_buf
, file_name
);
1447 SVN_ERR(info
->dir
->update_editor
->delete_entry(name_buf
->data
,
1449 info
->dir
->dir_baton
,
1452 svn_pool_destroy(tmppool
);
1454 else if ((state
== OPEN_DIR
|| state
== ADD_DIR
) &&
1455 strcmp(name
.name
, "absent-directory") == 0)
1457 const char *file_name
;
1458 report_info_t
*info
;
1460 file_name
= svn_xml_get_attr_value("name", attrs
);
1464 return svn_error_create
1465 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1466 _("Missing name attr in absent-directory element"));
1469 info
= parser
->state
->private;
1471 SVN_ERR(open_dir(info
->dir
));
1473 ctx
->update_editor
->absent_directory(file_name
,
1474 info
->dir
->dir_baton
,
1477 else if ((state
== OPEN_DIR
|| state
== ADD_DIR
) &&
1478 strcmp(name
.name
, "absent-file") == 0)
1480 const char *file_name
;
1481 report_info_t
*info
;
1483 file_name
= svn_xml_get_attr_value("name", attrs
);
1487 return svn_error_create
1488 (SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1489 _("Missing name attr in absent-file element"));
1492 info
= parser
->state
->private;
1494 SVN_ERR(open_dir(info
->dir
));
1496 ctx
->update_editor
->absent_file(file_name
,
1497 info
->dir
->dir_baton
,
1500 else if (state
== OPEN_DIR
|| state
== ADD_DIR
)
1502 report_info_t
*info
;
1504 if (strcmp(name
.name
, "checked-in") == 0)
1506 info
= push_state(parser
, ctx
, IGNORE_PROP_NAME
);
1507 info
->prop_ns
= name
.namespace;
1508 info
->prop_name
= apr_pstrdup(parser
->state
->pool
, name
.name
);
1509 info
->prop_encoding
= NULL
;
1510 info
->prop_val
= NULL
;
1511 info
->prop_val_len
= 0;
1513 else if (strcmp(name
.name
, "set-prop") == 0 ||
1514 strcmp(name
.name
, "remove-prop") == 0)
1516 const char *full_prop_name
;
1519 info
= push_state(parser
, ctx
, PROP
);
1521 full_prop_name
= svn_xml_get_attr_value("name", attrs
);
1522 if (!full_prop_name
)
1524 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1525 _("Missing name attr in %s element"),
1529 colon
= strchr(full_prop_name
, ':');
1534 colon
= full_prop_name
;
1536 info
->prop_ns
= apr_pstrmemdup(info
->dir
->pool
, full_prop_name
,
1537 colon
- full_prop_name
);
1538 info
->prop_name
= apr_pstrdup(parser
->state
->pool
, colon
);
1539 info
->prop_encoding
= svn_xml_get_attr_value("encoding", attrs
);
1540 info
->prop_val
= NULL
;
1541 info
->prop_val_len
= 0;
1543 else if (strcmp(name
.name
, "prop") == 0)
1545 /* need to fetch it. */
1546 push_state(parser
, ctx
, NEED_PROP_NAME
);
1548 else if (strcmp(name
.name
, "fetch-props") == 0)
1550 info
= parser
->state
->private;
1552 info
->dir
->fetch_props
= TRUE
;
1560 else if (state
== OPEN_FILE
|| state
== ADD_FILE
)
1562 report_info_t
*info
;
1564 if (strcmp(name
.name
, "checked-in") == 0)
1566 info
= push_state(parser
, ctx
, IGNORE_PROP_NAME
);
1567 info
->prop_ns
= name
.namespace;
1568 info
->prop_name
= apr_pstrdup(parser
->state
->pool
, name
.name
);
1569 info
->prop_encoding
= NULL
;
1570 info
->prop_val
= NULL
;
1571 info
->prop_val_len
= 0;
1573 else if (strcmp(name
.name
, "prop") == 0)
1575 /* need to fetch it. */
1576 push_state(parser
, ctx
, NEED_PROP_NAME
);
1578 else if (strcmp(name
.name
, "fetch-props") == 0)
1580 info
= parser
->state
->private;
1582 info
->fetch_props
= TRUE
;
1584 else if (strcmp(name
.name
, "fetch-file") == 0)
1586 info
= parser
->state
->private;
1588 info
->fetch_file
= TRUE
;
1590 else if (strcmp(name
.name
, "set-prop") == 0 ||
1591 strcmp(name
.name
, "remove-prop") == 0)
1593 const char *full_prop_name
;
1596 info
= push_state(parser
, ctx
, PROP
);
1598 full_prop_name
= svn_xml_get_attr_value("name", attrs
);
1599 if (!full_prop_name
)
1601 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
1602 _("Missing name attr in %s element"),
1605 colon
= strchr(full_prop_name
, ':');
1610 colon
= full_prop_name
;
1612 info
->prop_ns
= apr_pstrmemdup(info
->dir
->pool
, full_prop_name
,
1613 colon
- full_prop_name
);
1614 info
->prop_name
= apr_pstrdup(parser
->state
->pool
, colon
);
1615 info
->prop_encoding
= svn_xml_get_attr_value("encoding", attrs
);
1616 info
->prop_val
= NULL
;
1617 info
->prop_val_len
= 0;
1624 else if (state
== IGNORE_PROP_NAME
)
1626 push_state(parser
, ctx
, PROP
);
1628 else if (state
== NEED_PROP_NAME
)
1630 report_info_t
*info
;
1632 info
= push_state(parser
, ctx
, PROP
);
1634 info
->prop_ns
= name
.namespace;
1635 info
->prop_name
= apr_pstrdup(parser
->state
->pool
, name
.name
);
1636 info
->prop_val
= NULL
;
1637 info
->prop_val_len
= 0;
1640 return SVN_NO_ERROR
;
1643 static svn_error_t
*
1644 end_report(svn_ra_serf__xml_parser_t
*parser
,
1646 svn_ra_serf__dav_props_t name
)
1648 report_context_t
*ctx
= userData
;
1649 report_state_e state
;
1651 state
= parser
->state
->current_state
;
1655 /* nothing to close yet. */
1656 return SVN_NO_ERROR
;
1659 if (((state
== OPEN_DIR
&& (strcmp(name
.name
, "open-directory") == 0)) ||
1660 (state
== ADD_DIR
&& (strcmp(name
.name
, "add-directory") == 0))))
1662 const char *checked_in_url
;
1663 report_info_t
*info
= parser
->state
->private;
1665 /* We've now closed this directory; note it. */
1666 info
->dir
->tag_closed
= TRUE
;
1668 /* go fetch info->file_name from DAV:checked-in */
1670 svn_ra_serf__get_ver_prop(info
->dir
->props
, info
->base_name
,
1671 info
->base_rev
, "DAV:", "checked-in");
1673 /* If we were expecting to have the properties and we aren't able to
1676 if (!checked_in_url
&&
1677 (!SVN_IS_VALID_REVNUM(info
->dir
->base_rev
) || info
->dir
->fetch_props
))
1679 return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED
, NULL
,
1680 _("The OPTIONS response did not include the "
1681 "requested checked-in value"));
1684 info
->dir
->url
= checked_in_url
;
1686 /* At this point, we should have the checked-in href.
1687 * If needed, create the PROPFIND to retrieve the dir's properties.
1689 if (!SVN_IS_VALID_REVNUM(info
->dir
->base_rev
) || info
->dir
->fetch_props
)
1691 /* Unconditionally set fetch_props now. */
1692 info
->dir
->fetch_props
= TRUE
;
1694 svn_ra_serf__deliver_props(&info
->dir
->propfind
, info
->dir
->props
,
1696 ctx
->sess
->conns
[ctx
->sess
->cur_conn
],
1697 info
->dir
->url
, info
->dir
->target_rev
,
1698 "0", all_props
, FALSE
,
1699 &ctx
->done_propfinds
, info
->dir
->pool
);
1701 if (!info
->dir
->propfind
)
1706 ctx
->active_propfinds
++;
1710 info
->dir
->propfind
= NULL
;
1713 svn_ra_serf__xml_pop_state(parser
);
1715 else if (state
== OPEN_FILE
&& strcmp(name
.name
, "open-file") == 0)
1717 report_info_t
*info
= parser
->state
->private;
1719 /* Expand our full name now if we haven't done so yet. */
1722 info
->name_buf
= svn_stringbuf_dup(info
->dir
->name_buf
, info
->pool
);
1723 svn_path_add_component(info
->name_buf
, info
->base_name
);
1724 info
->name
= info
->name_buf
->data
;
1727 info
->lock_token
= apr_hash_get(ctx
->lock_path_tokens
, info
->name
,
1728 APR_HASH_KEY_STRING
);
1730 if (info
->lock_token
&& info
->fetch_props
== FALSE
)
1731 info
->fetch_props
= TRUE
;
1733 /* If we have a WC, we can dive all the way into the WC to get the
1734 * previous URL so we can do an differential GET with the base URL.
1736 * If we don't have a WC (as is the case for URL<->URL diff), we can
1737 * manually reconstruct the base URL. This avoids us having to grab
1738 * two full-text for URL<->URL diffs. Instead, we can just grab one
1739 * full-text and a diff from the server against that other file.
1741 if (ctx
->sess
->wc_callbacks
->get_wc_prop
)
1743 ctx
->sess
->wc_callbacks
->get_wc_prop(ctx
->sess
->wc_callback_baton
,
1745 SVN_RA_SERF__WC_CHECKED_IN_URL
,
1752 apr_size_t comp_count
;
1753 svn_stringbuf_t
*path
;
1755 c
= svn_ra_serf__get_ver_prop(info
->props
, info
->base_name
,
1756 info
->base_rev
, "DAV:", "checked-in");
1758 path
= svn_stringbuf_create(c
, info
->pool
);
1760 comp_count
= svn_path_component_count(info
->name_buf
->data
);
1762 svn_path_remove_components(path
, comp_count
);
1764 /* Find out the difference of the destination compared to the repos
1765 * root url. Cut of this difference from path, which will give us our
1766 * version resource root path.
1770 * /repositories/log_tests-17/!svn/ver/4/branches/a
1772 * http://localhost/repositories/log_tests-17
1774 * http://localhost/repositories/log_tests-17/branches/a
1776 * So, find 'branches/a' as the difference. Cut it of path, gives us:
1777 * /repositories/log_tests-17/!svn/ver/4
1779 if (ctx
->destination
&&
1780 strcmp(ctx
->destination
, ctx
->sess
->repos_root_str
) != 0)
1782 apr_size_t root_count
, src_count
;
1784 src_count
= svn_path_component_count(ctx
->destination
);
1785 root_count
= svn_path_component_count(ctx
->sess
->repos_root_str
);
1787 svn_path_remove_components(path
, src_count
- root_count
);
1790 /* At this point, we should just have the version number
1791 * remaining. We know our target revision, so we'll replace it
1792 * and recreate what we just chopped off.
1794 svn_path_remove_component(path
);
1796 svn_path_add_component(path
, apr_ltoa(info
->pool
, info
->base_rev
));
1798 /* Similar as above, we now have to add the relative path between
1799 * source and root path.
1803 * /repositories/log_tests-17/!svn/ver/2
1805 * /repositories/log_tests-17
1807 * /repositories/log_tests-17/trunk
1809 * So, find 'trunk' as the difference. Addding it to path, gives us:
1810 * /repositories/log_tests-17/!svn/ver/2/trunk
1812 if (strcmp(ctx
->source
, ctx
->sess
->repos_root
.path
) != 0)
1814 apr_size_t root_len
;
1816 root_len
= strlen(ctx
->sess
->repos_root
.path
) + 1;
1818 svn_path_add_component(path
, &ctx
->source
[root_len
]);
1821 /* Re-add the filename. */
1822 svn_path_add_component(path
, info
->name
);
1824 info
->delta_base
= svn_string_create_from_buf(path
, info
->pool
);
1827 SVN_ERR(fetch_file(ctx
, info
));
1828 svn_ra_serf__xml_pop_state(parser
);
1830 else if (state
== ADD_FILE
&& strcmp(name
.name
, "add-file") == 0)
1832 /* We should have everything we need to fetch the file. */
1833 SVN_ERR(fetch_file(ctx
, parser
->state
->private));
1834 svn_ra_serf__xml_pop_state(parser
);
1836 else if (state
== PROP
)
1838 /* We need to move the prop_ns, prop_name, and prop_val into the
1839 * same lifetime as the dir->pool.
1841 svn_ra_serf__ns_t
*ns
, *ns_name_match
;
1843 report_info_t
*info
;
1846 const char *set_val
;
1847 svn_string_t
*set_val_str
;
1850 info
= parser
->state
->private;
1853 /* We're going to be slightly tricky. We don't care what the ->url
1854 * field is here at this point. So, we're going to stick a single
1855 * copy of the property name inside of the ->url field.
1857 ns_name_match
= NULL
;
1858 for (ns
= dir
->ns_list
; ns
; ns
= ns
->next
)
1860 if (strcmp(ns
->namespace, info
->prop_ns
) == 0)
1863 if (strcmp(ns
->url
, info
->prop_name
) == 0)
1873 ns
= apr_palloc(dir
->pool
, sizeof(*ns
));
1876 ns
->namespace = apr_pstrdup(dir
->pool
, info
->prop_ns
);
1880 ns
->namespace = ns_name_match
->namespace;
1882 ns
->url
= apr_pstrdup(dir
->pool
, info
->prop_name
);
1884 ns
->next
= dir
->ns_list
;
1888 if (strcmp(name
.name
, "remove-prop") != 0)
1890 props
= info
->props
;
1895 props
= dir
->removed_props
;
1897 info
->prop_val
= "";
1898 info
->prop_val_len
= 1;
1901 if (info
->prop_encoding
)
1903 if (strcmp(info
->prop_encoding
, "base64") == 0)
1905 svn_string_t encoded
;
1906 const svn_string_t
*decoded
;
1908 encoded
.data
= info
->prop_val
;
1909 encoded
.len
= info
->prop_val_len
;
1911 decoded
= svn_base64_decode_string(&encoded
, parser
->state
->pool
);
1913 info
->prop_val
= decoded
->data
;
1914 info
->prop_val_len
= decoded
->len
;
1918 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA
,
1920 _("Got unrecognized encoding '%s'"),
1921 info
->prop_encoding
);
1926 set_val
= apr_pmemdup(pool
, info
->prop_val
, info
->prop_val_len
);
1927 set_val_str
= svn_string_ncreate(set_val
, info
->prop_val_len
, pool
);
1929 svn_ra_serf__set_ver_prop(props
, info
->base_name
, info
->base_rev
,
1930 ns
->namespace, ns
->url
, set_val_str
, pool
);
1931 svn_ra_serf__xml_pop_state(parser
);
1933 else if (state
== IGNORE_PROP_NAME
|| state
== NEED_PROP_NAME
)
1935 svn_ra_serf__xml_pop_state(parser
);
1938 return SVN_NO_ERROR
;
1941 static svn_error_t
*
1942 cdata_report(svn_ra_serf__xml_parser_t
*parser
,
1947 report_context_t
*ctx
= userData
;
1951 if (parser
->state
->current_state
== PROP
)
1953 report_info_t
*info
= parser
->state
->private;
1955 svn_ra_serf__expand_string(&info
->prop_val
, &info
->prop_val_len
,
1956 data
, len
, parser
->state
->pool
);
1959 return SVN_NO_ERROR
;
1963 /** Editor callbacks given to callers to create request body */
1965 static svn_error_t
*
1966 set_path(void *report_baton
,
1968 svn_revnum_t revision
,
1970 svn_boolean_t start_empty
,
1971 const char *lock_token
,
1974 report_context_t
*report
= report_baton
;
1976 svn_stringbuf_t
*path_buf
;
1978 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("<S:entry rev=\"",
1979 sizeof("<S:entry rev=\"")-1,
1980 report
->sess
->bkt_alloc
);
1981 serf_bucket_aggregate_append(report
->buckets
, tmp
);
1983 tmp
= SERF_BUCKET_SIMPLE_STRING(apr_ltoa(report
->pool
, revision
),
1984 report
->sess
->bkt_alloc
);
1985 serf_bucket_aggregate_append(report
->buckets
, tmp
);
1987 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\"", sizeof("\"")-1,
1988 report
->sess
->bkt_alloc
);
1989 serf_bucket_aggregate_append(report
->buckets
, tmp
);
1993 apr_hash_set(report
->lock_path_tokens
,
1994 apr_pstrdup(report
->pool
, path
),
1995 APR_HASH_KEY_STRING
,
1996 apr_pstrdup(report
->pool
, lock_token
));
1998 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(" lock-token=\"",
1999 sizeof(" lock-token=\"")-1,
2000 report
->sess
->bkt_alloc
);
2001 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2003 tmp
= SERF_BUCKET_SIMPLE_STRING(lock_token
,
2004 report
->sess
->bkt_alloc
);
2005 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2007 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\"", sizeof("\"")-1,
2008 report
->sess
->bkt_alloc
);
2009 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2013 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(" depth=\"",
2014 sizeof(" depth=\"")-1,
2015 report
->sess
->bkt_alloc
);
2016 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2018 tmp
= SERF_BUCKET_SIMPLE_STRING(svn_depth_to_word(depth
),
2019 report
->sess
->bkt_alloc
);
2020 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2022 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\"", sizeof("\"")-1,
2023 report
->sess
->bkt_alloc
);
2024 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2028 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(" start-empty=\"true\"",
2029 sizeof(" start-empty=\"true\"")-1,
2030 report
->sess
->bkt_alloc
);
2031 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2034 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(">", sizeof(">")-1,
2035 report
->sess
->bkt_alloc
);
2036 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2039 svn_xml_escape_cdata_cstring(&path_buf
, path
, report
->pool
);
2041 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(path_buf
->data
, path_buf
->len
,
2042 report
->sess
->bkt_alloc
);
2043 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2045 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("</S:entry>",
2046 sizeof("</S:entry>")-1,
2047 report
->sess
->bkt_alloc
);
2049 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2053 static svn_error_t
*
2054 delete_path(void *report_baton
,
2058 report_context_t
*report
= report_baton
;
2061 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("<S:missing>",
2062 sizeof("<S:missing>")-1,
2063 report
->sess
->bkt_alloc
);
2064 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2066 tmp
= SERF_BUCKET_SIMPLE_STRING(apr_pstrdup(report
->pool
, path
),
2067 report
->sess
->bkt_alloc
);
2068 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2070 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("</S:missing>",
2071 sizeof("</S:missing>")-1,
2072 report
->sess
->bkt_alloc
);
2074 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2078 static svn_error_t
*
2079 link_path(void *report_baton
,
2082 svn_revnum_t revision
,
2084 svn_boolean_t start_empty
,
2085 const char *lock_token
,
2088 report_context_t
*report
= report_baton
;
2090 const char *link
, *vcc_url
;
2092 apr_status_t status
;
2094 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("<S:entry rev=\"",
2095 sizeof("<S:entry rev=\"")-1,
2096 report
->sess
->bkt_alloc
);
2097 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2099 tmp
= SERF_BUCKET_SIMPLE_STRING(apr_ltoa(report
->pool
, revision
),
2100 report
->sess
->bkt_alloc
);
2101 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2103 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\"", sizeof("\"")-1,
2104 report
->sess
->bkt_alloc
);
2105 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2109 apr_hash_set(report
->lock_path_tokens
,
2110 apr_pstrdup(report
->pool
, path
),
2111 APR_HASH_KEY_STRING
,
2112 apr_pstrdup(report
->pool
, lock_token
));
2114 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(" lock-token=\"",
2115 sizeof(" lock-token=\"")-1,
2116 report
->sess
->bkt_alloc
);
2117 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2119 tmp
= SERF_BUCKET_SIMPLE_STRING(lock_token
,
2120 report
->sess
->bkt_alloc
);
2121 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2123 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\"", sizeof("\"")-1,
2124 report
->sess
->bkt_alloc
);
2125 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2129 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(" depth=\"",
2130 sizeof(" depth=\"")-1,
2131 report
->sess
->bkt_alloc
);
2132 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2134 tmp
= SERF_BUCKET_SIMPLE_STRING(svn_depth_to_word(depth
),
2135 report
->sess
->bkt_alloc
);
2136 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2138 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\"", sizeof("\"")-1,
2139 report
->sess
->bkt_alloc
);
2140 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2144 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(" start-empty=\"true\"",
2145 sizeof(" start-empty=\"true\"")-1,
2146 report
->sess
->bkt_alloc
);
2147 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2150 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(" linkpath=\"/",
2151 sizeof(" linkpath=\"/")-1,
2152 report
->sess
->bkt_alloc
);
2153 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2155 /* We need to pass in the baseline relative path.
2157 * TODO Confirm that it's on the same server?
2159 status
= apr_uri_parse(pool
, url
, &uri
);
2162 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA
, NULL
,
2163 _("Unable to parse URL '%s'"), url
);
2166 SVN_ERR(svn_ra_serf__discover_root(&vcc_url
, &link
, report
->sess
,
2167 report
->sess
->conns
[0], uri
.path
, pool
));
2169 tmp
= SERF_BUCKET_SIMPLE_STRING(apr_pstrdup(report
->pool
, link
),
2170 report
->sess
->bkt_alloc
);
2171 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2173 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\"", sizeof("\"")-1,
2174 report
->sess
->bkt_alloc
);
2175 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2177 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(">", sizeof(">")-1,
2178 report
->sess
->bkt_alloc
);
2179 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2181 tmp
= SERF_BUCKET_SIMPLE_STRING(apr_pstrdup(report
->pool
, path
),
2182 report
->sess
->bkt_alloc
);
2183 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2185 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("</S:entry>",
2186 sizeof("</S:entry>")-1,
2187 report
->sess
->bkt_alloc
);
2189 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2193 static svn_error_t
*
2194 finish_report(void *report_baton
,
2197 report_context_t
*report
= report_baton
;
2198 svn_ra_serf__session_t
*sess
= report
->sess
;
2199 svn_ra_serf__handler_t
*handler
;
2200 svn_ra_serf__xml_parser_t
*parser_ctx
;
2201 svn_ra_serf__list_t
*done_list
;
2203 const char *vcc_url
;
2205 apr_status_t status
;
2206 svn_boolean_t closed_root
;
2209 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("</S:update-report>",
2210 sizeof("</S:update-report>")-1,
2211 report
->sess
->bkt_alloc
);
2212 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2214 props
= apr_hash_make(pool
);
2216 SVN_ERR(svn_ra_serf__discover_root(&vcc_url
, NULL
, sess
, sess
->conns
[0],
2217 sess
->repos_url
.path
, pool
));
2221 return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED
, NULL
,
2222 _("The OPTIONS response did not include the "
2223 "requested version-controlled-configuration "
2227 /* create and deliver request */
2228 report
->path
= vcc_url
;
2230 handler
= apr_pcalloc(pool
, sizeof(*handler
));
2232 handler
->method
= "REPORT";
2233 handler
->path
= report
->path
;
2234 handler
->body_buckets
= report
->buckets
;
2235 handler
->body_type
= "text/xml";
2236 handler
->conn
= sess
->conns
[0];
2237 handler
->session
= sess
;
2239 parser_ctx
= apr_pcalloc(pool
, sizeof(*parser_ctx
));
2241 parser_ctx
->pool
= pool
;
2242 parser_ctx
->user_data
= report
;
2243 parser_ctx
->start
= start_report
;
2244 parser_ctx
->end
= end_report
;
2245 parser_ctx
->cdata
= cdata_report
;
2246 parser_ctx
->done
= &report
->done
;
2248 handler
->response_handler
= svn_ra_serf__handle_xml_parser
;
2249 handler
->response_baton
= parser_ctx
;
2251 svn_ra_serf__request_create(handler
);
2253 for (i
= sess
->num_conns
; i
< 4; i
++)
2255 sess
->conns
[i
] = apr_palloc(sess
->pool
, sizeof(*sess
->conns
[i
]));
2256 sess
->conns
[i
]->bkt_alloc
= serf_bucket_allocator_create(sess
->pool
,
2258 sess
->conns
[i
]->address
= sess
->conns
[0]->address
;
2259 sess
->conns
[i
]->hostinfo
= sess
->conns
[0]->hostinfo
;
2260 sess
->conns
[i
]->using_ssl
= sess
->conns
[0]->using_ssl
;
2261 sess
->conns
[i
]->using_compression
= sess
->conns
[0]->using_compression
;
2262 sess
->conns
[i
]->proxy_auth_header
= sess
->conns
[0]->proxy_auth_header
;
2263 sess
->conns
[i
]->proxy_auth_value
= sess
->conns
[0]->proxy_auth_value
;
2264 sess
->conns
[i
]->useragent
= sess
->conns
[0]->useragent
;
2265 sess
->conns
[i
]->last_status_code
= -1;
2266 sess
->conns
[i
]->ssl_context
= NULL
;
2267 sess
->conns
[i
]->session
= sess
;
2268 sess
->conns
[i
]->conn
= serf_connection_create(sess
->context
,
2269 sess
->conns
[i
]->address
,
2270 svn_ra_serf__conn_setup
,
2272 svn_ra_serf__conn_closed
,
2277 /* Authentication protocol specific initalization. */
2278 if (sess
->auth_protocol
)
2279 sess
->auth_protocol
->init_conn_func(sess
, sess
->conns
[i
], pool
);
2283 closed_root
= FALSE
;
2285 while (!report
->done
|| report
->active_fetches
|| report
->active_propfinds
)
2287 status
= serf_context_run(sess
->context
, SERF_DURATION_FOREVER
, pool
);
2288 if (APR_STATUS_IS_TIMEUP(status
))
2294 SVN_ERR(sess
->pending_error
);
2296 return svn_error_wrap_apr(status
, _("Error retrieving REPORT (%d)"),
2300 /* Switch our connection. */
2302 if (++sess
->cur_conn
== sess
->num_conns
)
2305 /* prune our propfind list if they are done. */
2306 done_list
= report
->done_propfinds
;
2309 report
->active_propfinds
--;
2311 /* If we have some files that we won't be fetching the content
2312 * for, ensure that we update the file with any altered props.
2314 if (report
->file_propchanges_only
)
2316 svn_ra_serf__list_t
*cur
, *prev
;
2319 cur
= report
->file_propchanges_only
;
2323 report_info_t
*item
= cur
->data
;
2325 if (item
->propfind
== done_list
->data
)
2334 /* If we found a match, set the new props and remove this
2335 * propchange from our list.
2339 SVN_ERR(handle_propchange_only(cur
->data
));
2343 report
->file_propchanges_only
= cur
->next
;
2347 prev
->next
= cur
->next
;
2352 done_list
= done_list
->next
;
2354 report
->done_propfinds
= NULL
;
2356 /* prune our fetches list if they are done. */
2357 done_list
= report
->done_fetches
;
2360 report_fetch_t
*done_fetch
= done_list
->data
;
2361 report_dir_t
*cur_dir
;
2363 if (done_fetch
->err
)
2365 svn_error_t
*err
= done_fetch
->err
;
2366 /* Error found. There might be more, clear those first. */
2367 done_list
= done_list
->next
;
2370 done_fetch
= done_list
->data
;
2371 if (done_fetch
->err
)
2372 svn_error_clear(done_fetch
->err
);
2373 done_list
= done_list
->next
;
2378 /* decrease our parent's directory refcount. */
2379 cur_dir
= done_fetch
->info
->dir
;
2380 cur_dir
->ref_count
--;
2382 /* Decrement our active fetch count. */
2383 report
->active_fetches
--;
2385 done_list
= done_list
->next
;
2387 /* If we have a valid directory and
2388 * we have no open items in this dir and
2389 * we've closed the directory tag (no more children can be added)
2391 * we know we won't be fetching props or
2392 * we've already completed the propfind
2393 * then, we know it's time for us to close this directory.
2395 while (cur_dir
&& !cur_dir
->ref_count
&& cur_dir
->tag_closed
&&
2396 (!cur_dir
->fetch_props
||
2397 svn_ra_serf__propfind_is_done(cur_dir
->propfind
)))
2399 report_dir_t
*parent
= cur_dir
->parent_dir
;
2401 SVN_ERR(close_dir(cur_dir
));
2404 parent
->ref_count
--;
2413 report
->done_fetches
= NULL
;
2415 /* Debugging purposes only! */
2416 serf_debug__closed_conn(sess
->bkt_alloc
);
2417 for (i
= 0; i
< sess
->num_conns
; i
++)
2419 serf_debug__closed_conn(sess
->conns
[i
]->bkt_alloc
);
2423 /* Ensure that we opened and closed our root dir and that we closed
2424 * all of our children. */
2425 if (closed_root
== FALSE
)
2427 SVN_ERR(close_all_dirs(report
->root_dir
));
2431 SVN_ERR(report
->update_editor
->close_edit(report
->update_baton
, sess
->pool
));
2433 return SVN_NO_ERROR
;
2436 static svn_error_t
*
2437 abort_report(void *report_baton
,
2441 report_context_t
*report
= report_baton
;
2446 static const svn_ra_reporter3_t ra_serf_reporter
= {
2455 /** RA function implementations and body */
2457 static svn_error_t
*
2458 make_update_reporter(svn_ra_session_t
*ra_session
,
2459 const svn_ra_reporter3_t
**reporter
,
2460 void **report_baton
,
2461 svn_revnum_t revision
,
2462 const char *src_path
,
2463 const char *dest_path
,
2464 const char *update_target
,
2466 svn_boolean_t ignore_ancestry
,
2467 svn_boolean_t text_deltas
,
2468 svn_boolean_t send_copyfrom_args
,
2469 const svn_delta_editor_t
*update_editor
,
2473 report_context_t
*report
;
2475 svn_stringbuf_t
*path_buf
;
2476 const svn_delta_editor_t
*filter_editor
;
2478 svn_boolean_t has_target
= *update_target
? TRUE
: FALSE
;
2479 svn_boolean_t server_supports_depth
;
2480 svn_ra_serf__session_t
*sess
= ra_session
->priv
;
2482 SVN_ERR(svn_ra_serf__has_capability(ra_session
, &server_supports_depth
,
2483 SVN_RA_CAPABILITY_DEPTH
, pool
));
2484 /* We can skip the depth filtering when the user requested
2485 depth_files or depth_infinity because the server will
2486 transmit the right stuff anyway. */
2487 if ((depth
!= svn_depth_files
)
2488 && (depth
!= svn_depth_infinity
)
2489 && ! server_supports_depth
)
2491 SVN_ERR(svn_delta_depth_filter_editor(&filter_editor
,
2497 update_editor
= filter_editor
;
2498 update_baton
= filter_baton
;
2501 report
= apr_pcalloc(pool
, sizeof(*report
));
2502 report
->pool
= pool
;
2503 report
->sess
= sess
;
2504 report
->conn
= report
->sess
->conns
[0];
2505 report
->target_rev
= revision
;
2506 report
->ignore_ancestry
= ignore_ancestry
;
2507 report
->send_copyfrom_args
= send_copyfrom_args
;
2508 report
->text_deltas
= text_deltas
;
2509 report
->lock_path_tokens
= apr_hash_make(pool
);
2514 svn_xml_escape_cdata_cstring(&path_buf
, src_path
, report
->pool
);
2515 report
->source
= path_buf
->data
;
2521 svn_xml_escape_cdata_cstring(&path_buf
, dest_path
, report
->pool
);
2522 report
->destination
= path_buf
->data
;
2528 svn_xml_escape_cdata_cstring(&path_buf
, update_target
, report
->pool
);
2529 report
->update_target
= path_buf
->data
;
2532 report
->update_editor
= update_editor
;
2533 report
->update_baton
= update_baton
;
2534 report
->done
= FALSE
;
2536 *reporter
= &ra_serf_reporter
;
2537 *report_baton
= report
;
2539 report
->buckets
= serf_bucket_aggregate_create(report
->sess
->bkt_alloc
);
2541 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("<S:update-report xmlns:S=\"",
2542 sizeof("<S:update-report xmlns:S=\"")-1,
2543 report
->sess
->bkt_alloc
);
2544 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2546 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN(SVN_XML_NAMESPACE
,
2547 sizeof(SVN_XML_NAMESPACE
)-1,
2548 report
->sess
->bkt_alloc
);
2549 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2551 tmp
= SERF_BUCKET_SIMPLE_STRING_LEN("\">",
2553 report
->sess
->bkt_alloc
);
2554 serf_bucket_aggregate_append(report
->buckets
, tmp
);
2556 svn_ra_serf__add_tag_buckets(report
->buckets
,
2557 "S:src-path", report
->source
,
2558 report
->sess
->bkt_alloc
);
2560 if (SVN_IS_VALID_REVNUM(report
->target_rev
))
2562 svn_ra_serf__add_tag_buckets(report
->buckets
,
2563 "S:target-revision",
2564 apr_ltoa(pool
, report
->target_rev
),
2565 report
->sess
->bkt_alloc
);
2568 if (report
->destination
&& *report
->destination
)
2570 svn_ra_serf__add_tag_buckets(report
->buckets
,
2572 report
->destination
,
2573 report
->sess
->bkt_alloc
);
2576 if (report
->update_target
&& *report
->update_target
)
2578 svn_ra_serf__add_tag_buckets(report
->buckets
,
2579 "S:update-target", report
->update_target
,
2580 report
->sess
->bkt_alloc
);
2583 if (report
->ignore_ancestry
)
2585 svn_ra_serf__add_tag_buckets(report
->buckets
,
2586 "S:ignore-ancestry", "yes",
2587 report
->sess
->bkt_alloc
);
2590 if (report
->send_copyfrom_args
)
2592 svn_ra_serf__add_tag_buckets(report
->buckets
,
2593 "S:send-copyfrom-args", "yes",
2594 report
->sess
->bkt_alloc
);
2597 /* Old servers know "recursive" but not "depth"; help them DTRT. */
2598 if (depth
== svn_depth_files
|| depth
== svn_depth_empty
)
2600 svn_ra_serf__add_tag_buckets(report
->buckets
,
2601 "S:recursive", "no",
2602 report
->sess
->bkt_alloc
);
2605 svn_ra_serf__add_tag_buckets(report
->buckets
,
2606 "S:depth", svn_depth_to_word(depth
),
2607 report
->sess
->bkt_alloc
);
2609 return SVN_NO_ERROR
;
2613 svn_ra_serf__do_update(svn_ra_session_t
*ra_session
,
2614 const svn_ra_reporter3_t
**reporter
,
2615 void **report_baton
,
2616 svn_revnum_t revision_to_update_to
,
2617 const char *update_target
,
2619 svn_boolean_t send_copyfrom_args
,
2620 const svn_delta_editor_t
*update_editor
,
2624 svn_ra_serf__session_t
*session
= ra_session
->priv
;
2626 return make_update_reporter(ra_session
, reporter
, report_baton
,
2627 revision_to_update_to
,
2628 session
->repos_url
.path
, NULL
, update_target
,
2629 depth
, FALSE
, TRUE
, send_copyfrom_args
,
2630 update_editor
, update_baton
, pool
);
2634 svn_ra_serf__do_diff(svn_ra_session_t
*ra_session
,
2635 const svn_ra_reporter3_t
**reporter
,
2636 void **report_baton
,
2637 svn_revnum_t revision
,
2638 const char *diff_target
,
2640 svn_boolean_t ignore_ancestry
,
2641 svn_boolean_t text_deltas
,
2642 const char *versus_url
,
2643 const svn_delta_editor_t
*diff_editor
,
2647 svn_ra_serf__session_t
*session
= ra_session
->priv
;
2649 return make_update_reporter(ra_session
, reporter
, report_baton
,
2651 session
->repos_url
.path
, versus_url
, diff_target
,
2652 depth
, ignore_ancestry
, text_deltas
, FALSE
,
2653 diff_editor
, diff_baton
, pool
);
2657 svn_ra_serf__do_status(svn_ra_session_t
*ra_session
,
2658 const svn_ra_reporter3_t
**reporter
,
2659 void **report_baton
,
2660 const char *status_target
,
2661 svn_revnum_t revision
,
2663 const svn_delta_editor_t
*status_editor
,
2667 svn_ra_serf__session_t
*session
= ra_session
->priv
;
2669 return make_update_reporter(ra_session
, reporter
, report_baton
,
2671 session
->repos_url
.path
, NULL
, status_target
,
2672 depth
, FALSE
, FALSE
, FALSE
,
2673 status_editor
, status_baton
, pool
);
2677 svn_ra_serf__do_switch(svn_ra_session_t
*ra_session
,
2678 const svn_ra_reporter3_t
**reporter
,
2679 void **report_baton
,
2680 svn_revnum_t revision_to_switch_to
,
2681 const char *switch_target
,
2683 const char *switch_url
,
2684 const svn_delta_editor_t
*switch_editor
,
2688 svn_ra_serf__session_t
*session
= ra_session
->priv
;
2690 return make_update_reporter(ra_session
, reporter
, report_baton
,
2691 revision_to_switch_to
,
2692 session
->repos_url
.path
,
2693 switch_url
, switch_target
,
2694 depth
, TRUE
, TRUE
, FALSE
/* TODO(sussman) */,
2695 switch_editor
, switch_baton
, pool
);
2699 svn_ra_serf__get_file(svn_ra_session_t
*ra_session
,
2701 svn_revnum_t revision
,
2702 svn_stream_t
*stream
,
2703 svn_revnum_t
*fetched_rev
,
2707 svn_ra_serf__session_t
*session
= ra_session
->priv
;
2708 svn_ra_serf__connection_t
*conn
;
2709 svn_ra_serf__handler_t
*handler
;
2710 const char *fetch_url
;
2711 apr_hash_t
*fetch_props
;
2713 /* What connection should we go on? */
2714 conn
= session
->conns
[session
->cur_conn
];
2716 /* Fetch properties. */
2717 fetch_props
= apr_hash_make(pool
);
2719 fetch_url
= svn_path_url_add_component(session
->repos_url
.path
, path
, pool
);
2721 /* The simple case is if we want HEAD - then a GET on the fetch_url is fine.
2723 * Otherwise, we need to get the baseline version for this particular
2724 * revision and then fetch that file.
2726 if (SVN_IS_VALID_REVNUM(revision
))
2728 const char *vcc_url
, *rel_path
, *baseline_url
;
2730 SVN_ERR(svn_ra_serf__discover_root(&vcc_url
, &rel_path
,
2731 session
, conn
, fetch_url
, pool
));
2733 SVN_ERR(svn_ra_serf__retrieve_props(fetch_props
, session
, conn
, vcc_url
,
2734 revision
, "0", baseline_props
, pool
));
2736 baseline_url
= svn_ra_serf__get_ver_prop(fetch_props
, vcc_url
, revision
,
2737 "DAV:", "baseline-collection");
2739 fetch_url
= svn_path_url_add_component(baseline_url
, rel_path
, pool
);
2740 revision
= SVN_INVALID_REVNUM
;
2743 /* TODO Filter out all of our props into a usable format. */
2746 *props
= apr_hash_make(pool
);
2748 SVN_ERR(svn_ra_serf__retrieve_props(fetch_props
, session
, conn
, fetch_url
,
2749 revision
, "0", all_props
, pool
));
2751 svn_ra_serf__walk_all_props(fetch_props
, fetch_url
, revision
,
2752 svn_ra_serf__set_flat_props
, *props
, pool
);
2757 report_fetch_t
*stream_ctx
;
2759 /* Create the fetch context. */
2760 stream_ctx
= apr_pcalloc(pool
, sizeof(*stream_ctx
));
2761 stream_ctx
->pool
= pool
;
2762 stream_ctx
->target_stream
= stream
;
2763 stream_ctx
->sess
= session
;
2764 stream_ctx
->conn
= conn
;
2765 stream_ctx
->info
= apr_pcalloc(pool
, sizeof(*stream_ctx
->info
));
2766 stream_ctx
->info
->name
= fetch_url
;
2768 handler
= apr_pcalloc(pool
, sizeof(*handler
));
2769 handler
->method
= "GET";
2770 handler
->path
= fetch_url
;
2771 handler
->conn
= conn
;
2772 handler
->session
= session
;
2774 handler
->response_handler
= handle_stream
;
2775 handler
->response_baton
= stream_ctx
;
2777 handler
->response_error
= cancel_fetch
;
2778 handler
->response_error_baton
= stream_ctx
;
2780 svn_ra_serf__request_create(handler
);
2782 SVN_ERR(svn_ra_serf__context_run_wait(&stream_ctx
->done
, session
, pool
));
2783 SVN_ERR(stream_ctx
->err
);
2786 return SVN_NO_ERROR
;