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, repos_URL,
94                            callbacks2, callback_baton, config, sesspool));
95
96  if (strcmp(repos_URL, session_url) != 0)
97    {
98      svn_pool_destroy(sesspool);
99      return svn_error_createf(SVN_ERR_RA_SESSION_URL_MISMATCH, NULL,
100                               _("Session URL '%s' does not match requested "
101                                 " URL '%s', and redirection was disallowed."),
102                               session_url, repos_URL);
103    }
104
105  *session_baton = sess;
106  return SVN_NO_ERROR;
107}
108
109static svn_error_t *compat_get_latest_revnum(void *session_baton,
110                                             svn_revnum_t *latest_revnum,
111                                             apr_pool_t *pool)
112{
113  return VTBL.get_latest_revnum(session_baton, latest_revnum, pool);
114}
115
116static svn_error_t *compat_get_dated_revision(void *session_baton,
117                                              svn_revnum_t *revision,
118                                              apr_time_t tm,
119                                              apr_pool_t *pool)
120{
121  return VTBL.get_dated_revision(session_baton, revision, tm, pool);
122}
123
124static svn_error_t *compat_change_rev_prop(void *session_baton,
125                                           svn_revnum_t rev,
126                                           const char *propname,
127                                           const svn_string_t *value,
128                                           apr_pool_t *pool)
129{
130  return VTBL.change_rev_prop(session_baton, rev, propname, NULL, value, pool);
131}
132
133static svn_error_t *compat_rev_proplist(void *session_baton,
134                                        svn_revnum_t rev,
135                                        apr_hash_t **props,
136                                        apr_pool_t *pool)
137{
138  return VTBL.rev_proplist(session_baton, rev, props, pool);
139}
140
141static svn_error_t *compat_rev_prop(void *session_baton,
142                                    svn_revnum_t rev,
143                                    const char *propname,
144                                    svn_string_t **value,
145                                    apr_pool_t *pool)
146{
147  return VTBL.rev_prop(session_baton, rev, propname, value, pool);
148}
149
150static svn_error_t *compat_get_commit_editor(void *session_baton,
151                                             const svn_delta_editor_t
152                                             **editor,
153                                             void **edit_baton,
154                                             const char *log_msg,
155                                             svn_commit_callback_t callback,
156                                             void *callback_baton,
157                                             apr_pool_t *pool)
158{
159  svn_commit_callback2_t callback2;
160  void *callback2_baton;
161  apr_hash_t *revprop_table = apr_hash_make(pool);
162
163  svn_compat_wrap_commit_callback(&callback2, &callback2_baton,
164                                  callback, callback_baton,
165                                  pool);
166  apr_hash_set(revprop_table, SVN_PROP_REVISION_LOG, APR_HASH_KEY_STRING,
167               svn_string_create(log_msg, pool));
168  return VTBL.get_commit_editor(session_baton, editor, edit_baton,
169                                revprop_table, callback2, callback2_baton,
170                                NULL, TRUE, pool);
171}
172
173static svn_error_t *compat_get_file(void *session_baton,
174                                    const char *path,
175                                    svn_revnum_t revision,
176                                    svn_stream_t *stream,
177                                    svn_revnum_t *fetched_rev,
178                                    apr_hash_t **props,
179                                    apr_pool_t *pool)
180{
181  return VTBL.get_file(session_baton, path, revision, stream, fetched_rev,
182                       props, pool);
183}
184
185static svn_error_t *compat_get_dir(void *session_baton,
186                                   const char *path,
187                                   svn_revnum_t revision,
188                                   apr_hash_t **dirents,
189                                   svn_revnum_t *fetched_rev,
190                                   apr_hash_t **props,
191                                   apr_pool_t *pool)
192{
193  return VTBL.get_dir(session_baton, dirents, fetched_rev, props,
194                      path, revision, SVN_DIRENT_ALL, pool);
195}
196
197/** Reporter compat code. **/
198
199struct compat_report_baton {
200  const svn_ra_reporter3_t *reporter;
201  void *baton;
202};
203
204static svn_error_t *compat_set_path(void *report_baton,
205                                    const char *path,
206                                    svn_revnum_t revision,
207                                    svn_boolean_t start_empty,
208                                    apr_pool_t *pool)
209{
210  struct compat_report_baton *crb = report_baton;
211
212  return crb->reporter->set_path(crb->baton, path, revision,
213                                 svn_depth_infinity, start_empty,
214                                 NULL, pool);
215}
216
217static svn_error_t *compat_delete_path(void *report_baton,
218                                       const char *path,
219                                       apr_pool_t *pool)
220{
221  struct compat_report_baton *crb = report_baton;
222
223  return crb->reporter->delete_path(crb->baton, path, pool);
224}
225
226static svn_error_t *compat_link_path(void *report_baton,
227                                     const char *path,
228                                     const char *url,
229                                     svn_revnum_t revision,
230                                     svn_boolean_t start_empty,
231                                     apr_pool_t *pool)
232{
233  struct compat_report_baton *crb = report_baton;
234
235  return crb->reporter->link_path(crb->baton, path, url, revision,
236                                  svn_depth_infinity, start_empty,
237                                  NULL, pool);
238}
239
240static svn_error_t *compat_finish_report(void *report_baton,
241                                         apr_pool_t *pool)
242{
243  struct compat_report_baton *crb = report_baton;
244
245  return crb->reporter->finish_report(crb->baton, pool);
246}
247
248static svn_error_t *compat_abort_report(void *report_baton,
249                                        apr_pool_t *pool)
250{
251  struct compat_report_baton *crb = report_baton;
252
253  return crb->reporter->abort_report(crb->baton, pool);
254}
255
256static const svn_ra_reporter_t compat_reporter = {
257  compat_set_path,
258  compat_delete_path,
259  compat_link_path,
260  compat_finish_report,
261  compat_abort_report
262};
263
264static void compat_wrap_reporter(const svn_ra_reporter_t **reporter,
265                                 void **baton,
266                                 const svn_ra_reporter3_t *wrapped,
267                                 void *wrapped_baton,
268                                 apr_pool_t *pool)
269{
270  struct compat_report_baton *crb = apr_palloc(pool, sizeof(*crb));
271  crb->reporter = wrapped;
272  crb->baton = wrapped_baton;
273
274  *reporter = &compat_reporter;
275  *baton = crb;
276}
277
278static svn_error_t *compat_do_update(void *session_baton,
279                                     const svn_ra_reporter_t **reporter,
280                                     void **report_baton,
281                                     svn_revnum_t revision_to_update_to,
282                                     const char *update_target,
283                                     svn_boolean_t recurse,
284                                     const svn_delta_editor_t *editor,
285                                     void *update_baton,
286                                     apr_pool_t *pool)
287{
288  const svn_ra_reporter3_t *reporter3;
289  void *baton3;
290  svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
291
292  SVN_ERR(VTBL.do_update(session_baton, &reporter3, &baton3,
293                         revision_to_update_to, update_target, depth,
294                         FALSE /* send_copyfrom_args */,
295                         FALSE /* ignore_ancestry */,
296                         editor, update_baton,
297                         pool, pool));
298  compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
299
300  return SVN_NO_ERROR;
301}
302
303static svn_error_t *compat_do_switch(void *session_baton,
304                                     const svn_ra_reporter_t **reporter,
305                                     void **report_baton,
306                                     svn_revnum_t revision_to_switch_to,
307                                     const char *switch_target,
308                                     svn_boolean_t recurse,
309                                     const char *switch_url,
310                                     const svn_delta_editor_t *editor,
311                                     void *switch_baton,
312                                     apr_pool_t *pool)
313{
314  const svn_ra_reporter3_t *reporter3;
315  void *baton3;
316  svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
317
318  SVN_ERR(VTBL.do_switch(session_baton, &reporter3, &baton3,
319                         revision_to_switch_to, switch_target, depth,
320                         switch_url,
321                         FALSE /* send_copyfrom_args */,
322                         TRUE /* ignore_ancestry */,
323                         editor, switch_baton,
324                         pool /* result_pool */, pool /* scratch_pool */));
325
326  compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
327
328  return SVN_NO_ERROR;
329}
330
331static svn_error_t *compat_do_status(void *session_baton,
332                                     const svn_ra_reporter_t **reporter,
333                                     void **report_baton,
334                                     const char *status_target,
335                                     svn_revnum_t revision,
336                                     svn_boolean_t recurse,
337                                     const svn_delta_editor_t *editor,
338                                     void *status_baton,
339                                     apr_pool_t *pool)
340{
341  const svn_ra_reporter3_t *reporter3;
342  void *baton3;
343  svn_depth_t depth = SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse);
344
345  SVN_ERR(VTBL.do_status(session_baton, &reporter3, &baton3, status_target,
346                         revision, depth, editor, status_baton, pool));
347
348  compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
349
350  return SVN_NO_ERROR;
351}
352
353static svn_error_t *compat_do_diff(void *session_baton,
354                                   const svn_ra_reporter_t **reporter,
355                                   void **report_baton,
356                                   svn_revnum_t revision,
357                                   const char *diff_target,
358                                   svn_boolean_t recurse,
359                                   svn_boolean_t ignore_ancestry,
360                                   const char *versus_url,
361                                   const svn_delta_editor_t *diff_editor,
362                                   void *diff_baton,
363                                   apr_pool_t *pool)
364{
365  const svn_ra_reporter3_t *reporter3;
366  void *baton3;
367  svn_depth_t depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
368
369  SVN_ERR(VTBL.do_diff(session_baton, &reporter3, &baton3, revision,
370                       diff_target, depth, ignore_ancestry, TRUE,
371                       versus_url, diff_editor, diff_baton, pool));
372
373  compat_wrap_reporter(reporter, report_baton, reporter3, baton3, pool);
374
375  return SVN_NO_ERROR;
376}
377
378static svn_error_t *compat_get_log(void *session_baton,
379                                   const apr_array_header_t *paths,
380                                   svn_revnum_t start,
381                                   svn_revnum_t end,
382                                   svn_boolean_t discover_changed_paths,
383                                   svn_boolean_t strict_node_history,
384                                   svn_log_message_receiver_t receiver,
385                                   void *receiver_baton,
386                                   apr_pool_t *pool)
387{
388  svn_log_entry_receiver_t receiver2;
389  void *receiver2_baton;
390
391  svn_compat_wrap_log_receiver(&receiver2, &receiver2_baton,
392                               receiver, receiver_baton,
393                               pool);
394
395  return VTBL.get_log(session_baton, paths, start, end, 0, /* limit */
396                      discover_changed_paths, strict_node_history,
397                      FALSE, /* include_merged_revisions */
398                      svn_compat_log_revprops_in(pool), /* revprops */
399                      receiver2, receiver2_baton, pool);
400}
401
402static svn_error_t *compat_check_path(void *session_baton,
403                                      const char *path,
404                                      svn_revnum_t revision,
405                                      svn_node_kind_t *kind,
406                                      apr_pool_t *pool)
407{
408  return VTBL.check_path(session_baton, path, revision, kind, pool);
409}
410
411static svn_error_t *compat_get_uuid(void *session_baton,
412                                    const char **uuid,
413                                    apr_pool_t *pool)
414{
415  return VTBL.get_uuid(session_baton, uuid, pool);
416}
417
418static svn_error_t *compat_get_repos_root(void *session_baton,
419                                          const char **url,
420                                          apr_pool_t *pool)
421{
422  return VTBL.get_repos_root(session_baton, url, pool);
423}
424
425static svn_error_t *compat_get_locations(void *session_baton,
426                                         apr_hash_t **locations,
427                                         const char *path,
428                                         svn_revnum_t peg_revision,
429                                         apr_array_header_t *location_revs,
430                                         apr_pool_t *pool)
431{
432  return VTBL.get_locations(session_baton, locations, path, peg_revision,
433                            location_revs, pool);
434}
435
436static svn_error_t *compat_get_file_revs(void *session_baton,
437                                         const char *path,
438                                         svn_revnum_t start,
439                                         svn_revnum_t end,
440                                         svn_ra_file_rev_handler_t handler,
441                                         void *handler_baton,
442                                         apr_pool_t *pool)
443{
444  svn_file_rev_handler_t handler2;
445  void *handler2_baton;
446
447  svn_compat_wrap_file_rev_handler(&handler2, &handler2_baton,
448                                   handler, handler_baton,
449                                   pool);
450
451  return VTBL.get_file_revs(session_baton, path, start, end,
452                            FALSE, /* include merged revisions */
453                            handler2, handler2_baton, pool);
454}
455
456static const svn_version_t *compat_get_version(void)
457{
458  return VTBL.get_version();
459}
460
461
462static const svn_ra_plugin_t compat_plugin = {
463  NAME,
464  DESCRIPTION,
465  compat_open,
466  compat_get_latest_revnum,
467  compat_get_dated_revision,
468  compat_change_rev_prop,
469  compat_rev_proplist,
470  compat_rev_prop,
471  compat_get_commit_editor,
472  compat_get_file,
473  compat_get_dir,
474  compat_do_update,
475  compat_do_switch,
476  compat_do_status,
477  compat_do_diff,
478  compat_get_log,
479  compat_check_path,
480  compat_get_uuid,
481  compat_get_repos_root,
482  compat_get_locations,
483  compat_get_file_revs,
484  compat_get_version
485};
486
487svn_error_t *
488COMPAT_INITFUNC(int abi_version,
489                apr_pool_t *pool,
490                apr_hash_t *hash)
491{
492  const svn_ra__vtable_t *vtable;
493  const char * const * schemes;
494
495  if (abi_version < 1
496      || abi_version > SVN_RA_ABI_VERSION)
497    return svn_error_createf(SVN_ERR_RA_UNSUPPORTED_ABI_VERSION, NULL,
498                             _("Unsupported RA plugin ABI version (%d) "
499                               "for %s"), abi_version, NAME);
500
501  /* We call the new init function so it can check library dependencies or
502     do other initialization things.  We fake the loader version, since we
503     rely on the ABI version check instead. */
504  SVN_ERR(INITFUNC(VTBL.get_version(), &vtable, pool));
505
506  schemes = VTBL.get_schemes(pool);
507
508  for (; *schemes != NULL; ++schemes)
509    apr_hash_set(hash, *schemes, APR_HASH_KEY_STRING, &compat_plugin);
510
511  return SVN_NO_ERROR;
512}
513