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.
@@ -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: []