net-sftp 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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&#8230;
65
+ <ul>
66
+ <li>...connect to an <span class="caps">SFTP</span> server?
67
+ <ul>
68
+ <li><a href='#2203252'>I&#8217;d like to connect without first getting a Net::SSH connection&#8230;</a></li>
69
+ <li><a href='#2203212'>I already have an open Net::SSH connection&#8230;</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&#8230;</a></li>
75
+ <li><a href='#2203092'>I want to upload bytes from a string or other object&#8230;</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&#8230;</a></li>
81
+ <li><a href='#2202972'>I want to download to a string in memory&#8230;</a></li>
82
+ <li><a href='#2202932'>I want to be notified of the progress of the download&#8230;</a></li>
83
+ </ul>
84
+ </li>
85
+ <li>...manage file permissions?
86
+ <ul>
87
+ <li><a href='#2202862'>I want to query a file&#8217;s permissions&#8230;</a></li>
88
+ <li><a href='#2202822'>I want to change a file&#8217;s permissions&#8230;</a></li>
89
+ <li><a href='#2202782'>I already have an open handle for the remove file&#8230;</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&#8230;</a></li>
95
+ <li><a href='#2202652'>I want to create a directory&#8230;</a></li>
96
+ <li><a href='#2202612'>I want to remove a directory&#8230;</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&#8217;s
108
+ &#8220;SFTP&#8221; as in &#8220;Secure File Transfer Protocol&#8221;, as defined as an adjuct to the
109
+ <span class="caps">SSH</span> specification. <em>Not</em> &#8220;SFTP&#8221; as in &#8220;Secure <span class="caps">FTP</span>&#8221; (a <em>completely</em> different
110
+ beast). Nor is it an implementation of the &#8220;Simple File Transfer Protocol&#8221;
111
+ (which is in no way secure).</p></div>
112
+ <a name='2203252'></a>
113
+ <div class='faq-title'>How do I&#8230; ...connect to an <span class="caps">SFTP</span> server? I&#8217;d like to connect without first getting a Net::SSH connection&#8230;</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&#8217;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&#8230; ...connect to an <span class="caps">SFTP</span> server? I already have an open Net::SSH connection&#8230;</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&#8217;ve already got an <span class="caps">SSH</span>
131
+ connection that you&#8217;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&#8230; ...upload data? I want to upload an entire file on disk&#8230;</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&#8230; ...upload data? I want to upload bytes from a string or other object&#8230;</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&#8217;d rather not use blocks, you can do
168
+ without, but be sure to call <code>close_handle</code> when you&#8217;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&#8230; ...download data? I want to download directly to a local file&#8230;</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&#8230; ...download data? I want to download to a string in memory&#8230;</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&#8230; ...download data? I want to be notified of the progress of the download&#8230;</div>
198
+ <div class='faq-answer'><p>You can specify both a &#8220;chunk size&#8221; and a &#8220;progress callback&#8221;. The
199
+ callback will be invoked for every &#8220;chunk size&#8221; 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 =&gt; 4096,
208
+ :progress_callback =&gt; 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&#8230; ...manage file permissions? I want to query a file&#8217;s permissions&#8230;</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&#8230; ...manage file permissions? I want to change a file&#8217;s permissions&#8230;</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 =&gt; 0644)
231
+ </pre></code></div>
232
+ <a name='2202782'></a>
233
+ <div class='faq-title'>How do I&#8230; ...manage file permissions? I already have an open handle for the remove file&#8230;</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 =&gt; permissions | 0444)
242
+ end
243
+ </pre></code></div>
244
+ <a name='2202692'></a>
245
+ <div class='faq-title'>How do I&#8230; ...manage directories? I want to query the contents of a directory&#8230;</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&#8217;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&#8230; ...manage directories? I want to create a directory&#8230;</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 =&gt; 0500)
269
+ </pre></code></div>
270
+ <a name='2202612'></a>
271
+ <div class='faq-title'>How do I&#8230; ...manage directories? I want to remove a directory&#8230;</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&#8230; ...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&#8230; ...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&#8217;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>
@@ -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>"
@@ -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
@@ -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
 
@@ -17,7 +17,7 @@
17
17
  module Net ; module SFTP
18
18
 
19
19
  # The base exception class for the SFTP system.
20
- class Exception < ::Exception; end
20
+ class Exception < RuntimeError; end
21
21
 
22
22
  # An exception class representing a bug condition.
23
23
  class Bug < Exception; end
@@ -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 file.
28
- # (Setting this to a larger value may cause problems, especially with
29
- # very large files, so beware!)
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, offset=0, length=-1 )
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 < 0 ? CHUNK_SIZE : length )
42
- @driver.read( nil, handle, offset, real_length )
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 = CHUNK_SIZE
59
+ length = @chunk_size
56
60
  else
57
61
  length = @length - @data.length
58
- length = length > CHUNK_SIZE ? CHUNK_SIZE : 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?
@@ -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, "w" ) { |f| f.write contents }
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.read( local_path )
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
 
@@ -16,8 +16,8 @@
16
16
 
17
17
  module Net ; module SFTP ; module Version
18
18
 
19
- MAJOR = 0
20
- MINOR = 9
19
+ MAJOR = 1
20
+ MINOR = 0
21
21
  TINY = 0
22
22
 
23
23
  STRING = [MAJOR,MINOR,TINY].join('.')
@@ -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
@@ -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
@@ -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 => 'root' }, :gid, Etc.getpwnam('root').uid ]
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 => 'root' }, :group, 'root' ],
153
+ [ { :group => 'wheel' }, :group, 'wheel' ],
154
154
  [ { :uid => Etc.getpwnam(ENV['USER']).uid }, :owner, ENV['USER'] ],
155
- [ { :gid => Etc.getgrnam('root').gid }, :group, 'root' ],
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.4
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.9.0
7
- date: 2005-01-11
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: jgb3@email.byu.edu
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/LICENSE-RUBY
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/errors.rb
46
+ - lib/net/sftp/operations/abstract.rb
41
47
  - lib/net/sftp/operations/close.rb
42
- - lib/net/sftp/operations/read.rb
43
- - lib/net/sftp/operations/rmdir.rb
44
- - lib/net/sftp/operations/realpath.rb
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/rename.rb
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/fsetstat.rb
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/01/impl.rb
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/02/impl.rb
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/tc_opendir.rb
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/tc_stat.rb
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/02/tc_packet_assistant.rb
122
+ - test/protocol/01/tc_packet_assistant.rb
116
123
  - test/protocol/02/tc_impl.rb
117
- - test/protocol/03/tc_packet_assistant.rb
124
+ - test/protocol/02/tc_packet_assistant.rb
118
125
  - test/protocol/03/tc_impl.rb
119
- - test/protocol/04/tc_packet_assistant.rb
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: []