]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_ra_serf/blame.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_ra_serf / blame.c
1 /*
2  * blame.c :  entry point for blame 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_uri.h>
25 #include <serf.h>
26
27 #include "svn_hash.h"
28 #include "svn_pools.h"
29 #include "svn_ra.h"
30 #include "svn_dav.h"
31 #include "svn_xml.h"
32 #include "svn_config.h"
33 #include "svn_delta.h"
34 #include "svn_path.h"
35 #include "svn_base64.h"
36 #include "svn_props.h"
37
38 #include "svn_private_config.h"
39
40 #include "private/svn_string_private.h"
41
42 #include "ra_serf.h"
43 #include "../libsvn_ra/ra_loader.h"
44
45 \f
46 /*
47  * This enum represents the current state of our XML parsing for a REPORT.
48  */
49 typedef enum blame_state_e {
50   INITIAL = 0,
51   FILE_REVS_REPORT,
52   FILE_REV,
53   REV_PROP,
54   SET_PROP,
55   REMOVE_PROP,
56   MERGED_REVISION,
57   TXDELTA
58 } blame_state_e;
59
60
61 typedef struct blame_context_t {
62   /* pool passed to get_file_revs */
63   apr_pool_t *pool;
64
65   /* parameters set by our caller */
66   const char *path;
67   svn_revnum_t start;
68   svn_revnum_t end;
69   svn_boolean_t include_merged_revisions;
70
71   /* blame handler and baton */
72   svn_file_rev_handler_t file_rev;
73   void *file_rev_baton;
74
75   /* As we parse each FILE_REV, we collect data in these variables:
76      property changes and new content.  STREAM is valid when we're
77      in the TXDELTA state, processing the incoming cdata.  */
78   apr_hash_t *rev_props;
79   apr_array_header_t *prop_diffs;
80   apr_pool_t *state_pool;  /* put property stuff in here  */
81
82   svn_stream_t *stream;
83
84 } blame_context_t;
85
86
87 #define D_ "DAV:"
88 #define S_ SVN_XML_NAMESPACE
89 static const svn_ra_serf__xml_transition_t blame_ttable[] = {
90   { INITIAL, S_, "file-revs-report", FILE_REVS_REPORT,
91     FALSE, { NULL }, FALSE },
92
93   { FILE_REVS_REPORT, S_, "file-rev", FILE_REV,
94     FALSE, { "path", "rev", NULL }, TRUE },
95
96   { FILE_REV, S_, "rev-prop", REV_PROP,
97     TRUE, { "name", "?encoding", NULL }, TRUE },
98
99   { FILE_REV, S_, "set-prop", SET_PROP,
100     TRUE, { "name", "?encoding", NULL }, TRUE },
101
102   { FILE_REV, S_, "remove-prop", REMOVE_PROP,
103     FALSE, { "name", NULL }, TRUE },
104
105   { FILE_REV, S_, "merged-revision", MERGED_REVISION,
106     FALSE, { NULL }, TRUE },
107
108   { FILE_REV, S_, "txdelta", TXDELTA,
109     FALSE, { NULL }, TRUE },
110
111   { 0 }
112 };
113
114
115 /* Conforms to svn_ra_serf__xml_opened_t  */
116 static svn_error_t *
117 blame_opened(svn_ra_serf__xml_estate_t *xes,
118              void *baton,
119              int entered_state,
120              const svn_ra_serf__dav_props_t *tag,
121              apr_pool_t *scratch_pool)
122 {
123   blame_context_t *blame_ctx = baton;
124
125   if (entered_state == FILE_REV)
126     {
127       apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes);
128
129       /* Child elements will store properties in these structures.  */
130       blame_ctx->rev_props = apr_hash_make(state_pool);
131       blame_ctx->prop_diffs = apr_array_make(state_pool,
132                                              5, sizeof(svn_prop_t));
133       blame_ctx->state_pool = state_pool;
134
135       /* Clear this, so we can detect the absence of a TXDELTA.  */
136       blame_ctx->stream = NULL;
137     }
138   else if (entered_state == TXDELTA)
139     {
140       apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes);
141       apr_hash_t *gathered = svn_ra_serf__xml_gather_since(xes, FILE_REV);
142       const char *path;
143       const char *rev;
144       const char *merged_revision;
145       svn_txdelta_window_handler_t txdelta;
146       void *txdelta_baton;
147
148       path = svn_hash_gets(gathered, "path");
149       rev = svn_hash_gets(gathered, "rev");
150       merged_revision = svn_hash_gets(gathered, "merged-revision");
151
152       SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton,
153                                   path, SVN_STR_TO_REV(rev),
154                                   blame_ctx->rev_props,
155                                   merged_revision != NULL,
156                                   &txdelta, &txdelta_baton,
157                                   blame_ctx->prop_diffs,
158                                   state_pool));
159
160       blame_ctx->stream = svn_base64_decode(svn_txdelta_parse_svndiff(
161                                               txdelta, txdelta_baton,
162                                               TRUE /* error_on_early_close */,
163                                               state_pool),
164                                             state_pool);
165     }
166
167   return SVN_NO_ERROR;
168 }
169
170
171 /* Conforms to svn_ra_serf__xml_closed_t  */
172 static svn_error_t *
173 blame_closed(svn_ra_serf__xml_estate_t *xes,
174              void *baton,
175              int leaving_state,
176              const svn_string_t *cdata,
177              apr_hash_t *attrs,
178              apr_pool_t *scratch_pool)
179 {
180   blame_context_t *blame_ctx = baton;
181
182   if (leaving_state == FILE_REV)
183     {
184       /* Note that we test STREAM, but any pointer is currently invalid.
185          It was closed when left the TXDELTA state.  */
186       if (blame_ctx->stream == NULL)
187         {
188           const char *path;
189           const char *rev;
190
191           path = svn_hash_gets(attrs, "path");
192           rev = svn_hash_gets(attrs, "rev");
193
194           /* Send a "no content" notification.  */
195           SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton,
196                                       path, SVN_STR_TO_REV(rev),
197                                       blame_ctx->rev_props,
198                                       FALSE /* result_of_merge */,
199                                       NULL, NULL, /* txdelta / baton */
200                                       blame_ctx->prop_diffs,
201                                       scratch_pool));
202         }
203     }
204   else if (leaving_state == MERGED_REVISION)
205     {
206       svn_ra_serf__xml_note(xes, FILE_REV, "merged-revision", "*");
207     }
208   else if (leaving_state == TXDELTA)
209     {
210       SVN_ERR(svn_stream_close(blame_ctx->stream));
211     }
212   else
213     {
214       const char *name;
215       const svn_string_t *value;
216
217       SVN_ERR_ASSERT(leaving_state == REV_PROP
218                      || leaving_state == SET_PROP
219                      || leaving_state == REMOVE_PROP);
220
221       name = apr_pstrdup(blame_ctx->state_pool,
222                          svn_hash_gets(attrs, "name"));
223
224       if (leaving_state == REMOVE_PROP)
225         {
226           value = NULL;
227         }
228       else
229         {
230           const char *encoding = svn_hash_gets(attrs, "encoding");
231
232           if (encoding && strcmp(encoding, "base64") == 0)
233             value = svn_base64_decode_string(cdata, blame_ctx->state_pool);
234           else
235             value = svn_string_dup(cdata, blame_ctx->state_pool);
236         }
237
238       if (leaving_state == REV_PROP)
239         {
240           svn_hash_sets(blame_ctx->rev_props, name, value);
241         }
242       else
243         {
244           svn_prop_t *prop = apr_array_push(blame_ctx->prop_diffs);
245
246           prop->name = name;
247           prop->value = value;
248         }
249     }
250
251   return SVN_NO_ERROR;
252 }
253
254
255 /* Conforms to svn_ra_serf__xml_cdata_t  */
256 static svn_error_t *
257 blame_cdata(svn_ra_serf__xml_estate_t *xes,
258             void *baton,
259             int current_state,
260             const char *data,
261             apr_size_t len,
262             apr_pool_t *scratch_pool)
263 {
264   blame_context_t *blame_ctx = baton;
265
266   if (current_state == TXDELTA)
267     {
268       SVN_ERR(svn_stream_write(blame_ctx->stream, data, &len));
269       /* Ignore the returned LEN value.  */
270     }
271
272   return SVN_NO_ERROR;
273 }
274
275
276 /* Implements svn_ra_serf__request_body_delegate_t */
277 static svn_error_t *
278 create_file_revs_body(serf_bucket_t **body_bkt,
279                       void *baton,
280                       serf_bucket_alloc_t *alloc,
281                       apr_pool_t *pool)
282 {
283   serf_bucket_t *buckets;
284   blame_context_t *blame_ctx = baton;
285
286   buckets = serf_bucket_aggregate_create(alloc);
287
288   svn_ra_serf__add_open_tag_buckets(buckets, alloc,
289                                     "S:file-revs-report",
290                                     "xmlns:S", SVN_XML_NAMESPACE,
291                                     NULL);
292
293   svn_ra_serf__add_tag_buckets(buckets,
294                                "S:start-revision", apr_ltoa(pool, blame_ctx->start),
295                                alloc);
296
297   svn_ra_serf__add_tag_buckets(buckets,
298                                "S:end-revision", apr_ltoa(pool, blame_ctx->end),
299                                alloc);
300
301   if (blame_ctx->include_merged_revisions)
302     {
303       svn_ra_serf__add_tag_buckets(buckets,
304                                    "S:include-merged-revisions", NULL,
305                                    alloc);
306     }
307
308   svn_ra_serf__add_tag_buckets(buckets,
309                                "S:path", blame_ctx->path,
310                                alloc);
311
312   svn_ra_serf__add_close_tag_buckets(buckets, alloc,
313                                      "S:file-revs-report");
314
315   *body_bkt = buckets;
316   return SVN_NO_ERROR;
317 }
318
319 svn_error_t *
320 svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session,
321                            const char *path,
322                            svn_revnum_t start,
323                            svn_revnum_t end,
324                            svn_boolean_t include_merged_revisions,
325                            svn_file_rev_handler_t rev_handler,
326                            void *rev_handler_baton,
327                            apr_pool_t *pool)
328 {
329   blame_context_t *blame_ctx;
330   svn_ra_serf__session_t *session = ra_session->priv;
331   svn_ra_serf__handler_t *handler;
332   svn_ra_serf__xml_context_t *xmlctx;
333   const char *req_url;
334   svn_error_t *err;
335
336   blame_ctx = apr_pcalloc(pool, sizeof(*blame_ctx));
337   blame_ctx->pool = pool;
338   blame_ctx->path = path;
339   blame_ctx->file_rev = rev_handler;
340   blame_ctx->file_rev_baton = rev_handler_baton;
341   blame_ctx->start = start;
342   blame_ctx->end = end;
343   blame_ctx->include_merged_revisions = include_merged_revisions;
344
345   SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
346                                       session, NULL /* conn */,
347                                       NULL /* url */, end,
348                                       pool, pool));
349
350   xmlctx = svn_ra_serf__xml_context_create(blame_ttable,
351                                            blame_opened,
352                                            blame_closed,
353                                            blame_cdata,
354                                            blame_ctx,
355                                            pool);
356   handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
357
358   handler->method = "REPORT";
359   handler->path = req_url;
360   handler->body_type = "text/xml";
361   handler->body_delegate = create_file_revs_body;
362   handler->body_delegate_baton = blame_ctx;
363   handler->conn = session->conns[0];
364   handler->session = session;
365
366   err = svn_ra_serf__context_run_one(handler, pool);
367
368   err = svn_error_compose_create(
369             svn_ra_serf__error_on_status(handler->sline,
370                                          handler->path,
371                                          handler->location),
372             err);
373
374   return svn_error_trace(err);
375 }