1251881Speter/*
2251881Speter * blame.c :  entry point for blame RA functions for ra_serf
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter#include <apr_uri.h>
25251881Speter#include <serf.h>
26251881Speter
27251881Speter#include "svn_hash.h"
28251881Speter#include "svn_pools.h"
29251881Speter#include "svn_ra.h"
30251881Speter#include "svn_dav.h"
31251881Speter#include "svn_xml.h"
32251881Speter#include "svn_config.h"
33251881Speter#include "svn_delta.h"
34251881Speter#include "svn_path.h"
35251881Speter#include "svn_base64.h"
36251881Speter#include "svn_props.h"
37251881Speter
38251881Speter#include "svn_private_config.h"
39251881Speter
40251881Speter#include "private/svn_string_private.h"
41251881Speter
42251881Speter#include "ra_serf.h"
43251881Speter#include "../libsvn_ra/ra_loader.h"
44251881Speter
45251881Speter
46251881Speter/*
47251881Speter * This enum represents the current state of our XML parsing for a REPORT.
48251881Speter */
49251881Spetertypedef enum blame_state_e {
50251881Speter  INITIAL = 0,
51251881Speter  FILE_REVS_REPORT,
52251881Speter  FILE_REV,
53251881Speter  REV_PROP,
54251881Speter  SET_PROP,
55251881Speter  REMOVE_PROP,
56251881Speter  MERGED_REVISION,
57251881Speter  TXDELTA
58251881Speter} blame_state_e;
59251881Speter
60251881Speter
61251881Spetertypedef struct blame_context_t {
62251881Speter  /* pool passed to get_file_revs */
63251881Speter  apr_pool_t *pool;
64251881Speter
65251881Speter  /* parameters set by our caller */
66251881Speter  const char *path;
67251881Speter  svn_revnum_t start;
68251881Speter  svn_revnum_t end;
69251881Speter  svn_boolean_t include_merged_revisions;
70251881Speter
71251881Speter  /* blame handler and baton */
72251881Speter  svn_file_rev_handler_t file_rev;
73251881Speter  void *file_rev_baton;
74251881Speter
75251881Speter  /* As we parse each FILE_REV, we collect data in these variables:
76251881Speter     property changes and new content.  STREAM is valid when we're
77251881Speter     in the TXDELTA state, processing the incoming cdata.  */
78251881Speter  apr_hash_t *rev_props;
79251881Speter  apr_array_header_t *prop_diffs;
80251881Speter  apr_pool_t *state_pool;  /* put property stuff in here  */
81251881Speter
82251881Speter  svn_stream_t *stream;
83251881Speter
84251881Speter} blame_context_t;
85251881Speter
86251881Speter
87251881Speter#define D_ "DAV:"
88251881Speter#define S_ SVN_XML_NAMESPACE
89251881Speterstatic const svn_ra_serf__xml_transition_t blame_ttable[] = {
90251881Speter  { INITIAL, S_, "file-revs-report", FILE_REVS_REPORT,
91251881Speter    FALSE, { NULL }, FALSE },
92251881Speter
93251881Speter  { FILE_REVS_REPORT, S_, "file-rev", FILE_REV,
94251881Speter    FALSE, { "path", "rev", NULL }, TRUE },
95251881Speter
96251881Speter  { FILE_REV, S_, "rev-prop", REV_PROP,
97251881Speter    TRUE, { "name", "?encoding", NULL }, TRUE },
98251881Speter
99251881Speter  { FILE_REV, S_, "set-prop", SET_PROP,
100251881Speter    TRUE, { "name", "?encoding", NULL }, TRUE },
101251881Speter
102251881Speter  { FILE_REV, S_, "remove-prop", REMOVE_PROP,
103251881Speter    FALSE, { "name", NULL }, TRUE },
104251881Speter
105251881Speter  { FILE_REV, S_, "merged-revision", MERGED_REVISION,
106251881Speter    FALSE, { NULL }, TRUE },
107251881Speter
108251881Speter  { FILE_REV, S_, "txdelta", TXDELTA,
109251881Speter    FALSE, { NULL }, TRUE },
110251881Speter
111251881Speter  { 0 }
112251881Speter};
113251881Speter
114251881Speter
115251881Speter/* Conforms to svn_ra_serf__xml_opened_t  */
116251881Speterstatic svn_error_t *
117251881Speterblame_opened(svn_ra_serf__xml_estate_t *xes,
118251881Speter             void *baton,
119251881Speter             int entered_state,
120251881Speter             const svn_ra_serf__dav_props_t *tag,
121251881Speter             apr_pool_t *scratch_pool)
122251881Speter{
123251881Speter  blame_context_t *blame_ctx = baton;
124251881Speter
125251881Speter  if (entered_state == FILE_REV)
126251881Speter    {
127251881Speter      apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes);
128251881Speter
129251881Speter      /* Child elements will store properties in these structures.  */
130251881Speter      blame_ctx->rev_props = apr_hash_make(state_pool);
131251881Speter      blame_ctx->prop_diffs = apr_array_make(state_pool,
132251881Speter                                             5, sizeof(svn_prop_t));
133251881Speter      blame_ctx->state_pool = state_pool;
134251881Speter
135251881Speter      /* Clear this, so we can detect the absence of a TXDELTA.  */
136251881Speter      blame_ctx->stream = NULL;
137251881Speter    }
138251881Speter  else if (entered_state == TXDELTA)
139251881Speter    {
140251881Speter      apr_pool_t *state_pool = svn_ra_serf__xml_state_pool(xes);
141251881Speter      apr_hash_t *gathered = svn_ra_serf__xml_gather_since(xes, FILE_REV);
142251881Speter      const char *path;
143251881Speter      const char *rev;
144251881Speter      const char *merged_revision;
145251881Speter      svn_txdelta_window_handler_t txdelta;
146251881Speter      void *txdelta_baton;
147251881Speter
148251881Speter      path = svn_hash_gets(gathered, "path");
149251881Speter      rev = svn_hash_gets(gathered, "rev");
150251881Speter      merged_revision = svn_hash_gets(gathered, "merged-revision");
151251881Speter
152251881Speter      SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton,
153251881Speter                                  path, SVN_STR_TO_REV(rev),
154251881Speter                                  blame_ctx->rev_props,
155251881Speter                                  merged_revision != NULL,
156251881Speter                                  &txdelta, &txdelta_baton,
157251881Speter                                  blame_ctx->prop_diffs,
158251881Speter                                  state_pool));
159251881Speter
160251881Speter      blame_ctx->stream = svn_base64_decode(svn_txdelta_parse_svndiff(
161251881Speter                                              txdelta, txdelta_baton,
162251881Speter                                              TRUE /* error_on_early_close */,
163251881Speter                                              state_pool),
164251881Speter                                            state_pool);
165251881Speter    }
166251881Speter
167251881Speter  return SVN_NO_ERROR;
168251881Speter}
169251881Speter
170251881Speter
171251881Speter/* Conforms to svn_ra_serf__xml_closed_t  */
172251881Speterstatic svn_error_t *
173251881Speterblame_closed(svn_ra_serf__xml_estate_t *xes,
174251881Speter             void *baton,
175251881Speter             int leaving_state,
176251881Speter             const svn_string_t *cdata,
177251881Speter             apr_hash_t *attrs,
178251881Speter             apr_pool_t *scratch_pool)
179251881Speter{
180251881Speter  blame_context_t *blame_ctx = baton;
181251881Speter
182251881Speter  if (leaving_state == FILE_REV)
183251881Speter    {
184251881Speter      /* Note that we test STREAM, but any pointer is currently invalid.
185251881Speter         It was closed when left the TXDELTA state.  */
186251881Speter      if (blame_ctx->stream == NULL)
187251881Speter        {
188251881Speter          const char *path;
189251881Speter          const char *rev;
190251881Speter
191251881Speter          path = svn_hash_gets(attrs, "path");
192251881Speter          rev = svn_hash_gets(attrs, "rev");
193251881Speter
194251881Speter          /* Send a "no content" notification.  */
195251881Speter          SVN_ERR(blame_ctx->file_rev(blame_ctx->file_rev_baton,
196251881Speter                                      path, SVN_STR_TO_REV(rev),
197251881Speter                                      blame_ctx->rev_props,
198251881Speter                                      FALSE /* result_of_merge */,
199251881Speter                                      NULL, NULL, /* txdelta / baton */
200251881Speter                                      blame_ctx->prop_diffs,
201251881Speter                                      scratch_pool));
202251881Speter        }
203251881Speter    }
204251881Speter  else if (leaving_state == MERGED_REVISION)
205251881Speter    {
206251881Speter      svn_ra_serf__xml_note(xes, FILE_REV, "merged-revision", "*");
207251881Speter    }
208251881Speter  else if (leaving_state == TXDELTA)
209251881Speter    {
210251881Speter      SVN_ERR(svn_stream_close(blame_ctx->stream));
211251881Speter    }
212251881Speter  else
213251881Speter    {
214251881Speter      const char *name;
215251881Speter      const svn_string_t *value;
216251881Speter
217251881Speter      SVN_ERR_ASSERT(leaving_state == REV_PROP
218251881Speter                     || leaving_state == SET_PROP
219251881Speter                     || leaving_state == REMOVE_PROP);
220251881Speter
221251881Speter      name = apr_pstrdup(blame_ctx->state_pool,
222251881Speter                         svn_hash_gets(attrs, "name"));
223251881Speter
224251881Speter      if (leaving_state == REMOVE_PROP)
225251881Speter        {
226251881Speter          value = NULL;
227251881Speter        }
228251881Speter      else
229251881Speter        {
230251881Speter          const char *encoding = svn_hash_gets(attrs, "encoding");
231251881Speter
232251881Speter          if (encoding && strcmp(encoding, "base64") == 0)
233251881Speter            value = svn_base64_decode_string(cdata, blame_ctx->state_pool);
234251881Speter          else
235251881Speter            value = svn_string_dup(cdata, blame_ctx->state_pool);
236251881Speter        }
237251881Speter
238251881Speter      if (leaving_state == REV_PROP)
239251881Speter        {
240251881Speter          svn_hash_sets(blame_ctx->rev_props, name, value);
241251881Speter        }
242251881Speter      else
243251881Speter        {
244251881Speter          svn_prop_t *prop = apr_array_push(blame_ctx->prop_diffs);
245251881Speter
246251881Speter          prop->name = name;
247251881Speter          prop->value = value;
248251881Speter        }
249251881Speter    }
250251881Speter
251251881Speter  return SVN_NO_ERROR;
252251881Speter}
253251881Speter
254251881Speter
255251881Speter/* Conforms to svn_ra_serf__xml_cdata_t  */
256251881Speterstatic svn_error_t *
257251881Speterblame_cdata(svn_ra_serf__xml_estate_t *xes,
258251881Speter            void *baton,
259251881Speter            int current_state,
260251881Speter            const char *data,
261251881Speter            apr_size_t len,
262251881Speter            apr_pool_t *scratch_pool)
263251881Speter{
264251881Speter  blame_context_t *blame_ctx = baton;
265251881Speter
266251881Speter  if (current_state == TXDELTA)
267251881Speter    {
268251881Speter      SVN_ERR(svn_stream_write(blame_ctx->stream, data, &len));
269251881Speter      /* Ignore the returned LEN value.  */
270251881Speter    }
271251881Speter
272251881Speter  return SVN_NO_ERROR;
273251881Speter}
274251881Speter
275251881Speter
276251881Speter/* Implements svn_ra_serf__request_body_delegate_t */
277251881Speterstatic svn_error_t *
278251881Spetercreate_file_revs_body(serf_bucket_t **body_bkt,
279251881Speter                      void *baton,
280251881Speter                      serf_bucket_alloc_t *alloc,
281251881Speter                      apr_pool_t *pool)
282251881Speter{
283251881Speter  serf_bucket_t *buckets;
284251881Speter  blame_context_t *blame_ctx = baton;
285251881Speter
286251881Speter  buckets = serf_bucket_aggregate_create(alloc);
287251881Speter
288251881Speter  svn_ra_serf__add_open_tag_buckets(buckets, alloc,
289251881Speter                                    "S:file-revs-report",
290251881Speter                                    "xmlns:S", SVN_XML_NAMESPACE,
291251881Speter                                    NULL);
292251881Speter
293251881Speter  svn_ra_serf__add_tag_buckets(buckets,
294251881Speter                               "S:start-revision", apr_ltoa(pool, blame_ctx->start),
295251881Speter                               alloc);
296251881Speter
297251881Speter  svn_ra_serf__add_tag_buckets(buckets,
298251881Speter                               "S:end-revision", apr_ltoa(pool, blame_ctx->end),
299251881Speter                               alloc);
300251881Speter
301251881Speter  if (blame_ctx->include_merged_revisions)
302251881Speter    {
303251881Speter      svn_ra_serf__add_tag_buckets(buckets,
304251881Speter                                   "S:include-merged-revisions", NULL,
305251881Speter                                   alloc);
306251881Speter    }
307251881Speter
308251881Speter  svn_ra_serf__add_tag_buckets(buckets,
309251881Speter                               "S:path", blame_ctx->path,
310251881Speter                               alloc);
311251881Speter
312251881Speter  svn_ra_serf__add_close_tag_buckets(buckets, alloc,
313251881Speter                                     "S:file-revs-report");
314251881Speter
315251881Speter  *body_bkt = buckets;
316251881Speter  return SVN_NO_ERROR;
317251881Speter}
318251881Speter
319251881Spetersvn_error_t *
320251881Spetersvn_ra_serf__get_file_revs(svn_ra_session_t *ra_session,
321251881Speter                           const char *path,
322251881Speter                           svn_revnum_t start,
323251881Speter                           svn_revnum_t end,
324251881Speter                           svn_boolean_t include_merged_revisions,
325251881Speter                           svn_file_rev_handler_t rev_handler,
326251881Speter                           void *rev_handler_baton,
327251881Speter                           apr_pool_t *pool)
328251881Speter{
329251881Speter  blame_context_t *blame_ctx;
330251881Speter  svn_ra_serf__session_t *session = ra_session->priv;
331251881Speter  svn_ra_serf__handler_t *handler;
332251881Speter  svn_ra_serf__xml_context_t *xmlctx;
333251881Speter  const char *req_url;
334251881Speter  svn_error_t *err;
335251881Speter
336251881Speter  blame_ctx = apr_pcalloc(pool, sizeof(*blame_ctx));
337251881Speter  blame_ctx->pool = pool;
338251881Speter  blame_ctx->path = path;
339251881Speter  blame_ctx->file_rev = rev_handler;
340251881Speter  blame_ctx->file_rev_baton = rev_handler_baton;
341251881Speter  blame_ctx->start = start;
342251881Speter  blame_ctx->end = end;
343251881Speter  blame_ctx->include_merged_revisions = include_merged_revisions;
344251881Speter
345251881Speter  SVN_ERR(svn_ra_serf__get_stable_url(&req_url, NULL /* latest_revnum */,
346251881Speter                                      session, NULL /* conn */,
347251881Speter                                      NULL /* url */, end,
348251881Speter                                      pool, pool));
349251881Speter
350251881Speter  xmlctx = svn_ra_serf__xml_context_create(blame_ttable,
351251881Speter                                           blame_opened,
352251881Speter                                           blame_closed,
353251881Speter                                           blame_cdata,
354251881Speter                                           blame_ctx,
355251881Speter                                           pool);
356251881Speter  handler = svn_ra_serf__create_expat_handler(xmlctx, pool);
357251881Speter
358251881Speter  handler->method = "REPORT";
359251881Speter  handler->path = req_url;
360251881Speter  handler->body_type = "text/xml";
361251881Speter  handler->body_delegate = create_file_revs_body;
362251881Speter  handler->body_delegate_baton = blame_ctx;
363251881Speter  handler->conn = session->conns[0];
364251881Speter  handler->session = session;
365251881Speter
366251881Speter  err = svn_ra_serf__context_run_one(handler, pool);
367251881Speter
368251881Speter  err = svn_error_compose_create(
369253734Speter            svn_ra_serf__error_on_status(handler->sline,
370251881Speter                                         handler->path,
371251881Speter                                         handler->location),
372251881Speter            err);
373251881Speter
374251881Speter  return svn_error_trace(err);
375251881Speter}
376