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
|
#
# Author:: Jesse Campbell (<hikeit@gmail.com>)
# Copyright:: Copyright 2013-2016, Jesse Campbell
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require "uri"
require "tempfile"
require "net/ftp"
require "chef/provider/remote_file"
require "chef/file_content_management/tempfile"
class Chef
class Provider
class RemoteFile
class FTP
attr_reader :uri
attr_reader :new_resource
attr_reader :current_resource
def initialize(uri, new_resource, current_resource)
@uri = uri
@new_resource = new_resource
@current_resource = current_resource
validate_typecode!
validate_path!
end
def hostname
@uri.host
end
def port
@uri.port
end
def use_passive_mode?
! new_resource.ftp_active_mode
end
def typecode
uri.typecode
end
def user
if uri.userinfo
URI.unescape(uri.user)
else
"anonymous"
end
end
def pass
if uri.userinfo
URI.unescape(uri.password)
else
nil
end
end
def directories
parse_path if @directories.nil?
@directories
end
def filename
parse_path if @filename.nil?
@filename
end
def fetch
with_connection do
get
end
end
def ftp
@ftp ||= Net::FTP.new
end
private
def with_proxy_env
saved_socks_env = ENV["SOCKS_SERVER"]
ENV["SOCKS_SERVER"] = proxy_uri(@uri).to_s
yield
ensure
ENV["SOCKS_SERVER"] = saved_socks_env
end
def with_connection
with_proxy_env do
connect
yield
end
ensure
disconnect
end
def validate_typecode!
# Only support ascii and binary types
if typecode && /\A[ai]\z/ !~ typecode
raise ArgumentError, "invalid typecode: #{typecode.inspect}"
end
end
def validate_path!
parse_path
end
def connect
# The access sequence is defined by RFC 1738
ftp.connect(hostname, port)
ftp.passive = use_passive_mode?
ftp.login(user, pass)
directories.each do |cwd|
ftp.voidcmd("CWD #{cwd}")
end
end
def disconnect
ftp.close
end
# Fetches using Net::FTP, returns a Tempfile with the content
def get
tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile
if typecode
ftp.voidcmd("TYPE #{typecode.upcase}")
end
ftp.getbinaryfile(filename, tempfile.path)
tempfile.close if tempfile
tempfile
end
def proxy_uri(uri)
ChefConfig::Config.proxy_uri("ftp", hostname, port)
end
def parse_path
path = uri.path.sub(%r{\A/}, "%2F") # re-encode the beginning slash because uri library decodes it.
directories = path.split(%r{/}, -1)
directories.each {|d|
d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") }
}
unless filename = directories.pop
raise ArgumentError, "no filename: #{path.inspect}"
end
if filename.length == 0 || filename.end_with?( "/" )
raise ArgumentError, "no filename: #{path.inspect}"
end
@directories, @filename = directories, filename
end
end
end
end
end
|