~ubuntu-branches/ubuntu/quantal/ruby1.9.1/quantal

« back to all changes in this revision

Viewing changes to lib/net/ftp.rb

  • Committer: Bazaar Package Importer
  • Author(s): Lucas Nussbaum
  • Date: 2011-09-24 19:16:17 UTC
  • mfrom: (1.1.8 upstream) (13.1.7 experimental)
  • Revision ID: james.westby@ubuntu.com-20110924191617-o1qz4rcmqjot8zuy
Tags: 1.9.3~rc1-1
* New upstream release: 1.9.3 RC1.
  + Includes load.c fixes. Closes: #639959.
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
136
136
      @sock = NullSocket.new
137
137
      @logged_in = false
138
138
      if host
139
 
        connect(host)
140
 
        if user
141
 
          login(user, passwd, acct)
142
 
        end
 
139
        connect(host)
 
140
        if user
 
141
          login(user, passwd, acct)
 
142
        end
143
143
      end
144
144
    end
145
145
 
 
146
    # A setter to toggle transfers in binary mode.
 
147
    # +newmode+ is either +true+ or +false+
146
148
    def binary=(newmode)
147
149
      if newmode != @binary
148
150
        @binary = newmode
150
152
      end
151
153
    end
152
154
 
153
 
    def send_type_command
 
155
    # Sends a command to destination host, with the current binary sendmode
 
156
    # type.
 
157
    #
 
158
    # If binary mode is +true+, then "TYPE I" (image) is sent, otherwise "TYPE
 
159
    # A" (ascii) is sent.
 
160
    def send_type_command # :nodoc:
154
161
      if @binary
155
162
        voidcmd("TYPE I")
156
163
      else
159
166
    end
160
167
    private :send_type_command
161
168
 
162
 
    def with_binary(newmode)
 
169
    # Toggles transfers in binary mode and yields to a block.
 
170
    # This preserves your current binary send mode, but allows a temporary
 
171
    # transaction with binary sendmode of +newmode+.
 
172
    #
 
173
    # +newmode+ is either +true+ or +false+
 
174
    def with_binary(newmode) # :nodoc:
163
175
      oldmode = binary
164
176
      self.binary = newmode
165
177
      begin
171
183
    private :with_binary
172
184
 
173
185
    # Obsolete
174
 
    def return_code
 
186
    def return_code # :nodoc:
175
187
      $stderr.puts("warning: Net::FTP#return_code is obsolete and do nothing")
176
188
      return "\n"
177
189
    end
178
190
 
179
191
    # Obsolete
180
 
    def return_code=(s)
 
192
    def return_code=(s) # :nodoc:
181
193
      $stderr.puts("warning: Net::FTP#return_code= is obsolete and do nothing")
182
194
    end
183
195
 
184
 
    def open_socket(host, port)
 
196
    # Contructs a socket with +host+ and +port+.
 
197
    #
 
198
    # If SOCKSSocket is defined and the environment (ENV) defines
 
199
    # SOCKS_SERVER, then a SOCKSSocket is returned, else a TCPSocket is
 
200
    # returned.
 
201
    def open_socket(host, port) # :nodoc:
185
202
      if defined? SOCKSSocket and ENV["SOCKS_SERVER"]
186
 
        @passive = true
187
 
        return SOCKSSocket.open(host, port)
 
203
        @passive = true
 
204
        return SOCKSSocket.open(host, port)
188
205
      else
189
 
        return TCPSocket.open(host, port)
 
206
        return TCPSocket.open(host, port)
190
207
      end
191
208
    end
192
209
    private :open_socket
199
216
    #
200
217
    def connect(host, port = FTP_PORT)
201
218
      if @debug_mode
202
 
        print "connect: ", host, ", ", port, "\n"
 
219
        print "connect: ", host, ", ", port, "\n"
203
220
      end
204
221
      synchronize do
205
 
        @sock = open_socket(host, port)
206
 
        voidresp
 
222
        @sock = open_socket(host, port)
 
223
        voidresp
207
224
      end
208
225
    end
209
226
 
212
229
    #
213
230
    def set_socket(sock, get_greeting = true)
214
231
      synchronize do
215
 
        @sock = sock
216
 
        if get_greeting
217
 
          voidresp
218
 
        end
 
232
        @sock = sock
 
233
        if get_greeting
 
234
          voidresp
 
235
        end
219
236
      end
220
237
    end
221
238
 
222
 
    def sanitize(s)
 
239
    # If string +s+ includes the PASS command (password), then the contents of
 
240
    # the password are cleaned from the string using "*"
 
241
    def sanitize(s) # :nodoc:
223
242
      if s =~ /^PASS /i
224
 
        return s[0, 5] + "*" * (s.length - 5)
 
243
        return s[0, 5] + "*" * (s.length - 5)
225
244
      else
226
 
        return s
 
245
        return s
227
246
      end
228
247
    end
229
248
    private :sanitize
230
249
 
231
 
    def putline(line)
 
250
    # Ensures that +line+ has a control return / line feed (CRLF) and writes
 
251
    # it to the socket.
 
252
    def putline(line) # :nodoc:
232
253
      if @debug_mode
233
 
        print "put: ", sanitize(line), "\n"
 
254
        print "put: ", sanitize(line), "\n"
234
255
      end
235
256
      line = line + CRLF
236
257
      @sock.write(line)
237
258
    end
238
259
    private :putline
239
260
 
240
 
    def getline
 
261
    # Reads a line from the sock.  If EOF, then it will raise EOFError
 
262
    def getline # :nodoc:
241
263
      line = @sock.readline # if get EOF, raise EOFError
242
264
      line.sub!(/(\r\n|\n|\r)\z/n, "")
243
265
      if @debug_mode
244
 
        print "get: ", sanitize(line), "\n"
 
266
        print "get: ", sanitize(line), "\n"
245
267
      end
246
268
      return line
247
269
    end
248
270
    private :getline
249
271
 
250
 
    def getmultiline
 
272
    # Receive a section of lines until the response code's match.
 
273
    def getmultiline # :nodoc:
251
274
      line = getline
252
275
      buff = line
253
276
      if line[3] == ?-
254
 
          code = line[0, 3]
255
 
        begin
256
 
          line = getline
257
 
          buff << "\n" << line
258
 
        end until line[0, 3] == code and line[3] != ?-
 
277
          code = line[0, 3]
 
278
        begin
 
279
          line = getline
 
280
          buff << "\n" << line
 
281
        end until line[0, 3] == code and line[3] != ?-
259
282
      end
260
283
      return buff << "\n"
261
284
    end
262
285
    private :getmultiline
263
286
 
264
 
    def getresp
 
287
    # Recieves a response from the destination host.
 
288
    #
 
289
    # Returns the response code or raises FTPTempError, FTPPermError, or
 
290
    # FTPProtoError
 
291
    def getresp # :nodoc:
265
292
      @last_response = getmultiline
266
293
      @last_response_code = @last_response[0, 3]
267
294
      case @last_response_code
268
295
      when /\A[123]/
269
 
        return @last_response
 
296
        return @last_response
270
297
      when /\A4/
271
 
        raise FTPTempError, @last_response
 
298
        raise FTPTempError, @last_response
272
299
      when /\A5/
273
 
        raise FTPPermError, @last_response
 
300
        raise FTPPermError, @last_response
274
301
      else
275
 
        raise FTPProtoError, @last_response
 
302
        raise FTPProtoError, @last_response
276
303
      end
277
304
    end
278
305
    private :getresp
279
306
 
280
 
    def voidresp
 
307
    # Recieves a response.
 
308
    #
 
309
    # Raises FTPReplyError if the first position of the response code is not
 
310
    # equal 2.
 
311
    def voidresp # :nodoc:
281
312
      resp = getresp
282
313
      if resp[0] != ?2
283
 
        raise FTPReplyError, resp
 
314
        raise FTPReplyError, resp
284
315
      end
285
316
    end
286
317
    private :voidresp
290
321
    #
291
322
    def sendcmd(cmd)
292
323
      synchronize do
293
 
        putline(cmd)
294
 
        return getresp
 
324
        putline(cmd)
 
325
        return getresp
295
326
      end
296
327
    end
297
328
 
300
331
    #
301
332
    def voidcmd(cmd)
302
333
      synchronize do
303
 
        putline(cmd)
304
 
        voidresp
 
334
        putline(cmd)
 
335
        voidresp
305
336
      end
306
337
    end
307
338
 
308
 
    def sendport(host, port)
 
339
    # Constructs and send the appropriate PORT (or EPRT) command
 
340
    def sendport(host, port) # :nodoc:
309
341
      af = (@sock.peeraddr)[0]
310
342
      if af == "AF_INET"
311
 
        cmd = "PORT " + (host.split(".") + port.divmod(256)).join(",")
 
343
        cmd = "PORT " + (host.split(".") + port.divmod(256)).join(",")
312
344
      elsif af == "AF_INET6"
313
 
        cmd = sprintf("EPRT |2|%s|%d|", host, port)
 
345
        cmd = sprintf("EPRT |2|%s|%d|", host, port)
314
346
      else
315
 
        raise FTPProtoError, host
 
347
        raise FTPProtoError, host
316
348
      end
317
349
      voidcmd(cmd)
318
350
    end
319
351
    private :sendport
320
352
 
321
 
    def makeport
 
353
    # Constructs a TCPServer socket, and sends it the PORT command
 
354
    #
 
355
    # Returns the constructed TCPServer socket
 
356
    def makeport # :nodoc:
322
357
      sock = TCPServer.open(@sock.addr[3], 0)
323
358
      port = sock.addr[1]
324
359
      host = sock.addr[3]
325
 
      resp = sendport(host, port)
 
360
      sendport(host, port)
326
361
      return sock
327
362
    end
328
363
    private :makeport
329
364
 
330
 
    def makepasv
 
365
    # sends the appropriate command to enable a passive connection
 
366
    def makepasv # :nodoc:
331
367
      if @sock.peeraddr[0] == "AF_INET"
332
 
        host, port = parse227(sendcmd("PASV"))
 
368
        host, port = parse227(sendcmd("PASV"))
333
369
      else
334
 
        host, port = parse229(sendcmd("EPSV"))
335
 
        #     host, port = parse228(sendcmd("LPSV"))
 
370
        host, port = parse229(sendcmd("EPSV"))
 
371
        #     host, port = parse228(sendcmd("LPSV"))
336
372
      end
337
373
      return host, port
338
374
    end
339
375
    private :makepasv
340
376
 
341
 
    def transfercmd(cmd, rest_offset = nil)
 
377
    # Constructs a connection for transferring data
 
378
    def transfercmd(cmd, rest_offset = nil) # :nodoc:
342
379
      if @passive
343
 
        host, port = makepasv
344
 
        conn = open_socket(host, port)
345
 
        if @resume and rest_offset
346
 
          resp = sendcmd("REST " + rest_offset.to_s)
347
 
          if resp[0] != ?3
348
 
            raise FTPReplyError, resp
349
 
          end
350
 
        end
351
 
        resp = sendcmd(cmd)
 
380
        host, port = makepasv
 
381
        conn = open_socket(host, port)
 
382
        if @resume and rest_offset
 
383
          resp = sendcmd("REST " + rest_offset.to_s)
 
384
          if resp[0] != ?3
 
385
            raise FTPReplyError, resp
 
386
          end
 
387
        end
 
388
        resp = sendcmd(cmd)
352
389
        # skip 2XX for some ftp servers
353
390
        resp = getresp if resp[0] == ?2
354
 
        if resp[0] != ?1
355
 
          raise FTPReplyError, resp
356
 
        end
 
391
        if resp[0] != ?1
 
392
          raise FTPReplyError, resp
 
393
        end
357
394
      else
358
 
        sock = makeport
359
 
        if @resume and rest_offset
360
 
          resp = sendcmd("REST " + rest_offset.to_s)
361
 
          if resp[0] != ?3
362
 
            raise FTPReplyError, resp
363
 
          end
364
 
        end
365
 
        resp = sendcmd(cmd)
 
395
        sock = makeport
 
396
        if @resume and rest_offset
 
397
          resp = sendcmd("REST " + rest_offset.to_s)
 
398
          if resp[0] != ?3
 
399
            raise FTPReplyError, resp
 
400
          end
 
401
        end
 
402
        resp = sendcmd(cmd)
366
403
        # skip 2XX for some ftp servers
367
404
        resp = getresp if resp[0] == ?2
368
 
        if resp[0] != ?1
369
 
          raise FTPReplyError, resp
370
 
        end
371
 
        conn = sock.accept
372
 
        sock.close
 
405
        if resp[0] != ?1
 
406
          raise FTPReplyError, resp
 
407
        end
 
408
        conn = sock.accept
 
409
        sock.close
373
410
      end
374
411
      return conn
375
412
    end
385
422
    #
386
423
    def login(user = "anonymous", passwd = nil, acct = nil)
387
424
      if user == "anonymous" and passwd == nil
388
 
        passwd = "anonymous@"
 
425
        passwd = "anonymous@"
389
426
      end
390
427
 
391
428
      resp = ""
392
429
      synchronize do
393
 
        resp = sendcmd('USER ' + user)
394
 
        if resp[0] == ?3
 
430
        resp = sendcmd('USER ' + user)
 
431
        if resp[0] == ?3
395
432
          raise FTPReplyError, resp if passwd.nil?
396
 
          resp = sendcmd('PASS ' + passwd)
397
 
        end
398
 
        if resp[0] == ?3
 
433
          resp = sendcmd('PASS ' + passwd)
 
434
        end
 
435
        if resp[0] == ?3
399
436
          raise FTPReplyError, resp if acct.nil?
400
 
          resp = sendcmd('ACCT ' + acct)
401
 
        end
 
437
          resp = sendcmd('ACCT ' + acct)
 
438
        end
402
439
      end
403
440
      if resp[0] != ?2
404
 
        raise FTPReplyError, resp
 
441
        raise FTPReplyError, resp
405
442
      end
406
443
      @welcome = resp
407
444
      send_type_command
416
453
    #
417
454
    def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data
418
455
      synchronize do
419
 
        with_binary(true) do
 
456
        with_binary(true) do
420
457
          conn = transfercmd(cmd, rest_offset)
421
458
          loop do
422
459
            data = conn.read(blocksize)
437
474
    #
438
475
    def retrlines(cmd) # :yield: line
439
476
      synchronize do
440
 
        with_binary(false) do
 
477
        with_binary(false) do
441
478
          conn = transfercmd(cmd)
442
479
          loop do
443
480
            line = conn.gets
461
498
        file.seek(rest_offset, IO::SEEK_SET)
462
499
      end
463
500
      synchronize do
464
 
        with_binary(true) do
 
501
        with_binary(true) do
465
502
          conn = transfercmd(cmd)
466
503
          loop do
467
504
            buf = file.read(blocksize)
490
527
    #
491
528
    def storlines(cmd, file, &block) # :yield: line
492
529
      synchronize do
493
 
        with_binary(false) do
 
530
        with_binary(false) do
494
531
          conn = transfercmd(cmd)
495
532
          loop do
496
533
            buf = file.gets
521
558
    # chunks.
522
559
    #
523
560
    def getbinaryfile(remotefile, localfile = File.basename(remotefile),
524
 
                      blocksize = DEFAULT_BLOCKSIZE) # :yield: data
 
561
                      blocksize = DEFAULT_BLOCKSIZE) # :yield: data
525
562
      result = nil
526
563
      if localfile
527
564
        if @resume
535
572
        result = ""
536
573
      end
537
574
      begin
538
 
        f.binmode if localfile
539
 
        retrbinary("RETR " + remotefile.to_s, blocksize, rest_offset) do |data|
540
 
          f.write(data) if localfile
541
 
          yield(data) if block_given?
 
575
        f.binmode if localfile
 
576
        retrbinary("RETR " + remotefile.to_s, blocksize, rest_offset) do |data|
 
577
          f.write(data) if localfile
 
578
          yield(data) if block_given?
542
579
          result.concat(data) if result
543
 
        end
 
580
        end
544
581
        return result
545
582
      ensure
546
 
        f.close if localfile
 
583
        f.close if localfile
547
584
      end
548
585
    end
549
586
 
562
599
        result = ""
563
600
      end
564
601
      begin
565
 
        retrlines("RETR " + remotefile) do |line, newline|
 
602
        retrlines("RETR " + remotefile) do |line, newline|
566
603
          l = newline ? line + "\n" : line
567
 
          f.print(l) if localfile
568
 
          yield(line, newline) if block_given?
 
604
          f.print(l) if localfile
 
605
          yield(line, newline) if block_given?
569
606
          result.concat(l) if result
570
 
        end
 
607
        end
571
608
        return result
572
609
      ensure
573
 
        f.close if localfile
 
610
        f.close if localfile
574
611
      end
575
612
    end
576
613
 
579
616
    # binary).  See #gettextfile and #getbinaryfile.
580
617
    #
581
618
    def get(remotefile, localfile = File.basename(remotefile),
582
 
            blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
 
619
            blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
583
620
      if @binary
584
 
        getbinaryfile(remotefile, localfile, blocksize, &block)
 
621
        getbinaryfile(remotefile, localfile, blocksize, &block)
585
622
      else
586
 
        gettextfile(remotefile, localfile, &block)
 
623
        gettextfile(remotefile, localfile, &block)
587
624
      end
588
625
    end
589
626
 
593
630
    # data in +blocksize+ chunks.
594
631
    #
595
632
    def putbinaryfile(localfile, remotefile = File.basename(localfile),
596
 
                      blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
 
633
                      blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data
597
634
      if @resume
598
635
        begin
599
636
          rest_offset = size(remotefile)
601
638
          rest_offset = nil
602
639
        end
603
640
      else
604
 
        rest_offset = nil
 
641
        rest_offset = nil
605
642
      end
606
643
      f = open(localfile)
607
644
      begin
608
 
        f.binmode
 
645
        f.binmode
609
646
        if rest_offset
610
647
          storbinary("APPE " + remotefile, f, blocksize, rest_offset, &block)
611
648
        else
612
649
          storbinary("STOR " + remotefile, f, blocksize, rest_offset, &block)
613
650
        end
614
651
      ensure
615
 
        f.close
 
652
        f.close
616
653
      end
617
654
    end
618
655
 
624
661
    def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line
625
662
      f = open(localfile)
626
663
      begin
627
 
        storlines("STOR " + remotefile, f, &block)
 
664
        storlines("STOR " + remotefile, f, &block)
628
665
      ensure
629
 
        f.close
 
666
        f.close
630
667
      end
631
668
    end
632
669
 
635
672
    # (text or binary).  See #puttextfile and #putbinaryfile.
636
673
    #
637
674
    def put(localfile, remotefile = File.basename(localfile),
638
 
            blocksize = DEFAULT_BLOCKSIZE, &block)
 
675
            blocksize = DEFAULT_BLOCKSIZE, &block)
639
676
      if @binary
640
 
        putbinaryfile(localfile, remotefile, blocksize, &block)
 
677
        putbinaryfile(localfile, remotefile, blocksize, &block)
641
678
      else
642
 
        puttextfile(localfile, remotefile, &block)
 
679
        puttextfile(localfile, remotefile, &block)
643
680
      end
644
681
    end
645
682
 
646
683
    #
647
 
    # Sends the ACCT command.  TODO: more info.
 
684
    # Sends the ACCT command.
 
685
    #
 
686
    # This is a less common FTP command, to send account
 
687
    # information if the destination host requires it.
648
688
    #
649
689
    def acct(account)
650
690
      cmd = "ACCT " + account
657
697
    def nlst(dir = nil)
658
698
      cmd = "NLST"
659
699
      if dir
660
 
        cmd = cmd + " " + dir
 
700
        cmd = cmd + " " + dir
661
701
      end
662
702
      files = []
663
703
      retrlines(cmd) do |line|
664
 
        files.push(line)
 
704
        files.push(line)
665
705
      end
666
706
      return files
667
707
    end
673
713
    def list(*args, &block) # :yield: line
674
714
      cmd = "LIST"
675
715
      args.each do |arg|
676
 
        cmd = cmd + " " + arg.to_s
 
716
        cmd = cmd + " " + arg.to_s
677
717
      end
678
718
      if block
679
 
        retrlines(cmd, &block)
 
719
        retrlines(cmd, &block)
680
720
      else
681
 
        lines = []
682
 
        retrlines(cmd) do |line|
683
 
          lines << line
684
 
        end
685
 
        return lines
 
721
        lines = []
 
722
        retrlines(cmd) do |line|
 
723
          lines << line
 
724
        end
 
725
        return lines
686
726
      end
687
727
    end
688
728
    alias ls list
694
734
    def rename(fromname, toname)
695
735
      resp = sendcmd("RNFR " + fromname)
696
736
      if resp[0] != ?3
697
 
        raise FTPReplyError, resp
 
737
        raise FTPReplyError, resp
698
738
      end
699
739
      voidcmd("RNTO " + toname)
700
740
    end
705
745
    def delete(filename)
706
746
      resp = sendcmd("DELE " + filename)
707
747
      if resp[0, 3] == "250"
708
 
        return
 
748
        return
709
749
      elsif resp[0] == ?5
710
 
        raise FTPPermError, resp
 
750
        raise FTPPermError, resp
711
751
      else
712
 
        raise FTPReplyError, resp
 
752
        raise FTPReplyError, resp
713
753
      end
714
754
    end
715
755
 
718
758
    #
719
759
    def chdir(dirname)
720
760
      if dirname == ".."
721
 
        begin
722
 
          voidcmd("CDUP")
723
 
          return
724
 
        rescue FTPPermError => e
725
 
          if e.message[0, 3] != "500"
726
 
            raise e
727
 
          end
728
 
        end
 
761
        begin
 
762
          voidcmd("CDUP")
 
763
          return
 
764
        rescue FTPPermError => e
 
765
          if e.message[0, 3] != "500"
 
766
            raise e
 
767
          end
 
768
        end
729
769
      end
730
770
      cmd = "CWD " + dirname
731
771
      voidcmd(cmd)
786
826
    def system
787
827
      resp = sendcmd("SYST")
788
828
      if resp[0, 3] != "215"
789
 
        raise FTPReplyError, resp
 
829
        raise FTPReplyError, resp
790
830
      end
791
831
      return resp[4 .. -1]
792
832
    end
800
840
      @sock.send(line, Socket::MSG_OOB)
801
841
      resp = getmultiline
802
842
      unless ["426", "226", "225"].include?(resp[0, 3])
803
 
        raise FTPProtoError, resp
 
843
        raise FTPProtoError, resp
804
844
      end
805
845
      return resp
806
846
    end
821
861
    def mdtm(filename)
822
862
      resp = sendcmd("MDTM " + filename)
823
863
      if resp[0, 3] == "213"
824
 
        return resp[3 .. -1].strip
 
864
        return resp[3 .. -1].strip
825
865
      end
826
866
    end
827
867
 
831
871
    def help(arg = nil)
832
872
      cmd = "HELP"
833
873
      if arg
834
 
        cmd = cmd + " " + arg
 
874
        cmd = cmd + " " + arg
835
875
      end
836
876
      sendcmd(cmd)
837
877
    end
846
886
    #
847
887
    # Issues a NOOP command.
848
888
    #
 
889
    # Does nothing except return a response.
 
890
    #
849
891
    def noop
850
892
      voidcmd("NOOP")
851
893
    end
873
915
      @sock == nil or @sock.closed?
874
916
    end
875
917
 
876
 
    def parse227(resp)
 
918
    # handler for response code 227
 
919
    # (Entering Passive Mode (h1,h2,h3,h4,p1,p2))
 
920
    #
 
921
    # Returns host and port.
 
922
    def parse227(resp) # :nodoc:
877
923
      if resp[0, 3] != "227"
878
 
        raise FTPReplyError, resp
 
924
        raise FTPReplyError, resp
879
925
      end
880
926
      left = resp.index("(")
881
927
      right = resp.index(")")
882
928
      if left == nil or right == nil
883
 
        raise FTPProtoError, resp
 
929
        raise FTPProtoError, resp
884
930
      end
885
931
      numbers = resp[left + 1 .. right - 1].split(",")
886
932
      if numbers.length != 6
887
 
        raise FTPProtoError, resp
 
933
        raise FTPProtoError, resp
888
934
      end
889
935
      host = numbers[0, 4].join(".")
890
936
      port = (numbers[4].to_i << 8) + numbers[5].to_i
892
938
    end
893
939
    private :parse227
894
940
 
895
 
    def parse228(resp)
 
941
    # handler for response code 228
 
942
    # (Entering Long Passive Mode)
 
943
    #
 
944
    # Returns host and port.
 
945
    def parse228(resp) # :nodoc:
896
946
      if resp[0, 3] != "228"
897
 
        raise FTPReplyError, resp
 
947
        raise FTPReplyError, resp
898
948
      end
899
949
      left = resp.index("(")
900
950
      right = resp.index(")")
901
951
      if left == nil or right == nil
902
 
        raise FTPProtoError, resp
 
952
        raise FTPProtoError, resp
903
953
      end
904
954
      numbers = resp[left + 1 .. right - 1].split(",")
905
955
      if numbers[0] == "4"
906
 
        if numbers.length != 9 || numbers[1] != "4" || numbers[2 + 4] != "2"
907
 
          raise FTPProtoError, resp
908
 
        end
909
 
        host = numbers[2, 4].join(".")
910
 
        port = (numbers[7].to_i << 8) + numbers[8].to_i
 
956
        if numbers.length != 9 || numbers[1] != "4" || numbers[2 + 4] != "2"
 
957
          raise FTPProtoError, resp
 
958
        end
 
959
        host = numbers[2, 4].join(".")
 
960
        port = (numbers[7].to_i << 8) + numbers[8].to_i
911
961
      elsif numbers[0] == "6"
912
 
        if numbers.length != 21 || numbers[1] != "16" || numbers[2 + 16] != "2"
913
 
          raise FTPProtoError, resp
914
 
        end
915
 
        v6 = ["", "", "", "", "", "", "", ""]
916
 
        for i in 0 .. 7
917
 
          v6[i] = sprintf("%02x%02x", numbers[(i * 2) + 2].to_i,
918
 
                          numbers[(i * 2) + 3].to_i)
919
 
        end
920
 
        host = v6[0, 8].join(":")
921
 
        port = (numbers[19].to_i << 8) + numbers[20].to_i
 
962
        if numbers.length != 21 || numbers[1] != "16" || numbers[2 + 16] != "2"
 
963
          raise FTPProtoError, resp
 
964
        end
 
965
        v6 = ["", "", "", "", "", "", "", ""]
 
966
        for i in 0 .. 7
 
967
          v6[i] = sprintf("%02x%02x", numbers[(i * 2) + 2].to_i,
 
968
                          numbers[(i * 2) + 3].to_i)
 
969
        end
 
970
        host = v6[0, 8].join(":")
 
971
        port = (numbers[19].to_i << 8) + numbers[20].to_i
922
972
      end
923
973
      return host, port
924
974
    end
925
975
    private :parse228
926
976
 
927
 
    def parse229(resp)
 
977
    # handler for response code 229
 
978
    # (Extended Passive Mode Entered)
 
979
    #
 
980
    # Returns host and port.
 
981
    def parse229(resp) # :nodoc:
928
982
      if resp[0, 3] != "229"
929
 
        raise FTPReplyError, resp
 
983
        raise FTPReplyError, resp
930
984
      end
931
985
      left = resp.index("(")
932
986
      right = resp.index(")")
933
987
      if left == nil or right == nil
934
 
        raise FTPProtoError, resp
 
988
        raise FTPProtoError, resp
935
989
      end
936
990
      numbers = resp[left + 1 .. right - 1].split(resp[left + 1, 1])
937
991
      if numbers.length != 4
938
 
        raise FTPProtoError, resp
 
992
        raise FTPProtoError, resp
939
993
      end
940
994
      port = numbers[3].to_i
941
995
      host = (@sock.peeraddr())[3]
943
997
    end
944
998
    private :parse229
945
999
 
946
 
    def parse257(resp)
 
1000
    # handler for response code 257
 
1001
    # ("PATHNAME" created)
 
1002
    #
 
1003
    # Returns host and port.
 
1004
    def parse257(resp) # :nodoc:
947
1005
      if resp[0, 3] != "257"
948
 
        raise FTPReplyError, resp
 
1006
        raise FTPReplyError, resp
949
1007
      end
950
1008
      if resp[3, 2] != ' "'
951
 
        return ""
 
1009
        return ""
952
1010
      end
953
1011
      dirname = ""
954
1012
      i = 5
955
1013
      n = resp.length
956
1014
      while i < n
957
 
        c = resp[i, 1]
958
 
        i = i + 1
959
 
        if c == '"'
960
 
          if i > n or resp[i, 1] != '"'
961
 
            break
962
 
          end
963
 
          i = i + 1
964
 
        end
965
 
        dirname = dirname + c
 
1015
        c = resp[i, 1]
 
1016
        i = i + 1
 
1017
        if c == '"'
 
1018
          if i > n or resp[i, 1] != '"'
 
1019
            break
 
1020
          end
 
1021
          i = i + 1
 
1022
        end
 
1023
        dirname = dirname + c
966
1024
      end
967
1025
      return dirname
968
1026
    end