1# ==================================================================== 2# Licensed to the Apache Software Foundation (ASF) under one 3# or more contributor license agreements. See the NOTICE file 4# distributed with this work for additional information 5# regarding copyright ownership. The ASF licenses this file 6# to you under the Apache License, Version 2.0 (the 7# "License"); you may not use this file except in compliance 8# with the License. You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, 13# software distributed under the License is distributed on an 14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15# KIND, either express or implied. See the License for the 16# specific language governing permissions and limitations 17# under the License. 18# ==================================================================== 19 20require "English" 21require "time" 22require "stringio" 23require "tempfile" 24require "svn/util" 25require "svn/error" 26require "svn/ext/core" 27 28class Time 29 MILLION = 1_000_000 #:nodoc: 30 31 class << self 32 def from_apr_time(apr_time) 33 return apr_time if apr_time.is_a?(Time) 34 sec, usec = apr_time.divmod(MILLION) 35 Time.at(sec, usec) 36 end 37 38 def from_svn_format(str) 39 return nil if str.nil? 40 return str if str.is_a?(Time) 41 from_apr_time(Svn::Core.time_from_cstring(str)) 42 end 43 44 def parse_svn_format(str) 45 return str if str.is_a?(Time) 46 matched, result = Svn::Core.parse_date(str, Time.now.to_apr_time) 47 if matched 48 from_apr_time(result) 49 else 50 nil 51 end 52 end 53 end 54 55 def to_apr_time 56 to_i * MILLION + usec 57 end 58 59 def to_svn_format 60 Svn::Core.time_to_cstring(self.to_apr_time) 61 end 62 63 def to_svn_human_format 64 Svn::Core.time_to_human_cstring(self.to_apr_time) 65 end 66end 67 68module Svn 69 module Core 70 Util.set_constants(Ext::Core, self) 71 Util.set_methods(Ext::Core, self) 72 73 nls_init 74 Util.reset_message_directory 75 76 # for backward compatibility 77 SWIG_INVALID_REVNUM = INVALID_REVNUM 78 SWIG_IGNORED_REVNUM = IGNORED_REVNUM 79 80 class << self 81 alias binary_mime_type? mime_type_is_binary 82 alias prop_diffs2 prop_diffs 83 84 def prop_diffs(target_props, source_props) 85 Property.prop_diffs(target_props, source_props) 86 end 87 end 88 89 90 DEFAULT_CHARSET = default_charset 91 LOCALE_CHARSET = locale_charset 92 93 AuthCredSSLClientCert = AuthCredSslClientCert 94 AuthCredSSLClientCertPw = AuthCredSslClientCertPw 95 AuthCredSSLServerTrust = AuthCredSslServerTrust 96 97 dirent_all = 0 98 constants.each do |name| 99 dirent_all |= const_get(name) if /^DIRENT_/ =~ name 100 end 101 DIRENT_ALL = dirent_all 102 103 Pool = Svn::Ext::Core::Apr_pool_wrapper_t 104 105 class Pool 106 RECOMMENDED_MAX_FREE_SIZE = ALLOCATOR_RECOMMENDED_MAX_FREE 107 MAX_FREE_UNLIMITED = ALLOCATOR_MAX_FREE_UNLIMITED 108 109 class << self 110 def number_of_pools 111 ObjectSpace.each_object(Pool) {} 112 end 113 end 114 115 alias _initialize initialize 116 private :_initialize 117 def initialize(parent=nil) 118 _initialize(parent) 119 @parent = parent 120 end 121 122 def destroy 123 @parent = nil 124 _destroy 125 end 126 private :_destroy 127 end 128 129 class Stream 130 if Core.const_defined?(:STREAM_CHUNK_SIZE) 131 CHUNK_SIZE = Core::STREAM_CHUNK_SIZE 132 else 133 CHUNK_SIZE = 8192 134 end 135 136 def write(data) 137 Core.stream_write(self, data) 138 end 139 140 def read(len=nil) 141 if len.nil? 142 read_all 143 else 144 buf = "" 145 while len > CHUNK_SIZE 146 buf << _read(CHUNK_SIZE) 147 len -= CHUNK_SIZE 148 end 149 buf << _read(len) 150 buf 151 end 152 end 153 154 def close 155 Core.stream_close(self) 156 end 157 158 def copy(other, &cancel_proc) 159 Core.stream_copy2(self, other, cancel_proc) 160 end 161 162 private 163 def _read(size) 164 Core.stream_read(self, size) 165 end 166 167 def read_all 168 buf = "" 169 while chunk = _read(CHUNK_SIZE) 170 buf << chunk 171 end 172 buf 173 end 174 end 175 176 177 class AuthBaton 178 attr_reader :providers, :parameters 179 180 alias _initialize initialize 181 private :_initialize 182 def initialize(providers=[], parameters={}) 183 _initialize(providers) 184 @providers = providers 185 self.parameters = parameters 186 end 187 188 def [](name) 189 Core.auth_get_parameter(self, name) 190 end 191 192 def []=(name, value) 193 Core.auth_set_parameter(self, name, value) 194 @parameters[name] = value 195 end 196 197 def parameters=(params) 198 @parameters = {} 199 params.each do |key, value| 200 self[key] = value 201 end 202 end 203 end 204 205 module Authenticatable 206 attr_accessor :auth_baton 207 208 def add_simple_provider 209 add_provider(Core.auth_get_simple_provider) 210 end 211 212 if Util.windows? 213 if Core.respond_to?(:auth_get_windows_simple_provider) 214 def add_windows_simple_provider 215 add_provider(Core.auth_get_windows_simple_provider) 216 end 217 elsif Core.respond_to?(:auth_get_platform_specific_provider) 218 def add_windows_simple_provider 219 add_provider(Core.auth_get_platform_specific_provider("windows","simple")) 220 end 221 end 222 end 223 224 if Core.respond_to?(:auth_get_keychain_simple_provider) 225 def add_keychain_simple_provider 226 add_provider(Core.auth_get_keychain_simple_provider) 227 end 228 end 229 230 def add_username_provider 231 add_provider(Core.auth_get_username_provider) 232 end 233 234 def add_ssl_client_cert_file_provider 235 add_provider(Core.auth_get_ssl_client_cert_file_provider) 236 end 237 238 def add_ssl_client_cert_pw_file_provider 239 add_provider(Core.auth_get_ssl_client_cert_pw_file_provider) 240 end 241 242 def add_ssl_server_trust_file_provider 243 add_provider(Core.auth_get_ssl_server_trust_file_provider) 244 end 245 246 if Core.respond_to?(:auth_get_windows_ssl_server_trust_provider) 247 def add_windows_ssl_server_trust_provider 248 add_provider(Core.auth_get_windows_ssl_server_trust_provider) 249 end 250 end 251 252 def add_simple_prompt_provider(retry_limit, &prompt) 253 args = [retry_limit] 254 klass = AuthCredSimple 255 add_prompt_provider("simple", args, prompt, klass) 256 end 257 258 def add_username_prompt_provider(retry_limit, &prompt) 259 args = [retry_limit] 260 klass = AuthCredUsername 261 add_prompt_provider("username", args, prompt, klass) 262 end 263 264 def add_ssl_server_trust_prompt_provider(&prompt) 265 args = [] 266 klass = AuthCredSSLServerTrust 267 add_prompt_provider("ssl_server_trust", args, prompt, klass) 268 end 269 270 def add_ssl_client_cert_prompt_provider(retry_limit, &prompt) 271 args = [retry_limit] 272 klass = AuthCredSSLClientCert 273 add_prompt_provider("ssl_client_cert", args, prompt, klass) 274 end 275 276 def add_ssl_client_cert_pw_prompt_provider(retry_limit, &prompt) 277 args = [retry_limit] 278 klass = AuthCredSSLClientCertPw 279 add_prompt_provider("ssl_client_cert_pw", args, prompt, klass) 280 end 281 282 def add_platform_specific_client_providers(config=nil) 283 add_providers(Core.auth_get_platform_specific_client_providers(config)) 284 end 285 286 private 287 def add_prompt_provider(name, args, prompt, credential_class) 288 real_prompt = Proc.new do |*prompt_args| 289 credential = credential_class.new 290 prompt.call(credential, *prompt_args) 291 credential 292 end 293 method_name = "swig_rb_auth_get_#{name}_prompt_provider" 294 baton, provider = Core.send(method_name, real_prompt, *args) 295 provider.instance_variable_set("@baton", baton) 296 provider.instance_variable_set("@prompt", real_prompt) 297 add_provider(provider) 298 end 299 300 def add_provider(provider) 301 add_providers([provider]) 302 end 303 304 def add_providers(new_providers) 305 if auth_baton 306 providers = auth_baton.providers 307 parameters = auth_baton.parameters 308 else 309 providers = [] 310 parameters = {} 311 end 312 self.auth_baton = AuthBaton.new(providers + new_providers, parameters) 313 end 314 end 315 316 class AuthProviderObject 317 class << self 318 undef new 319 end 320 end 321 322 323 Diff = SWIG::TYPE_p_svn_diff_t 324 class Diff 325 attr_accessor :original, :modified, :latest, :ancestor 326 327 class << self 328 def version 329 Core.diff_version 330 end 331 332 def file_diff(original, modified, options=nil) 333 options ||= Core::DiffFileOptions.new 334 diff = Core.diff_file_diff_2(original, modified, options) 335 if diff 336 diff.original = original 337 diff.modified = modified 338 end 339 diff 340 end 341 342 def file_diff3(original, modified, latest, options=nil) 343 options ||= Core::DiffFileOptions.new 344 diff = Core.diff_file_diff3_2(original, modified, latest, options) 345 if diff 346 diff.original = original 347 diff.modified = modified 348 diff.latest = latest 349 end 350 diff 351 end 352 353 def file_diff4(original, modified, latest, ancestor, options=nil) 354 options ||= Core::DiffFileOptions.new 355 args = [original, modified, latest, ancestor, options] 356 diff = Core.diff_file_diff4_2(*args) 357 if diff 358 diff.original = original 359 diff.modified = modified 360 diff.latest = latest 361 diff.ancestor = ancestor 362 end 363 diff 364 end 365 end 366 367 def unified(orig_label, mod_label, header_encoding=nil) 368 header_encoding ||= Svn::Core.locale_charset 369 output = StringIO.new 370 args = [ 371 output, self, @original, @modified, 372 orig_label, mod_label, header_encoding 373 ] 374 Core.diff_file_output_unified2(*args) 375 output.rewind 376 output.read 377 end 378 379 def merge(conflict_original=nil, conflict_modified=nil, 380 conflict_latest=nil, conflict_separator=nil, 381 display_original_in_conflict=true, 382 display_resolved_conflicts=true) 383 header_encoding ||= Svn::Core.locale_charset 384 output = StringIO.new 385 args = [ 386 output, self, @original, @modified, @latest, 387 conflict_original, conflict_modified, 388 conflict_latest, conflict_separator, 389 display_original_in_conflict, 390 display_resolved_conflicts, 391 ] 392 Core.diff_file_output_merge(*args) 393 output.rewind 394 output.read 395 end 396 397 def conflict? 398 Core.diff_contains_conflicts(self) 399 end 400 401 def diff? 402 Core.diff_contains_diffs(self) 403 end 404 end 405 406 class DiffFileOptions 407 class << self 408 def parse(*args) 409 options = new 410 options.parse(*args) 411 options 412 end 413 end 414 415 def parse(*args) 416 args = args.first if args.size == 1 and args.first.is_a?(Array) 417 Svn::Core.diff_file_options_parse(self, args) 418 end 419 end 420 421 class Version 422 423 alias _initialize initialize 424 def initialize(major=nil, minor=nil, patch=nil, tag=nil) 425 _initialize 426 self.major = major if major 427 self.minor = minor if minor 428 self.patch = patch if patch 429 self.tag = tag || "" 430 end 431 432 def ==(other) 433 valid? and other.valid? and Core.ver_equal(self, other) 434 end 435 436 def compatible?(other) 437 valid? and other.valid? and Core.ver_compatible(self, other) 438 end 439 440 def valid? 441 (major and minor and patch and tag) ? true : false 442 end 443 444 alias _tag= tag= 445 def tag=(value) 446 @tag = value 447 self._tag = value 448 end 449 450 def to_a 451 [major, minor, patch, tag] 452 end 453 454 def to_s 455 "#{major}.#{minor}.#{patch}#{tag}" 456 end 457 end 458 459 # Following methods are also available: 460 # 461 # [created_rev] 462 # Returns a revision at which the instance was last modified. 463 # [have_props?] 464 # Returns +true+ if the instance has properties. 465 # [last_author] 466 # Returns an author who last modified the instance. 467 # [size] 468 # Returns a size of the instance. 469 class Dirent 470 alias have_props? has_props 471 472 # Returns +true+ when the instance is none. 473 def none? 474 kind == NODE_NONE 475 end 476 477 # Returns +true+ when the instance is a directory. 478 def directory? 479 kind == NODE_DIR 480 end 481 482 # Returns +true+ when the instance is a file. 483 def file? 484 kind == NODE_FILE 485 end 486 487 # Returns +true+ when the instance is an unknown node. 488 def unknown? 489 kind == NODE_UNKNOWN 490 end 491 492 # Returns a Time when the instance was last changed. 493 # 494 # Svn::Core::Dirent#time is replaced by this method, _deprecated_, 495 # and provided for backward compatibility with the 1.3 API. 496 def time2 497 __time = time 498 __time && Time.from_apr_time(__time) 499 end 500 end 501 502 Config = SWIG::TYPE_p_svn_config_t 503 504 class Config 505 include Enumerable 506 507 class << self 508 def get(path=nil) 509 Core.config_get_config(path) 510 end 511 alias config get 512 513 def read(file, must_exist=true) 514 Core.config_read(file, must_exist) 515 end 516 517 def ensure(dir) 518 Core.config_ensure(dir) 519 end 520 521 def read_auth_data(cred_kind, realm_string, config_dir=nil) 522 Core.config_read_auth_data(cred_kind, realm_string, config_dir) 523 end 524 525 def write_auth_data(hash, cred_kind, realm_string, config_dir=nil) 526 Core.config_write_auth_data(hash, cred_kind, 527 realm_string, config_dir) 528 end 529 end 530 531 def merge(file, must_exist=true) 532 Core.config_merge(self, file, must_exist) 533 end 534 535 def get(section, option, default=nil) 536 Core.config_get(self, section, option, default) 537 end 538 539 def get_bool(section, option, default) 540 Core.config_get_bool(self, section, option, default) 541 end 542 543 def set(section, option, value) 544 Core.config_set(self, section, option, value) 545 end 546 alias_method :[]=, :set 547 548 def set_bool(section, option, value) 549 Core.config_set_bool(self, section, option, value) 550 end 551 552 def each 553 each_section do |section| 554 each_option(section) do |name, value| 555 yield(section, name, value) 556 true 557 end 558 true 559 end 560 end 561 562 def each_option(section) 563 receiver = Proc.new do |name, value| 564 yield(name, value) 565 end 566 Core.config_enumerate2(self, section, receiver) 567 end 568 569 def each_section 570 receiver = Proc.new do |name| 571 yield(name) 572 end 573 Core.config_enumerate_sections2(self, receiver) 574 end 575 576 def find_group(key, section) 577 Core.config_find_group(self, key, section) 578 end 579 580 def get_server_setting(group, name, default=nil) 581 Core.config_get_server_setting(self, group, name, default) 582 end 583 584 def get_server_setting_int(group, name, default) 585 Core.config_get_server_setting_int(self, group, name, default) 586 end 587 588 alias_method :_to_s, :to_s 589 def to_s 590 result = "" 591 each_section do |section| 592 result << "[#{section}]\n" 593 each_option(section) do |name, value| 594 result << "#{name} = #{value}\n" 595 end 596 result << "\n" 597 end 598 result 599 end 600 601 def inspect 602 "#{_to_s}#{to_hash.inspect}" 603 end 604 605 def to_hash 606 sections = {} 607 each do |section, name, value| 608 sections[section] ||= {} 609 sections[section][name] = value 610 end 611 sections 612 end 613 614 def ==(other) 615 other.is_a?(self.class) and to_hash == other.to_hash 616 end 617 end 618 619 module Property 620 module_function 621 def kind(name) 622 kind, len = Core.property_kind(name) 623 [kind, name[0...len]] 624 end 625 626 def svn_prop?(name) 627 Core.prop_is_svn_prop(name) 628 end 629 630 def needs_translation?(name) 631 Core.prop_needs_translation(name) 632 end 633 634 def categorize(props) 635 categorize2(props).collect do |categorized_props| 636 Util.hash_to_prop_array(categorized_props) 637 end 638 end 639 alias_method :categorize_props, :categorize 640 module_function :categorize_props 641 642 def categorize2(props) 643 Core.categorize_props(props) 644 end 645 646 def diffs(target_props, source_props) 647 Util.hash_to_prop_array(diffs2(target_props, source_props)) 648 end 649 alias_method :prop_diffs, :diffs 650 module_function :prop_diffs 651 652 def diffs2(target_props, source_props) 653 Core.prop_diffs2(target_props, source_props) 654 end 655 656 def has_svn_prop?(props) 657 Core.prop_has_svn_prop(props) 658 end 659 alias_method :have_svn_prop?, :has_svn_prop? 660 module_function :have_svn_prop? 661 662 def valid_name?(name) 663 Core.prop_name_is_valid(name) 664 end 665 end 666 667 module Depth 668 module_function 669 def from_string(str) 670 return nil if str.nil? 671 Core.depth_from_word(str) 672 end 673 674 def to_string(depth) 675 Core.depth_to_word(depth) 676 end 677 678 def infinity_or_empty_from_recurse(depth_or_recurse) 679 case depth_or_recurse 680 when true then DEPTH_INFINITY 681 when false then DEPTH_EMPTY 682 else depth_or_recurse 683 end 684 end 685 686 def infinity_or_immediates_from_recurse(depth_or_recurse) 687 case depth_or_recurse 688 when true then DEPTH_INFINITY 689 when false then DEPTH_IMMEDIATES 690 else depth_or_recurse 691 end 692 end 693 end 694 695 module MimeType 696 module_function 697 def parse(source) 698 file = Tempfile.new("svn-ruby-mime-type") 699 file.print(source) 700 file.close 701 Core.io_parse_mimetypes_file(file.path) 702 end 703 704 def parse_file(path) 705 Core.io_parse_mimetypes_file(path) 706 end 707 708 def detect(path, type_map={}) 709 Core.io_detect_mimetype2(path, type_map) 710 end 711 end 712 713 class CommitInfo 714 alias _date date 715 def date 716 Time.from_svn_format(_date) 717 end 718 end 719 720 # Following methods are also available: 721 # 722 # [action] 723 # Returns an action taken to the path at the revision. 724 # [copyfrom_path] 725 # If the path was added at the revision by the copy action from 726 # another path at another revision, returns an original path. 727 # Otherwise, returns +nil+. 728 # [copyfrom_rev] 729 # If the path was added at the revision by the copy action from 730 # another path at another revision, returns an original revision. 731 # Otherwise, returns <tt>-1</tt>. 732 class LogChangedPath 733 # Returns +true+ when the path is added by the copy action. 734 def copied? 735 Util.copy?(copyfrom_path, copyfrom_rev) 736 end 737 end 738 739 # For backward compatibility 740 class Prop 741 attr_accessor :name, :value 742 def initialize(name, value) 743 @name = name 744 @value = value 745 end 746 747 def ==(other) 748 other.is_a?(self.class) and 749 [@name, @value] == [other.name, other.value] 750 end 751 end 752 753 class MergeRange 754 def to_a 755 [self.start, self.end, self.inheritable] 756 end 757 758 def inspect 759 super.gsub(/>$/, ":#{to_a.inspect}>") 760 end 761 762 def ==(other) 763 to_a == other.to_a 764 end 765 end 766 767 class MergeInfo < Hash 768 class << self 769 def parse(input) 770 new(Core.mergeinfo_parse(input)) 771 end 772 end 773 774 def initialize(info) 775 super() 776 info.each do |path, ranges| 777 self[path] = RangeList.new(*ranges) 778 end 779 end 780 781 def diff(to, consider_inheritance=false) 782 Core.mergeinfo_diff(self, to, consider_inheritance).collect do |result| 783 self.class.new(result) 784 end 785 end 786 787 def merge(changes) 788 self.class.new(Core.swig_mergeinfo_merge(self, changes)) 789 end 790 791 def remove(eraser) 792 self.class.new(Core.mergeinfo_remove(eraser, self)) 793 end 794 795 def sort 796 self.class.new(Core.swig_mergeinfo_sort(self)) 797 end 798 799 def to_s 800 Core.mergeinfo_to_string(self) 801 end 802 end 803 804 class RangeList < Array 805 def initialize(*ranges) 806 super() 807 ranges.each do |range| 808 self << Svn::Core::MergeRange.new(*range.to_a) 809 end 810 end 811 812 def diff(to, consider_inheritance=false) 813 result = Core.rangelist_diff(self, to, consider_inheritance) 814 deleted = result.pop 815 added = result 816 [added, deleted].collect do |result| 817 self.class.new(*result) 818 end 819 end 820 821 def merge(changes) 822 self.class.new(*Core.swig_rangelist_merge(self, changes)) 823 end 824 825 def remove(eraser, consider_inheritance=false) 826 self.class.new(*Core.rangelist_remove(eraser, self, 827 consider_inheritance)) 828 end 829 830 def intersect(other, consider_inheritance=false) 831 self.class.new(*Core.rangelist_intersect(self, other, 832 consider_inheritance)) 833 end 834 835 def reverse 836 self.class.new(*Core.swig_rangelist_reverse(self)) 837 end 838 839 def to_s 840 Core.rangelist_to_string(self) 841 end 842 end 843 844 class LogEntry 845 alias_method(:revision_properties, :revprops) 846 alias_method(:has_children?, :has_children) 847 undef_method(:has_children) 848 end 849 end 850end 851