mergeinfo.c revision 299742
1/*
2 * mergeinfo.c : entry point for mergeinfo RA functions for ra_serf
3 *
4 * ====================================================================
5 *    Licensed to the Apache Software Foundation (ASF) under one
6 *    or more contributor license agreements.  See the NOTICE file
7 *    distributed with this work for additional information
8 *    regarding copyright ownership.  The ASF licenses this file
9 *    to you under the Apache License, Version 2.0 (the
10 *    "License"); you may not use this file except in compliance
11 *    with the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 *    Unless required by applicable law or agreed to in writing,
16 *    software distributed under the License is distributed on an
17 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 *    KIND, either express or implied.  See the License for the
19 *    specific language governing permissions and limitations
20 *    under the License.
21 * ====================================================================
22 */
23
24#include <apr_tables.h>
25#include <apr_xml.h>
26
27#include "svn_hash.h"
28#include "svn_mergeinfo.h"
29#include "svn_path.h"
30#include "svn_ra.h"
31#include "svn_string.h"
32#include "svn_xml.h"
33
34#include "private/svn_dav_protocol.h"
35#include "../libsvn_ra/ra_loader.h"
36#include "svn_private_config.h"
37#include "ra_serf.h"
38
39
40
41
42/* The current state of our XML parsing. */
43typedef enum mergeinfo_state_e {
44  INITIAL = XML_STATE_INITIAL,
45  MERGEINFO_REPORT,
46  MERGEINFO_ITEM,
47  MERGEINFO_PATH,
48  MERGEINFO_INFO
49} mergeinfo_state_e;
50
51/* Baton for accumulating mergeinfo.  RESULT_CATALOG stores the final
52   mergeinfo catalog result we are going to hand back to the caller of
53   get_mergeinfo.  */
54typedef struct mergeinfo_context_t {
55  apr_pool_t *pool;
56  svn_mergeinfo_t result_catalog;
57  const apr_array_header_t *paths;
58  svn_revnum_t revision;
59  svn_mergeinfo_inheritance_t inherit;
60  svn_boolean_t include_descendants;
61} mergeinfo_context_t;
62
63
64#define D_ "DAV:"
65#define S_ SVN_XML_NAMESPACE
66static const svn_ra_serf__xml_transition_t mergeinfo_ttable[] = {
67  { INITIAL, S_, SVN_DAV__MERGEINFO_REPORT, MERGEINFO_REPORT,
68    FALSE, { NULL }, FALSE },
69
70  { MERGEINFO_REPORT, S_, SVN_DAV__MERGEINFO_ITEM, MERGEINFO_ITEM,
71    FALSE, { NULL }, TRUE },
72
73  { MERGEINFO_ITEM, S_, SVN_DAV__MERGEINFO_PATH, MERGEINFO_PATH,
74    TRUE, { NULL }, TRUE },
75
76  { MERGEINFO_ITEM, S_, SVN_DAV__MERGEINFO_INFO, MERGEINFO_INFO,
77    TRUE, { NULL }, TRUE },
78
79  { 0 }
80};
81
82
83/* Conforms to svn_ra_serf__xml_closed_t  */
84static svn_error_t *
85mergeinfo_closed(svn_ra_serf__xml_estate_t *xes,
86                 void *baton,
87                 int leaving_state,
88                 const svn_string_t *cdata,
89                 apr_hash_t *attrs,
90                 apr_pool_t *scratch_pool)
91{
92  mergeinfo_context_t *mergeinfo_ctx = baton;
93
94  if (leaving_state == MERGEINFO_ITEM)
95    {
96      /* Placed here from the child elements.  */
97      const char *path = svn_hash_gets(attrs, "path");
98      const char *info = svn_hash_gets(attrs, "info");
99
100      if (path != NULL && info != NULL)
101        {
102          svn_mergeinfo_t path_mergeinfo;
103
104          /* Correct for naughty servers that send "relative" paths
105             with leading slashes! */
106          if (path[0] == '/')
107            ++path;
108
109          SVN_ERR(svn_mergeinfo_parse(&path_mergeinfo, info,
110                                      mergeinfo_ctx->pool));
111
112          svn_hash_sets(mergeinfo_ctx->result_catalog,
113                        apr_pstrdup(mergeinfo_ctx->pool, path),
114                        path_mergeinfo);
115        }
116    }
117  else
118    {
119      SVN_ERR_ASSERT(leaving_state == MERGEINFO_PATH
120                     || leaving_state == MERGEINFO_INFO);
121
122      /* Stash the value onto the parent MERGEINFO_ITEM.  */
123      svn_ra_serf__xml_note(xes, MERGEINFO_ITEM,
124                            leaving_state == MERGEINFO_PATH
125                              ? "path"
126                              : "info",
127                            cdata->data);
128    }
129
130  return SVN_NO_ERROR;
131}
132
133/* Implements svn_ra_serf__request_body_delegate_t */
134static svn_error_t *
135create_mergeinfo_body(serf_bucket_t **bkt,
136                      void *baton,
137                      serf_bucket_alloc_t *alloc,
138                      apr_pool_t *pool /* request pool */,
139                      apr_pool_t *scratch_pool)
140{
141  mergeinfo_context_t *mergeinfo_ctx = baton;
142  serf_bucket_t *body_bkt;
143
144  body_bkt = serf_bucket_aggregate_create(alloc);
145
146  svn_ra_serf__add_open_tag_buckets(body_bkt, alloc,
147                                    "S:" SVN_DAV__MERGEINFO_REPORT,
148                                    "xmlns:S", SVN_XML_NAMESPACE,
149                                    SVN_VA_NULL);
150
151  svn_ra_serf__add_tag_buckets(body_bkt,
152                               "S:" SVN_DAV__REVISION,
153                               apr_ltoa(pool, mergeinfo_ctx->revision),
154                               alloc);
155  svn_ra_serf__add_tag_buckets(body_bkt, "S:" SVN_DAV__INHERIT,
156                               svn_inheritance_to_word(mergeinfo_ctx->inherit),
157                               alloc);
158  if (mergeinfo_ctx->include_descendants)
159    {
160      svn_ra_serf__add_tag_buckets(body_bkt, "S:"
161                                   SVN_DAV__INCLUDE_DESCENDANTS,
162                                   "yes", alloc);
163    }
164
165  if (mergeinfo_ctx->paths)
166    {
167      int i;
168
169      for (i = 0; i < mergeinfo_ctx->paths->nelts; i++)
170        {
171          const char *this_path = APR_ARRAY_IDX(mergeinfo_ctx->paths,
172                                                i, const char *);
173
174          svn_ra_serf__add_tag_buckets(body_bkt, "S:" SVN_DAV__PATH,
175                                       this_path, alloc);
176        }
177    }
178
179  svn_ra_serf__add_close_tag_buckets(body_bkt, alloc,
180                                     "S:" SVN_DAV__MERGEINFO_REPORT);
181
182  *bkt = body_bkt;
183  return SVN_NO_ERROR;
184}
185
186svn_error_t *
187svn_ra_serf__get_mergeinfo(svn_ra_session_t *ra_session,
188                           svn_mergeinfo_catalog_t *catalog,
189                           const apr_array_header_t *paths,
190                           svn_revnum_t revision,
191                           svn_mergeinfo_inheritance_t inherit,
192                           svn_boolean_t include_descendants,
193                           apr_pool_t *pool)
194{
195  mergeinfo_context_t *mergeinfo_ctx;
196  svn_ra_serf__session_t *session = ra_session->priv;
197  svn_ra_serf__handler_t *handler;
198  svn_ra_serf__xml_context_t *xmlctx;
199  const char *path;
200
201  *catalog = NULL;
202
203  SVN_ERR(svn_ra_serf__get_stable_url(&path, NULL /* latest_revnum */,
204                                      session,
205                                      NULL /* url */, revision,
206                                      pool, pool));
207
208  mergeinfo_ctx = apr_pcalloc(pool, sizeof(*mergeinfo_ctx));
209  mergeinfo_ctx->pool = pool;
210  mergeinfo_ctx->result_catalog = apr_hash_make(pool);
211  mergeinfo_ctx->paths = paths;
212  mergeinfo_ctx->revision = revision;
213  mergeinfo_ctx->inherit = inherit;
214  mergeinfo_ctx->include_descendants = include_descendants;
215
216  xmlctx = svn_ra_serf__xml_context_create(mergeinfo_ttable,
217                                           NULL, mergeinfo_closed, NULL,
218                                           mergeinfo_ctx,
219                                           pool);
220  handler = svn_ra_serf__create_expat_handler(session, xmlctx, NULL, pool);
221
222  handler->method = "REPORT";
223  handler->path = path;
224
225  handler->body_delegate = create_mergeinfo_body;
226  handler->body_delegate_baton = mergeinfo_ctx;
227  handler->body_type = "text/xml";
228
229  SVN_ERR(svn_ra_serf__context_run_one(handler, pool));
230
231  SVN_ERR(svn_ra_serf__error_on_status(handler->sline, handler->path,
232                                       handler->location));
233
234  if (apr_hash_count(mergeinfo_ctx->result_catalog))
235    *catalog = mergeinfo_ctx->result_catalog;
236
237  return SVN_NO_ERROR;
238}
239