1 | /* |
---|
2 | * merge.c : MERGE response parsing functions for ra_serf |
---|
3 | * |
---|
4 | * ==================================================================== |
---|
5 | * Copyright (c) 2006-2007 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 <apr_uri.h> |
---|
22 | |
---|
23 | #include <expat.h> |
---|
24 | |
---|
25 | #include <serf.h> |
---|
26 | |
---|
27 | #include "svn_pools.h" |
---|
28 | #include "svn_ra.h" |
---|
29 | #include "svn_dav.h" |
---|
30 | #include "svn_xml.h" |
---|
31 | #include "svn_config.h" |
---|
32 | #include "svn_delta.h" |
---|
33 | #include "svn_version.h" |
---|
34 | #include "svn_path.h" |
---|
35 | #include "svn_props.h" |
---|
36 | |
---|
37 | #include "private/svn_dav_protocol.h" |
---|
38 | #include "svn_private_config.h" |
---|
39 | |
---|
40 | #include "ra_serf.h" |
---|
41 | #include "../libsvn_ra/ra_loader.h" |
---|
42 | |
---|
43 | |
---|
44 | /* |
---|
45 | * This enum represents the current state of our XML parsing for a MERGE. |
---|
46 | */ |
---|
47 | typedef enum { |
---|
48 | NONE = 0, |
---|
49 | MERGE_RESPONSE, |
---|
50 | UPDATED_SET, |
---|
51 | RESPONSE, |
---|
52 | HREF, |
---|
53 | PROPSTAT, |
---|
54 | PROP, |
---|
55 | RESOURCE_TYPE, |
---|
56 | AUTHOR, |
---|
57 | NAME, |
---|
58 | DATE, |
---|
59 | IGNORE_PROP_NAME, |
---|
60 | NEED_PROP_NAME, |
---|
61 | PROP_VAL, |
---|
62 | } merge_state_e; |
---|
63 | |
---|
64 | typedef enum { |
---|
65 | UNSET, |
---|
66 | BASELINE, |
---|
67 | COLLECTION, |
---|
68 | CHECKED_IN, |
---|
69 | } resource_type_e; |
---|
70 | |
---|
71 | typedef struct { |
---|
72 | /* Temporary allocations here please */ |
---|
73 | apr_pool_t *pool; |
---|
74 | |
---|
75 | resource_type_e type; |
---|
76 | |
---|
77 | apr_hash_t *props; |
---|
78 | |
---|
79 | const char *prop_ns; |
---|
80 | const char *prop_name; |
---|
81 | const char *prop_val; |
---|
82 | apr_size_t prop_val_len; |
---|
83 | } merge_info_t; |
---|
84 | |
---|
85 | /* Structure associated with a MERGE request. */ |
---|
86 | struct svn_ra_serf__merge_context_t |
---|
87 | { |
---|
88 | apr_pool_t *pool; |
---|
89 | |
---|
90 | svn_ra_serf__session_t *session; |
---|
91 | |
---|
92 | apr_hash_t *lock_tokens; |
---|
93 | svn_boolean_t keep_locks; |
---|
94 | |
---|
95 | const char *activity_url; |
---|
96 | apr_size_t activity_url_len; |
---|
97 | |
---|
98 | const char *merge_url; |
---|
99 | |
---|
100 | int status; |
---|
101 | |
---|
102 | svn_boolean_t done; |
---|
103 | |
---|
104 | svn_commit_info_t *commit_info; |
---|
105 | }; |
---|
106 | |
---|
107 | |
---|
108 | static merge_info_t * |
---|
109 | push_state(svn_ra_serf__xml_parser_t *parser, |
---|
110 | svn_ra_serf__merge_context_t *ctx, |
---|
111 | merge_state_e state) |
---|
112 | { |
---|
113 | merge_info_t *info; |
---|
114 | |
---|
115 | svn_ra_serf__xml_push_state(parser, state); |
---|
116 | |
---|
117 | if (state == RESPONSE) |
---|
118 | { |
---|
119 | info = apr_palloc(parser->state->pool, sizeof(*info)); |
---|
120 | info->pool = parser->state->pool; |
---|
121 | info->props = apr_hash_make(info->pool); |
---|
122 | |
---|
123 | parser->state->private = info; |
---|
124 | } |
---|
125 | |
---|
126 | return parser->state->private; |
---|
127 | } |
---|
128 | |
---|
129 | static svn_error_t * |
---|
130 | start_merge(svn_ra_serf__xml_parser_t *parser, |
---|
131 | void *userData, |
---|
132 | svn_ra_serf__dav_props_t name, |
---|
133 | const char **attrs) |
---|
134 | { |
---|
135 | svn_ra_serf__merge_context_t *ctx = userData; |
---|
136 | merge_state_e state; |
---|
137 | merge_info_t *info; |
---|
138 | |
---|
139 | state = parser->state->current_state; |
---|
140 | |
---|
141 | if (state == NONE && |
---|
142 | strcmp(name.name, "merge-response") == 0) |
---|
143 | { |
---|
144 | push_state(parser, ctx, MERGE_RESPONSE); |
---|
145 | } |
---|
146 | else if (state == NONE) |
---|
147 | { |
---|
148 | /* do nothing as we haven't seen our valid start tag yet. */ |
---|
149 | } |
---|
150 | else if (state == MERGE_RESPONSE && |
---|
151 | strcmp(name.name, "updated-set") == 0) |
---|
152 | { |
---|
153 | push_state(parser, ctx, UPDATED_SET); |
---|
154 | } |
---|
155 | else if (state == UPDATED_SET && |
---|
156 | strcmp(name.name, "response") == 0) |
---|
157 | { |
---|
158 | push_state(parser, ctx, RESPONSE); |
---|
159 | } |
---|
160 | else if (state == RESPONSE && |
---|
161 | strcmp(name.name, "href") == 0) |
---|
162 | { |
---|
163 | info = push_state(parser, ctx, PROP_VAL); |
---|
164 | |
---|
165 | info->prop_ns = name.namespace; |
---|
166 | info->prop_name = apr_pstrdup(info->pool, name.name); |
---|
167 | info->prop_val = NULL; |
---|
168 | info->prop_val_len = 0; |
---|
169 | } |
---|
170 | else if (state == RESPONSE && |
---|
171 | strcmp(name.name, "propstat") == 0) |
---|
172 | { |
---|
173 | push_state(parser, ctx, PROPSTAT); |
---|
174 | } |
---|
175 | else if (state == PROPSTAT && |
---|
176 | strcmp(name.name, "prop") == 0) |
---|
177 | { |
---|
178 | push_state(parser, ctx, PROP); |
---|
179 | } |
---|
180 | else if (state == PROPSTAT && |
---|
181 | strcmp(name.name, "status") == 0) |
---|
182 | { |
---|
183 | /* Do nothing for now. */ |
---|
184 | } |
---|
185 | else if (state == PROP && |
---|
186 | strcmp(name.name, "resourcetype") == 0) |
---|
187 | { |
---|
188 | info = push_state(parser, ctx, RESOURCE_TYPE); |
---|
189 | info->type = UNSET; |
---|
190 | } |
---|
191 | else if (state == RESOURCE_TYPE && |
---|
192 | strcmp(name.name, "baseline") == 0) |
---|
193 | { |
---|
194 | info = parser->state->private; |
---|
195 | |
---|
196 | info->type = BASELINE; |
---|
197 | } |
---|
198 | else if (state == RESOURCE_TYPE && |
---|
199 | strcmp(name.name, "collection") == 0) |
---|
200 | { |
---|
201 | info = parser->state->private; |
---|
202 | |
---|
203 | info->type = COLLECTION; |
---|
204 | } |
---|
205 | else if (state == PROP && |
---|
206 | strcmp(name.name, "checked-in") == 0) |
---|
207 | { |
---|
208 | info = push_state(parser, ctx, IGNORE_PROP_NAME); |
---|
209 | |
---|
210 | info->prop_ns = name.namespace; |
---|
211 | info->prop_name = apr_pstrdup(info->pool, name.name); |
---|
212 | info->prop_val = NULL; |
---|
213 | info->prop_val_len = 0; |
---|
214 | } |
---|
215 | else if (state == PROP) |
---|
216 | { |
---|
217 | push_state(parser, ctx, PROP_VAL); |
---|
218 | } |
---|
219 | else if (state == IGNORE_PROP_NAME) |
---|
220 | { |
---|
221 | push_state(parser, ctx, PROP_VAL); |
---|
222 | } |
---|
223 | else if (state == NEED_PROP_NAME) |
---|
224 | { |
---|
225 | info = push_state(parser, ctx, PROP_VAL); |
---|
226 | info->prop_ns = name.namespace; |
---|
227 | info->prop_name = apr_pstrdup(info->pool, name.name); |
---|
228 | info->prop_val = NULL; |
---|
229 | info->prop_val_len = 0; |
---|
230 | } |
---|
231 | else |
---|
232 | { |
---|
233 | SVN_ERR_MALFUNCTION(); |
---|
234 | } |
---|
235 | |
---|
236 | return SVN_NO_ERROR; |
---|
237 | } |
---|
238 | |
---|
239 | static svn_error_t * |
---|
240 | end_merge(svn_ra_serf__xml_parser_t *parser, |
---|
241 | void *userData, |
---|
242 | svn_ra_serf__dav_props_t name) |
---|
243 | { |
---|
244 | svn_ra_serf__merge_context_t *ctx = userData; |
---|
245 | merge_state_e state; |
---|
246 | merge_info_t *info; |
---|
247 | |
---|
248 | state = parser->state->current_state; |
---|
249 | info = parser->state->private; |
---|
250 | |
---|
251 | if (state == NONE) |
---|
252 | { |
---|
253 | /* nothing to close yet. */ |
---|
254 | return SVN_NO_ERROR; |
---|
255 | } |
---|
256 | |
---|
257 | if (state == RESPONSE && |
---|
258 | strcmp(name.name, "response") == 0) |
---|
259 | { |
---|
260 | if (info->type == BASELINE) |
---|
261 | { |
---|
262 | const char *str; |
---|
263 | |
---|
264 | str = apr_hash_get(info->props, SVN_DAV__VERSION_NAME, |
---|
265 | APR_HASH_KEY_STRING); |
---|
266 | if (str) |
---|
267 | { |
---|
268 | ctx->commit_info->revision = SVN_STR_TO_REV(str); |
---|
269 | } |
---|
270 | else |
---|
271 | { |
---|
272 | ctx->commit_info->revision = SVN_INVALID_REVNUM; |
---|
273 | } |
---|
274 | |
---|
275 | ctx->commit_info->date = |
---|
276 | apr_pstrdup(ctx->pool, |
---|
277 | apr_hash_get(info->props, SVN_DAV__CREATIONDATE, |
---|
278 | APR_HASH_KEY_STRING)); |
---|
279 | |
---|
280 | ctx->commit_info->author = |
---|
281 | apr_pstrdup(ctx->pool, |
---|
282 | apr_hash_get(info->props, "creator-displayname", |
---|
283 | APR_HASH_KEY_STRING)); |
---|
284 | |
---|
285 | ctx->commit_info->post_commit_err = |
---|
286 | apr_pstrdup(ctx->pool, |
---|
287 | apr_hash_get(info->props, |
---|
288 | "post-commit-err", APR_HASH_KEY_STRING)); |
---|
289 | } |
---|
290 | else if (ctx->session->wc_callbacks->push_wc_prop) |
---|
291 | { |
---|
292 | const char *href, *checked_in; |
---|
293 | svn_string_t checked_in_str; |
---|
294 | |
---|
295 | href = apr_hash_get(info->props, "href", APR_HASH_KEY_STRING); |
---|
296 | checked_in = apr_hash_get(info->props, "checked-in", |
---|
297 | APR_HASH_KEY_STRING); |
---|
298 | |
---|
299 | if (! svn_path_is_ancestor(ctx->merge_url, href)) |
---|
300 | { |
---|
301 | /* ### need something better than APR_EGENERAL */ |
---|
302 | return svn_error_createf(APR_EGENERAL, NULL, |
---|
303 | _("A MERGE response for '%s' is not a child " |
---|
304 | "of the destination ('%s')"), |
---|
305 | href, ctx->merge_url); |
---|
306 | } |
---|
307 | href = svn_path_is_child(ctx->merge_url, href, NULL); |
---|
308 | if (! href) /* the paths are equal */ |
---|
309 | href = ""; |
---|
310 | |
---|
311 | checked_in_str.data = checked_in; |
---|
312 | checked_in_str.len = strlen(checked_in); |
---|
313 | |
---|
314 | /* We now need to dive all the way into the WC to update the |
---|
315 | * base VCC url. |
---|
316 | */ |
---|
317 | SVN_ERR(ctx->session->wc_callbacks->push_wc_prop( |
---|
318 | ctx->session->wc_callback_baton, |
---|
319 | href, |
---|
320 | SVN_RA_SERF__WC_CHECKED_IN_URL, |
---|
321 | &checked_in_str, |
---|
322 | info->pool)); |
---|
323 | |
---|
324 | } |
---|
325 | |
---|
326 | svn_ra_serf__xml_pop_state(parser); |
---|
327 | } |
---|
328 | else if (state == PROPSTAT && |
---|
329 | strcmp(name.name, "propstat") == 0) |
---|
330 | { |
---|
331 | svn_ra_serf__xml_pop_state(parser); |
---|
332 | } |
---|
333 | else if (state == PROP && |
---|
334 | strcmp(name.name, "prop") == 0) |
---|
335 | { |
---|
336 | svn_ra_serf__xml_pop_state(parser); |
---|
337 | } |
---|
338 | else if (state == RESOURCE_TYPE && |
---|
339 | strcmp(name.name, "resourcetype") == 0) |
---|
340 | { |
---|
341 | svn_ra_serf__xml_pop_state(parser); |
---|
342 | } |
---|
343 | else if (state == IGNORE_PROP_NAME || state == NEED_PROP_NAME) |
---|
344 | { |
---|
345 | svn_ra_serf__xml_pop_state(parser); |
---|
346 | } |
---|
347 | else if (state == PROP_VAL) |
---|
348 | { |
---|
349 | if (!info->prop_name) |
---|
350 | { |
---|
351 | info->prop_name = apr_pstrdup(info->pool, name.name); |
---|
352 | } |
---|
353 | info->prop_val = apr_pstrmemdup(info->pool, info->prop_val, |
---|
354 | info->prop_val_len); |
---|
355 | if (strcmp(info->prop_name, "href") == 0) |
---|
356 | info->prop_val = svn_path_canonicalize(info->prop_val, info->pool); |
---|
357 | |
---|
358 | /* Set our property. */ |
---|
359 | apr_hash_set(info->props, info->prop_name, APR_HASH_KEY_STRING, |
---|
360 | info->prop_val); |
---|
361 | |
---|
362 | info->prop_ns = NULL; |
---|
363 | info->prop_name = NULL; |
---|
364 | info->prop_val = NULL; |
---|
365 | info->prop_val_len = 0; |
---|
366 | |
---|
367 | svn_ra_serf__xml_pop_state(parser); |
---|
368 | } |
---|
369 | |
---|
370 | return SVN_NO_ERROR; |
---|
371 | } |
---|
372 | |
---|
373 | static svn_error_t * |
---|
374 | cdata_merge(svn_ra_serf__xml_parser_t *parser, |
---|
375 | void *userData, |
---|
376 | const char *data, |
---|
377 | apr_size_t len) |
---|
378 | { |
---|
379 | svn_ra_serf__merge_context_t *ctx = userData; |
---|
380 | merge_state_e state; |
---|
381 | merge_info_t *info; |
---|
382 | |
---|
383 | UNUSED_CTX(ctx); |
---|
384 | |
---|
385 | state = parser->state->current_state; |
---|
386 | info = parser->state->private; |
---|
387 | |
---|
388 | if (state == PROP_VAL) |
---|
389 | { |
---|
390 | svn_ra_serf__expand_string(&info->prop_val, &info->prop_val_len, |
---|
391 | data, len, parser->state->pool); |
---|
392 | } |
---|
393 | |
---|
394 | return SVN_NO_ERROR; |
---|
395 | } |
---|
396 | |
---|
397 | static apr_status_t |
---|
398 | setup_merge_headers(serf_bucket_t *headers, |
---|
399 | void *baton, |
---|
400 | apr_pool_t *pool) |
---|
401 | { |
---|
402 | svn_ra_serf__merge_context_t *ctx = baton; |
---|
403 | |
---|
404 | if (!ctx->keep_locks) |
---|
405 | { |
---|
406 | serf_bucket_headers_set(headers, SVN_DAV_OPTIONS_HEADER, |
---|
407 | SVN_DAV_OPTION_RELEASE_LOCKS); |
---|
408 | } |
---|
409 | |
---|
410 | return APR_SUCCESS; |
---|
411 | } |
---|
412 | |
---|
413 | void |
---|
414 | svn_ra_serf__merge_lock_token_list(apr_hash_t *lock_tokens, |
---|
415 | const char *parent, |
---|
416 | serf_bucket_t *body, |
---|
417 | serf_bucket_alloc_t *alloc, |
---|
418 | apr_pool_t *pool) |
---|
419 | { |
---|
420 | apr_hash_index_t *hi; |
---|
421 | |
---|
422 | if (!lock_tokens || apr_hash_count(lock_tokens) == 0) |
---|
423 | return; |
---|
424 | |
---|
425 | svn_ra_serf__add_open_tag_buckets(body, alloc, |
---|
426 | "S:lock-token-list", |
---|
427 | "xmlns:S", SVN_XML_NAMESPACE, |
---|
428 | NULL); |
---|
429 | |
---|
430 | for (hi = apr_hash_first(pool, lock_tokens); |
---|
431 | hi; |
---|
432 | hi = apr_hash_next(hi)) |
---|
433 | { |
---|
434 | const void *key; |
---|
435 | apr_ssize_t klen; |
---|
436 | void *val; |
---|
437 | svn_string_t path; |
---|
438 | |
---|
439 | apr_hash_this(hi, &key, &klen, &val); |
---|
440 | |
---|
441 | path.data = key; |
---|
442 | path.len = klen; |
---|
443 | |
---|
444 | if (parent && !svn_path_is_ancestor(parent, key)) |
---|
445 | continue; |
---|
446 | |
---|
447 | svn_ra_serf__add_open_tag_buckets(body, alloc, "S:lock", NULL); |
---|
448 | |
---|
449 | svn_ra_serf__add_open_tag_buckets(body, alloc, "lock-path", NULL); |
---|
450 | svn_ra_serf__add_cdata_len_buckets(body, alloc, path.data, path.len); |
---|
451 | svn_ra_serf__add_close_tag_buckets(body, alloc, "lock-path"); |
---|
452 | |
---|
453 | svn_ra_serf__add_tag_buckets(body, "lock-token", val, alloc); |
---|
454 | |
---|
455 | svn_ra_serf__add_close_tag_buckets(body, alloc, "S:lock"); |
---|
456 | } |
---|
457 | |
---|
458 | svn_ra_serf__add_close_tag_buckets(body, alloc, "S:lock-token-list"); |
---|
459 | } |
---|
460 | |
---|
461 | static serf_bucket_t* |
---|
462 | create_merge_body(void *baton, |
---|
463 | serf_bucket_alloc_t *alloc, |
---|
464 | apr_pool_t *pool) |
---|
465 | { |
---|
466 | svn_ra_serf__merge_context_t *ctx = baton; |
---|
467 | serf_bucket_t *body_bkt; |
---|
468 | |
---|
469 | body_bkt = serf_bucket_aggregate_create(alloc); |
---|
470 | |
---|
471 | svn_ra_serf__add_xml_header_buckets(body_bkt, alloc); |
---|
472 | svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:merge", |
---|
473 | "xmlns:D", "DAV:", |
---|
474 | NULL); |
---|
475 | svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:source", NULL); |
---|
476 | svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:href", NULL); |
---|
477 | |
---|
478 | svn_ra_serf__add_cdata_len_buckets(body_bkt, alloc, |
---|
479 | ctx->activity_url, ctx->activity_url_len); |
---|
480 | |
---|
481 | svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:href"); |
---|
482 | svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:source"); |
---|
483 | |
---|
484 | svn_ra_serf__add_tag_buckets(body_bkt, "D:no-auto-merge", NULL, alloc); |
---|
485 | svn_ra_serf__add_tag_buckets(body_bkt, "D:no-checkout", NULL, alloc); |
---|
486 | |
---|
487 | svn_ra_serf__add_open_tag_buckets(body_bkt, alloc, "D:prop", NULL); |
---|
488 | svn_ra_serf__add_tag_buckets(body_bkt, "D:checked-in", NULL, alloc); |
---|
489 | svn_ra_serf__add_tag_buckets(body_bkt, "D:" SVN_DAV__VERSION_NAME, NULL, alloc); |
---|
490 | svn_ra_serf__add_tag_buckets(body_bkt, "D:resourcetype", NULL, alloc); |
---|
491 | svn_ra_serf__add_tag_buckets(body_bkt, "D:" SVN_DAV__CREATIONDATE, NULL, alloc); |
---|
492 | svn_ra_serf__add_tag_buckets(body_bkt, "D:creator-displayname", NULL, alloc); |
---|
493 | svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:prop"); |
---|
494 | |
---|
495 | svn_ra_serf__merge_lock_token_list(ctx->lock_tokens, NULL, body_bkt, alloc, |
---|
496 | pool); |
---|
497 | |
---|
498 | svn_ra_serf__add_close_tag_buckets(body_bkt, alloc, "D:merge"); |
---|
499 | |
---|
500 | return body_bkt; |
---|
501 | } |
---|
502 | |
---|
503 | svn_error_t * |
---|
504 | svn_ra_serf__merge_create_req(svn_ra_serf__merge_context_t **ret_ctx, |
---|
505 | svn_ra_serf__session_t *session, |
---|
506 | svn_ra_serf__connection_t *conn, |
---|
507 | const char *path, |
---|
508 | const char *activity_url, |
---|
509 | apr_size_t activity_url_len, |
---|
510 | apr_hash_t *lock_tokens, |
---|
511 | svn_boolean_t keep_locks, |
---|
512 | apr_pool_t *pool) |
---|
513 | { |
---|
514 | svn_ra_serf__merge_context_t *merge_ctx; |
---|
515 | svn_ra_serf__handler_t *handler; |
---|
516 | svn_ra_serf__xml_parser_t *parser_ctx; |
---|
517 | |
---|
518 | merge_ctx = apr_pcalloc(pool, sizeof(*merge_ctx)); |
---|
519 | |
---|
520 | merge_ctx->pool = pool; |
---|
521 | merge_ctx->session = session; |
---|
522 | |
---|
523 | merge_ctx->activity_url = activity_url; |
---|
524 | merge_ctx->activity_url_len = activity_url_len; |
---|
525 | |
---|
526 | merge_ctx->lock_tokens = lock_tokens; |
---|
527 | merge_ctx->keep_locks = keep_locks; |
---|
528 | |
---|
529 | merge_ctx->commit_info = svn_create_commit_info(pool); |
---|
530 | |
---|
531 | merge_ctx->merge_url = session->repos_url.path; |
---|
532 | |
---|
533 | handler = apr_pcalloc(pool, sizeof(*handler)); |
---|
534 | |
---|
535 | handler->method = "MERGE"; |
---|
536 | handler->path = merge_ctx->merge_url; |
---|
537 | handler->body_delegate = create_merge_body; |
---|
538 | handler->body_delegate_baton = merge_ctx; |
---|
539 | handler->conn = conn; |
---|
540 | handler->session = session; |
---|
541 | |
---|
542 | parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx)); |
---|
543 | |
---|
544 | parser_ctx->pool = pool; |
---|
545 | parser_ctx->user_data = merge_ctx; |
---|
546 | parser_ctx->start = start_merge; |
---|
547 | parser_ctx->end = end_merge; |
---|
548 | parser_ctx->cdata = cdata_merge; |
---|
549 | parser_ctx->done = &merge_ctx->done; |
---|
550 | parser_ctx->status_code = &merge_ctx->status; |
---|
551 | |
---|
552 | handler->header_delegate = setup_merge_headers; |
---|
553 | handler->header_delegate_baton = merge_ctx; |
---|
554 | |
---|
555 | handler->response_handler = svn_ra_serf__handle_xml_parser; |
---|
556 | handler->response_baton = parser_ctx; |
---|
557 | |
---|
558 | svn_ra_serf__request_create(handler); |
---|
559 | |
---|
560 | *ret_ctx = merge_ctx; |
---|
561 | |
---|
562 | return SVN_NO_ERROR; |
---|
563 | } |
---|
564 | |
---|
565 | svn_boolean_t* |
---|
566 | svn_ra_serf__merge_get_done_ptr(svn_ra_serf__merge_context_t *ctx) |
---|
567 | { |
---|
568 | return &ctx->done; |
---|
569 | } |
---|
570 | |
---|
571 | svn_commit_info_t* |
---|
572 | svn_ra_serf__merge_get_commit_info(svn_ra_serf__merge_context_t *ctx) |
---|
573 | { |
---|
574 | return ctx->commit_info; |
---|
575 | } |
---|
576 | |
---|
577 | int |
---|
578 | svn_ra_serf__merge_get_status(svn_ra_serf__merge_context_t *ctx) |
---|
579 | { |
---|
580 | return ctx->status; |
---|
581 | } |
---|