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