wrapper_template.h revision 362181
1/**
2 * @copyright
3 * ====================================================================
4 *    Licensed to the Apache Software Foundation (ASF) under one
5 *    or more contributor license agreements.  See the NOTICE file
6 *    distributed with this work for additional information
7 *    regarding copyright ownership.  The ASF licenses this file
8 *    to you under the Apache License, Version 2.0 (the
9 *    "License"); you may not use this file except in compliance
10 *    with the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 *    Unless required by applicable law or agreed to in writing,
15 *    software distributed under the License is distributed on an
16 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 *    KIND, either express or implied.  See the License for the
18 *    specific language governing permissions and limitations
19 *    under the License.
20 * ====================================================================
21 * @endcopyright
22 */
23
24#include <apr_pools.h>
25#include <apr_hash.h>
26#include <apr_time.h>
27
28#include "svn_types.h"
29#include "svn_string.h"
30#include "svn_props.h"
31#include "svn_compat.h"
32
33/* This file is a template for a compatibility wrapper for an RA library.
34 * It contains an svn_ra_plugin_t and wrappers for all of its functions,
35 * implemented in terms of svn_ra__vtable_t functions.  It also contains
36 * the implementations of an svn_ra_FOO_init for the FOO RA library.
37 *
38 * A file in the RA library includes this file, providing the
39 * following macros before inclusion:
40 *
41 * NAME             The library name, e.g. "ra_local".
42 * DESCRIPTION      The short library description as a string constant.
43 * VTBL             The name of an svn_ra_vtable_t object for the library.
44 * INITFUNC         The init function for the library, e.g. svn_ra_local__init.
45 * COMPAT_INITFUNC  The compatibility init function, e.g. svn_ra_local_init.
46 */
47
48/* Check that all our "arguments" are defined. */
49#if ! defined(NAME) || ! defined(DESCRIPTION) || ! defined(VTBL) \
50    || ! defined(INITFUNC) || ! defined(COMPAT_INITFUNC)
51#error Missing define for RA compatibility wrapper.
52#endif
53
54
55static svn_error_t *compat_open(void **session_baton,
56                                const char *repos_URL,
57                                const svn_ra_callbacks_t *callbacks,
58                                void *callback_baton,
59                                apr_hash_t *config,
60                                apr_pool_t *pool)
61{
62  /* Here, we should be calling svn_ra_create_callbacks to initialize
63   * the svn_ra_callbacks2_t structure.  However, doing that
64   * introduces a circular dependancy between libsvn_ra and
65   * libsvn_ra_{local,neon,serf,svn}, which include
66   * wrapper_template.h.  In turn, circular dependancies break the
67   * build on win32 (and possibly other systems).
68   *
69   * In order to avoid this happening at all, the code of
70   * svn_ra_create_callbacks is duplicated here.  This is evil, but
71   * the alternative (creating a new ra_util library) would be massive
72   * overkill for the time being.  Just be sure to keep the following
73   * line and the code of svn_ra_create_callbacks in sync.  */
74  apr_pool_t *sesspool = svn_pool_create(pool);
75  svn_ra_callbacks2_t *callbacks2 = apr_pcalloc(sesspool,
76                                                sizeof(*callbacks2));
77
78  svn_ra_session_t *sess = apr_pcalloc(sesspool, sizeof(*sess));
79  const char *session_url;
80
81  sess->vtable = &VTBL;
82  sess->pool = sesspool;
83
84  callbacks2->open_tmp_file = callbacks->open_tmp_file;
85  callbacks2->auth_baton = callbacks->auth_baton;
86  callbacks2->get_wc_prop = callbacks->get_wc_prop;
87  callbacks2->set_wc_prop = callbacks->set_wc_prop;
88  callbacks2->push_wc_prop = callbacks->push_wc_prop;
89  callbacks2->invalidate_wc_props = callbacks->invalidate_wc_props;
90  callbacks2->progress_func = NULL;
91  callbacks2->progress_baton = NULL;
92
93  SVN_ERR(VTBL.open_session(sess, &session_url, NULL, repos_URL,
94                            callbacks2, callback_baton,
95                            callbacks ? callbacks->auth_baton : NULL,
96                            config, sesspool, sesspool));
97
98  if (strcmp(repos_URL, session_url) != 0)
99    {
100      svn_pool_destroy(sesspool);
101      return svn_error_createf(SVN_ERR_RA_SESSION_URL_MISMATCH, NULL,
102                               _("Session URL '%s' does not match requested "
103                                 " URL '%s', and redirection was disallowed."),
104                               session_url, repos_URL);
105    }
106
107  *session_baton = sess;
108  return SVN_NO_ERROR;
109}
110
111static svn_error_t *compat_get_latest_revnum(void *session_baton,
112                                             svn_revnum_t *latest_revnum,
113                                             apr_pool_t *pool)
114{
115  return VTBL.get_latest_revnum(session_baton, latest_revnum, pool);
116}
117
118static svn_error_t *compat_get_dated_revision(void *session_baton,
119                                              svn_revnum_t *revision,
120                                              apr_time_t tm,
121                                              apr_pool_t *pool)
122{
123  return VTBL.get_dated_revision(session_baton, revision, tm, pool);
124}
125
126static svn_error_t *compat_change_rev_prop(void *session_baton,
127                                           svn_revnum_t rev,
128                                           const char *propname,
129                                           const svn_string_t *value,
130                                           apr_pool_t *pool)
131{
132  return VTBL.change_rev_prop(session_baton, rev, propname, NULL, value, pool);
133}
134
135static svn_error_t *compat_rev_proplist(void *session_baton,
136                                        svn_revnum_t rev,
137                                        apr_hash_t **props,
138                                        apr_pool_t *pool)
139{
140  return VTBL.rev_proplist(session_baton, rev, props, pool);
141}
142
143static svn_error_t *compat_rev_prop(void *session_baton,
144                                    svn_revnum_t rev,
145                                    const char *propname,
146                                    svn_string_t **value,
147                                    apr_pool_t *pool)
148{
149  return VTBL.rev_prop(session_baton, rev, propname, value, pool);
150}
151
152static svn_error_t *compat_get_commit_editor(void *session_baton,
153                                             const svn_delta_editor_t
154                                             **editor,
155                                             void **edit_baton,
156                                             const char *log_msg,
157                                             svn_commit_callback_t callback,
158                                             void *callback_baton,
159                                             apr_pool_t *pool)
160{
161  svn_commit_callback2_t callback2;
162  void *callback2_baton;
163  apr_hash_t *revprop_table = apr_hash_make(pool);
164
165  svn_compat_wrap_commit_callback(&callback2, &callback2_baton,
166                                  callback, callback_baton,
167                                  pool);
168  apr_hash_set(revprop_table, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING,
169               svn_string_create(log_msg, pool));
170  return VTBL.get_commit_editor(session_baton, editor, edit_baton,
171                                revprop_table, callback2, callback2_baton,
172                                NULL, TRUE, pool);
173}
174
175static svn_error_t *compat_get_file(void *session_baton,
176                                    const char *path,
177                                    svn_revnum_t revision,
178                                    svn_stream_t *stream,
179                                    svn_revnum_t *fetched_rev,
180                                    apr_hash_t **props,
181                                    apr_pool_t *pool)
182{
183  return VTBL.get_file(session_baton, path, revision, stream, fetched_rev,
184                       props, pool);
185}
186
187static svn_error_t *compat_get_dir(void *session_baton,
188                                   const char *path,
189                                   svn_revnum_t revision,
190                                   apr_hash_t **dirents,
191                                   svn_revnum_t *fetched_rev,
192                                   apr_hash_t **props,
193                                   apr_pool_t *pool)
194{
195  return VTBL.get_dir(session_baton, dirents, fetched_rev, props,
196                      path, revision, SVN_DIRENT_ALL, pool);
197}
198
199/** Reporter compat code. **/
200
201struct compat_report_baton {
202  const svn_ra_reporter3_t *reporter;
203  void *baton;
204};
205
206static svn_error_t *compat_set_path(void *report_baton,
207                                    const char *path,
208                                    svn_revnum_t revision,
209                                    svn_boolean_t start_empty,
210                                    apr_pool_t *pool)
211{
212  struct compat_report_baton *crb = report_baton;
213
214  return crb->reporter->set_path(crb->baton, path, revision,
215                                 svn_depth_infinity, start_empty,
216                                 NULL, pool);
217}
218
219static svn_error_t *compat_delete_path(void *report_baton,
220                                       const char *path,
221                                       apr_pool_t *pool)
222{
223  struct compat_report_baton *crb = report_baton;
224
225  return crb->reporter->delete_path(crb->baton, path, pool);
226}
227
228static svn_error_t *compat_link_path(void *report_baton,
229                                     const char *path,
230                                     const char *url,
231                                     svn_revnum_t revision,
232                                     svn_boolean_t start_empty,
233                                     apr_pool_t *pool)
234{
235  struct compat_report_baton *crb = report_baton;
236
237  return crb->reporter->link_path(crb->baton, path, url, revision,
238                                  svn_depth_infinity, start_empty,
239                                  NULL, pool);
240}
241
242static svn_error_t *compat_finish_report(void *report_baton,
243                                         apr_pool_t *pool)
244{
245  struct compat_report_baton *crb = report_baton;
246
247  return crb->reporter->finish_report(crb->baton, pool);
248}
249
250static svn_error_t *compat_abort_report(void *report_baton,
251                                        apr_pool_t *pool)
252{
253  struct compat_report_baton *crb = report_baton;
254
255  return crb->reporter->abort_report(crb->baton, pool);
256}
257
258static const svn_ra_reporter_t compat_reporter = {
259  compat_set_path,
260  compat_delete_path,
261  compat_link_path,
262  compat_finish_report,
263  compat_abort_report
264};
265
266static void compat_wrap_reporter(const svn_ra_reporter_t **reporter,
267                                 void **baton,
268                                 const svn_ra_reporter3_t *wrapped,
269                                 void *wrapped_baton,
270                                 apr_pool_t *pool)
271{
272  struct compat_report_baton *crb = apr_palloc(pool, sizeof(*crb));
273  crb->reporter = wrapped;
274  crb->baton = wrapped_baton;
275
276  *reporter = &compat_reporter;
277  *baton = crb;
278}
279
280static svn_error_t *compat_do_update(void *session_baton,
281                                     const svn_ra_reporter_t **reporter,
282                                     void **report_baton,
283                                     svn_revnum_t revision_to_update_to,
284                                     const char *update_target,
285                                     svn_boolean_t recurse,
286                                     const svn_delta_editor_t *editor,
287                                     void *update_baton,
288                                     apr_pool_t *pool)
289{
290  const svn_ra_reporter3_t *reporter3;
291  void *baton3;
292  svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
293
294  SVN_ERR(VTBL.do_update(session_baton, &reporter3, &baton3,
295                         revision_to_update_to, update_target, depth,
296                         FALSE /* send_copyfrom_args */,
297                         FALSE /* ignore_ancestry */,
298                         editor, update_baton,
299                         pool, pool));
300  compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
301
302  return SVN_NO_ERROR;
303}
304
305static svn_error_t *compat_do_switch(void *session_baton,
306                                     const svn_ra_reporter_t **reporter,
307                                     void **report_baton,
308                                     svn_revnum_t revision_to_switch_to,
309                                     const char *switch_target,
310                                     svn_boolean_t recurse,
311                                     const char *switch_url,
312                                     const svn_delta_editor_t *editor,
313                                     void *switch_baton,
314                                     apr_pool_t *pool)
315{
316  const svn_ra_reporter3_t *reporter3;
317  void *baton3;
318  svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
319
320  SVN_ERR(VTBL.do_switch(session_baton, &reporter3, &baton3,
321                         revision_to_switch_to, switch_target, depth,
322                         switch_url,
323                         FALSE /* send_copyfrom_args */,
324                         TRUE /* ignore_ancestry */,
325                         editor, switch_baton,
326                         pool /* result_pool */, pool /* scratch_pool */));
327
328  compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
329
330  return SVN_NO_ERROR;
331}
332
333static svn_error_t *compat_do_status(void *session_baton,
334                                     const svn_ra_reporter_t **reporter,
335                                     void **report_baton,
336                                     const char *status_target,
337                                     svn_revnum_t revision,
338                                     svn_boolean_t recurse,
339                                     const svn_delta_editor_t *editor,
340                                     void *status_baton,
341                                     apr_pool_t *pool)
342{
343  const svn_ra_reporter3_t *reporter3;
344  void *baton3;
345  svn_depth_t depth = SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse);
346
347  SVN_ERR(VTBL.do_status(session_baton, &reporter3, &baton3, status_target,
348                         revision, depth, editor, status_baton, pool));
349
350  compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
351
352  return SVN_NO_ERROR;
353}
354
355static svn_error_t *compat_do_diff(void *session_baton,
356                                   const svn_ra_reporter_t **reporter,
357                                   void **report_baton,
358                                   svn_revnum_t revision,
359                                   const char *diff_target,
360                                   svn_boolean_t recurse,
361                                   svn_boolean_t ignore_ancestry,
362                                   const char *versus_url,
363                                   const svn_delta_editor_t *diff_editor,
364                                   void *diff_baton,
365                                   apr_pool_t *pool)
366{
367  const svn_ra_reporter3_t *reporter3;
368  void *baton3;
369  svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
370
371  SVN_ERR(VTBL.do_diff(session_baton, &reporter3, &baton3, revision,
372                       diff_target, depth, ignore_ancestry, TRUE,
373                       versus_url, diff_editor, diff_baton, pool));
374
375  compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
376
377  return SVN_NO_ERROR;
378}
379
380static svn_error_t *compat_get_log(void *session_baton,
381                                   const apr_array_header_t *paths,
382                                   svn_revnum_t start,
383                                   svn_revnum_t end,
384                                   svn_boolean_t discover_changed_paths,
385                                   svn_boolean_t strict_node_history,
386                                   svn_log_message_receiver_t receiver,
387                                   void *receiver_baton,
388                                   apr_pool_t *pool)
389{
390  svn_log_entry_receiver_t receiver2;
391  void *receiver2_baton;
392
393  svn_compat_wrap_log_receiver(&receiver2, &receiver2_baton,
394                               receiver, receiver_baton,
395                               pool);
396
397  return VTBL.get_log(session_baton, paths, start, end, 0, /* limit */
398                      discover_changed_paths, strict_node_history,
399                      FALSE, /* include_merged_revisions */
400                      svn_compat_log_revprops_in(pool), /* revprops */
401                      receiver2, receiver2_baton, pool);
402}
403
404static svn_error_t *compat_check_path(void *session_baton,
405                                      const char *path,
406                                      svn_revnum_t revision,
407                                      svn_node_kind_t *kind,
408                                      apr_pool_t *pool)
409{
410  return VTBL.check_path(session_baton, path, revision, kind, pool);
411}
412
413static svn_error_t *compat_get_uuid(void *session_baton,
414                                    const char **uuid,
415                                    apr_pool_t *pool)
416{
417  return VTBL.get_uuid(session_baton, uuid, pool);
418}
419
420static svn_error_t *compat_get_repos_root(void *session_baton,
421                                          const char **url,
422                                          apr_pool_t *pool)
423{
424  return VTBL.get_repos_root(session_baton, url, pool);
425}
426
427static svn_error_t *compat_get_locations(void *session_baton,
428                                         apr_hash_t **locations,
429                                         const char *path,
430                                         svn_revnum_t peg_revision,
431                                         apr_array_header_t *location_revs,
432                                         apr_pool_t *pool)
433{
434  return VTBL.get_locations(session_baton, locations, path, peg_revision,
435                            location_revs, pool);
436}
437
438static svn_error_t *compat_get_file_revs(void *session_baton,
439                                         const char *path,
440                                         svn_revnum_t start,
441                                         svn_revnum_t end,
442                                         svn_ra_file_rev_handler_t handler,
443                                         void *handler_baton,
444                                         apr_pool_t *pool)
445{
446  svn_file_rev_handler_t handler2;
447  void *handler2_baton;
448
449  svn_compat_wrap_file_rev_handler(&handler2, &handler2_baton,
450                                   handler, handler_baton,
451                                   pool);
452
453  return VTBL.get_file_revs(session_baton, path, start, end,
454                            FALSE, /* include merged revisions */
455                            handler2, handler2_baton, pool);
456}
457
458static const svn_version_t *compat_get_version(void)
459{
460  return VTBL.get_version();
461}
462
463
464static const svn_ra_plugin_t compat_plugin = {
465  NAME,
466  DESCRIPTION,
467  compat_open,
468  compat_get_latest_revnum,
469  compat_get_dated_revision,
470  compat_change_rev_prop,
471  compat_rev_proplist,
472  compat_rev_prop,
473  compat_get_commit_editor,
474  compat_get_file,
475  compat_get_dir,
476  compat_do_update,
477  compat_do_switch,
478  compat_do_status,
479  compat_do_diff,
480  compat_get_log,
481  compat_check_path,
482  compat_get_uuid,
483  compat_get_repos_root,
484  compat_get_locations,
485  compat_get_file_revs,
486  compat_get_version
487};
488
489svn_error_t *
490COMPAT_INITFUNC(int abi_version,
491                apr_pool_t *pool,
492                apr_hash_t *hash)
493{
494  const svn_ra__vtable_t *vtable;
495  const char * const * schemes;
496
497  if (abi_version < 1
498      || abi_version > SVN_RA_ABI_VERSION)
499    return svn_error_createf(SVN_ERR_RA_UNSUPPORTED_ABI_VERSION, NULL,
500                             _("Unsupported RA plugin ABI version (%d) "
501                               "for %s"), abi_version, NAME);
502
503  /* We call the new init function so it can check library dependencies or
504     do other initialization things.  We fake the loader version, since we
505     rely on the ABI version check instead. */
506  SVN_ERR(INITFUNC(VTBL.get_version(), &vtable, pool));
507
508  schemes = VTBL.get_schemes(pool);
509
510  for (; *schemes != NULL; ++schemes)
511    apr_hash_set(hash, *schemes, APR_HASH_KEY_STRING, &compat_plugin);
512
513  return SVN_NO_ERROR;
514}
515