1# = uri/ftp.rb 2# 3# Author:: Akira Yamada <akira@ruby-lang.org> 4# License:: You can redistribute it and/or modify it under the same term as Ruby. 5# Revision:: $Id: ftp.rb 39014 2013-02-02 03:31:56Z zzak $ 6# 7# See URI for general documentation 8# 9 10require 'uri/generic' 11 12module URI 13 14 # 15 # FTP URI syntax is defined by RFC1738 section 3.2. 16 # 17 # This class will be redesigned because of difference of implementations; 18 # the structure of its path. draft-hoffman-ftp-uri-04 is a draft but it 19 # is a good summary about the de facto spec. 20 # http://tools.ietf.org/html/draft-hoffman-ftp-uri-04 21 # 22 class FTP < Generic 23 # A Default port of 21 for URI::FTP 24 DEFAULT_PORT = 21 25 26 # 27 # An Array of the available components for URI::FTP 28 # 29 COMPONENT = [ 30 :scheme, 31 :userinfo, :host, :port, 32 :path, :typecode 33 ].freeze 34 35 # 36 # Typecode is "a", "i" or "d". 37 # 38 # * "a" indicates a text file (the FTP command was ASCII) 39 # * "i" indicates a binary file (FTP command IMAGE) 40 # * "d" indicates the contents of a directory should be displayed 41 # 42 TYPECODE = ['a', 'i', 'd'].freeze 43 44 # Typecode prefix 45 # ';type=' 46 TYPECODE_PREFIX = ';type='.freeze 47 48 def self.new2(user, password, host, port, path, 49 typecode = nil, arg_check = true) # :nodoc: 50 # Do not use this method! Not tested. [Bug #7301] 51 # This methods remains just for compatibility, 52 # Keep it undocumented until the active maintainer is assigned. 53 typecode = nil if typecode.size == 0 54 if typecode && !TYPECODE.include?(typecode) 55 raise ArgumentError, 56 "bad typecode is specified: #{typecode}" 57 end 58 59 # do escape 60 61 self.new('ftp', 62 [user, password], 63 host, port, nil, 64 typecode ? path + TYPECODE_PREFIX + typecode : path, 65 nil, nil, nil, arg_check) 66 end 67 68 # 69 # == Description 70 # 71 # Creates a new URI::FTP object from components, with syntax checking. 72 # 73 # The components accepted are +userinfo+, +host+, +port+, +path+ and 74 # +typecode+. 75 # 76 # The components should be provided either as an Array, or as a Hash 77 # with keys formed by preceding the component names with a colon. 78 # 79 # If an Array is used, the components must be passed in the order 80 # [userinfo, host, port, path, typecode] 81 # 82 # If the path supplied is absolute, it will be escaped in order to 83 # make it absolute in the URI. Examples: 84 # 85 # require 'uri' 86 # 87 # uri = URI::FTP.build(['user:password', 'ftp.example.com', nil, 88 # '/path/file.> zip', 'i']) 89 # puts uri.to_s -> ftp://user:password@ftp.example.com/%2Fpath/file.zip;type=a 90 # 91 # uri2 = URI::FTP.build({:host => 'ftp.example.com', 92 # :path => 'ruby/src'}) 93 # puts uri2.to_s -> ftp://ftp.example.com/ruby/src 94 # 95 def self.build(args) 96 97 # Fix the incoming path to be generic URL syntax 98 # FTP path -> URL path 99 # foo/bar /foo/bar 100 # /foo/bar /%2Ffoo/bar 101 # 102 if args.kind_of?(Array) 103 args[3] = '/' + args[3].sub(/^\//, '%2F') 104 else 105 args[:path] = '/' + args[:path].sub(/^\//, '%2F') 106 end 107 108 tmp = Util::make_components_hash(self, args) 109 110 if tmp[:typecode] 111 if tmp[:typecode].size == 1 112 tmp[:typecode] = TYPECODE_PREFIX + tmp[:typecode] 113 end 114 tmp[:path] << tmp[:typecode] 115 end 116 117 return super(tmp) 118 end 119 120 # 121 # == Description 122 # 123 # Creates a new URI::FTP object from generic URL components with no 124 # syntax checking. 125 # 126 # Unlike build(), this method does not escape the path component as 127 # required by RFC1738; instead it is treated as per RFC2396. 128 # 129 # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+, 130 # +opaque+, +query+ and +fragment+, in that order. 131 # 132 def initialize(*arg) 133 raise InvalidURIError unless arg[5] 134 arg[5] = arg[5].sub(/^\//,'').sub(/^%2F/,'/') 135 super(*arg) 136 @typecode = nil 137 tmp = @path.index(TYPECODE_PREFIX) 138 if tmp 139 typecode = @path[tmp + TYPECODE_PREFIX.size..-1] 140 @path = @path[0..tmp - 1] 141 142 if arg[-1] 143 self.typecode = typecode 144 else 145 self.set_typecode(typecode) 146 end 147 end 148 end 149 150 # typecode accessor 151 # 152 # see URI::FTP::COMPONENT 153 attr_reader :typecode 154 155 # validates typecode +v+, 156 # returns a +true+ or +false+ boolean 157 # 158 def check_typecode(v) 159 if TYPECODE.include?(v) 160 return true 161 else 162 raise InvalidComponentError, 163 "bad typecode(expected #{TYPECODE.join(', ')}): #{v}" 164 end 165 end 166 private :check_typecode 167 168 # private setter for the typecode +v+ 169 # 170 # see also URI::FTP.typecode= 171 # 172 def set_typecode(v) 173 @typecode = v 174 end 175 protected :set_typecode 176 177 # 178 # == Args 179 # 180 # +v+:: 181 # String 182 # 183 # == Description 184 # 185 # public setter for the typecode +v+. 186 # (with validation) 187 # 188 # see also URI::FTP.check_typecode 189 # 190 # == Usage 191 # 192 # require 'uri' 193 # 194 # uri = URI.parse("ftp://john@ftp.example.com/my_file.img") 195 # #=> #<URI::FTP:0x00000000923650 URL:ftp://john@ftp.example.com/my_file.img> 196 # uri.typecode = "i" 197 # # => "i" 198 # uri 199 # #=> #<URI::FTP:0x00000000923650 URL:ftp://john@ftp.example.com/my_file.img;type=i> 200 # 201 def typecode=(typecode) 202 check_typecode(typecode) 203 set_typecode(typecode) 204 typecode 205 end 206 207 def merge(oth) # :nodoc: 208 tmp = super(oth) 209 if self != tmp 210 tmp.set_typecode(oth.typecode) 211 end 212 213 return tmp 214 end 215 216 # Returns the path from an FTP URI. 217 # 218 # RFC 1738 specifically states that the path for an FTP URI does not 219 # include the / which separates the URI path from the URI host. Example: 220 # 221 # ftp://ftp.example.com/pub/ruby 222 # 223 # The above URI indicates that the client should connect to 224 # ftp.example.com then cd pub/ruby from the initial login directory. 225 # 226 # If you want to cd to an absolute directory, you must include an 227 # escaped / (%2F) in the path. Example: 228 # 229 # ftp://ftp.example.com/%2Fpub/ruby 230 # 231 # This method will then return "/pub/ruby" 232 # 233 def path 234 return @path.sub(/^\//,'').sub(/^%2F/,'/') 235 end 236 237 def set_path(v) 238 super("/" + v.sub(/^\//, "%2F")) 239 end 240 protected :set_path 241 242 def to_s 243 save_path = nil 244 if @typecode 245 save_path = @path 246 @path = @path + TYPECODE_PREFIX + @typecode 247 end 248 str = super 249 if @typecode 250 @path = save_path 251 end 252 253 return str 254 end 255 end 256 @@schemes['FTP'] = FTP 257end 258