dcparker (owner)

Revisions

gist: 85632 Download_button fork
public
Public Clone URL: git://gist.github.com/85632.git
Embed All Files: show embed
ftps_implicit.rb #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
require 'socket'
require 'openssl'
require 'net/ftp'
 
class Net::FTPS < Net::FTP
end
 
class Net::FTPS::Implicit < Net::FTP
  FTP_PORT = 990
 
  def initialize(host=nil, user=nil, passwd=nil, acct=nil)
    super
    @passive = true
    @binary = false
    @debug_mode = true
    @data_protection = 'P'
    @data_protected = false
  end
  attr_accessor :data_protection
 
  def open_socket(host, port, data_socket=false)
    tcpsock = if defined? SOCKSsocket and ENV["SOCKS_SERVER"]
      @passive = true
      SOCKSsocket.open(host, port)
    else
      TCPSocket.new(host, port)
    end
    if !data_socket || @data_protection == 'P'
      ssl_context = OpenSSL::SSL::SSLContext.new('SSLv23')
      ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
      ssl_context.key = nil
      ssl_context.cert = nil
      ssl_context.timeout = 10
 
      sock = OpenSSL::SSL::SSLSocket.new(tcpsock, ssl_context)
      sock.connect
    else
      sock = tcpsock
    end
    return sock
  end
  private :open_socket
 
  def connect(host, port=FTP_PORT)
    @sock = open_socket(host, port)
    mon_initialize
    getresp
    at_exit {
      if @sock && !@sock.closed?
        voidcmd("ABOR") rescue EOFError
        voidcmd("QUIT") rescue EOFError
        @sock.close
      end
    }
  end
 
  def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data
    synchronize do
      voidcmd("TYPE I")
      conn = transfercmd(cmd, rest_offset)
      data = get_data(conn,blocksize)
      yield(data)
      voidresp
    end
  end
 
  def get_data(sock,blocksize=1024)
    timeout = 10
    starttime = Time.now
    buffer = ''
    timeouts = 0
    catch :done do
      loop do
        event = select([sock],nil,nil,0.5)
        if event.nil? # nil would be a timeout, we'd do nothing and start loop over. Of course here we really have no timeout...
          timeouts += 0.5
          break if timeouts > timeout
        else
          event[0].each do |sock| # Iterate through all sockets that have pending activity
            if sock.eof? # Socket's been closed by the client
              throw :done
            else
              buffer << sock.readpartial(blocksize)
              if block_given? # we're in line-by-line mode
                lines = buffer.split(/\r?\n/)
                buffer = buffer =~ /\n$/ ? '' : lines.pop
                lines.each do |line|
                  yield(line)
                end
              end
            end
          end
        end
      end
    end
    sock.close
    buffer
  end
 
  def retrlines(cmd) # :yield: line
    synchronize do
      voidcmd("TYPE A")
      voidcmd("STRU F")
      voidcmd("MODE S")
      conn = transfercmd(cmd)
      get_data(conn) do |line|
        yield(line)
      end
      getresp
    end
  end
 
  #
  # Puts the connection into binary (image) mode, issues the given server-side
  # command (such as "STOR myfile"), and sends the contents of the file named
  # +file+ to the server. If the optional block is given, it also passes it
  # the data, in chunks of +blocksize+ characters.
  #
  def storbinary(cmd, file, blocksize, rest_offset = nil, &block) # :yield: data
    if rest_offset
      file.seek(rest_offset, IO::SEEK_SET)
    end
    synchronize do
      voidcmd("TYPE I")
      conn = transfercmd(cmd, rest_offset)
      loop do
        buf = file.read(blocksize)
        break if buf == nil
        conn.write(buf)
        yield(buf) if block
      end
      conn.close # closes the SSL
      conn.io.close # closes the TCP below it
      voidresp
    end
  end
 
  #
  # Puts the connection into ASCII (text) mode, issues the given server-side
  # command (such as "STOR myfile"), and sends the contents of the file
  # named +file+ to the server, one line at a time. If the optional block is
  # given, it also passes it the lines.
  #
  def storlines(cmd, file, &block) # :yield: line
    synchronize do
      voidcmd("TYPE A")
      conn = transfercmd(cmd)
      loop do
        buf = file.gets
        break if buf == nil
        if buf[-2, 2] != CRLF
          buf = buf.chomp + CRLF
        end
        conn.write(buf)
        yield(buf) if block
      end
      conn.close # closes the SSL
      conn.io.close # closes the TCP below it
      voidresp
    end
  end
 
  def transfercmd(cmd, rest_offset=nil)
    unless @data_protected
      voidcmd('PBSZ 0')
      sendcmd("PROT #{@data_protection}")
      @data_protected = true
    end
 
    if @passive
      host, port = makepasv
      if @resume and rest_offset
        resp = sendcmd("REST " + rest_offset.to_s)
        if resp[0] != ?3
          raise FTPReplyError, resp
        end
      end
      putline(cmd)
      conn = open_socket(host, port, true)
      resp = getresp # Should be a 150 response
      if resp[0] != ?1
        raise FTPReplyError, resp
      end
    else
      sock = makeport
      if @resume and rest_offset
        resp = sendcmd("REST " + rest_offset.to_s)
        if resp[0] != ?3
          raise FTPReplyError, resp
        end
      end
      resp = sendcmd(cmd)
      if resp[0] != ?1
        raise FTPReplyError, resp
      end
      conn = sock.accept
      sock.close
    end
    return conn
  end
  private :transfercmd
end