net-sftp 0.9.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/doc/faq/faq.html +298 -0
- data/doc/faq/faq.rb +154 -0
- data/doc/faq/faq.yml +183 -0
- data/examples/sftp-open-uri.rb +30 -0
- data/examples/synchronous.rb +3 -1
- data/lib/net/sftp/errors.rb +1 -1
- data/lib/net/sftp/operations/read.rb +15 -11
- data/lib/net/sftp/session.rb +2 -2
- data/lib/net/sftp/version.rb +2 -2
- data/lib/uri/open-sftp.rb +54 -0
- data/lib/uri/sftp.rb +42 -0
- data/test/operations/tc_read.rb +3 -3
- data/test/protocol/01/tc_attributes.rb +1 -1
- data/test/protocol/04/tc_attributes.rb +2 -2
- metadata +48 -40
data/doc/faq/faq.html
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Net::SFTP FAQ</title>
|
4
|
+
<style type="text/css">
|
5
|
+
a, a:visited, a:active {
|
6
|
+
color: #00F;
|
7
|
+
text-decoration: none;
|
8
|
+
}
|
9
|
+
|
10
|
+
a:hover {
|
11
|
+
text-decoration: underline;
|
12
|
+
}
|
13
|
+
|
14
|
+
.faq-list {
|
15
|
+
color: #000;
|
16
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
17
|
+
}
|
18
|
+
|
19
|
+
.faq-title {
|
20
|
+
background: #007;
|
21
|
+
color: #FFF;
|
22
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
23
|
+
padding-left: 1em;
|
24
|
+
padding-top: 0.5em;
|
25
|
+
padding-bottom: 0.5em;
|
26
|
+
font-weight: bold;
|
27
|
+
font-size: large;
|
28
|
+
border: 1px solid #000;
|
29
|
+
}
|
30
|
+
|
31
|
+
.faq-answer {
|
32
|
+
margin-left: 1em;
|
33
|
+
color: #000;
|
34
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
35
|
+
}
|
36
|
+
|
37
|
+
.faq-answer pre {
|
38
|
+
margin-left: 1em;
|
39
|
+
color: #000;
|
40
|
+
background: #FFE;
|
41
|
+
font-size: normal;
|
42
|
+
border: 1px dotted #CCC;
|
43
|
+
padding: 1em;
|
44
|
+
}
|
45
|
+
|
46
|
+
h1 {
|
47
|
+
background: #005;
|
48
|
+
color: #FFF;
|
49
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
50
|
+
padding-left: 1em;
|
51
|
+
padding-top: 1em;
|
52
|
+
padding-bottom: 1em;
|
53
|
+
font-weight: bold;
|
54
|
+
font-size: x-large;
|
55
|
+
border: 1px solid #00F;
|
56
|
+
}
|
57
|
+
</style>
|
58
|
+
</head>
|
59
|
+
<body>
|
60
|
+
<h1>Net::SFTP FAQ</h1>
|
61
|
+
<div class="faq-list">
|
62
|
+
<ul>
|
63
|
+
<li><a href='#2203312'>What is Net::SFTP?</a></li>
|
64
|
+
<li>How do I…
|
65
|
+
<ul>
|
66
|
+
<li>...connect to an <span class="caps">SFTP</span> server?
|
67
|
+
<ul>
|
68
|
+
<li><a href='#2203252'>I’d like to connect without first getting a Net::SSH connection…</a></li>
|
69
|
+
<li><a href='#2203212'>I already have an open Net::SSH connection…</a></li>
|
70
|
+
</ul>
|
71
|
+
</li>
|
72
|
+
<li>...upload data?
|
73
|
+
<ul>
|
74
|
+
<li><a href='#2203132'>I want to upload an entire file on disk…</a></li>
|
75
|
+
<li><a href='#2203092'>I want to upload bytes from a string or other object…</a></li>
|
76
|
+
</ul>
|
77
|
+
</li>
|
78
|
+
<li>...download data?
|
79
|
+
<ul>
|
80
|
+
<li><a href='#2203012'>I want to download directly to a local file…</a></li>
|
81
|
+
<li><a href='#2202972'>I want to download to a string in memory…</a></li>
|
82
|
+
<li><a href='#2202932'>I want to be notified of the progress of the download…</a></li>
|
83
|
+
</ul>
|
84
|
+
</li>
|
85
|
+
<li>...manage file permissions?
|
86
|
+
<ul>
|
87
|
+
<li><a href='#2202862'>I want to query a file’s permissions…</a></li>
|
88
|
+
<li><a href='#2202822'>I want to change a file’s permissions…</a></li>
|
89
|
+
<li><a href='#2202782'>I already have an open handle for the remove file…</a></li>
|
90
|
+
</ul>
|
91
|
+
</li>
|
92
|
+
<li>...manage directories?
|
93
|
+
<ul>
|
94
|
+
<li><a href='#2202692'>I want to query the contents of a directory…</a></li>
|
95
|
+
<li><a href='#2202652'>I want to create a directory…</a></li>
|
96
|
+
<li><a href='#2202612'>I want to remove a directory…</a></li>
|
97
|
+
</ul>
|
98
|
+
</li>
|
99
|
+
<li><a href='#2202542'>...delete a file?</a></li>
|
100
|
+
<li><a href='#2202502'>...rename a file?</a></li>
|
101
|
+
</ul>
|
102
|
+
</li>
|
103
|
+
</ul>
|
104
|
+
</div>
|
105
|
+
<a name='2203312'></a>
|
106
|
+
<div class='faq-title'>What is Net::SFTP?</div>
|
107
|
+
<div class='faq-answer'><p>Net::SFTP is a pure-Ruby implementation of the <span class="caps">SFTP</span> protocol. That’s
|
108
|
+
“SFTP” as in “Secure File Transfer Protocol”, as defined as an adjuct to the
|
109
|
+
<span class="caps">SSH</span> specification. <em>Not</em> “SFTP” as in “Secure <span class="caps">FTP</span>” (a <em>completely</em> different
|
110
|
+
beast). Nor is it an implementation of the “Simple File Transfer Protocol”
|
111
|
+
(which is in no way secure).</p></div>
|
112
|
+
<a name='2203252'></a>
|
113
|
+
<div class='faq-title'>How do I… ...connect to an <span class="caps">SFTP</span> server? I’d like to connect without first getting a Net::SSH connection…</div>
|
114
|
+
<div class='faq-answer'><p>Something like this:</p>
|
115
|
+
|
116
|
+
|
117
|
+
<code><pre>
|
118
|
+
require 'net/sftp'
|
119
|
+
|
120
|
+
Net::SFTP.start(host, user, password) do |sftp|
|
121
|
+
...
|
122
|
+
end
|
123
|
+
</pre></code>
|
124
|
+
|
125
|
+
<p><code>Net::SFTP.start</code> accepts the same parameters as <code>Net::SSH.start</code>,
|
126
|
+
so I’ll direct you to that documentation for all the particulars.</p></div>
|
127
|
+
<a name='2203212'></a>
|
128
|
+
<div class='faq-title'>How do I… ...connect to an <span class="caps">SFTP</span> server? I already have an open Net::SSH connection…</div>
|
129
|
+
<div class='faq-answer'><p>You can piggy-back an <span class="caps">SFTP</span> connection on an existing Net::SSH
|
130
|
+
connection, which can be useful if you’ve already got an <span class="caps">SSH</span>
|
131
|
+
connection that you’re using for port forwarding or whatever.</p>
|
132
|
+
|
133
|
+
|
134
|
+
<code><pre>
|
135
|
+
require 'net/ssh'
|
136
|
+
require 'net/sftp'
|
137
|
+
|
138
|
+
Net::SSH.start(host, user, password) do |ssh|
|
139
|
+
...
|
140
|
+
ssh.sftp.connect do |sftp|
|
141
|
+
...
|
142
|
+
end
|
143
|
+
...
|
144
|
+
end
|
145
|
+
</pre></code></div>
|
146
|
+
<a name='2203132'></a>
|
147
|
+
<div class='faq-title'>How do I… ...upload data? I want to upload an entire file on disk…</div>
|
148
|
+
<div class='faq-answer'><p>Assuming you already have an <span class="caps">SFTP</span> connection:</p>
|
149
|
+
|
150
|
+
|
151
|
+
<code><pre>
|
152
|
+
sftp.put_file "/path/to/local.file", "/path/to/remote.file"
|
153
|
+
</pre></code></div>
|
154
|
+
<a name='2203092'></a>
|
155
|
+
<div class='faq-title'>How do I… ...upload data? I want to upload bytes from a string or other object…</div>
|
156
|
+
<div class='faq-answer'><p>Assuming you already have an <span class="caps">SFTP</span> connection, and your data is stored
|
157
|
+
in a string named <code>data</code>:</p>
|
158
|
+
|
159
|
+
|
160
|
+
<code><pre>
|
161
|
+
sftp.open_handle("/path/to/remote.file", "w") do |handle|
|
162
|
+
result = sftp.write(handle, data)
|
163
|
+
puts result.code # the result of the operation
|
164
|
+
end
|
165
|
+
</pre></code>
|
166
|
+
|
167
|
+
<p>If (for whatever reason) you’d rather not use blocks, you can do
|
168
|
+
without, but be sure to call <code>close_handle</code> when you’re done:</p>
|
169
|
+
|
170
|
+
|
171
|
+
<code><pre>
|
172
|
+
handle = sftp.open_handle("/path/to/remote.file", "w")
|
173
|
+
result = sftp.write(handle, data)
|
174
|
+
puts result.code # the result of the operation
|
175
|
+
sftp.close_handle(handle)
|
176
|
+
</pre></code></div>
|
177
|
+
<a name='2203012'></a>
|
178
|
+
<div class='faq-title'>How do I… ...download data? I want to download directly to a local file…</div>
|
179
|
+
<div class='faq-answer'><p>Assuming you already have an <span class="caps">SFTP</span> connection:</p>
|
180
|
+
|
181
|
+
|
182
|
+
<code><pre>
|
183
|
+
sftp.get_file "/path/to/remote.file", "/path/to/local.file"
|
184
|
+
</pre></code></div>
|
185
|
+
<a name='2202972'></a>
|
186
|
+
<div class='faq-title'>How do I… ...download data? I want to download to a string in memory…</div>
|
187
|
+
<div class='faq-answer'><p>Assuming you already have an <span class="caps">SFTP</span> connection:</p>
|
188
|
+
|
189
|
+
|
190
|
+
<code><pre>
|
191
|
+
data = nil
|
192
|
+
sftp.open_handle("/path/to/remote.file") do |handle|
|
193
|
+
data = sftp.read(handle)
|
194
|
+
end
|
195
|
+
</pre></code></div>
|
196
|
+
<a name='2202932'></a>
|
197
|
+
<div class='faq-title'>How do I… ...download data? I want to be notified of the progress of the download…</div>
|
198
|
+
<div class='faq-answer'><p>You can specify both a “chunk size” and a “progress callback”. The
|
199
|
+
callback will be invoked for every “chunk size” bytes that are
|
200
|
+
received:</p>
|
201
|
+
|
202
|
+
|
203
|
+
<code><pre>
|
204
|
+
sftp.open_handle("/path/to/remote.file") do |handle|
|
205
|
+
begin
|
206
|
+
STDOUT.sync = true
|
207
|
+
data = sftp.read(handle, :chunk_size => 4096,
|
208
|
+
:progress_callback => lambda { |data| print "." })
|
209
|
+
puts
|
210
|
+
ensure
|
211
|
+
STDOUT.sync = false
|
212
|
+
end
|
213
|
+
end
|
214
|
+
</pre></code></div>
|
215
|
+
<a name='2202862'></a>
|
216
|
+
<div class='faq-title'>How do I… ...manage file permissions? I want to query a file’s permissions…</div>
|
217
|
+
<div class='faq-answer'><p>File permissions are one of the <code>stat</code> attributes of files and
|
218
|
+
directories:</p>
|
219
|
+
|
220
|
+
|
221
|
+
<code><pre>
|
222
|
+
p sftp.stat("/path/to/remote.file").permissions
|
223
|
+
</pre></code></div>
|
224
|
+
<a name='2202822'></a>
|
225
|
+
<div class='faq-title'>How do I… ...manage file permissions? I want to change a file’s permissions…</div>
|
226
|
+
<div class='faq-answer'><p>Just use <code>setstat</code> to change the permissions of an existing file:</p>
|
227
|
+
|
228
|
+
|
229
|
+
<code><pre>
|
230
|
+
sftp.setstat("/path/to/remote.file", :permissions => 0644)
|
231
|
+
</pre></code></div>
|
232
|
+
<a name='2202782'></a>
|
233
|
+
<div class='faq-title'>How do I… ...manage file permissions? I already have an open handle for the remove file…</div>
|
234
|
+
<div class='faq-answer'><p>If you have a handle for the remote file, you can use <code>fstat</code> and
|
235
|
+
<code>fsetstat</code> to query and set the permissions:</p>
|
236
|
+
|
237
|
+
|
238
|
+
<code><pre>
|
239
|
+
sftp.open_handle("/path/to/remote.file") do |handle|
|
240
|
+
permissions = sftp.fstat(handle)
|
241
|
+
sftp.fsetstat(handle, :permissions => permissions | 0444)
|
242
|
+
end
|
243
|
+
</pre></code></div>
|
244
|
+
<a name='2202692'></a>
|
245
|
+
<div class='faq-title'>How do I… ...manage directories? I want to query the contents of a directory…</div>
|
246
|
+
<div class='faq-answer'><p>You query the contents of a directory by calling <code>opendir</code> to obtain
|
247
|
+
a handle to the directory, and then using <code>readdir</code> on the handle to
|
248
|
+
obtain a list of directory entries. Be sure to close the handle when
|
249
|
+
you’re done:</p>
|
250
|
+
|
251
|
+
|
252
|
+
<code><pre>
|
253
|
+
handle = sftp.opendir("/usr/lib")
|
254
|
+
items = sftp.readdir(handle)
|
255
|
+
items.each do |item|
|
256
|
+
puts item.filename
|
257
|
+
puts item.longname
|
258
|
+
p item.attributes # permissions, atime, etc.
|
259
|
+
end
|
260
|
+
sftp.close_handle(handle)
|
261
|
+
</pre></code></div>
|
262
|
+
<a name='2202652'></a>
|
263
|
+
<div class='faq-title'>How do I… ...manage directories? I want to create a directory…</div>
|
264
|
+
<div class='faq-answer'><p>Use <code>mkdir</code>:</p>
|
265
|
+
|
266
|
+
|
267
|
+
<code><pre>
|
268
|
+
sftp.mkdir("/path/to/remote/dir", :permissions => 0500)
|
269
|
+
</pre></code></div>
|
270
|
+
<a name='2202612'></a>
|
271
|
+
<div class='faq-title'>How do I… ...manage directories? I want to remove a directory…</div>
|
272
|
+
<div class='faq-answer'><p>Use <code>rmdir</code>:</p>
|
273
|
+
|
274
|
+
|
275
|
+
<code><pre>
|
276
|
+
sftp.rmdir("/path/to/remote/dir")
|
277
|
+
</pre></code></div>
|
278
|
+
<a name='2202542'></a>
|
279
|
+
<div class='faq-title'>How do I… ...delete a file?</div>
|
280
|
+
<div class='faq-answer'><p>Use <code>remove</code>:</p>
|
281
|
+
|
282
|
+
|
283
|
+
<code><pre>
|
284
|
+
sftp.remove("/path/to/remote.file")
|
285
|
+
</pre></code></div>
|
286
|
+
<a name='2202502'></a>
|
287
|
+
<div class='faq-title'>How do I… ...rename a file?</div>
|
288
|
+
<div class='faq-answer'><p>Use <code>rename</code>:</p>
|
289
|
+
|
290
|
+
|
291
|
+
<code><pre>
|
292
|
+
sftp.rename("/path/to/remote.file", "/path/to/new.file")
|
293
|
+
</pre></code>
|
294
|
+
|
295
|
+
<p>It should be noted that <code>rename</code> is only supported by version 2 or
|
296
|
+
later of the <span class="caps">SFTP</span> protocol, so if you’re using an older <span class="caps">SFTP</span> server you
|
297
|
+
might not be able to use this operation.</p></div>
|
298
|
+
</body></html>
|
data/doc/faq/faq.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2005, Jamis Buck (jamis@jamisbuck.org)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
15
|
+
#
|
16
|
+
# * The names of its contributors may not be used to endorse or promote
|
17
|
+
# products derived from this software without specific prior written
|
18
|
+
# permission.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
24
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
25
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
27
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
28
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
# =============================================================================
|
31
|
+
#++
|
32
|
+
|
33
|
+
require 'yaml'
|
34
|
+
require 'redcloth'
|
35
|
+
|
36
|
+
def process_faq_list( faqs )
|
37
|
+
puts "<ul>"
|
38
|
+
faqs.each do |faq|
|
39
|
+
process_faq_list_item faq
|
40
|
+
end
|
41
|
+
puts "</ul>"
|
42
|
+
end
|
43
|
+
|
44
|
+
def process_faq_list_item( faq )
|
45
|
+
question = faq.keys.first
|
46
|
+
answer = faq.values.first
|
47
|
+
|
48
|
+
print "<li>"
|
49
|
+
|
50
|
+
question_text = RedCloth.new(question).to_html.gsub( %r{</?p>},"" )
|
51
|
+
if answer.is_a?( Array )
|
52
|
+
puts question_text
|
53
|
+
process_faq_list answer
|
54
|
+
else
|
55
|
+
print "<a href='##{question.object_id}'>#{question_text}</a>"
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "</li>"
|
59
|
+
end
|
60
|
+
|
61
|
+
def process_faq_descriptions( faqs, path=nil )
|
62
|
+
faqs.each do |faq|
|
63
|
+
process_faq_description faq, path
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def process_faq_description( faq, path )
|
68
|
+
question = faq.keys.first
|
69
|
+
path = ( path ? path + " " : "" ) + question
|
70
|
+
answer = faq.values.first
|
71
|
+
|
72
|
+
if answer.is_a?( Array )
|
73
|
+
process_faq_descriptions( answer, path )
|
74
|
+
else
|
75
|
+
title = RedCloth.new( path ).to_html.gsub( %r{</?p>}, "" )
|
76
|
+
answer = RedCloth.new( answer || "" )
|
77
|
+
|
78
|
+
puts "<a name='#{question.object_id}'></a>"
|
79
|
+
puts "<div class='faq-title'>#{title}</div>"
|
80
|
+
puts "<div class='faq-answer'>#{answer.to_html}</div>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
faqs = YAML.load( File.read( "faq.yml" ) )
|
85
|
+
|
86
|
+
puts <<-EOF
|
87
|
+
<html>
|
88
|
+
<head>
|
89
|
+
<title>Net::SFTP FAQ</title>
|
90
|
+
<style type="text/css">
|
91
|
+
a, a:visited, a:active {
|
92
|
+
color: #00F;
|
93
|
+
text-decoration: none;
|
94
|
+
}
|
95
|
+
|
96
|
+
a:hover {
|
97
|
+
text-decoration: underline;
|
98
|
+
}
|
99
|
+
|
100
|
+
.faq-list {
|
101
|
+
color: #000;
|
102
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
103
|
+
}
|
104
|
+
|
105
|
+
.faq-title {
|
106
|
+
background: #007;
|
107
|
+
color: #FFF;
|
108
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
109
|
+
padding-left: 1em;
|
110
|
+
padding-top: 0.5em;
|
111
|
+
padding-bottom: 0.5em;
|
112
|
+
font-weight: bold;
|
113
|
+
font-size: large;
|
114
|
+
border: 1px solid #000;
|
115
|
+
}
|
116
|
+
|
117
|
+
.faq-answer {
|
118
|
+
margin-left: 1em;
|
119
|
+
color: #000;
|
120
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
121
|
+
}
|
122
|
+
|
123
|
+
.faq-answer pre {
|
124
|
+
margin-left: 1em;
|
125
|
+
color: #000;
|
126
|
+
background: #FFE;
|
127
|
+
font-size: normal;
|
128
|
+
border: 1px dotted #CCC;
|
129
|
+
padding: 1em;
|
130
|
+
}
|
131
|
+
|
132
|
+
h1 {
|
133
|
+
background: #005;
|
134
|
+
color: #FFF;
|
135
|
+
font-family: vera-sans, verdana, arial, sans-serif;
|
136
|
+
padding-left: 1em;
|
137
|
+
padding-top: 1em;
|
138
|
+
padding-bottom: 1em;
|
139
|
+
font-weight: bold;
|
140
|
+
font-size: x-large;
|
141
|
+
border: 1px solid #00F;
|
142
|
+
}
|
143
|
+
</style>
|
144
|
+
</head>
|
145
|
+
<body>
|
146
|
+
<h1>Net::SFTP FAQ</h1>
|
147
|
+
<div class="faq-list">
|
148
|
+
EOF
|
149
|
+
|
150
|
+
process_faq_list( faqs )
|
151
|
+
puts "</div>"
|
152
|
+
process_faq_descriptions( faqs )
|
153
|
+
|
154
|
+
puts "</body></html>"
|
data/doc/faq/faq.yml
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
---
|
2
|
+
- "What is Net::SFTP?": |
|
3
|
+
Net::SFTP is a pure-Ruby implementation of the SFTP protocol. That's
|
4
|
+
"SFTP" as in "Secure File Transfer Protocol", as defined as an adjuct to the
|
5
|
+
SSH specification. _Not_ "SFTP" as in "Secure FTP" (a _completely_ different
|
6
|
+
beast). Nor is it an implementation of the "Simple File Transfer Protocol"
|
7
|
+
(which is in no way secure).
|
8
|
+
|
9
|
+
- "How do I...":
|
10
|
+
- "...connect to an SFTP server?":
|
11
|
+
- "I'd like to connect without first getting a Net::SSH connection...": |
|
12
|
+
Something like this:
|
13
|
+
|
14
|
+
<code><pre>
|
15
|
+
require 'net/sftp'
|
16
|
+
|
17
|
+
Net::SFTP.start(host, user, password) do |sftp|
|
18
|
+
...
|
19
|
+
end
|
20
|
+
</pre></code>
|
21
|
+
|
22
|
+
@Net::SFTP.start@ accepts the same parameters as @Net::SSH.start@,
|
23
|
+
so I'll direct you to that documentation for all the particulars.
|
24
|
+
|
25
|
+
- "I already have an open Net::SSH connection...": |
|
26
|
+
You can piggy-back an SFTP connection on an existing Net::SSH
|
27
|
+
connection, which can be useful if you've already got an SSH
|
28
|
+
connection that you're using for port forwarding or whatever.
|
29
|
+
|
30
|
+
<code><pre>
|
31
|
+
require 'net/ssh'
|
32
|
+
require 'net/sftp'
|
33
|
+
|
34
|
+
Net::SSH.start(host, user, password) do |ssh|
|
35
|
+
...
|
36
|
+
ssh.sftp.connect do |sftp|
|
37
|
+
...
|
38
|
+
end
|
39
|
+
...
|
40
|
+
end
|
41
|
+
</pre></code>
|
42
|
+
|
43
|
+
- "...upload data?":
|
44
|
+
- "I want to upload an entire file on disk...": |
|
45
|
+
Assuming you already have an SFTP connection:
|
46
|
+
|
47
|
+
<code><pre>
|
48
|
+
sftp.put_file "/path/to/local.file", "/path/to/remote.file"
|
49
|
+
</pre></code>
|
50
|
+
|
51
|
+
- "I want to upload bytes from a string or other object...": |
|
52
|
+
Assuming you already have an SFTP connection, and your data is stored
|
53
|
+
in a string named @data@:
|
54
|
+
|
55
|
+
<code><pre>
|
56
|
+
sftp.open_handle("/path/to/remote.file", "w") do |handle|
|
57
|
+
result = sftp.write(handle, data)
|
58
|
+
puts result.code # the result of the operation
|
59
|
+
end
|
60
|
+
</pre></code>
|
61
|
+
|
62
|
+
If (for whatever reason) you'd rather not use blocks, you can do
|
63
|
+
without, but be sure to call @close_handle@ when you're done:
|
64
|
+
|
65
|
+
<code><pre>
|
66
|
+
handle = sftp.open_handle("/path/to/remote.file", "w")
|
67
|
+
result = sftp.write(handle, data)
|
68
|
+
puts result.code # the result of the operation
|
69
|
+
sftp.close_handle(handle)
|
70
|
+
</pre></code>
|
71
|
+
|
72
|
+
- "...download data?":
|
73
|
+
- "I want to download directly to a local file...": |
|
74
|
+
Assuming you already have an SFTP connection:
|
75
|
+
|
76
|
+
<code><pre>
|
77
|
+
sftp.get_file "/path/to/remote.file", "/path/to/local.file"
|
78
|
+
</pre></code>
|
79
|
+
|
80
|
+
- "I want to download to a string in memory...": |
|
81
|
+
Assuming you already have an SFTP connection:
|
82
|
+
|
83
|
+
<code><pre>
|
84
|
+
data = nil
|
85
|
+
sftp.open_handle("/path/to/remote.file") do |handle|
|
86
|
+
data = sftp.read(handle)
|
87
|
+
end
|
88
|
+
</pre></code>
|
89
|
+
|
90
|
+
- "I want to be notified of the progress of the download...": |
|
91
|
+
You can specify both a "chunk size" and a "progress callback". The
|
92
|
+
callback will be invoked for every "chunk size" bytes that are
|
93
|
+
received:
|
94
|
+
|
95
|
+
<code><pre>
|
96
|
+
sftp.open_handle("/path/to/remote.file") do |handle|
|
97
|
+
begin
|
98
|
+
STDOUT.sync = true
|
99
|
+
data = sftp.read(handle, :chunk_size => 4096,
|
100
|
+
:progress_callback => lambda { |data| print "." })
|
101
|
+
puts
|
102
|
+
ensure
|
103
|
+
STDOUT.sync = false
|
104
|
+
end
|
105
|
+
end
|
106
|
+
</pre></code>
|
107
|
+
|
108
|
+
- "...manage file permissions?":
|
109
|
+
- "I want to query a file's permissions...": |
|
110
|
+
File permissions are one of the @stat@ attributes of files and
|
111
|
+
directories:
|
112
|
+
|
113
|
+
<code><pre>
|
114
|
+
p sftp.stat("/path/to/remote.file").permissions
|
115
|
+
</pre></code>
|
116
|
+
|
117
|
+
- "I want to change a file's permissions...": |
|
118
|
+
Just use @setstat@ to change the permissions of an existing file:
|
119
|
+
|
120
|
+
<code><pre>
|
121
|
+
sftp.setstat("/path/to/remote.file", :permissions => 0644)
|
122
|
+
</pre></code>
|
123
|
+
|
124
|
+
- "I already have an open handle for the remove file...": |
|
125
|
+
If you have a handle for the remote file, you can use @fstat@ and
|
126
|
+
@fsetstat@ to query and set the permissions:
|
127
|
+
|
128
|
+
<code><pre>
|
129
|
+
sftp.open_handle("/path/to/remote.file") do |handle|
|
130
|
+
permissions = sftp.fstat(handle)
|
131
|
+
sftp.fsetstat(handle, :permissions => permissions | 0444)
|
132
|
+
end
|
133
|
+
</pre></code>
|
134
|
+
|
135
|
+
- "...manage directories?":
|
136
|
+
- "I want to query the contents of a directory...": |
|
137
|
+
You query the contents of a directory by calling @opendir@ to obtain
|
138
|
+
a handle to the directory, and then using @readdir@ on the handle to
|
139
|
+
obtain a list of directory entries. Be sure to close the handle when
|
140
|
+
you're done:
|
141
|
+
|
142
|
+
<code><pre>
|
143
|
+
handle = sftp.opendir("/usr/lib")
|
144
|
+
items = sftp.readdir(handle)
|
145
|
+
items.each do |item|
|
146
|
+
puts item.filename
|
147
|
+
puts item.longname
|
148
|
+
p item.attributes # permissions, atime, etc.
|
149
|
+
end
|
150
|
+
sftp.close_handle(handle)
|
151
|
+
</pre></code>
|
152
|
+
|
153
|
+
- "I want to create a directory...": |
|
154
|
+
Use @mkdir@:
|
155
|
+
|
156
|
+
<code><pre>
|
157
|
+
sftp.mkdir("/path/to/remote/dir", :permissions => 0500)
|
158
|
+
</pre></code>
|
159
|
+
|
160
|
+
- "I want to remove a directory...": |
|
161
|
+
Use @rmdir@:
|
162
|
+
|
163
|
+
<code><pre>
|
164
|
+
sftp.rmdir("/path/to/remote/dir")
|
165
|
+
</pre></code>
|
166
|
+
|
167
|
+
- "...delete a file?": |
|
168
|
+
Use @remove@:
|
169
|
+
|
170
|
+
<code><pre>
|
171
|
+
sftp.remove("/path/to/remote.file")
|
172
|
+
</pre></code>
|
173
|
+
|
174
|
+
- "...rename a file?": |
|
175
|
+
Use @rename@:
|
176
|
+
|
177
|
+
<code><pre>
|
178
|
+
sftp.rename("/path/to/remote.file", "/path/to/new.file")
|
179
|
+
</pre></code>
|
180
|
+
|
181
|
+
It should be noted that @rename@ is only supported by version 2 or
|
182
|
+
later of the SFTP protocol, so if you're using an older SFTP server you
|
183
|
+
might not be able to use this operation.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# This source file is distributed as part of the Net::SFTP Secure FTP Client
|
7
|
+
# library for Ruby. This file (and the library as a whole) may be used only as
|
8
|
+
# allowed by either the BSD license, or the Ruby license (or, by association
|
9
|
+
# with the Ruby license, the GPL). See the "doc" subdirectory of the Net::SFTP
|
10
|
+
# distribution for the texts of these licenses.
|
11
|
+
# -----------------------------------------------------------------------------
|
12
|
+
# net-sftp website: http://net-ssh.rubyforge.org/sftp
|
13
|
+
# project website : http://rubyforge.org/projects/net-ssh
|
14
|
+
# =============================================================================
|
15
|
+
#++
|
16
|
+
|
17
|
+
$:.unshift "../lib"
|
18
|
+
require 'uri/open-sftp'
|
19
|
+
|
20
|
+
uri = URI.parse( "sftp://localhost" +
|
21
|
+
"/home/jgb3/temp/out" +
|
22
|
+
"?encryption=blowfish-cbc&compression=zlib" )
|
23
|
+
|
24
|
+
data = uri.open.read
|
25
|
+
p data.length
|
26
|
+
|
27
|
+
stream = uri.open( :chunk_size=>128,
|
28
|
+
:progress_proc => proc { |d| print "."; $stdout.flush } )
|
29
|
+
puts
|
30
|
+
p stream.read.length
|
data/examples/synchronous.rb
CHANGED
@@ -25,7 +25,9 @@ Net::SFTP.start( 'localhost',
|
|
25
25
|
handle = sftp.open_handle( "temp/out" )
|
26
26
|
puts "got handle: #{handle.inspect}"
|
27
27
|
puts "reading..."
|
28
|
-
data = sftp.read( handle
|
28
|
+
data = sftp.read( handle,
|
29
|
+
:chunk_size => 4*1024,
|
30
|
+
:progress_callback => proc { |data| puts " [#{data.length}]" } )
|
29
31
|
puts "got data: #{data.length} bytes"
|
30
32
|
sftp.close_handle( handle )
|
31
33
|
|
data/lib/net/sftp/errors.rb
CHANGED
@@ -24,22 +24,24 @@ module Net ; module SFTP ; module Operations
|
|
24
24
|
# file in one chunk.
|
25
25
|
class Read < Abstract
|
26
26
|
|
27
|
-
# The maximum amount of data to read at once when reading an entire
|
28
|
-
#
|
29
|
-
|
30
|
-
CHUNK_SIZE = 64 * 1024
|
27
|
+
# The default maximum amount of data to read at once when reading an entire
|
28
|
+
# file.
|
29
|
+
DEFAULT_CHUNK_SIZE = 64 * 1024
|
31
30
|
|
32
31
|
# Perform the operation. If length is less than 0 (the default), then the
|
33
32
|
# entire file (from the given offset) will be read and returned in "one
|
34
33
|
# fell swoop". Otherwise, the given length of data will be requested.
|
35
|
-
def perform( handle,
|
36
|
-
@length = length
|
34
|
+
def perform( handle, options={} )
|
35
|
+
@length = options[:length] || -1
|
37
36
|
@handle = handle
|
38
|
-
@offset = offset
|
37
|
+
@offset = options[:offset] || 0
|
38
|
+
@chunk_size = options[:chunk_size] || DEFAULT_CHUNK_SIZE
|
39
|
+
@progress_callback = options[:progress_callback]
|
39
40
|
@data = ""
|
40
41
|
|
41
|
-
real_length = ( length
|
42
|
-
|
42
|
+
real_length = ( @length >= 0 && @length < @chunk_size ?
|
43
|
+
@length : @chunk_size )
|
44
|
+
@driver.read( nil, @handle, @offset, real_length )
|
43
45
|
end
|
44
46
|
|
45
47
|
# Invoked when a data packet is received from the server. If the original
|
@@ -50,12 +52,14 @@ module Net ; module SFTP ; module Operations
|
|
50
52
|
@log.debug "[#{@id}] got #{data.length} bytes" if @log.debug?
|
51
53
|
|
52
54
|
@data << data
|
55
|
+
@progress_callback[@data] if @progress_callback
|
56
|
+
|
53
57
|
if @length < 0 || @data.length < @length
|
54
58
|
if @length < 0
|
55
|
-
length =
|
59
|
+
length = @chunk_size
|
56
60
|
else
|
57
61
|
length = @length - @data.length
|
58
|
-
length = length >
|
62
|
+
length = length > @chunk_size ? @chunk_size : length
|
59
63
|
end
|
60
64
|
|
61
65
|
@log.debug "[#{@id}] requesting #{length} more bytes" if @log.debug?
|
data/lib/net/sftp/session.rb
CHANGED
@@ -181,14 +181,14 @@ module Net ; module SFTP
|
|
181
181
|
def get_file( remote_path, local_path )
|
182
182
|
open_handle( remote_path ) do |handle|
|
183
183
|
contents = read( handle )
|
184
|
-
File.open( local_path, "
|
184
|
+
File.open( local_path, "wb" ) { |f| f.write contents }
|
185
185
|
end
|
186
186
|
end
|
187
187
|
|
188
188
|
# This stores the given local file at the given remote path. This will
|
189
189
|
# overwrite any file at the remote path name. The local file must exist.
|
190
190
|
def put_file( local_path, remote_path )
|
191
|
-
contents = File.
|
191
|
+
contents = File.open( local_path, "rb" ) { |f| f.read }
|
192
192
|
open_handle( remote_path, "w" ) { |handle| write( handle, contents ) }
|
193
193
|
end
|
194
194
|
|
data/lib/net/sftp/version.rb
CHANGED
@@ -0,0 +1,54 @@
|
|
1
|
+
# =======================================================================
|
2
|
+
# Net::SSH -- A Ruby module implementing the SSH2 client protocol
|
3
|
+
# Copyright (C) 2004 Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
18
|
+
# =======================================================================
|
19
|
+
|
20
|
+
require 'open-uri'
|
21
|
+
require 'uri/sftp'
|
22
|
+
require 'net/sftp'
|
23
|
+
|
24
|
+
OpenURI::Options[ :chunk_size ] = true
|
25
|
+
|
26
|
+
module URI
|
27
|
+
|
28
|
+
class SFTP
|
29
|
+
def direct_open( buf, open_options )
|
30
|
+
Net::SFTP.start( host, port, user, password, options ) do |sftp|
|
31
|
+
if open_options[:content_length_proc]
|
32
|
+
open_options[:content_length_proc].call( sftp.lstat( path ).size )
|
33
|
+
end
|
34
|
+
|
35
|
+
body = nil
|
36
|
+
sftp.open_handle( path ) do |handle|
|
37
|
+
body = sftp.read( handle,
|
38
|
+
:chunk_size => open_options[:chunk_size],
|
39
|
+
:progress_callback => open_options[:progress_proc] )
|
40
|
+
end
|
41
|
+
|
42
|
+
if body.nil?
|
43
|
+
raise Net::SSH::SFTP::SFTPError, sftp.status[:message]
|
44
|
+
end
|
45
|
+
|
46
|
+
buf << body
|
47
|
+
buf.io.rewind
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
include OpenURI::OpenRead
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/uri/sftp.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'uri/generic'
|
2
|
+
|
3
|
+
module URI
|
4
|
+
|
5
|
+
class SFTP < Generic
|
6
|
+
DEFAULT_PORT = 22
|
7
|
+
|
8
|
+
COMPONENT = [
|
9
|
+
:scheme,
|
10
|
+
:userinfo,
|
11
|
+
:host, :port, :path,
|
12
|
+
:query
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
attr_reader :options
|
16
|
+
|
17
|
+
def self.new2( user, password, host, port, path, query )
|
18
|
+
new( 'sftp',
|
19
|
+
[ user, password ],
|
20
|
+
host, port,
|
21
|
+
nil, path,
|
22
|
+
nil, query )
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize( *args )
|
26
|
+
super( *args )
|
27
|
+
|
28
|
+
@options = Hash.new
|
29
|
+
( query || "" ).split( /&/ ).each do |pair|
|
30
|
+
name, value = pair.split( /=/ )
|
31
|
+
opt_name = name.intern
|
32
|
+
values = value.split( /,/ ).map { |v| v.to_i.to_s == v ? v.to_i : v }
|
33
|
+
values = values.first if values.length == 1
|
34
|
+
options[ opt_name ] = values
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
@@schemes['SFTP'] = SFTP
|
41
|
+
|
42
|
+
end
|
data/test/operations/tc_read.rb
CHANGED
@@ -45,7 +45,7 @@ class TC_Operations_Read < Test::Unit::TestCase
|
|
45
45
|
def test_perform_explicit
|
46
46
|
id = handle = offset = length = nil
|
47
47
|
@driver.mock_handle( :read ) { |i,h,o,l| id, handle, offset, length = i, h, o, l; 10 }
|
48
|
-
assert_equal 10, @operation.perform( "foo", 15, 72 )
|
48
|
+
assert_equal 10, @operation.perform( "foo", :offset=>15, :length=>72 )
|
49
49
|
assert_nil id
|
50
50
|
assert_equal "foo", handle
|
51
51
|
assert_equal 15, offset
|
@@ -72,7 +72,7 @@ class TC_Operations_Read < Test::Unit::TestCase
|
|
72
72
|
@session.mock_handle( :loop )
|
73
73
|
@session.mock_handle( :status= )
|
74
74
|
called = false
|
75
|
-
@operation.execute( "foo", 15, 20 ) { called = true }
|
75
|
+
@operation.execute( "foo", :offset=>15, :length=>20 ) { called = true }
|
76
76
|
@operation.do_data( "harbinger of doom" )
|
77
77
|
@operation.do_data( "abc" )
|
78
78
|
assert_equal 2, @driver.mock_count( :read )
|
@@ -89,7 +89,7 @@ class TC_Operations_Read < Test::Unit::TestCase
|
|
89
89
|
@session.mock_handle( :loop )
|
90
90
|
@session.mock_handle( :status= )
|
91
91
|
called = false
|
92
|
-
@operation.execute( "foo", 15, 20 ) { called = true }
|
92
|
+
@operation.execute( "foo", :offset=>15, :length=>20 ) { called = true }
|
93
93
|
assert_nothing_raised { @operation.do_status( 1, nil, nil ) }
|
94
94
|
assert called
|
95
95
|
end
|
@@ -126,7 +126,7 @@ class TC_01_Attributes < Test::Unit::TestCase
|
|
126
126
|
[ { :mtime => 789012 }, :mtime, 789012 ],
|
127
127
|
[ { :extended => { "foo" => "bar" } }, :extended, { "foo" => "bar" } ],
|
128
128
|
[ { :owner => ENV['USER'] }, :uid, Etc.getpwnam(ENV['USER']).uid ],
|
129
|
-
[ { :group => '
|
129
|
+
[ { :group => 'wheel' }, :gid, Etc.getgrnam('wheel').gid ]
|
130
130
|
].each do |fixture|
|
131
131
|
define_method( "test_from_hash_#{fixture[1]}" ) do
|
132
132
|
attrs = @factory.from_hash( fixture[0] )
|
@@ -150,9 +150,9 @@ class TC_04_Attributes < Test::Unit::TestCase
|
|
150
150
|
[ { :type => 3 }, :type, 3 ],
|
151
151
|
[ { :size => 1000 }, :size, 1000, :permissions ],
|
152
152
|
[ { :owner => ENV['USER'] }, :owner, ENV['USER'] ],
|
153
|
-
[ { :group => '
|
153
|
+
[ { :group => 'wheel' }, :group, 'wheel' ],
|
154
154
|
[ { :uid => Etc.getpwnam(ENV['USER']).uid }, :owner, ENV['USER'] ],
|
155
|
-
[ { :gid => Etc.getgrnam('
|
155
|
+
[ { :gid => Etc.getgrnam('wheel').gid }, :group, 'wheel' ],
|
156
156
|
[ { :permissions => 0600 }, :permissions, 0600 ],
|
157
157
|
[ { :atime => 1 }, :atime, 1 ],
|
158
158
|
[ { :atime_nseconds => 2 }, :atime_nseconds, 2 ],
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.
|
2
|
+
rubygems_version: 0.8.10
|
3
3
|
specification_version: 1
|
4
4
|
name: net-sftp
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2005-
|
6
|
+
version: 1.0.0
|
7
|
+
date: 2005-06-17
|
8
8
|
summary: Net::SFTP is a pure-Ruby implementation of the SFTP client protocol.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
|
-
email:
|
11
|
+
email: jamis@jamisbuck.org
|
12
12
|
homepage: http://net-ssh.rubyforge.org/sftp
|
13
13
|
rubyforge_project:
|
14
14
|
description:
|
@@ -27,98 +27,106 @@ platform: ruby
|
|
27
27
|
authors:
|
28
28
|
- Jamis Buck
|
29
29
|
files:
|
30
|
-
- doc/
|
30
|
+
- doc/faq
|
31
31
|
- doc/LICENSE-BSD
|
32
32
|
- doc/LICENSE-GPL
|
33
|
+
- doc/LICENSE-RUBY
|
34
|
+
- doc/faq/faq.html
|
35
|
+
- doc/faq/faq.rb
|
36
|
+
- doc/faq/faq.yml
|
33
37
|
- lib/net
|
38
|
+
- lib/uri
|
34
39
|
- lib/net/sftp
|
35
40
|
- lib/net/sftp.rb
|
41
|
+
- lib/net/sftp/errors.rb
|
36
42
|
- lib/net/sftp/operations
|
37
|
-
- lib/net/sftp/session.rb
|
38
43
|
- lib/net/sftp/protocol
|
44
|
+
- lib/net/sftp/session.rb
|
39
45
|
- lib/net/sftp/version.rb
|
40
|
-
- lib/net/sftp/
|
46
|
+
- lib/net/sftp/operations/abstract.rb
|
41
47
|
- lib/net/sftp/operations/close.rb
|
42
|
-
- lib/net/sftp/operations/
|
43
|
-
- lib/net/sftp/operations/
|
44
|
-
- lib/net/sftp/operations/
|
45
|
-
- lib/net/sftp/operations/stat.rb
|
46
|
-
- lib/net/sftp/operations/readdir.rb
|
48
|
+
- lib/net/sftp/operations/errors.rb
|
49
|
+
- lib/net/sftp/operations/fsetstat.rb
|
50
|
+
- lib/net/sftp/operations/fstat.rb
|
47
51
|
- lib/net/sftp/operations/lstat.rb
|
48
|
-
- lib/net/sftp/operations/setstat.rb
|
49
|
-
- lib/net/sftp/operations/abstract.rb
|
50
52
|
- lib/net/sftp/operations/mkdir.rb
|
51
|
-
- lib/net/sftp/operations/services.rb
|
52
53
|
- lib/net/sftp/operations/open.rb
|
53
54
|
- lib/net/sftp/operations/opendir.rb
|
54
|
-
- lib/net/sftp/operations/
|
55
|
+
- lib/net/sftp/operations/read.rb
|
56
|
+
- lib/net/sftp/operations/readdir.rb
|
57
|
+
- lib/net/sftp/operations/realpath.rb
|
55
58
|
- lib/net/sftp/operations/remove.rb
|
56
|
-
- lib/net/sftp/operations/
|
59
|
+
- lib/net/sftp/operations/rename.rb
|
60
|
+
- lib/net/sftp/operations/rmdir.rb
|
61
|
+
- lib/net/sftp/operations/services.rb
|
62
|
+
- lib/net/sftp/operations/setstat.rb
|
63
|
+
- lib/net/sftp/operations/stat.rb
|
57
64
|
- lib/net/sftp/operations/write.rb
|
58
|
-
- lib/net/sftp/operations/fstat.rb
|
59
|
-
- lib/net/sftp/operations/errors.rb
|
60
65
|
- lib/net/sftp/protocol/01
|
61
66
|
- lib/net/sftp/protocol/02
|
62
67
|
- lib/net/sftp/protocol/03
|
63
68
|
- lib/net/sftp/protocol/04
|
64
69
|
- lib/net/sftp/protocol/05
|
65
|
-
- lib/net/sftp/protocol/driver.rb
|
66
70
|
- lib/net/sftp/protocol/constants.rb
|
71
|
+
- lib/net/sftp/protocol/driver.rb
|
67
72
|
- lib/net/sftp/protocol/packet-assistant.rb
|
68
73
|
- lib/net/sftp/protocol/services.rb
|
69
74
|
- lib/net/sftp/protocol/01/attributes.rb
|
75
|
+
- lib/net/sftp/protocol/01/impl.rb
|
70
76
|
- lib/net/sftp/protocol/01/packet-assistant.rb
|
71
77
|
- lib/net/sftp/protocol/01/services.rb
|
72
|
-
- lib/net/sftp/protocol/
|
78
|
+
- lib/net/sftp/protocol/02/impl.rb
|
73
79
|
- lib/net/sftp/protocol/02/packet-assistant.rb
|
74
80
|
- lib/net/sftp/protocol/02/services.rb
|
75
|
-
- lib/net/sftp/protocol/
|
81
|
+
- lib/net/sftp/protocol/03/impl.rb
|
76
82
|
- lib/net/sftp/protocol/03/packet-assistant.rb
|
77
83
|
- lib/net/sftp/protocol/03/services.rb
|
78
|
-
- lib/net/sftp/protocol/03/impl.rb
|
79
84
|
- lib/net/sftp/protocol/04/attributes.rb
|
85
|
+
- lib/net/sftp/protocol/04/impl.rb
|
80
86
|
- lib/net/sftp/protocol/04/packet-assistant.rb
|
81
87
|
- lib/net/sftp/protocol/04/services.rb
|
82
|
-
- lib/net/sftp/protocol/04/impl.rb
|
83
88
|
- lib/net/sftp/protocol/05/services.rb
|
89
|
+
- lib/uri/open-sftp.rb
|
90
|
+
- lib/uri/sftp.rb
|
91
|
+
- examples/asynchronous.rb
|
84
92
|
- examples/get-put.rb
|
93
|
+
- examples/sftp-open-uri.rb
|
85
94
|
- examples/ssh-service.rb
|
86
95
|
- examples/synchronous.rb
|
87
|
-
- examples/asynchronous.rb
|
88
|
-
- test/operations
|
89
96
|
- test/ALL-TESTS.rb
|
97
|
+
- test/operations
|
90
98
|
- test/protocol
|
91
|
-
- test/operations/tc_write.rb
|
92
|
-
- test/operations/tc_fstat.rb
|
93
|
-
- test/operations/tc_close.rb
|
94
99
|
- test/operations/tc_abstract.rb
|
95
|
-
- test/operations/
|
96
|
-
- test/operations/tc_rmdir.rb
|
97
|
-
- test/operations/tc_open.rb
|
98
|
-
- test/operations/tc_remove.rb
|
99
|
-
- test/operations/tc_read.rb
|
100
|
+
- test/operations/tc_close.rb
|
100
101
|
- test/operations/tc_fsetstat.rb
|
102
|
+
- test/operations/tc_fstat.rb
|
101
103
|
- test/operations/tc_lstat.rb
|
102
|
-
- test/operations/tc_readdir.rb
|
103
104
|
- test/operations/tc_mkdir.rb
|
105
|
+
- test/operations/tc_open.rb
|
106
|
+
- test/operations/tc_opendir.rb
|
107
|
+
- test/operations/tc_read.rb
|
108
|
+
- test/operations/tc_readdir.rb
|
104
109
|
- test/operations/tc_realpath.rb
|
105
|
-
- test/operations/
|
110
|
+
- test/operations/tc_remove.rb
|
111
|
+
- test/operations/tc_rmdir.rb
|
106
112
|
- test/operations/tc_setstat.rb
|
113
|
+
- test/operations/tc_stat.rb
|
114
|
+
- test/operations/tc_write.rb
|
107
115
|
- test/protocol/01
|
108
116
|
- test/protocol/02
|
109
117
|
- test/protocol/03
|
110
118
|
- test/protocol/04
|
111
119
|
- test/protocol/tc_driver.rb
|
112
|
-
- test/protocol/01/tc_packet_assistant.rb
|
113
120
|
- test/protocol/01/tc_attributes.rb
|
114
121
|
- test/protocol/01/tc_impl.rb
|
115
|
-
- test/protocol/
|
122
|
+
- test/protocol/01/tc_packet_assistant.rb
|
116
123
|
- test/protocol/02/tc_impl.rb
|
117
|
-
- test/protocol/
|
124
|
+
- test/protocol/02/tc_packet_assistant.rb
|
118
125
|
- test/protocol/03/tc_impl.rb
|
119
|
-
- test/protocol/
|
126
|
+
- test/protocol/03/tc_packet_assistant.rb
|
120
127
|
- test/protocol/04/tc_attributes.rb
|
121
128
|
- test/protocol/04/tc_impl.rb
|
129
|
+
- test/protocol/04/tc_packet_assistant.rb
|
122
130
|
test_files:
|
123
131
|
- test/ALL-TESTS.rb
|
124
132
|
rdoc_options: []
|