source: valtobtest/subversion-1.6.2/subversion/libsvn_ra_serf/blame.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: 12.5 KB
Line 
1/*
2 * blame.c :  entry point for blame RA 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#include <apr_uri.h>
20
21#include <expat.h>
22
23#include <serf.h>
24
25#include "svn_pools.h"
26#include "svn_ra.h"
27#include "svn_dav.h"
28#include "svn_xml.h"
29#include "svn_config.h"
30#include "svn_delta.h"
31#include "svn_version.h"
32#include "svn_path.h"
33#include "svn_base64.h"
34#include "svn_props.h"
35
36#include "svn_private_config.h"
37
38#include "ra_serf.h"
39#include "../libsvn_ra/ra_loader.h"
40
41
42/*
43 * This enum represents the current state of our XML parsing for a REPORT.
44 */
45typedef enum {
46  NONE = 0,
47  FILE_REVS_REPORT,
48  FILE_REV,
49  REV_PROP,
50  SET_PROP,
51  REMOVE_PROP,
52  MERGED_REVISION,
53  TXDELTA,
54} blame_state_e;
55
56typedef struct {
57  /* Current pool. */
58  apr_pool_t *pool;
59
60  /* our suspicious file */
61  const char *path;
62
63  /* the intended suspect */
64  svn_revnum_t rev;
65
66  /* Hashtable of revision properties */
67  apr_hash_t *rev_props;
68
69  /* Added and removed properties (svn_prop_t*'s) */
70  apr_array_header_t *prop_diffs;
71
72  /* txdelta */
73  svn_txdelta_window_handler_t txdelta;
74  void *txdelta_baton;
75
76  /* returned txdelta stream */
77  svn_stream_t *stream;
78
79  /* Is this property base64-encoded? */
80  svn_boolean_t prop_base64;
81
82  /* The currently collected value as we build it up */
83  const char *prop_name;
84  const char *prop_attr;
85  apr_size_t prop_attr_len;
86
87  svn_string_t *prop_string;
88
89  /* Merged revision flag */
90  svn_boolean_t merged_revision;
91
92} blame_info_t;
93
94typedef struct {
95  /* pool passed to get_file_revs */
96  apr_pool_t *pool;
97
98  /* parameters set by our caller */
99  const char *path;
100  svn_revnum_t start;
101  svn_revnum_t end;
102
103  /* are we done? */
104  svn_boolean_t done;
105
106  /* blame handler and baton */
107  svn_file_rev_handler_t file_rev;
108  void *file_rev_baton;
109} blame_context_t;
110
111
112static blame_info_t *
113push_state(svn_ra_serf__xml_parser_t *parser,
114           blame_context_t *blame_ctx,
115           blame_state_e state)
116{
117  svn_ra_serf__xml_push_state(parser, state);
118
119  if (state == FILE_REV)
120    {
121      blame_info_t *info;
122
123      info = apr_palloc(parser->state->pool, sizeof(*info));
124
125      info->pool = parser->state->pool;
126
127      info->rev = SVN_INVALID_REVNUM;
128      info->path = NULL;
129
130      info->rev_props = apr_hash_make(info->pool);
131      info->prop_diffs = apr_array_make(info->pool, 0, sizeof(svn_prop_t));
132
133      info->stream = NULL;
134      info->merged_revision = FALSE;
135
136      parser->state->private = info;
137    }
138
139  return parser->state->private;
140}
141
142static const svn_string_t *
143create_propval(blame_info_t *info)
144{
145  const svn_string_t *s;
146
147  if (!info->prop_attr)
148    {
149      return svn_string_create("", info->pool);
150    }
151  else
152    {
153      info->prop_attr = apr_pmemdup(info->pool, info->prop_attr,
154                                    info->prop_attr_len + 1);
155    }
156
157  /* Include the null term. */
158  s = svn_string_ncreate(info->prop_attr, info->prop_attr_len + 1, info->pool);
159  if (info->prop_base64 == TRUE)
160    {
161      s = svn_base64_decode_string(s, info->pool);
162    }
163  return s;
164}
165
166static svn_error_t *
167start_blame(svn_ra_serf__xml_parser_t *parser,
168            void *userData,
169            svn_ra_serf__dav_props_t name,
170            const char **attrs)
171{
172  blame_context_t *blame_ctx = userData;
173  blame_state_e state;
174
175  state = parser->state->current_state;
176
177  if (state == NONE && strcmp(name.name, "file-revs-report") == 0)
178    {
179      push_state(parser, blame_ctx, FILE_REVS_REPORT);
180    }
181  else if (state == FILE_REVS_REPORT &&
182           strcmp(name.name, "file-rev") == 0)
183    {
184      blame_info_t *info;
185
186      info = push_state(parser, blame_ctx, FILE_REV);
187
188      info->path = apr_pstrdup(info->pool,
189                               svn_xml_get_attr_value("path", attrs));
190      info->rev = SVN_STR_TO_REV(svn_xml_get_attr_value("rev", attrs));
191    }
192  else if (state == FILE_REV)
193    {
194      blame_info_t *info;
195      const char *enc;
196
197      info = parser->state->private;
198
199      if (strcmp(name.name, "rev-prop") == 0)
200        {
201          push_state(parser, blame_ctx, REV_PROP);
202        }
203      else if (strcmp(name.name, "set-prop") == 0)
204        {
205          push_state(parser, blame_ctx, SET_PROP);
206        }
207      if (strcmp(name.name, "remove-prop") == 0)
208        {
209          push_state(parser, blame_ctx, REMOVE_PROP);
210        }
211      else if (strcmp(name.name, "merged-revision") == 0)
212        {
213          push_state(parser, blame_ctx, MERGED_REVISION);
214        }
215      else if (strcmp(name.name, "txdelta") == 0)
216        {
217          SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton,
218                                      info->path, info->rev,
219                                      info->rev_props, info->merged_revision,
220                                      &info->txdelta, &info->txdelta_baton,
221                                      info->prop_diffs, info->pool));
222
223          info->stream = svn_base64_decode
224              (svn_txdelta_parse_svndiff(info->txdelta, info->txdelta_baton,
225                                         TRUE, info->pool), info->pool);
226
227          push_state(parser, blame_ctx, TXDELTA);
228        }
229
230      state = parser->state->current_state;
231
232      switch (state)
233        {
234        case REV_PROP:
235        case SET_PROP:
236        case REMOVE_PROP:
237          info->prop_name = apr_pstrdup(info->pool,
238                                        svn_xml_get_attr_value("name", attrs));
239          info->prop_attr = NULL;
240          info->prop_attr_len = 0;
241
242          enc = svn_xml_get_attr_value("encoding", attrs);
243          if (enc && strcmp(enc, "base64") == 0)
244            {
245              info->prop_base64 = TRUE;
246            }
247          else
248            {
249              info->prop_base64 = FALSE;
250            }
251          break;
252        case MERGED_REVISION:
253            info->merged_revision = TRUE;
254          break;
255        default:
256          break;
257        }
258    }
259
260  return SVN_NO_ERROR;
261}
262
263static svn_error_t *
264end_blame(svn_ra_serf__xml_parser_t *parser,
265          void *userData,
266          svn_ra_serf__dav_props_t name)
267{
268  blame_context_t *blame_ctx = userData;
269  blame_state_e state;
270  blame_info_t *info;
271
272  state = parser->state->current_state;
273  info = parser->state->private;
274
275  if (state == NONE)
276    {
277      return SVN_NO_ERROR;
278    }
279
280  if (state == FILE_REVS_REPORT &&
281      strcmp(name.name, "file-revs-report") == 0)
282    {
283      svn_ra_serf__xml_pop_state(parser);
284    }
285  else if (state == FILE_REV &&
286           strcmp(name.name, "file-rev") == 0)
287    {
288      /* no file changes. */
289      if (!info->stream)
290        {
291          SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton,
292                                      info->path, info->rev,
293                                      info->rev_props, FALSE,
294                                      NULL, NULL,
295                                      info->prop_diffs, info->pool));
296        }
297      svn_ra_serf__xml_pop_state(parser);
298    }
299  else if (state == REV_PROP &&
300           strcmp(name.name, "rev-prop") == 0)
301    {
302      apr_hash_set(info->rev_props,
303                   info->prop_name, APR_HASH_KEY_STRING,
304                   create_propval(info));
305
306      svn_ra_serf__xml_pop_state(parser);
307    }
308  else if ((state == SET_PROP &&
309            strcmp(name.name, "set-prop") == 0) ||
310           (state == REMOVE_PROP &&
311            strcmp(name.name, "remove-prop") == 0))
312    {
313      svn_prop_t *prop = apr_array_push(info->prop_diffs);
314      prop->name = info->prop_name;
315      prop->value = create_propval(info);
316
317      svn_ra_serf__xml_pop_state(parser);
318    }
319  else if (state == MERGED_REVISION &&
320           strcmp(name.name, "merged-revision") == 0)
321    {
322      svn_ra_serf__xml_pop_state(parser);
323    }
324  else if (state == TXDELTA &&
325           strcmp(name.name, "txdelta") == 0)
326    {
327      svn_stream_close(info->stream);
328
329      svn_ra_serf__xml_pop_state(parser);
330    }
331
332  return SVN_NO_ERROR;
333}
334
335static svn_error_t *
336cdata_blame(svn_ra_serf__xml_parser_t *parser,
337            void *userData,
338            const char *data,
339            apr_size_t len)
340{
341  blame_context_t *blame_ctx = userData;
342  blame_state_e state;
343  blame_info_t *info;
344
345  UNUSED_CTX(blame_ctx);
346
347  state = parser->state->current_state;
348  info = parser->state->private;
349
350  if (state == NONE)
351    {
352      return SVN_NO_ERROR;
353    }
354
355  switch (state)
356    {
357      case REV_PROP:
358      case SET_PROP:
359        svn_ra_serf__expand_string(&info->prop_attr, &info->prop_attr_len,
360                                   data, len, parser->state->pool);
361        break;
362      case TXDELTA:
363        if (info->stream)
364          {
365            apr_size_t ret_len;
366
367            ret_len = len;
368
369            SVN_ERR(svn_stream_write(info->stream, data, &ret_len));
370          }
371        break;
372      default:
373        break;
374    }
375
376  return SVN_NO_ERROR;
377}
378
379svn_error_t *
380svn_ra_serf__get_file_revs(svn_ra_session_t *ra_session,
381                           const char *path,
382                           svn_revnum_t start,
383                           svn_revnum_t end,
384                           svn_boolean_t include_merged_revisions,
385                           svn_file_rev_handler_t rev_handler,
386                           void *rev_handler_baton,
387                           apr_pool_t *pool)
388{
389  blame_context_t *blame_ctx;
390  svn_ra_serf__session_t *session = ra_session->priv;
391  svn_ra_serf__handler_t *handler;
392  svn_ra_serf__xml_parser_t *parser_ctx;
393  serf_bucket_t *buckets;
394  const char *relative_url, *basecoll_url, *req_url;
395  int status_code;
396  svn_error_t *err;
397
398  blame_ctx = apr_pcalloc(pool, sizeof(*blame_ctx));
399  blame_ctx->pool = pool;
400  blame_ctx->file_rev = rev_handler;
401  blame_ctx->file_rev_baton = rev_handler_baton;
402  blame_ctx->start = start;
403  blame_ctx->end = end;
404  blame_ctx->done = FALSE;
405
406  buckets = serf_bucket_aggregate_create(session->bkt_alloc);
407
408  svn_ra_serf__add_open_tag_buckets(buckets, session->bkt_alloc,
409                                    "S:file-revs-report",
410                                    "xmlns:S", SVN_XML_NAMESPACE,
411                                    NULL);
412
413  svn_ra_serf__add_tag_buckets(buckets,
414                               "S:start-revision", apr_ltoa(pool, start),
415                               session->bkt_alloc);
416
417  svn_ra_serf__add_tag_buckets(buckets,
418                               "S:end-revision", apr_ltoa(pool, end),
419                               session->bkt_alloc);
420
421  if (include_merged_revisions)
422    {
423      svn_ra_serf__add_tag_buckets(buckets,
424                                   "S:include-merged-revisions", NULL,
425                                   session->bkt_alloc);
426    }
427
428  svn_ra_serf__add_tag_buckets(buckets,
429                               "S:path", path,
430                               session->bkt_alloc);
431
432  svn_ra_serf__add_close_tag_buckets(buckets, session->bkt_alloc,
433                                     "S:file-revs-report");
434
435  SVN_ERR(svn_ra_serf__get_baseline_info(&basecoll_url, &relative_url,
436                                         session, session->repos_url.path,
437                                         end, NULL, pool));
438  req_url = svn_path_url_add_component(basecoll_url, relative_url, pool);
439
440  handler = apr_pcalloc(pool, sizeof(*handler));
441
442  handler->method = "REPORT";
443  handler->path = req_url;
444  handler->body_buckets = buckets;
445  handler->body_type = "text/xml";
446  handler->conn = session->conns[0];
447  handler->session = session;
448
449  parser_ctx = apr_pcalloc(pool, sizeof(*parser_ctx));
450
451  parser_ctx->pool = pool;
452  parser_ctx->user_data = blame_ctx;
453  parser_ctx->start = start_blame;
454  parser_ctx->end = end_blame;
455  parser_ctx->cdata = cdata_blame;
456  parser_ctx->done = &blame_ctx->done;
457  parser_ctx->status_code = &status_code;
458
459  handler->response_handler = svn_ra_serf__handle_xml_parser;
460  handler->response_baton = parser_ctx;
461
462  svn_ra_serf__request_create(handler);
463
464  err = svn_ra_serf__context_run_wait(&blame_ctx->done, session, pool);
465
466  if (parser_ctx->error)
467    {
468      svn_error_clear(err);
469      err = SVN_NO_ERROR;
470      SVN_ERR(parser_ctx->error);
471    }
472
473  SVN_ERR(svn_ra_serf__error_on_status(status_code, handler->path));
474
475  return err;
476}
Note: See TracBrowser for help on using the repository browser.