source: valtobtest/subversion-1.6.2/subversion/libsvn_ra_serf/merge.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: 15.8 KB
Line 
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 */
47typedef 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
64typedef enum {
65  UNSET,
66  BASELINE,
67  COLLECTION,
68  CHECKED_IN,
69} resource_type_e;
70
71typedef 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. */
86struct 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
108static merge_info_t *
109push_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
129static svn_error_t *
130start_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
239static svn_error_t *
240end_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
373static svn_error_t *
374cdata_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
397static apr_status_t
398setup_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
413void
414svn_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
461static serf_bucket_t*
462create_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
503svn_error_t *
504svn_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
565svn_boolean_t*
566svn_ra_serf__merge_get_done_ptr(svn_ra_serf__merge_context_t *ctx)
567{
568  return &ctx->done;
569}
570
571svn_commit_info_t*
572svn_ra_serf__merge_get_commit_info(svn_ra_serf__merge_context_t *ctx)
573{
574  return ctx->commit_info;
575}
576
577int
578svn_ra_serf__merge_get_status(svn_ra_serf__merge_context_t *ctx)
579{
580  return ctx->status;
581}
Note: See TracBrowser for help on using the repository browser.