1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs.provider.sftp;
18
19 import com.jcraft.jsch.ChannelSftp;
20 import com.jcraft.jsch.ChannelSftp.LsEntry;
21 import com.jcraft.jsch.SftpATTRS;
22 import com.jcraft.jsch.SftpException;
23 import org.apache.commons.vfs.FileName;
24 import org.apache.commons.vfs.FileObject;
25 import org.apache.commons.vfs.FileSystemException;
26 import org.apache.commons.vfs.FileType;
27 import org.apache.commons.vfs.NameScope;
28 import org.apache.commons.vfs.RandomAccessContent;
29 import org.apache.commons.vfs.VFS;
30 import org.apache.commons.vfs.provider.AbstractFileObject;
31 import org.apache.commons.vfs.provider.UriParser;
32 import org.apache.commons.vfs.util.MonitorOutputStream;
33 import org.apache.commons.vfs.util.RandomAccessMode;
34 import org.apache.commons.vfs.util.FileObjectUtils;
35
36 import java.io.ByteArrayInputStream;
37 import java.io.ByteArrayOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.util.ArrayList;
42 import java.util.Iterator;
43 import java.util.Vector;
44
45 /***
46 * An SFTP file.
47 *
48 * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
49 * @version $Revision: 480428 $ $Date: 2005-10-14 19:59:47 +0200 (Fr, 14 Okt
50 * 2005) $
51 */
52 public class SftpFileObject extends AbstractFileObject implements FileObject
53 {
54 private final SftpFileSystem fileSystem;
55 private SftpATTRS attrs;
56 private final String relPath;
57
58 protected SftpFileObject(final FileName name,
59 final SftpFileSystem fileSystem) throws FileSystemException
60 {
61 super(name, fileSystem);
62 this.fileSystem = fileSystem;
63 relPath = UriParser.decode(fileSystem.getRootName().getRelativeName(
64 name));
65 }
66
67 /***
68 * Determines the type of this file, returns null if the file does not
69 * exist.
70 */
71 protected FileType doGetType() throws Exception
72 {
73 if (attrs == null)
74 {
75 statSelf();
76 }
77
78 if (attrs == null)
79 {
80 return FileType.IMAGINARY;
81 }
82
83 if ((attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS) == 0)
84 {
85 throw new FileSystemException(
86 "vfs.provider.sftp/unknown-permissions.error");
87 }
88 if (attrs.isDir())
89 {
90 return FileType.FOLDER;
91 }
92 else
93 {
94 return FileType.FILE;
95 }
96 }
97
98 /***
99 * Called when the type or content of this file changes.
100 */
101 protected void onChange() throws Exception
102 {
103 statSelf();
104 }
105
106 /***
107 * Fetches file attrs from server.
108 */
109 private void statSelf() throws Exception
110 {
111 ChannelSftp channel = fileSystem.getChannel();
112 try
113 {
114 setStat(channel.stat(relPath));
115 }
116 catch (final SftpException e)
117 {
118 try
119 {
120
121 if (e.id != ChannelSftp.SSH_FX_NO_SUCH_FILE)
122 {
123 channel.disconnect();
124 channel = fileSystem.getChannel();
125 setStat(channel.stat(relPath));
126 }
127 else
128 {
129
130 attrs = null;
131 }
132 }
133 catch (final SftpException e2)
134 {
135
136
137
138
139
140
141
142
143 attrs = null;
144 }
145 }
146 finally
147 {
148 fileSystem.putChannel(channel);
149 }
150 }
151
152 /***
153 * Set attrs from listChildrenResolved
154 */
155 private void setStat(SftpATTRS attrs)
156 {
157 this.attrs = attrs;
158 }
159
160 /***
161 * Creates this file as a folder.
162 */
163 protected void doCreateFolder() throws Exception
164 {
165 final ChannelSftp channel = fileSystem.getChannel();
166 try
167 {
168 channel.mkdir(relPath);
169 }
170 finally
171 {
172 fileSystem.putChannel(channel);
173 }
174 }
175
176 protected long doGetLastModifiedTime() throws Exception
177 {
178 if (attrs == null
179 || (attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_ACMODTIME) == 0)
180 {
181 throw new FileSystemException(
182 "vfs.provider.sftp/unknown-modtime.error");
183 }
184 return attrs.getMTime() * 1000L;
185 }
186
187 /***
188 * Sets the last modified time of this file. Is only called if
189 * {@link #doGetType} does not return {@link FileType#IMAGINARY}. <p/>
190 *
191 * @param modtime
192 * is modification time in milliseconds. SFTP protocol can send
193 * times with nanosecond precision but at the moment jsch send
194 * them with second precision.
195 */
196 protected void doSetLastModifiedTime(final long modtime) throws Exception
197 {
198 final ChannelSftp channel = fileSystem.getChannel();
199 try
200 {
201 int newMTime = (int) (modtime / 1000L);
202
203 attrs.setACMODTIME(attrs.getATime(), newMTime);
204 channel.setStat(relPath, attrs);
205 }
206 finally
207 {
208 fileSystem.putChannel(channel);
209 }
210 }
211
212 /***
213 * Deletes the file.
214 */
215 protected void doDelete() throws Exception
216 {
217 final ChannelSftp channel = fileSystem.getChannel();
218 try
219 {
220 if (getType() == FileType.FILE)
221 {
222 channel.rm(relPath);
223 }
224 else
225 {
226 channel.rmdir(relPath);
227 }
228 }
229 finally
230 {
231 fileSystem.putChannel(channel);
232 }
233 }
234
235 /***
236 * Rename the file.
237 */
238 protected void doRename(FileObject newfile) throws Exception
239 {
240 final ChannelSftp channel = fileSystem.getChannel();
241 try
242 {
243 channel.rename(relPath, ((SftpFileObject) newfile).relPath);
244 }
245 finally
246 {
247 fileSystem.putChannel(channel);
248 }
249 }
250
251 /***
252 * Lists the children of this file.
253 */
254 protected FileObject[] doListChildrenResolved() throws Exception
255 {
256
257 final Vector vector;
258 final ChannelSftp channel = fileSystem.getChannel();
259 try
260 {
261 vector = channel.ls(relPath);
262 }
263 finally
264 {
265 fileSystem.putChannel(channel);
266 }
267 if (vector == null)
268 {
269 throw new FileSystemException(
270 "vfs.provider.sftp/list-children.error");
271 }
272
273
274 final ArrayList children = new ArrayList();
275 for (Iterator iterator = vector.iterator(); iterator.hasNext();)
276 {
277 final LsEntry stat = (LsEntry) iterator.next();
278
279 String name = stat.getFilename();
280 if (VFS.isUriStyle())
281 {
282 if (stat.getAttrs().isDir()
283 && name.charAt(name.length() - 1) != '/')
284 {
285 name = name + "/";
286 }
287 }
288
289 if (name.equals(".") || name.equals("..") || name.equals("./")
290 || name.equals("../"))
291 {
292 continue;
293 }
294
295 FileObject fo =
296 getFileSystem()
297 .resolveFile(
298 getFileSystem().getFileSystemManager().resolveName(
299 getName(), UriParser.encode(name),
300 NameScope.CHILD));
301
302 ((SftpFileObject) FileObjectUtils.getAbstractFileObject(fo)).setStat(stat.getAttrs());
303
304 children.add(fo);
305 }
306
307 return (FileObject[]) children.toArray(new FileObject[children
308 .size()]);
309 }
310
311 /***
312 * Lists the children of this file.
313 */
314 protected String[] doListChildren() throws Exception
315 {
316
317 return null;
318 }
319
320 /***
321 * Returns the size of the file content (in bytes).
322 */
323 protected long doGetContentSize() throws Exception
324 {
325 if (attrs == null
326 || (attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_SIZE) == 0)
327 {
328 throw new FileSystemException(
329 "vfs.provider.sftp/unknown-size.error");
330 }
331 return attrs.getSize();
332 }
333
334 protected RandomAccessContent doGetRandomAccessContent(
335 final RandomAccessMode mode) throws Exception
336 {
337 return new SftpRandomAccessContent(this, mode);
338 }
339
340 /***
341 * Creates an input stream to read the file content from.
342 */
343 InputStream getInputStream(long filePointer) throws IOException
344 {
345 final ChannelSftp channel = fileSystem.getChannel();
346 try
347 {
348
349
350
351 ByteArrayOutputStream outstr = new ByteArrayOutputStream();
352 try
353 {
354 channel.get(getName().getPathDecoded(), outstr, null,
355 ChannelSftp.RESUME, filePointer);
356 }
357 catch (SftpException e)
358 {
359 throw new FileSystemException(e);
360 }
361 outstr.close();
362 return new ByteArrayInputStream(outstr.toByteArray());
363 }
364 finally
365 {
366 fileSystem.putChannel(channel);
367 }
368 }
369
370 /***
371 * Creates an input stream to read the file content from.
372 */
373 protected InputStream doGetInputStream() throws Exception
374 {
375 final ChannelSftp channel = fileSystem.getChannel();
376 try
377 {
378
379
380
381
382
383 final ByteArrayOutputStream outstr = new ByteArrayOutputStream();
384 channel.get(relPath, outstr);
385 outstr.close();
386 return new ByteArrayInputStream(outstr.toByteArray());
387
388 }
389 finally
390 {
391 fileSystem.putChannel(channel);
392 }
393 }
394
395 /***
396 * Creates an output stream to write the file content to.
397 */
398 protected OutputStream doGetOutputStream(boolean bAppend) throws Exception
399 {
400
401
402 final ChannelSftp channel = fileSystem.getChannel();
403 return new SftpOutputStream(channel);
404 }
405
406 /***
407 * An OutputStream that wraps an sftp OutputStream, and closes the channel
408 * when the stream is closed.
409 */
410 private class SftpOutputStream extends MonitorOutputStream
411 {
412 private final ChannelSftp channel;
413
414 public SftpOutputStream(final ChannelSftp channel)
415 {
416 super(new ByteArrayOutputStream());
417 this.channel = channel;
418 }
419
420 /***
421 * Called after this stream is closed.
422 */
423 protected void onClose() throws IOException
424 {
425 try
426 {
427 final ByteArrayOutputStream outstr = (ByteArrayOutputStream) out;
428 channel.put(new ByteArrayInputStream(outstr.toByteArray()),
429 relPath);
430 }
431 catch (final SftpException e)
432 {
433 throw new FileSystemException(e);
434 }
435 finally
436 {
437 fileSystem.putChannel(channel);
438 }
439 }
440 }
441 }