]>
Commit | Line | Data |
---|---|---|
1 | /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ | |
2 | /* | |
3 | Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved. | |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions are met: | |
7 | ||
8 | 1. Redistributions of source code must retain the above copyright notice, | |
9 | this list of conditions and the following disclaimer. | |
10 | ||
11 | 2. Redistributions in binary form must reproduce the above copyright | |
12 | notice, this list of conditions and the following disclaimer in | |
13 | the documentation and/or other materials provided with the distribution. | |
14 | ||
15 | 3. The names of the authors may not be used to endorse or promote products | |
16 | derived from this software without specific prior written permission. | |
17 | ||
18 | THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, | |
19 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
20 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, | |
21 | INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, | |
22 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, | |
24 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
27 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | ||
30 | package com.jcraft.jsch; | |
31 | ||
32 | import java.io.*; | |
33 | ||
34 | import java.util.Vector; | |
35 | ||
36 | public class ChannelSftp extends ChannelSession{ | |
37 | ||
38 | private static final byte SSH_FXP_INIT= 1; | |
39 | private static final byte SSH_FXP_VERSION= 2; | |
40 | private static final byte SSH_FXP_OPEN= 3; | |
41 | private static final byte SSH_FXP_CLOSE= 4; | |
42 | private static final byte SSH_FXP_READ= 5; | |
43 | private static final byte SSH_FXP_WRITE= 6; | |
44 | private static final byte SSH_FXP_LSTAT= 7; | |
45 | private static final byte SSH_FXP_FSTAT= 8; | |
46 | private static final byte SSH_FXP_SETSTAT= 9; | |
47 | private static final byte SSH_FXP_FSETSTAT= 10; | |
48 | private static final byte SSH_FXP_OPENDIR= 11; | |
49 | private static final byte SSH_FXP_READDIR= 12; | |
50 | private static final byte SSH_FXP_REMOVE= 13; | |
51 | private static final byte SSH_FXP_MKDIR= 14; | |
52 | private static final byte SSH_FXP_RMDIR= 15; | |
53 | private static final byte SSH_FXP_REALPATH= 16; | |
54 | private static final byte SSH_FXP_STAT= 17; | |
55 | private static final byte SSH_FXP_RENAME= 18; | |
56 | private static final byte SSH_FXP_READLINK= 19; | |
57 | private static final byte SSH_FXP_SYMLINK= 20; | |
58 | private static final byte SSH_FXP_STATUS= 101; | |
59 | private static final byte SSH_FXP_HANDLE= 102; | |
60 | private static final byte SSH_FXP_DATA= 103; | |
61 | private static final byte SSH_FXP_NAME= 104; | |
62 | private static final byte SSH_FXP_ATTRS= 105; | |
63 | private static final byte SSH_FXP_EXTENDED= (byte)200; | |
64 | private static final byte SSH_FXP_EXTENDED_REPLY= (byte)201; | |
65 | ||
66 | // pflags | |
67 | private static final int SSH_FXF_READ= 0x00000001; | |
68 | private static final int SSH_FXF_WRITE= 0x00000002; | |
69 | private static final int SSH_FXF_APPEND= 0x00000004; | |
70 | private static final int SSH_FXF_CREAT= 0x00000008; | |
71 | private static final int SSH_FXF_TRUNC= 0x00000010; | |
72 | private static final int SSH_FXF_EXCL= 0x00000020; | |
73 | ||
74 | private static final int SSH_FILEXFER_ATTR_SIZE= 0x00000001; | |
75 | private static final int SSH_FILEXFER_ATTR_UIDGID= 0x00000002; | |
76 | private static final int SSH_FILEXFER_ATTR_PERMISSIONS= 0x00000004; | |
77 | private static final int SSH_FILEXFER_ATTR_ACMODTIME= 0x00000008; | |
78 | private static final int SSH_FILEXFER_ATTR_EXTENDED= 0x80000000; | |
79 | ||
80 | public static final int SSH_FX_OK= 0; | |
81 | public static final int SSH_FX_EOF= 1; | |
82 | public static final int SSH_FX_NO_SUCH_FILE= 2; | |
83 | public static final int SSH_FX_PERMISSION_DENIED= 3; | |
84 | public static final int SSH_FX_FAILURE= 4; | |
85 | public static final int SSH_FX_BAD_MESSAGE= 5; | |
86 | public static final int SSH_FX_NO_CONNECTION= 6; | |
87 | public static final int SSH_FX_CONNECTION_LOST= 7; | |
88 | public static final int SSH_FX_OP_UNSUPPORTED= 8; | |
89 | /* | |
90 | SSH_FX_OK | |
91 | Indicates successful completion of the operation. | |
92 | SSH_FX_EOF | |
93 | indicates end-of-file condition; for SSH_FX_READ it means that no | |
94 | more data is available in the file, and for SSH_FX_READDIR it | |
95 | indicates that no more files are contained in the directory. | |
96 | SSH_FX_NO_SUCH_FILE | |
97 | is returned when a reference is made to a file which should exist | |
98 | but doesn't. | |
99 | SSH_FX_PERMISSION_DENIED | |
100 | is returned when the authenticated user does not have sufficient | |
101 | permissions to perform the operation. | |
102 | SSH_FX_FAILURE | |
103 | is a generic catch-all error message; it should be returned if an | |
104 | error occurs for which there is no more specific error code | |
105 | defined. | |
106 | SSH_FX_BAD_MESSAGE | |
107 | may be returned if a badly formatted packet or protocol | |
108 | incompatibility is detected. | |
109 | SSH_FX_NO_CONNECTION | |
110 | is a pseudo-error which indicates that the client has no | |
111 | connection to the server (it can only be generated locally by the | |
112 | client, and MUST NOT be returned by servers). | |
113 | SSH_FX_CONNECTION_LOST | |
114 | is a pseudo-error which indicates that the connection to the | |
115 | server has been lost (it can only be generated locally by the | |
116 | client, and MUST NOT be returned by servers). | |
117 | SSH_FX_OP_UNSUPPORTED | |
118 | indicates that an attempt was made to perform an operation which | |
119 | is not supported for the server (it may be generated locally by | |
120 | the client if e.g. the version number exchange indicates that a | |
121 | required feature is not supported by the server, or it may be | |
122 | returned by the server if the server does not implement an | |
123 | operation). | |
124 | */ | |
125 | private static final int MAX_MSG_LENGTH = 256* 1024; | |
126 | ||
127 | public static final int OVERWRITE=0; | |
128 | public static final int RESUME=1; | |
129 | public static final int APPEND=2; | |
130 | ||
131 | private boolean interactive=false; | |
132 | private int seq=1; | |
133 | private int[] ackid=new int[1]; | |
134 | private Buffer buf; | |
135 | private Packet packet=new Packet(buf); | |
136 | ||
137 | private int client_version=3; | |
138 | private int server_version=3; | |
139 | private String version=String.valueOf(client_version); | |
140 | ||
141 | private java.util.Hashtable extensions=null; | |
142 | private InputStream io_in=null; | |
143 | ||
144 | /* | |
145 | 10. Changes from previous protocol versions | |
146 | The SSH File Transfer Protocol has changed over time, before it's | |
147 | standardization. The following is a description of the incompatible | |
148 | changes between different versions. | |
149 | 10.1 Changes between versions 3 and 2 | |
150 | o The SSH_FXP_READLINK and SSH_FXP_SYMLINK messages were added. | |
151 | o The SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY messages were added. | |
152 | o The SSH_FXP_STATUS message was changed to include fields `error | |
153 | message' and `language tag'. | |
154 | 10.2 Changes between versions 2 and 1 | |
155 | o The SSH_FXP_RENAME message was added. | |
156 | 10.3 Changes between versions 1 and 0 | |
157 | o Implementation changes, no actual protocol changes. | |
158 | */ | |
159 | ||
160 | private static final String file_separator=java.io.File.separator; | |
161 | private static final char file_separatorc=java.io.File.separatorChar; | |
162 | private static boolean fs_is_bs=(byte)java.io.File.separatorChar == '\\'; | |
163 | ||
164 | private String cwd; | |
165 | private String home; | |
166 | private String lcwd; | |
167 | ||
168 | private static final String UTF8="UTF-8"; | |
169 | private String fEncoding=UTF8; | |
170 | private boolean fEncoding_is_utf8=true; | |
171 | ||
172 | void init(){ | |
173 | } | |
174 | ||
175 | public void start() throws JSchException{ | |
176 | try{ | |
177 | ||
178 | PipedOutputStream pos=new PipedOutputStream(); | |
179 | io.setOutputStream(pos); | |
180 | PipedInputStream pis=new MyPipedInputStream(pos, 32*1024); | |
181 | io.setInputStream(pis); | |
182 | ||
183 | io_in=io.in; | |
184 | ||
185 | if(io_in==null){ | |
186 | throw new JSchException("channel is down"); | |
187 | } | |
188 | ||
189 | Request request=new RequestSftp(); | |
190 | request.request(getSession(), this); | |
191 | ||
192 | /* | |
193 | System.err.println("lmpsize: "+lmpsize); | |
194 | System.err.println("lwsize: "+lwsize); | |
195 | System.err.println("rmpsize: "+rmpsize); | |
196 | System.err.println("rwsize: "+rwsize); | |
197 | */ | |
198 | ||
199 | buf=new Buffer(rmpsize); | |
200 | packet=new Packet(buf); | |
201 | int i=0; | |
202 | int length; | |
203 | int type; | |
204 | byte[] str; | |
205 | ||
206 | // send SSH_FXP_INIT | |
207 | sendINIT(); | |
208 | ||
209 | // receive SSH_FXP_VERSION | |
210 | Header header=new Header(); | |
211 | header=header(buf, header); | |
212 | length=header.length; | |
213 | if(length > MAX_MSG_LENGTH){ | |
214 | throw new SftpException(SSH_FX_FAILURE, | |
215 | "Received message is too long: " + length); | |
216 | } | |
217 | type=header.type; // 2 -> SSH_FXP_VERSION | |
218 | server_version=header.rid; | |
219 | //System.err.println("SFTP protocol server-version="+server_version); | |
220 | if(length>0){ | |
221 | extensions=new java.util.Hashtable(); | |
222 | // extension data | |
223 | fill(buf, length); | |
224 | byte[] extension_name=null; | |
225 | byte[] extension_data=null; | |
226 | while(length>0){ | |
227 | extension_name=buf.getString(); | |
228 | length-=(4+extension_name.length); | |
229 | extension_data=buf.getString(); | |
230 | length-=(4+extension_data.length); | |
231 | extensions.put(Util.byte2str(extension_name), | |
232 | Util.byte2str(extension_data)); | |
233 | } | |
234 | } | |
235 | ||
236 | lcwd=new File(".").getCanonicalPath(); | |
237 | } | |
238 | catch(Exception e){ | |
239 | //System.err.println(e); | |
240 | if(e instanceof JSchException) throw (JSchException)e; | |
241 | if(e instanceof Throwable) | |
242 | throw new JSchException(e.toString(), (Throwable)e); | |
243 | throw new JSchException(e.toString()); | |
244 | } | |
245 | } | |
246 | ||
247 | public void quit(){ disconnect();} | |
248 | public void exit(){ disconnect();} | |
249 | public void lcd(String path) throws SftpException{ | |
250 | path=localAbsolutePath(path); | |
251 | if((new File(path)).isDirectory()){ | |
252 | try{ | |
253 | path=(new File(path)).getCanonicalPath(); | |
254 | } | |
255 | catch(Exception e){} | |
256 | lcwd=path; | |
257 | return; | |
258 | } | |
259 | throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such directory"); | |
260 | } | |
261 | ||
262 | public void cd(String path) throws SftpException{ | |
263 | try{ | |
264 | path=remoteAbsolutePath(path); | |
265 | ||
266 | path=isUnique(path); | |
267 | ||
268 | byte[] str=_realpath(path); | |
269 | SftpATTRS attr=_stat(str); | |
270 | ||
271 | if((attr.getFlags()&SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS)==0){ | |
272 | throw new SftpException(SSH_FX_FAILURE, | |
273 | "Can't change directory: "+path); | |
274 | } | |
275 | if(!attr.isDir()){ | |
276 | throw new SftpException(SSH_FX_FAILURE, | |
277 | "Can't change directory: "+path); | |
278 | } | |
279 | ||
280 | setCwd(Util.byte2str(str, fEncoding)); | |
281 | } | |
282 | catch(Exception e){ | |
283 | if(e instanceof SftpException) throw (SftpException)e; | |
284 | if(e instanceof Throwable) | |
285 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
286 | throw new SftpException(SSH_FX_FAILURE, ""); | |
287 | } | |
288 | } | |
289 | ||
290 | public void put(String src, String dst) throws SftpException{ | |
291 | put(src, dst, null, OVERWRITE); | |
292 | } | |
293 | public void put(String src, String dst, int mode) throws SftpException{ | |
294 | put(src, dst, null, mode); | |
295 | } | |
296 | public void put(String src, String dst, | |
297 | SftpProgressMonitor monitor) throws SftpException{ | |
298 | put(src, dst, monitor, OVERWRITE); | |
299 | } | |
300 | public void put(String src, String dst, | |
301 | SftpProgressMonitor monitor, int mode) throws SftpException{ | |
302 | src=localAbsolutePath(src); | |
303 | dst=remoteAbsolutePath(dst); | |
304 | ||
305 | try{ | |
306 | ||
307 | Vector v=glob_remote(dst); | |
308 | int vsize=v.size(); | |
309 | if(vsize!=1){ | |
310 | if(vsize==0){ | |
311 | if(isPattern(dst)) | |
312 | throw new SftpException(SSH_FX_FAILURE, dst); | |
313 | else | |
314 | dst=Util.unquote(dst); | |
315 | } | |
316 | throw new SftpException(SSH_FX_FAILURE, v.toString()); | |
317 | } | |
318 | else{ | |
319 | dst=(String)(v.elementAt(0)); | |
320 | } | |
321 | ||
322 | boolean isRemoteDir=isRemoteDir(dst); | |
323 | ||
324 | v=glob_local(src); | |
325 | vsize=v.size(); | |
326 | ||
327 | StringBuffer dstsb=null; | |
328 | if(isRemoteDir){ | |
329 | if(!dst.endsWith("/")){ | |
330 | dst+="/"; | |
331 | } | |
332 | dstsb=new StringBuffer(dst); | |
333 | } | |
334 | else if(vsize>1){ | |
335 | throw new SftpException(SSH_FX_FAILURE, | |
336 | "Copying multiple files, but the destination is missing or a file."); | |
337 | } | |
338 | ||
339 | for(int j=0; j<vsize; j++){ | |
340 | String _src=(String)(v.elementAt(j)); | |
341 | String _dst=null; | |
342 | if(isRemoteDir){ | |
343 | int i=_src.lastIndexOf(file_separatorc); | |
344 | if(fs_is_bs){ | |
345 | int ii=_src.lastIndexOf('/'); | |
346 | if(ii!=-1 && ii>i) | |
347 | i=ii; | |
348 | } | |
349 | if(i==-1) dstsb.append(_src); | |
350 | else dstsb.append(_src.substring(i + 1)); | |
351 | _dst=dstsb.toString(); | |
352 | dstsb.delete(dst.length(), _dst.length()); | |
353 | } | |
354 | else{ | |
355 | _dst=dst; | |
356 | } | |
357 | //System.err.println("_dst "+_dst); | |
358 | ||
359 | long size_of_dst=0; | |
360 | if(mode==RESUME){ | |
361 | try{ | |
362 | SftpATTRS attr=_stat(_dst); | |
363 | size_of_dst=attr.getSize(); | |
364 | } | |
365 | catch(Exception eee){ | |
366 | //System.err.println(eee); | |
367 | } | |
368 | long size_of_src=new File(_src).length(); | |
369 | if(size_of_src<size_of_dst){ | |
370 | throw new SftpException(SSH_FX_FAILURE, | |
371 | "failed to resume for "+_dst); | |
372 | } | |
373 | if(size_of_src==size_of_dst){ | |
374 | return; | |
375 | } | |
376 | } | |
377 | ||
378 | if(monitor!=null){ | |
379 | monitor.init(SftpProgressMonitor.PUT, _src, _dst, | |
380 | (new File(_src)).length()); | |
381 | if(mode==RESUME){ | |
382 | monitor.count(size_of_dst); | |
383 | } | |
384 | } | |
385 | FileInputStream fis=null; | |
386 | try{ | |
387 | fis=new FileInputStream(_src); | |
388 | _put(fis, _dst, monitor, mode); | |
389 | } | |
390 | finally{ | |
391 | if(fis!=null) { | |
392 | fis.close(); | |
393 | } | |
394 | } | |
395 | } | |
396 | } | |
397 | catch(Exception e){ | |
398 | if(e instanceof SftpException) throw (SftpException)e; | |
399 | if(e instanceof Throwable) | |
400 | throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e); | |
401 | throw new SftpException(SSH_FX_FAILURE, e.toString()); | |
402 | } | |
403 | } | |
404 | public void put(InputStream src, String dst) throws SftpException{ | |
405 | put(src, dst, null, OVERWRITE); | |
406 | } | |
407 | public void put(InputStream src, String dst, int mode) throws SftpException{ | |
408 | put(src, dst, null, mode); | |
409 | } | |
410 | public void put(InputStream src, String dst, | |
411 | SftpProgressMonitor monitor) throws SftpException{ | |
412 | put(src, dst, monitor, OVERWRITE); | |
413 | } | |
414 | public void put(InputStream src, String dst, | |
415 | SftpProgressMonitor monitor, int mode) throws SftpException{ | |
416 | try{ | |
417 | dst=remoteAbsolutePath(dst); | |
418 | ||
419 | Vector v=glob_remote(dst); | |
420 | int vsize=v.size(); | |
421 | if(vsize!=1){ | |
422 | if(vsize==0){ | |
423 | if(isPattern(dst)) | |
424 | throw new SftpException(SSH_FX_FAILURE, dst); | |
425 | else | |
426 | dst=Util.unquote(dst); | |
427 | } | |
428 | throw new SftpException(SSH_FX_FAILURE, v.toString()); | |
429 | } | |
430 | else{ | |
431 | dst=(String)(v.elementAt(0)); | |
432 | } | |
433 | ||
434 | if(isRemoteDir(dst)){ | |
435 | throw new SftpException(SSH_FX_FAILURE, dst+" is a directory"); | |
436 | } | |
437 | ||
438 | _put(src, dst, monitor, mode); | |
439 | } | |
440 | catch(Exception e){ | |
441 | if(e instanceof SftpException) throw (SftpException)e; | |
442 | if(e instanceof Throwable) | |
443 | throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e); | |
444 | throw new SftpException(SSH_FX_FAILURE, e.toString()); | |
445 | } | |
446 | } | |
447 | ||
448 | public void _put(InputStream src, String dst, | |
449 | SftpProgressMonitor monitor, int mode) throws SftpException{ | |
450 | try{ | |
451 | byte[] dstb=Util.str2byte(dst, fEncoding); | |
452 | long skip=0; | |
453 | if(mode==RESUME || mode==APPEND){ | |
454 | try{ | |
455 | SftpATTRS attr=_stat(dstb); | |
456 | skip=attr.getSize(); | |
457 | } | |
458 | catch(Exception eee){ | |
459 | //System.err.println(eee); | |
460 | } | |
461 | } | |
462 | if(mode==RESUME && skip>0){ | |
463 | long skipped=src.skip(skip); | |
464 | if(skipped<skip){ | |
465 | throw new SftpException(SSH_FX_FAILURE, "failed to resume for "+dst); | |
466 | } | |
467 | } | |
468 | ||
469 | if(mode==OVERWRITE){ sendOPENW(dstb); } | |
470 | else{ sendOPENA(dstb); } | |
471 | ||
472 | Header header=new Header(); | |
473 | header=header(buf, header); | |
474 | int length=header.length; | |
475 | int type=header.type; | |
476 | ||
477 | fill(buf, length); | |
478 | ||
479 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ | |
480 | throw new SftpException(SSH_FX_FAILURE, "invalid type="+type); | |
481 | } | |
482 | if(type==SSH_FXP_STATUS){ | |
483 | int i=buf.getInt(); | |
484 | throwStatusError(buf, i); | |
485 | } | |
486 | byte[] handle=buf.getString(); // handle | |
487 | byte[] data=null; | |
488 | ||
489 | boolean dontcopy=true; | |
490 | ||
491 | if(!dontcopy){ | |
492 | data=new byte[buf.buffer.length | |
493 | -(5+13+21+handle.length | |
494 | +32 +20 // padding and mac | |
495 | ) | |
496 | ]; | |
497 | } | |
498 | ||
499 | long offset=0; | |
500 | if(mode==RESUME || mode==APPEND){ | |
501 | offset+=skip; | |
502 | } | |
503 | ||
504 | int startid=seq; | |
505 | int _ackid=seq; | |
506 | int ackcount=0; | |
507 | while(true){ | |
508 | int nread=0; | |
509 | int s=0; | |
510 | int datalen=0; | |
511 | int count=0; | |
512 | ||
513 | if(!dontcopy){ | |
514 | datalen=data.length-s; | |
515 | } | |
516 | else{ | |
517 | data=buf.buffer; | |
518 | s=5+13+21+handle.length; | |
519 | datalen=buf.buffer.length -s | |
520 | -32 -20; // padding and mac | |
521 | } | |
522 | ||
523 | do{ | |
524 | nread=src.read(data, s, datalen); | |
525 | if(nread>0){ | |
526 | s+=nread; | |
527 | datalen-=nread; | |
528 | count+=nread; | |
529 | } | |
530 | } | |
531 | while(datalen>0 && nread>0); | |
532 | if(count<=0)break; | |
533 | ||
534 | int _i=count; | |
535 | while(_i>0){ | |
536 | _i-=sendWRITE(handle, offset, data, 0, _i); | |
537 | if((seq-1)==startid || | |
538 | io_in.available()>=1024){ | |
539 | while(io_in.available()>0){ | |
540 | if(checkStatus(ackid, header)){ | |
541 | _ackid=ackid[0]; | |
542 | if(startid>_ackid || _ackid>seq-1){ | |
543 | if(_ackid==seq){ | |
544 | System.err.println("ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid); | |
545 | } | |
546 | else{ | |
547 | //throw new SftpException(SSH_FX_FAILURE, "ack error:"); | |
548 | throw new SftpException(SSH_FX_FAILURE, "ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid); | |
549 | } | |
550 | } | |
551 | ackcount++; | |
552 | } | |
553 | else{ | |
554 | break; | |
555 | } | |
556 | } | |
557 | } | |
558 | } | |
559 | offset+=count; | |
560 | if(monitor!=null && !monitor.count(count)){ | |
561 | break; | |
562 | } | |
563 | } | |
564 | int _ackcount=seq-startid; | |
565 | while(_ackcount>ackcount){ | |
566 | if(!checkStatus(null, header)){ | |
567 | break; | |
568 | } | |
569 | ackcount++; | |
570 | } | |
571 | if(monitor!=null)monitor.end(); | |
572 | _sendCLOSE(handle, header); | |
573 | } | |
574 | catch(Exception e){ | |
575 | if(e instanceof SftpException) throw (SftpException)e; | |
576 | if(e instanceof Throwable) | |
577 | throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e); | |
578 | throw new SftpException(SSH_FX_FAILURE, e.toString()); | |
579 | } | |
580 | } | |
581 | ||
582 | public OutputStream put(String dst) throws SftpException{ | |
583 | return put(dst, (SftpProgressMonitor)null, OVERWRITE); | |
584 | } | |
585 | public OutputStream put(String dst, final int mode) throws SftpException{ | |
586 | return put(dst, (SftpProgressMonitor)null, mode); | |
587 | } | |
588 | public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) throws SftpException{ | |
589 | return put(dst, monitor, mode, 0); | |
590 | } | |
591 | public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, long offset) throws SftpException{ | |
592 | dst=remoteAbsolutePath(dst); | |
593 | try{ | |
594 | ||
595 | dst=isUnique(dst); | |
596 | ||
597 | if(isRemoteDir(dst)){ | |
598 | throw new SftpException(SSH_FX_FAILURE, dst+" is a directory"); | |
599 | } | |
600 | ||
601 | byte[] dstb=Util.str2byte(dst, fEncoding); | |
602 | ||
603 | long skip=0; | |
604 | if(mode==RESUME || mode==APPEND){ | |
605 | try{ | |
606 | SftpATTRS attr=_stat(dstb); | |
607 | skip=attr.getSize(); | |
608 | } | |
609 | catch(Exception eee){ | |
610 | //System.err.println(eee); | |
611 | } | |
612 | } | |
613 | ||
614 | if(mode==OVERWRITE){ sendOPENW(dstb); } | |
615 | else{ sendOPENA(dstb); } | |
616 | ||
617 | Header header=new Header(); | |
618 | header=header(buf, header); | |
619 | int length=header.length; | |
620 | int type=header.type; | |
621 | ||
622 | fill(buf, length); | |
623 | ||
624 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ | |
625 | throw new SftpException(SSH_FX_FAILURE, ""); | |
626 | } | |
627 | if(type==SSH_FXP_STATUS){ | |
628 | int i=buf.getInt(); | |
629 | throwStatusError(buf, i); | |
630 | } | |
631 | final byte[] handle=buf.getString(); // handle | |
632 | ||
633 | if(mode==RESUME || mode==APPEND){ | |
634 | offset+=skip; | |
635 | } | |
636 | ||
637 | final long[] _offset=new long[1]; | |
638 | _offset[0]=offset; | |
639 | OutputStream out = new OutputStream(){ | |
640 | private boolean init=true; | |
641 | private boolean isClosed=false; | |
642 | private int[] ackid=new int[1]; | |
643 | private int startid=0; | |
644 | private int _ackid=0; | |
645 | private int ackcount=0; | |
646 | private int writecount=0; | |
647 | private Header header=new Header(); | |
648 | ||
649 | public void write(byte[] d) throws java.io.IOException{ | |
650 | write(d, 0, d.length); | |
651 | } | |
652 | ||
653 | public void write(byte[] d, int s, int len) throws java.io.IOException{ | |
654 | if(init){ | |
655 | startid=seq; | |
656 | _ackid=seq; | |
657 | init=false; | |
658 | } | |
659 | ||
660 | if(isClosed){ | |
661 | throw new IOException("stream already closed"); | |
662 | } | |
663 | ||
664 | try{ | |
665 | int _len=len; | |
666 | while(_len>0){ | |
667 | int sent=sendWRITE(handle, _offset[0], d, s, _len); | |
668 | writecount++; | |
669 | _offset[0]+=sent; | |
670 | s+=sent; | |
671 | _len-=sent; | |
672 | if((seq-1)==startid || | |
673 | io_in.available()>=1024){ | |
674 | while(io_in.available()>0){ | |
675 | if(checkStatus(ackid, header)){ | |
676 | _ackid=ackid[0]; | |
677 | if(startid>_ackid || _ackid>seq-1){ | |
678 | throw new SftpException(SSH_FX_FAILURE, ""); | |
679 | } | |
680 | ackcount++; | |
681 | } | |
682 | else{ | |
683 | break; | |
684 | } | |
685 | } | |
686 | } | |
687 | } | |
688 | if(monitor!=null && !monitor.count(len)){ | |
689 | close(); | |
690 | throw new IOException("canceled"); | |
691 | } | |
692 | } | |
693 | catch(IOException e){ throw e; } | |
694 | catch(Exception e){ throw new IOException(e.toString()); } | |
695 | } | |
696 | ||
697 | byte[] _data=new byte[1]; | |
698 | public void write(int foo) throws java.io.IOException{ | |
699 | _data[0]=(byte)foo; | |
700 | write(_data, 0, 1); | |
701 | } | |
702 | ||
703 | public void flush() throws java.io.IOException{ | |
704 | ||
705 | if(isClosed){ | |
706 | throw new IOException("stream already closed"); | |
707 | } | |
708 | ||
709 | if(!init){ | |
710 | try{ | |
711 | while(writecount>ackcount){ | |
712 | if(!checkStatus(null, header)){ | |
713 | break; | |
714 | } | |
715 | ackcount++; | |
716 | } | |
717 | } | |
718 | catch(SftpException e){ | |
719 | throw new IOException(e.toString()); | |
720 | } | |
721 | } | |
722 | } | |
723 | ||
724 | public void close() throws java.io.IOException{ | |
725 | if(isClosed){ | |
726 | return; | |
727 | } | |
728 | flush(); | |
729 | if(monitor!=null)monitor.end(); | |
730 | try{ _sendCLOSE(handle, header); } | |
731 | catch(IOException e){ throw e; } | |
732 | catch(Exception e){ | |
733 | throw new IOException(e.toString()); | |
734 | } | |
735 | isClosed=true; | |
736 | } | |
737 | }; | |
738 | return out; | |
739 | } | |
740 | catch(Exception e){ | |
741 | if(e instanceof SftpException) throw (SftpException)e; | |
742 | if(e instanceof Throwable) | |
743 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
744 | throw new SftpException(SSH_FX_FAILURE, ""); | |
745 | } | |
746 | } | |
747 | ||
748 | public void get(String src, String dst) throws SftpException{ | |
749 | get(src, dst, null, OVERWRITE); | |
750 | } | |
751 | public void get(String src, String dst, | |
752 | SftpProgressMonitor monitor) throws SftpException{ | |
753 | get(src, dst, monitor, OVERWRITE); | |
754 | } | |
755 | public void get(String src, String dst, | |
756 | SftpProgressMonitor monitor, int mode) throws SftpException{ | |
757 | // System.out.println("get: "+src+" "+dst); | |
758 | ||
759 | src=remoteAbsolutePath(src); | |
760 | dst=localAbsolutePath(dst); | |
761 | ||
762 | try{ | |
763 | Vector v=glob_remote(src); | |
764 | int vsize=v.size(); | |
765 | if(vsize==0){ | |
766 | throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such file"); | |
767 | } | |
768 | ||
769 | File dstFile=new File(dst); | |
770 | boolean isDstDir=dstFile.isDirectory(); | |
771 | StringBuffer dstsb=null; | |
772 | if(isDstDir){ | |
773 | if(!dst.endsWith(file_separator)){ | |
774 | dst+=file_separator; | |
775 | } | |
776 | dstsb=new StringBuffer(dst); | |
777 | } | |
778 | else if(vsize>1){ | |
779 | throw new SftpException(SSH_FX_FAILURE, | |
780 | "Copying multiple files, but destination is missing or a file."); | |
781 | } | |
782 | ||
783 | for(int j=0; j<vsize; j++){ | |
784 | String _src=(String)(v.elementAt(j)); | |
785 | SftpATTRS attr=_stat(_src); | |
786 | if(attr.isDir()){ | |
787 | throw new SftpException(SSH_FX_FAILURE, | |
788 | "not supported to get directory "+_src); | |
789 | } | |
790 | ||
791 | String _dst=null; | |
792 | if(isDstDir){ | |
793 | int i=_src.lastIndexOf('/'); | |
794 | if(i==-1) dstsb.append(_src); | |
795 | else dstsb.append(_src.substring(i + 1)); | |
796 | _dst=dstsb.toString(); | |
797 | dstsb.delete(dst.length(), _dst.length()); | |
798 | } | |
799 | else{ | |
800 | _dst=dst; | |
801 | } | |
802 | ||
803 | if(mode==RESUME){ | |
804 | long size_of_src=attr.getSize(); | |
805 | long size_of_dst=new File(_dst).length(); | |
806 | if(size_of_dst>size_of_src){ | |
807 | throw new SftpException(SSH_FX_FAILURE, | |
808 | "failed to resume for "+_dst); | |
809 | } | |
810 | if(size_of_dst==size_of_src){ | |
811 | return; | |
812 | } | |
813 | } | |
814 | ||
815 | if(monitor!=null){ | |
816 | monitor.init(SftpProgressMonitor.GET, _src, _dst, attr.getSize()); | |
817 | if(mode==RESUME){ | |
818 | monitor.count(new File(_dst).length()); | |
819 | } | |
820 | } | |
821 | ||
822 | FileOutputStream fos=null; | |
823 | try{ | |
824 | if(mode==OVERWRITE){ | |
825 | fos=new FileOutputStream(_dst); | |
826 | } | |
827 | else{ | |
828 | fos=new FileOutputStream(_dst, true); // append | |
829 | } | |
830 | // System.err.println("_get: "+_src+", "+_dst); | |
831 | _get(_src, fos, monitor, mode, new File(_dst).length()); | |
832 | } | |
833 | finally{ | |
834 | if(fos!=null){ | |
835 | fos.close(); | |
836 | } | |
837 | } | |
838 | } | |
839 | } | |
840 | catch(Exception e){ | |
841 | if(e instanceof SftpException) throw (SftpException)e; | |
842 | if(e instanceof Throwable) | |
843 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
844 | throw new SftpException(SSH_FX_FAILURE, ""); | |
845 | } | |
846 | } | |
847 | public void get(String src, OutputStream dst) throws SftpException{ | |
848 | get(src, dst, null, OVERWRITE, 0); | |
849 | } | |
850 | public void get(String src, OutputStream dst, | |
851 | SftpProgressMonitor monitor) throws SftpException{ | |
852 | get(src, dst, monitor, OVERWRITE, 0); | |
853 | } | |
854 | public void get(String src, OutputStream dst, | |
855 | SftpProgressMonitor monitor, int mode, long skip) throws SftpException{ | |
856 | //System.err.println("get: "+src+", "+dst); | |
857 | try{ | |
858 | src=remoteAbsolutePath(src); | |
859 | ||
860 | src=isUnique(src); | |
861 | ||
862 | if(monitor!=null){ | |
863 | SftpATTRS attr=_stat(src); | |
864 | monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize()); | |
865 | if(mode==RESUME){ | |
866 | monitor.count(skip); | |
867 | } | |
868 | } | |
869 | _get(src, dst, monitor, mode, skip); | |
870 | } | |
871 | catch(Exception e){ | |
872 | if(e instanceof SftpException) throw (SftpException)e; | |
873 | if(e instanceof Throwable) | |
874 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
875 | throw new SftpException(SSH_FX_FAILURE, ""); | |
876 | } | |
877 | } | |
878 | ||
879 | private void _get(String src, OutputStream dst, | |
880 | SftpProgressMonitor monitor, int mode, long skip) throws SftpException{ | |
881 | //System.err.println("_get: "+src+", "+dst); | |
882 | ||
883 | byte[] srcb=Util.str2byte(src, fEncoding); | |
884 | try{ | |
885 | sendOPENR(srcb); | |
886 | ||
887 | Header header=new Header(); | |
888 | header=header(buf, header); | |
889 | int length=header.length; | |
890 | int type=header.type; | |
891 | ||
892 | fill(buf, length); | |
893 | ||
894 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ | |
895 | throw new SftpException(SSH_FX_FAILURE, ""); | |
896 | } | |
897 | ||
898 | if(type==SSH_FXP_STATUS){ | |
899 | int i=buf.getInt(); | |
900 | throwStatusError(buf, i); | |
901 | } | |
902 | ||
903 | byte[] handle=buf.getString(); // filename | |
904 | ||
905 | long offset=0; | |
906 | if(mode==RESUME){ | |
907 | offset+=skip; | |
908 | } | |
909 | ||
910 | int request_len=0; | |
911 | loop: | |
912 | while(true){ | |
913 | ||
914 | request_len=buf.buffer.length-13; | |
915 | if(server_version==0){ request_len=1024; } | |
916 | sendREAD(handle, offset, request_len); | |
917 | ||
918 | header=header(buf, header); | |
919 | length=header.length; | |
920 | type=header.type; | |
921 | ||
922 | if(type==SSH_FXP_STATUS){ | |
923 | fill(buf, length); | |
924 | int i=buf.getInt(); | |
925 | if(i==SSH_FX_EOF){ | |
926 | break loop; | |
927 | } | |
928 | throwStatusError(buf, i); | |
929 | } | |
930 | ||
931 | if(type!=SSH_FXP_DATA){ | |
932 | break loop; | |
933 | } | |
934 | ||
935 | buf.rewind(); | |
936 | fill(buf.buffer, 0, 4); length-=4; | |
937 | int i=buf.getInt(); // length of data | |
938 | int foo=i; | |
939 | ||
940 | while(foo>0){ | |
941 | int bar=foo; | |
942 | if(bar>buf.buffer.length){ | |
943 | bar=buf.buffer.length; | |
944 | } | |
945 | i=io_in.read(buf.buffer, 0, bar); | |
946 | if(i<0){ | |
947 | break loop; | |
948 | } | |
949 | int data_len=i; | |
950 | dst.write(buf.buffer, 0, data_len); | |
951 | ||
952 | offset+=data_len; | |
953 | foo-=data_len; | |
954 | ||
955 | if(monitor!=null){ | |
956 | if(!monitor.count(data_len)){ | |
957 | while(foo>0){ | |
958 | i=io_in.read(buf.buffer, | |
959 | 0, | |
960 | (buf.buffer.length<foo?buf.buffer.length:foo)); | |
961 | if(i<=0) break; | |
962 | foo-=i; | |
963 | } | |
964 | break loop; | |
965 | } | |
966 | } | |
967 | ||
968 | } | |
969 | //System.err.println("length: "+length); // length should be 0 | |
970 | } | |
971 | dst.flush(); | |
972 | ||
973 | if(monitor!=null)monitor.end(); | |
974 | _sendCLOSE(handle, header); | |
975 | } | |
976 | catch(Exception e){ | |
977 | if(e instanceof SftpException) throw (SftpException)e; | |
978 | if(e instanceof Throwable) | |
979 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
980 | throw new SftpException(SSH_FX_FAILURE, ""); | |
981 | } | |
982 | } | |
983 | ||
984 | public InputStream get(String src) throws SftpException{ | |
985 | return get(src, null, 0L); | |
986 | } | |
987 | public InputStream get(String src, SftpProgressMonitor monitor) throws SftpException{ | |
988 | return get(src, monitor, 0L); | |
989 | } | |
990 | ||
991 | /** | |
992 | * @deprecated This method will be deleted in the future. | |
993 | */ | |
994 | public InputStream get(String src, int mode) throws SftpException{ | |
995 | return get(src, null, 0L); | |
996 | } | |
997 | /** | |
998 | * @deprecated This method will be deleted in the future. | |
999 | */ | |
1000 | public InputStream get(String src, final SftpProgressMonitor monitor, final int mode) throws SftpException{ | |
1001 | return get(src, monitor, 0L); | |
1002 | } | |
1003 | public InputStream get(String src, final SftpProgressMonitor monitor, final long skip) throws SftpException{ | |
1004 | src=remoteAbsolutePath(src); | |
1005 | try{ | |
1006 | src=isUnique(src); | |
1007 | ||
1008 | byte[] srcb=Util.str2byte(src, fEncoding); | |
1009 | ||
1010 | SftpATTRS attr=_stat(srcb); | |
1011 | if(monitor!=null){ | |
1012 | monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize()); | |
1013 | } | |
1014 | ||
1015 | sendOPENR(srcb); | |
1016 | ||
1017 | Header header=new Header(); | |
1018 | header=header(buf, header); | |
1019 | int length=header.length; | |
1020 | int type=header.type; | |
1021 | ||
1022 | fill(buf, length); | |
1023 | ||
1024 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ | |
1025 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1026 | } | |
1027 | if(type==SSH_FXP_STATUS){ | |
1028 | int i=buf.getInt(); | |
1029 | throwStatusError(buf, i); | |
1030 | } | |
1031 | ||
1032 | final byte[] handle=buf.getString(); // handle | |
1033 | ||
1034 | java.io.InputStream in=new java.io.InputStream(){ | |
1035 | long offset=skip; | |
1036 | boolean closed=false; | |
1037 | int rest_length=0; | |
1038 | byte[] _data=new byte[1]; | |
1039 | byte[] rest_byte=new byte[1024]; | |
1040 | Header header=new Header(); | |
1041 | ||
1042 | public int read() throws java.io.IOException{ | |
1043 | if(closed)return -1; | |
1044 | int i=read(_data, 0, 1); | |
1045 | if (i==-1) { return -1; } | |
1046 | else { | |
1047 | return _data[0]&0xff; | |
1048 | } | |
1049 | } | |
1050 | public int read(byte[] d) throws java.io.IOException{ | |
1051 | if(closed)return -1; | |
1052 | return read(d, 0, d.length); | |
1053 | } | |
1054 | public int read(byte[] d, int s, int len) throws java.io.IOException{ | |
1055 | if(closed)return -1; | |
1056 | if(d==null){throw new NullPointerException();} | |
1057 | if(s<0 || len <0 || s+len>d.length){ | |
1058 | throw new IndexOutOfBoundsException(); | |
1059 | } | |
1060 | if(len==0){ return 0; } | |
1061 | ||
1062 | if(rest_length>0){ | |
1063 | int foo=rest_length; | |
1064 | if(foo>len) foo=len; | |
1065 | System.arraycopy(rest_byte, 0, d, s, foo); | |
1066 | if(foo!=rest_length){ | |
1067 | System.arraycopy(rest_byte, foo, | |
1068 | rest_byte, 0, rest_length-foo); | |
1069 | } | |
1070 | ||
1071 | if(monitor!=null){ | |
1072 | if(!monitor.count(foo)){ | |
1073 | close(); | |
1074 | return -1; | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | rest_length-=foo; | |
1079 | return foo; | |
1080 | } | |
1081 | ||
1082 | if(buf.buffer.length-13<len){ | |
1083 | len=buf.buffer.length-13; | |
1084 | } | |
1085 | if(server_version==0 && len>1024){ | |
1086 | len=1024; | |
1087 | } | |
1088 | ||
1089 | try{sendREAD(handle, offset, len);} | |
1090 | catch(Exception e){ throw new IOException("error"); } | |
1091 | ||
1092 | header=header(buf, header); | |
1093 | rest_length=header.length; | |
1094 | int type=header.type; | |
1095 | int id=header.rid; | |
1096 | ||
1097 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_DATA){ | |
1098 | throw new IOException("error"); | |
1099 | } | |
1100 | if(type==SSH_FXP_STATUS){ | |
1101 | fill(buf, rest_length); | |
1102 | int i=buf.getInt(); | |
1103 | rest_length=0; | |
1104 | if(i==SSH_FX_EOF){ | |
1105 | close(); | |
1106 | return -1; | |
1107 | } | |
1108 | //throwStatusError(buf, i); | |
1109 | throw new IOException("error"); | |
1110 | } | |
1111 | buf.rewind(); | |
1112 | fill(buf.buffer, 0, 4); | |
1113 | int i=buf.getInt(); rest_length-=4; | |
1114 | ||
1115 | offset+=rest_length; | |
1116 | int foo=i; | |
1117 | if(foo>0){ | |
1118 | int bar=rest_length; | |
1119 | if(bar>len){ | |
1120 | bar=len; | |
1121 | } | |
1122 | i=io_in.read(d, s, bar); | |
1123 | if(i<0){ | |
1124 | return -1; | |
1125 | } | |
1126 | rest_length-=i; | |
1127 | ||
1128 | if(rest_length>0){ | |
1129 | if(rest_byte.length<rest_length){ | |
1130 | rest_byte=new byte[rest_length]; | |
1131 | } | |
1132 | int _s=0; | |
1133 | int _len=rest_length; | |
1134 | int j; | |
1135 | while(_len>0){ | |
1136 | j=io_in.read(rest_byte, _s, _len); | |
1137 | if(j<=0)break; | |
1138 | _s+=j; | |
1139 | _len-=j; | |
1140 | } | |
1141 | } | |
1142 | ||
1143 | if(monitor!=null){ | |
1144 | if(!monitor.count(i)){ | |
1145 | close(); | |
1146 | return -1; | |
1147 | } | |
1148 | } | |
1149 | ||
1150 | return i; | |
1151 | } | |
1152 | return 0; // ?? | |
1153 | } | |
1154 | public void close() throws IOException{ | |
1155 | if(closed)return; | |
1156 | closed=true; | |
1157 | if(monitor!=null)monitor.end(); | |
1158 | try{_sendCLOSE(handle, header);} | |
1159 | catch(Exception e){throw new IOException("error");} | |
1160 | } | |
1161 | }; | |
1162 | return in; | |
1163 | } | |
1164 | catch(Exception e){ | |
1165 | if(e instanceof SftpException) throw (SftpException)e; | |
1166 | if(e instanceof Throwable) | |
1167 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1168 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1169 | } | |
1170 | } | |
1171 | ||
1172 | public java.util.Vector ls(String path) throws SftpException{ | |
1173 | //System.out.println("ls: "+path); | |
1174 | try{ | |
1175 | path=remoteAbsolutePath(path); | |
1176 | byte[] pattern=null; | |
1177 | java.util.Vector v=new java.util.Vector(); | |
1178 | ||
1179 | int foo=path.lastIndexOf('/'); | |
1180 | String dir=path.substring(0, ((foo==0)?1:foo)); | |
1181 | String _pattern=path.substring(foo+1); | |
1182 | dir=Util.unquote(dir); | |
1183 | ||
1184 | // If pattern has included '*' or '?', we need to convert | |
1185 | // to UTF-8 string before globbing. | |
1186 | byte[][] _pattern_utf8=new byte[1][]; | |
1187 | boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8); | |
1188 | ||
1189 | if(pattern_has_wildcard){ | |
1190 | pattern=_pattern_utf8[0]; | |
1191 | } | |
1192 | else{ | |
1193 | String upath=Util.unquote(path); | |
1194 | //SftpATTRS attr=_lstat(upath); | |
1195 | SftpATTRS attr=_stat(upath); | |
1196 | if(attr.isDir()){ | |
1197 | pattern=null; | |
1198 | dir=upath; | |
1199 | } | |
1200 | else{ | |
1201 | /* | |
1202 | // If we can generage longname by ourself, | |
1203 | // we don't have to use openDIR. | |
1204 | String filename=Util.unquote(_pattern); | |
1205 | String longname=... | |
1206 | v.addElement(new LsEntry(filename, longname, attr)); | |
1207 | return v; | |
1208 | */ | |
1209 | ||
1210 | if(fEncoding_is_utf8){ | |
1211 | pattern=_pattern_utf8[0]; | |
1212 | pattern=Util.unquote(pattern); | |
1213 | } | |
1214 | else{ | |
1215 | _pattern=Util.unquote(_pattern); | |
1216 | pattern=Util.str2byte(_pattern, fEncoding); | |
1217 | } | |
1218 | ||
1219 | } | |
1220 | } | |
1221 | ||
1222 | sendOPENDIR(Util.str2byte(dir, fEncoding)); | |
1223 | ||
1224 | Header header=new Header(); | |
1225 | header=header(buf, header); | |
1226 | int length=header.length; | |
1227 | int type=header.type; | |
1228 | ||
1229 | fill(buf, length); | |
1230 | ||
1231 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ | |
1232 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1233 | } | |
1234 | if(type==SSH_FXP_STATUS){ | |
1235 | int i=buf.getInt(); | |
1236 | throwStatusError(buf, i); | |
1237 | } | |
1238 | ||
1239 | byte[] handle=buf.getString(); // handle | |
1240 | ||
1241 | while(true){ | |
1242 | sendREADDIR(handle); | |
1243 | ||
1244 | header=header(buf, header); | |
1245 | length=header.length; | |
1246 | type=header.type; | |
1247 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ | |
1248 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1249 | } | |
1250 | if(type==SSH_FXP_STATUS){ | |
1251 | fill(buf, length); | |
1252 | int i=buf.getInt(); | |
1253 | if(i==SSH_FX_EOF) | |
1254 | break; | |
1255 | throwStatusError(buf, i); | |
1256 | } | |
1257 | ||
1258 | buf.rewind(); | |
1259 | fill(buf.buffer, 0, 4); length-=4; | |
1260 | int count=buf.getInt(); | |
1261 | ||
1262 | byte[] str; | |
1263 | int flags; | |
1264 | ||
1265 | buf.reset(); | |
1266 | while(count>0){ | |
1267 | if(length>0){ | |
1268 | buf.shift(); | |
1269 | int j=(buf.buffer.length>(buf.index+length)) ? | |
1270 | length : | |
1271 | (buf.buffer.length-buf.index); | |
1272 | int i=fill(buf.buffer, buf.index, j); | |
1273 | buf.index+=i; | |
1274 | length-=i; | |
1275 | } | |
1276 | byte[] filename=buf.getString(); | |
1277 | byte[] longname=null; | |
1278 | if(server_version<=3){ | |
1279 | longname=buf.getString(); | |
1280 | } | |
1281 | SftpATTRS attrs=SftpATTRS.getATTR(buf); | |
1282 | ||
1283 | boolean find=false; | |
1284 | String f=null; | |
1285 | if(pattern==null){ | |
1286 | find=true; | |
1287 | } | |
1288 | else if(!pattern_has_wildcard){ | |
1289 | find=Util.array_equals(pattern, filename); | |
1290 | } | |
1291 | else{ | |
1292 | byte[] _filename=filename; | |
1293 | if(!fEncoding_is_utf8){ | |
1294 | f=Util.byte2str(_filename, fEncoding); | |
1295 | _filename=Util.str2byte(f, UTF8); | |
1296 | } | |
1297 | find=Util.glob(pattern, _filename); | |
1298 | } | |
1299 | ||
1300 | if(find){ | |
1301 | if(f==null){ | |
1302 | f=Util.byte2str(filename, fEncoding); | |
1303 | } | |
1304 | String l=null; | |
1305 | if(longname==null){ | |
1306 | // TODO: we need to generate long name from attrs | |
1307 | // for the sftp protocol 4(and later). | |
1308 | l=attrs.toString()+" "+f; | |
1309 | } | |
1310 | else{ | |
1311 | l=Util.byte2str(longname, fEncoding); | |
1312 | } | |
1313 | v.addElement(new LsEntry(f, l, attrs)); | |
1314 | } | |
1315 | ||
1316 | count--; | |
1317 | } | |
1318 | } | |
1319 | _sendCLOSE(handle, header); | |
1320 | ||
1321 | /* | |
1322 | if(v.size()==1 && pattern_has_wildcard){ | |
1323 | LsEntry le=(LsEntry)v.elementAt(0); | |
1324 | if(le.getAttrs().isDir()){ | |
1325 | String f=le.getFilename(); | |
1326 | if(isPattern(f)){ | |
1327 | f=Util.quote(f); | |
1328 | } | |
1329 | if(!dir.endsWith("/")){ | |
1330 | dir+="/"; | |
1331 | } | |
1332 | v=null; | |
1333 | return ls(dir+f); | |
1334 | } | |
1335 | } | |
1336 | */ | |
1337 | ||
1338 | return v; | |
1339 | } | |
1340 | catch(Exception e){ | |
1341 | if(e instanceof SftpException) throw (SftpException)e; | |
1342 | if(e instanceof Throwable) | |
1343 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1344 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1345 | } | |
1346 | } | |
1347 | public String readlink(String path) throws SftpException{ | |
1348 | try{ | |
1349 | ||
1350 | if(server_version<3){ | |
1351 | throw new SftpException(SSH_FX_OP_UNSUPPORTED, | |
1352 | "The remote sshd is too old to support symlink operation."); | |
1353 | } | |
1354 | ||
1355 | path=remoteAbsolutePath(path); | |
1356 | ||
1357 | path=isUnique(path); | |
1358 | ||
1359 | sendREADLINK(Util.str2byte(path, fEncoding)); | |
1360 | ||
1361 | Header header=new Header(); | |
1362 | header=header(buf, header); | |
1363 | int length=header.length; | |
1364 | int type=header.type; | |
1365 | ||
1366 | fill(buf, length); | |
1367 | ||
1368 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ | |
1369 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1370 | } | |
1371 | if(type==SSH_FXP_NAME){ | |
1372 | int count=buf.getInt(); // count | |
1373 | byte[] filename=null; | |
1374 | for(int i=0; i<count; i++){ | |
1375 | filename=buf.getString(); | |
1376 | if(server_version<=3){ | |
1377 | byte[] longname=buf.getString(); | |
1378 | } | |
1379 | SftpATTRS.getATTR(buf); | |
1380 | } | |
1381 | return Util.byte2str(filename, fEncoding); | |
1382 | } | |
1383 | ||
1384 | int i=buf.getInt(); | |
1385 | throwStatusError(buf, i); | |
1386 | } | |
1387 | catch(Exception e){ | |
1388 | if(e instanceof SftpException) throw (SftpException)e; | |
1389 | if(e instanceof Throwable) | |
1390 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1391 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1392 | } | |
1393 | return null; | |
1394 | } | |
1395 | public void symlink(String oldpath, String newpath) throws SftpException{ | |
1396 | if(server_version<3){ | |
1397 | throw new SftpException(SSH_FX_OP_UNSUPPORTED, | |
1398 | "The remote sshd is too old to support symlink operation."); | |
1399 | } | |
1400 | ||
1401 | try{ | |
1402 | oldpath=remoteAbsolutePath(oldpath); | |
1403 | newpath=remoteAbsolutePath(newpath); | |
1404 | ||
1405 | oldpath=isUnique(oldpath); | |
1406 | ||
1407 | if(isPattern(newpath)){ | |
1408 | throw new SftpException(SSH_FX_FAILURE, newpath); | |
1409 | } | |
1410 | newpath=Util.unquote(newpath); | |
1411 | ||
1412 | sendSYMLINK(Util.str2byte(oldpath, fEncoding), | |
1413 | Util.str2byte(newpath, fEncoding)); | |
1414 | ||
1415 | Header header=new Header(); | |
1416 | header=header(buf, header); | |
1417 | int length=header.length; | |
1418 | int type=header.type; | |
1419 | ||
1420 | fill(buf, length); | |
1421 | ||
1422 | if(type!=SSH_FXP_STATUS){ | |
1423 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1424 | } | |
1425 | ||
1426 | int i=buf.getInt(); | |
1427 | if(i==SSH_FX_OK) return; | |
1428 | throwStatusError(buf, i); | |
1429 | } | |
1430 | catch(Exception e){ | |
1431 | if(e instanceof SftpException) throw (SftpException)e; | |
1432 | if(e instanceof Throwable) | |
1433 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1434 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1435 | } | |
1436 | } | |
1437 | ||
1438 | public void rename(String oldpath, String newpath) throws SftpException{ | |
1439 | if(server_version<2){ | |
1440 | throw new SftpException(SSH_FX_OP_UNSUPPORTED, | |
1441 | "The remote sshd is too old to support rename operation."); | |
1442 | } | |
1443 | ||
1444 | try{ | |
1445 | oldpath=remoteAbsolutePath(oldpath); | |
1446 | newpath=remoteAbsolutePath(newpath); | |
1447 | ||
1448 | oldpath=isUnique(oldpath); | |
1449 | ||
1450 | Vector v=glob_remote(newpath); | |
1451 | int vsize=v.size(); | |
1452 | if(vsize>=2){ | |
1453 | throw new SftpException(SSH_FX_FAILURE, v.toString()); | |
1454 | } | |
1455 | if(vsize==1){ | |
1456 | newpath=(String)(v.elementAt(0)); | |
1457 | } | |
1458 | else{ // vsize==0 | |
1459 | if(isPattern(newpath)) | |
1460 | throw new SftpException(SSH_FX_FAILURE, newpath); | |
1461 | newpath=Util.unquote(newpath); | |
1462 | } | |
1463 | ||
1464 | sendRENAME(Util.str2byte(oldpath, fEncoding), | |
1465 | Util.str2byte(newpath, fEncoding)); | |
1466 | ||
1467 | Header header=new Header(); | |
1468 | header=header(buf, header); | |
1469 | int length=header.length; | |
1470 | int type=header.type; | |
1471 | ||
1472 | fill(buf, length); | |
1473 | ||
1474 | if(type!=SSH_FXP_STATUS){ | |
1475 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1476 | } | |
1477 | ||
1478 | int i=buf.getInt(); | |
1479 | if(i==SSH_FX_OK) return; | |
1480 | throwStatusError(buf, i); | |
1481 | } | |
1482 | catch(Exception e){ | |
1483 | if(e instanceof SftpException) throw (SftpException)e; | |
1484 | if(e instanceof Throwable) | |
1485 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1486 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1487 | } | |
1488 | } | |
1489 | public void rm(String path) throws SftpException{ | |
1490 | try{ | |
1491 | path=remoteAbsolutePath(path); | |
1492 | ||
1493 | Vector v=glob_remote(path); | |
1494 | int vsize=v.size(); | |
1495 | ||
1496 | Header header=new Header(); | |
1497 | ||
1498 | for(int j=0; j<vsize; j++){ | |
1499 | path=(String)(v.elementAt(j)); | |
1500 | sendREMOVE(Util.str2byte(path, fEncoding)); | |
1501 | ||
1502 | header=header(buf, header); | |
1503 | int length=header.length; | |
1504 | int type=header.type; | |
1505 | ||
1506 | fill(buf, length); | |
1507 | ||
1508 | if(type!=SSH_FXP_STATUS){ | |
1509 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1510 | } | |
1511 | int i=buf.getInt(); | |
1512 | if(i!=SSH_FX_OK){ | |
1513 | throwStatusError(buf, i); | |
1514 | } | |
1515 | } | |
1516 | } | |
1517 | catch(Exception e){ | |
1518 | if(e instanceof SftpException) throw (SftpException)e; | |
1519 | if(e instanceof Throwable) | |
1520 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1521 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1522 | } | |
1523 | } | |
1524 | ||
1525 | private boolean isRemoteDir(String path){ | |
1526 | try{ | |
1527 | sendSTAT(Util.str2byte(path, fEncoding)); | |
1528 | ||
1529 | Header header=new Header(); | |
1530 | header=header(buf, header); | |
1531 | int length=header.length; | |
1532 | int type=header.type; | |
1533 | ||
1534 | fill(buf, length); | |
1535 | ||
1536 | if(type!=SSH_FXP_ATTRS){ | |
1537 | return false; | |
1538 | } | |
1539 | SftpATTRS attr=SftpATTRS.getATTR(buf); | |
1540 | return attr.isDir(); | |
1541 | } | |
1542 | catch(Exception e){} | |
1543 | return false; | |
1544 | } | |
1545 | ||
1546 | public void chgrp(int gid, String path) throws SftpException{ | |
1547 | try{ | |
1548 | path=remoteAbsolutePath(path); | |
1549 | ||
1550 | Vector v=glob_remote(path); | |
1551 | int vsize=v.size(); | |
1552 | for(int j=0; j<vsize; j++){ | |
1553 | path=(String)(v.elementAt(j)); | |
1554 | ||
1555 | SftpATTRS attr=_stat(path); | |
1556 | ||
1557 | attr.setFLAGS(0); | |
1558 | attr.setUIDGID(attr.uid, gid); | |
1559 | _setStat(path, attr); | |
1560 | } | |
1561 | } | |
1562 | catch(Exception e){ | |
1563 | if(e instanceof SftpException) throw (SftpException)e; | |
1564 | if(e instanceof Throwable) | |
1565 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1566 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1567 | } | |
1568 | } | |
1569 | ||
1570 | public void chown(int uid, String path) throws SftpException{ | |
1571 | try{ | |
1572 | path=remoteAbsolutePath(path); | |
1573 | ||
1574 | Vector v=glob_remote(path); | |
1575 | int vsize=v.size(); | |
1576 | for(int j=0; j<vsize; j++){ | |
1577 | path=(String)(v.elementAt(j)); | |
1578 | ||
1579 | SftpATTRS attr=_stat(path); | |
1580 | ||
1581 | attr.setFLAGS(0); | |
1582 | attr.setUIDGID(uid, attr.gid); | |
1583 | _setStat(path, attr); | |
1584 | } | |
1585 | } | |
1586 | catch(Exception e){ | |
1587 | if(e instanceof SftpException) throw (SftpException)e; | |
1588 | if(e instanceof Throwable) | |
1589 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1590 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1591 | } | |
1592 | } | |
1593 | ||
1594 | public void chmod(int permissions, String path) throws SftpException{ | |
1595 | try{ | |
1596 | path=remoteAbsolutePath(path); | |
1597 | ||
1598 | Vector v=glob_remote(path); | |
1599 | int vsize=v.size(); | |
1600 | for(int j=0; j<vsize; j++){ | |
1601 | path=(String)(v.elementAt(j)); | |
1602 | ||
1603 | SftpATTRS attr=_stat(path); | |
1604 | ||
1605 | attr.setFLAGS(0); | |
1606 | attr.setPERMISSIONS(permissions); | |
1607 | _setStat(path, attr); | |
1608 | } | |
1609 | } | |
1610 | catch(Exception e){ | |
1611 | if(e instanceof SftpException) throw (SftpException)e; | |
1612 | if(e instanceof Throwable) | |
1613 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1614 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1615 | } | |
1616 | } | |
1617 | ||
1618 | public void setMtime(String path, int mtime) throws SftpException{ | |
1619 | try{ | |
1620 | path=remoteAbsolutePath(path); | |
1621 | ||
1622 | Vector v=glob_remote(path); | |
1623 | int vsize=v.size(); | |
1624 | for(int j=0; j<vsize; j++){ | |
1625 | path=(String)(v.elementAt(j)); | |
1626 | ||
1627 | SftpATTRS attr=_stat(path); | |
1628 | ||
1629 | attr.setFLAGS(0); | |
1630 | attr.setACMODTIME(attr.getATime(), mtime); | |
1631 | _setStat(path, attr); | |
1632 | } | |
1633 | } | |
1634 | catch(Exception e){ | |
1635 | if(e instanceof SftpException) throw (SftpException)e; | |
1636 | if(e instanceof Throwable) | |
1637 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1638 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1639 | } | |
1640 | } | |
1641 | ||
1642 | public void rmdir(String path) throws SftpException{ | |
1643 | try{ | |
1644 | path=remoteAbsolutePath(path); | |
1645 | ||
1646 | Vector v=glob_remote(path); | |
1647 | int vsize=v.size(); | |
1648 | ||
1649 | Header header=new Header(); | |
1650 | ||
1651 | for(int j=0; j<vsize; j++){ | |
1652 | path=(String)(v.elementAt(j)); | |
1653 | sendRMDIR(Util.str2byte(path, fEncoding)); | |
1654 | ||
1655 | header=header(buf, header); | |
1656 | int length=header.length; | |
1657 | int type=header.type; | |
1658 | ||
1659 | fill(buf, length); | |
1660 | ||
1661 | if(type!=SSH_FXP_STATUS){ | |
1662 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1663 | } | |
1664 | ||
1665 | int i=buf.getInt(); | |
1666 | if(i!=SSH_FX_OK){ | |
1667 | throwStatusError(buf, i); | |
1668 | } | |
1669 | } | |
1670 | } | |
1671 | catch(Exception e){ | |
1672 | if(e instanceof SftpException) throw (SftpException)e; | |
1673 | if(e instanceof Throwable) | |
1674 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1675 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1676 | } | |
1677 | } | |
1678 | ||
1679 | public void mkdir(String path) throws SftpException{ | |
1680 | try{ | |
1681 | path=remoteAbsolutePath(path); | |
1682 | ||
1683 | sendMKDIR(Util.str2byte(path, fEncoding), null); | |
1684 | ||
1685 | Header header=new Header(); | |
1686 | header=header(buf, header); | |
1687 | int length=header.length; | |
1688 | int type=header.type; | |
1689 | ||
1690 | fill(buf, length); | |
1691 | ||
1692 | if(type!=SSH_FXP_STATUS){ | |
1693 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1694 | } | |
1695 | ||
1696 | int i=buf.getInt(); | |
1697 | if(i==SSH_FX_OK) return; | |
1698 | throwStatusError(buf, i); | |
1699 | } | |
1700 | catch(Exception e){ | |
1701 | if(e instanceof SftpException) throw (SftpException)e; | |
1702 | if(e instanceof Throwable) | |
1703 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1704 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1705 | } | |
1706 | } | |
1707 | ||
1708 | public SftpATTRS stat(String path) throws SftpException{ | |
1709 | try{ | |
1710 | path=remoteAbsolutePath(path); | |
1711 | ||
1712 | path=isUnique(path); | |
1713 | ||
1714 | return _stat(path); | |
1715 | } | |
1716 | catch(Exception e){ | |
1717 | if(e instanceof SftpException) throw (SftpException)e; | |
1718 | if(e instanceof Throwable) | |
1719 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1720 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1721 | } | |
1722 | //return null; | |
1723 | } | |
1724 | ||
1725 | private SftpATTRS _stat(byte[] path) throws SftpException{ | |
1726 | try{ | |
1727 | ||
1728 | sendSTAT(path); | |
1729 | ||
1730 | Header header=new Header(); | |
1731 | header=header(buf, header); | |
1732 | int length=header.length; | |
1733 | int type=header.type; | |
1734 | ||
1735 | fill(buf, length); | |
1736 | ||
1737 | if(type!=SSH_FXP_ATTRS){ | |
1738 | if(type==SSH_FXP_STATUS){ | |
1739 | int i=buf.getInt(); | |
1740 | throwStatusError(buf, i); | |
1741 | } | |
1742 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1743 | } | |
1744 | SftpATTRS attr=SftpATTRS.getATTR(buf); | |
1745 | return attr; | |
1746 | } | |
1747 | catch(Exception e){ | |
1748 | if(e instanceof SftpException) throw (SftpException)e; | |
1749 | if(e instanceof Throwable) | |
1750 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1751 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1752 | } | |
1753 | //return null; | |
1754 | } | |
1755 | ||
1756 | private SftpATTRS _stat(String path) throws SftpException{ | |
1757 | return _stat(Util.str2byte(path, fEncoding)); | |
1758 | } | |
1759 | ||
1760 | public SftpATTRS lstat(String path) throws SftpException{ | |
1761 | try{ | |
1762 | path=remoteAbsolutePath(path); | |
1763 | ||
1764 | path=isUnique(path); | |
1765 | ||
1766 | return _lstat(path); | |
1767 | } | |
1768 | catch(Exception e){ | |
1769 | if(e instanceof SftpException) throw (SftpException)e; | |
1770 | if(e instanceof Throwable) | |
1771 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1772 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1773 | } | |
1774 | } | |
1775 | ||
1776 | private SftpATTRS _lstat(String path) throws SftpException{ | |
1777 | try{ | |
1778 | sendLSTAT(Util.str2byte(path, fEncoding)); | |
1779 | ||
1780 | Header header=new Header(); | |
1781 | header=header(buf, header); | |
1782 | int length=header.length; | |
1783 | int type=header.type; | |
1784 | ||
1785 | fill(buf, length); | |
1786 | ||
1787 | if(type!=SSH_FXP_ATTRS){ | |
1788 | if(type==SSH_FXP_STATUS){ | |
1789 | int i=buf.getInt(); | |
1790 | throwStatusError(buf, i); | |
1791 | } | |
1792 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1793 | } | |
1794 | SftpATTRS attr=SftpATTRS.getATTR(buf); | |
1795 | return attr; | |
1796 | } | |
1797 | catch(Exception e){ | |
1798 | if(e instanceof SftpException) throw (SftpException)e; | |
1799 | if(e instanceof Throwable) | |
1800 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1801 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1802 | } | |
1803 | } | |
1804 | ||
1805 | private byte[] _realpath(String path) throws SftpException, IOException, Exception{ | |
1806 | sendREALPATH(Util.str2byte(path, fEncoding)); | |
1807 | ||
1808 | Header header=new Header(); | |
1809 | header=header(buf, header); | |
1810 | int length=header.length; | |
1811 | int type=header.type; | |
1812 | ||
1813 | fill(buf, length); | |
1814 | ||
1815 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ | |
1816 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1817 | } | |
1818 | int i; | |
1819 | if(type==SSH_FXP_STATUS){ | |
1820 | i=buf.getInt(); | |
1821 | throwStatusError(buf, i); | |
1822 | } | |
1823 | i=buf.getInt(); // count | |
1824 | ||
1825 | byte[] str=null; | |
1826 | while(i-->0){ | |
1827 | str=buf.getString(); // absolute path; | |
1828 | if(server_version<=3){ | |
1829 | byte[] lname=buf.getString(); // long filename | |
1830 | } | |
1831 | SftpATTRS attr=SftpATTRS.getATTR(buf); // dummy attribute | |
1832 | } | |
1833 | return str; | |
1834 | } | |
1835 | ||
1836 | public void setStat(String path, SftpATTRS attr) throws SftpException{ | |
1837 | try{ | |
1838 | path=remoteAbsolutePath(path); | |
1839 | ||
1840 | Vector v=glob_remote(path); | |
1841 | int vsize=v.size(); | |
1842 | for(int j=0; j<vsize; j++){ | |
1843 | path=(String)(v.elementAt(j)); | |
1844 | _setStat(path, attr); | |
1845 | } | |
1846 | } | |
1847 | catch(Exception e){ | |
1848 | if(e instanceof SftpException) throw (SftpException)e; | |
1849 | if(e instanceof Throwable) | |
1850 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1851 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1852 | } | |
1853 | } | |
1854 | private void _setStat(String path, SftpATTRS attr) throws SftpException{ | |
1855 | try{ | |
1856 | sendSETSTAT(Util.str2byte(path, fEncoding), attr); | |
1857 | ||
1858 | Header header=new Header(); | |
1859 | header=header(buf, header); | |
1860 | int length=header.length; | |
1861 | int type=header.type; | |
1862 | ||
1863 | fill(buf, length); | |
1864 | ||
1865 | if(type!=SSH_FXP_STATUS){ | |
1866 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1867 | } | |
1868 | int i=buf.getInt(); | |
1869 | if(i!=SSH_FX_OK){ | |
1870 | throwStatusError(buf, i); | |
1871 | } | |
1872 | } | |
1873 | catch(Exception e){ | |
1874 | if(e instanceof SftpException) throw (SftpException)e; | |
1875 | if(e instanceof Throwable) | |
1876 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1877 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1878 | } | |
1879 | } | |
1880 | ||
1881 | public String pwd() throws SftpException{ return getCwd(); } | |
1882 | public String lpwd(){ return lcwd; } | |
1883 | public String version(){ return version; } | |
1884 | public String getHome() throws SftpException { | |
1885 | if(home==null){ | |
1886 | try{ | |
1887 | byte[] _home=_realpath(""); | |
1888 | home=Util.byte2str(_home, fEncoding); | |
1889 | } | |
1890 | catch(Exception e){ | |
1891 | if(e instanceof SftpException) throw (SftpException)e; | |
1892 | if(e instanceof Throwable) | |
1893 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
1894 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1895 | } | |
1896 | } | |
1897 | return home; | |
1898 | } | |
1899 | ||
1900 | private String getCwd() throws SftpException{ | |
1901 | if(cwd==null) | |
1902 | cwd=getHome(); | |
1903 | return cwd; | |
1904 | } | |
1905 | ||
1906 | private void setCwd(String cwd){ | |
1907 | this.cwd=cwd; | |
1908 | } | |
1909 | ||
1910 | private void read(byte[] buf, int s, int l) throws IOException, SftpException{ | |
1911 | int i=0; | |
1912 | while(l>0){ | |
1913 | i=io_in.read(buf, s, l); | |
1914 | if(i<=0){ | |
1915 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1916 | } | |
1917 | s+=i; | |
1918 | l-=i; | |
1919 | } | |
1920 | } | |
1921 | ||
1922 | private boolean checkStatus(int[] ackid, Header header) throws IOException, SftpException{ | |
1923 | header=header(buf, header); | |
1924 | int length=header.length; | |
1925 | int type=header.type; | |
1926 | if(ackid!=null) | |
1927 | ackid[0]=header.rid; | |
1928 | ||
1929 | fill(buf, length); | |
1930 | ||
1931 | if(type!=SSH_FXP_STATUS){ | |
1932 | throw new SftpException(SSH_FX_FAILURE, ""); | |
1933 | } | |
1934 | int i=buf.getInt(); | |
1935 | if(i!=SSH_FX_OK){ | |
1936 | throwStatusError(buf, i); | |
1937 | } | |
1938 | return true; | |
1939 | } | |
1940 | private boolean _sendCLOSE(byte[] handle, Header header) throws Exception{ | |
1941 | sendCLOSE(handle); | |
1942 | return checkStatus(null, header); | |
1943 | } | |
1944 | ||
1945 | private void sendINIT() throws Exception{ | |
1946 | packet.reset(); | |
1947 | putHEAD(SSH_FXP_INIT, 5); | |
1948 | buf.putInt(3); // version 3 | |
1949 | getSession().write(packet, this, 5+4); | |
1950 | } | |
1951 | ||
1952 | private void sendREALPATH(byte[] path) throws Exception{ | |
1953 | sendPacketPath(SSH_FXP_REALPATH, path); | |
1954 | } | |
1955 | private void sendSTAT(byte[] path) throws Exception{ | |
1956 | sendPacketPath(SSH_FXP_STAT, path); | |
1957 | } | |
1958 | private void sendLSTAT(byte[] path) throws Exception{ | |
1959 | sendPacketPath(SSH_FXP_LSTAT, path); | |
1960 | } | |
1961 | private void sendFSTAT(byte[] handle) throws Exception{ | |
1962 | sendPacketPath(SSH_FXP_FSTAT, handle); | |
1963 | } | |
1964 | private void sendSETSTAT(byte[] path, SftpATTRS attr) throws Exception{ | |
1965 | packet.reset(); | |
1966 | putHEAD(SSH_FXP_SETSTAT, 9+path.length+attr.length()); | |
1967 | buf.putInt(seq++); | |
1968 | buf.putString(path); // path | |
1969 | attr.dump(buf); | |
1970 | getSession().write(packet, this, 9+path.length+attr.length()+4); | |
1971 | } | |
1972 | private void sendREMOVE(byte[] path) throws Exception{ | |
1973 | sendPacketPath(SSH_FXP_REMOVE, path); | |
1974 | } | |
1975 | private void sendMKDIR(byte[] path, SftpATTRS attr) throws Exception{ | |
1976 | packet.reset(); | |
1977 | putHEAD(SSH_FXP_MKDIR, 9+path.length+(attr!=null?attr.length():4)); | |
1978 | buf.putInt(seq++); | |
1979 | buf.putString(path); // path | |
1980 | if(attr!=null) attr.dump(buf); | |
1981 | else buf.putInt(0); | |
1982 | getSession().write(packet, this, 9+path.length+(attr!=null?attr.length():4)+4); | |
1983 | } | |
1984 | private void sendRMDIR(byte[] path) throws Exception{ | |
1985 | sendPacketPath(SSH_FXP_RMDIR, path); | |
1986 | } | |
1987 | private void sendSYMLINK(byte[] p1, byte[] p2) throws Exception{ | |
1988 | sendPacketPath(SSH_FXP_SYMLINK, p1, p2); | |
1989 | } | |
1990 | private void sendREADLINK(byte[] path) throws Exception{ | |
1991 | sendPacketPath(SSH_FXP_READLINK, path); | |
1992 | } | |
1993 | private void sendOPENDIR(byte[] path) throws Exception{ | |
1994 | sendPacketPath(SSH_FXP_OPENDIR, path); | |
1995 | } | |
1996 | private void sendREADDIR(byte[] path) throws Exception{ | |
1997 | sendPacketPath(SSH_FXP_READDIR, path); | |
1998 | } | |
1999 | private void sendRENAME(byte[] p1, byte[] p2) throws Exception{ | |
2000 | sendPacketPath(SSH_FXP_RENAME, p1, p2); | |
2001 | } | |
2002 | private void sendCLOSE(byte[] path) throws Exception{ | |
2003 | sendPacketPath(SSH_FXP_CLOSE, path); | |
2004 | } | |
2005 | private void sendOPENR(byte[] path) throws Exception{ | |
2006 | sendOPEN(path, SSH_FXF_READ); | |
2007 | } | |
2008 | private void sendOPENW(byte[] path) throws Exception{ | |
2009 | sendOPEN(path, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_TRUNC); | |
2010 | } | |
2011 | private void sendOPENA(byte[] path) throws Exception{ | |
2012 | sendOPEN(path, SSH_FXF_WRITE|/*SSH_FXF_APPEND|*/SSH_FXF_CREAT); | |
2013 | } | |
2014 | private void sendOPEN(byte[] path, int mode) throws Exception{ | |
2015 | packet.reset(); | |
2016 | putHEAD(SSH_FXP_OPEN, 17+path.length); | |
2017 | buf.putInt(seq++); | |
2018 | buf.putString(path); | |
2019 | buf.putInt(mode); | |
2020 | buf.putInt(0); // attrs | |
2021 | getSession().write(packet, this, 17+path.length+4); | |
2022 | } | |
2023 | private void sendPacketPath(byte fxp, byte[] path) throws Exception{ | |
2024 | packet.reset(); | |
2025 | putHEAD(fxp, 9+path.length); | |
2026 | buf.putInt(seq++); | |
2027 | buf.putString(path); // path | |
2028 | getSession().write(packet, this, 9+path.length+4); | |
2029 | } | |
2030 | private void sendPacketPath(byte fxp, byte[] p1, byte[] p2) throws Exception{ | |
2031 | packet.reset(); | |
2032 | putHEAD(fxp, 13+p1.length+p2.length); | |
2033 | buf.putInt(seq++); | |
2034 | buf.putString(p1); | |
2035 | buf.putString(p2); | |
2036 | getSession().write(packet, this, 13+p1.length+p2.length+4); | |
2037 | } | |
2038 | ||
2039 | private int sendWRITE(byte[] handle, long offset, | |
2040 | byte[] data, int start, int length) throws Exception{ | |
2041 | int _length=length; | |
2042 | packet.reset(); | |
2043 | if(buf.buffer.length<buf.index+13+21+handle.length+length | |
2044 | +32 +20 // padding and mac | |
2045 | ){ | |
2046 | _length=buf.buffer.length-(buf.index+13+21+handle.length | |
2047 | +32 +20 // padding and mac | |
2048 | ); | |
2049 | //System.err.println("_length="+_length+" length="+length); | |
2050 | } | |
2051 | ||
2052 | putHEAD(SSH_FXP_WRITE, 21+handle.length+_length); // 14 | |
2053 | buf.putInt(seq++); // 4 | |
2054 | buf.putString(handle); // 4+handle.length | |
2055 | buf.putLong(offset); // 8 | |
2056 | if(buf.buffer!=data){ | |
2057 | buf.putString(data, start, _length); // 4+_length | |
2058 | } | |
2059 | else{ | |
2060 | buf.putInt(_length); | |
2061 | buf.skip(_length); | |
2062 | } | |
2063 | getSession().write(packet, this, 21+handle.length+_length+4); | |
2064 | return _length; | |
2065 | } | |
2066 | ||
2067 | private void sendREAD(byte[] handle, long offset, int length) throws Exception{ | |
2068 | packet.reset(); | |
2069 | putHEAD(SSH_FXP_READ, 21+handle.length); | |
2070 | buf.putInt(seq++); | |
2071 | buf.putString(handle); | |
2072 | buf.putLong(offset); | |
2073 | buf.putInt(length); | |
2074 | getSession().write(packet, this, 21+handle.length+4); | |
2075 | } | |
2076 | ||
2077 | private void putHEAD(byte type, int length) throws Exception{ | |
2078 | buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA); | |
2079 | buf.putInt(recipient); | |
2080 | buf.putInt(length+4); | |
2081 | buf.putInt(length); | |
2082 | buf.putByte(type); | |
2083 | } | |
2084 | ||
2085 | private Vector glob_remote(String _path) throws Exception{ | |
2086 | Vector v=new Vector(); | |
2087 | int i=0; | |
2088 | ||
2089 | int foo=_path.lastIndexOf('/'); | |
2090 | if(foo<0){ // it is not absolute path. | |
2091 | v.addElement(Util.unquote(_path)); | |
2092 | return v; | |
2093 | } | |
2094 | ||
2095 | String dir=_path.substring(0, ((foo==0)?1:foo)); | |
2096 | String _pattern=_path.substring(foo+1); | |
2097 | ||
2098 | dir=Util.unquote(dir); | |
2099 | ||
2100 | byte[] pattern=null; | |
2101 | byte[][] _pattern_utf8=new byte[1][]; | |
2102 | boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8); | |
2103 | ||
2104 | if(!pattern_has_wildcard){ | |
2105 | if(!dir.equals("/")) | |
2106 | dir+="/"; | |
2107 | v.addElement(dir+Util.unquote(_pattern)); | |
2108 | return v; | |
2109 | } | |
2110 | ||
2111 | pattern=_pattern_utf8[0]; | |
2112 | ||
2113 | sendOPENDIR(Util.str2byte(dir, fEncoding)); | |
2114 | ||
2115 | Header header=new Header(); | |
2116 | header=header(buf, header); | |
2117 | int length=header.length; | |
2118 | int type=header.type; | |
2119 | ||
2120 | fill(buf, length); | |
2121 | ||
2122 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){ | |
2123 | throw new SftpException(SSH_FX_FAILURE, ""); | |
2124 | } | |
2125 | if(type==SSH_FXP_STATUS){ | |
2126 | i=buf.getInt(); | |
2127 | throwStatusError(buf, i); | |
2128 | } | |
2129 | ||
2130 | byte[] handle=buf.getString(); // filename | |
2131 | String pdir=null; // parent directory | |
2132 | ||
2133 | while(true){ | |
2134 | sendREADDIR(handle); | |
2135 | header=header(buf, header); | |
2136 | length=header.length; | |
2137 | type=header.type; | |
2138 | ||
2139 | if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){ | |
2140 | throw new SftpException(SSH_FX_FAILURE, ""); | |
2141 | } | |
2142 | if(type==SSH_FXP_STATUS){ | |
2143 | fill(buf, length); | |
2144 | break; | |
2145 | } | |
2146 | ||
2147 | buf.rewind(); | |
2148 | fill(buf.buffer, 0, 4); length-=4; | |
2149 | int count=buf.getInt(); | |
2150 | ||
2151 | byte[] str; | |
2152 | int flags; | |
2153 | ||
2154 | buf.reset(); | |
2155 | while(count>0){ | |
2156 | if(length>0){ | |
2157 | buf.shift(); | |
2158 | int j=(buf.buffer.length>(buf.index+length)) ? length : (buf.buffer.length-buf.index); | |
2159 | i=io_in.read(buf.buffer, buf.index, j); | |
2160 | if(i<=0)break; | |
2161 | buf.index+=i; | |
2162 | length-=i; | |
2163 | } | |
2164 | ||
2165 | byte[] filename=buf.getString(); | |
2166 | //System.err.println("filename: "+new String(filename)); | |
2167 | if(server_version<=3){ | |
2168 | str=buf.getString(); // longname | |
2169 | } | |
2170 | SftpATTRS attrs=SftpATTRS.getATTR(buf); | |
2171 | ||
2172 | byte[] _filename=filename; | |
2173 | String f=null; | |
2174 | boolean found=false; | |
2175 | ||
2176 | if(!fEncoding_is_utf8){ | |
2177 | f=Util.byte2str(filename, fEncoding); | |
2178 | _filename=Util.str2byte(f, UTF8); | |
2179 | } | |
2180 | found=Util.glob(pattern, _filename); | |
2181 | ||
2182 | if(found){ | |
2183 | if(f==null){ | |
2184 | f=Util.byte2str(filename, fEncoding); | |
2185 | } | |
2186 | if(pdir==null){ | |
2187 | pdir=dir; | |
2188 | if(!pdir.endsWith("/")){ | |
2189 | pdir+="/"; | |
2190 | } | |
2191 | } | |
2192 | v.addElement(pdir+f); | |
2193 | } | |
2194 | count--; | |
2195 | } | |
2196 | } | |
2197 | if(_sendCLOSE(handle, header)) | |
2198 | return v; | |
2199 | return null; | |
2200 | } | |
2201 | ||
2202 | private boolean isPattern(byte[] path){ | |
2203 | int i=path.length-1; | |
2204 | while(i>=0){ | |
2205 | if(path[i]=='*' || path[i]=='?'){ | |
2206 | if(i>0 && path[i-1]=='\\'){ | |
2207 | i--; | |
2208 | if(i>0 && path[i-1]=='\\'){ // \\* or \\? | |
2209 | break; | |
2210 | } | |
2211 | } | |
2212 | else{ | |
2213 | break; | |
2214 | } | |
2215 | } | |
2216 | i--; | |
2217 | } | |
2218 | // System.err.println("isPattern: ["+(new String(path))+"] "+(!(i<0))); | |
2219 | return !(i<0); | |
2220 | } | |
2221 | ||
2222 | private Vector glob_local(String _path) throws Exception{ | |
2223 | //System.err.println("glob_local: "+_path); | |
2224 | Vector v=new Vector(); | |
2225 | byte[] path=Util.str2byte(_path, UTF8); | |
2226 | int i=path.length-1; | |
2227 | while(i>=0){ | |
2228 | if(path[i]!='*' && path[i]!='?'){ | |
2229 | i--; | |
2230 | continue; | |
2231 | } | |
2232 | if(!fs_is_bs && | |
2233 | i>0 && path[i-1]=='\\'){ | |
2234 | i--; | |
2235 | if(i>0 && path[i-1]=='\\'){ | |
2236 | i--; | |
2237 | i--; | |
2238 | continue; | |
2239 | } | |
2240 | } | |
2241 | break; | |
2242 | } | |
2243 | ||
2244 | if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;} | |
2245 | ||
2246 | while(i>=0){ | |
2247 | if(path[i]==file_separatorc || | |
2248 | (fs_is_bs && path[i]=='/')){ // On Windows, '/' is also the separator. | |
2249 | break; | |
2250 | } | |
2251 | i--; | |
2252 | } | |
2253 | ||
2254 | if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;} | |
2255 | ||
2256 | byte[] dir; | |
2257 | if(i==0){dir=new byte[]{(byte)file_separatorc};} | |
2258 | else{ | |
2259 | dir=new byte[i]; | |
2260 | System.arraycopy(path, 0, dir, 0, i); | |
2261 | } | |
2262 | ||
2263 | byte[] pattern=new byte[path.length-i-1]; | |
2264 | System.arraycopy(path, i+1, pattern, 0, pattern.length); | |
2265 | ||
2266 | //System.err.println("dir: "+new String(dir)+" pattern: "+new String(pattern)); | |
2267 | try{ | |
2268 | String[] children=(new File(Util.byte2str(dir, UTF8))).list(); | |
2269 | String pdir=Util.byte2str(dir)+file_separator; | |
2270 | for(int j=0; j<children.length; j++){ | |
2271 | //System.err.println("children: "+children[j]); | |
2272 | if(Util.glob(pattern, Util.str2byte(children[j], UTF8))){ | |
2273 | v.addElement(pdir+children[j]); | |
2274 | } | |
2275 | } | |
2276 | } | |
2277 | catch(Exception e){ | |
2278 | } | |
2279 | return v; | |
2280 | } | |
2281 | ||
2282 | private void throwStatusError(Buffer buf, int i) throws SftpException{ | |
2283 | if(server_version>=3 && // WindRiver's sftp will send invalid | |
2284 | buf.getLength()>=4){ // SSH_FXP_STATUS packet. | |
2285 | byte[] str=buf.getString(); | |
2286 | //byte[] tag=buf.getString(); | |
2287 | throw new SftpException(i, Util.byte2str(str, UTF8)); | |
2288 | } | |
2289 | else{ | |
2290 | throw new SftpException(i, "Failure"); | |
2291 | } | |
2292 | } | |
2293 | ||
2294 | private static boolean isLocalAbsolutePath(String path){ | |
2295 | return (new File(path)).isAbsolute(); | |
2296 | } | |
2297 | ||
2298 | public void disconnect(){ | |
2299 | super.disconnect(); | |
2300 | } | |
2301 | ||
2302 | private boolean isPattern(String path, byte[][] utf8){ | |
2303 | byte[] _path=Util.str2byte(path, UTF8); | |
2304 | if(utf8!=null) | |
2305 | utf8[0]=_path; | |
2306 | return isPattern(_path); | |
2307 | } | |
2308 | ||
2309 | private boolean isPattern(String path){ | |
2310 | return isPattern(path, null); | |
2311 | } | |
2312 | ||
2313 | private void fill(Buffer buf, int len) throws IOException{ | |
2314 | buf.reset(); | |
2315 | fill(buf.buffer, 0, len); | |
2316 | buf.skip(len); | |
2317 | } | |
2318 | ||
2319 | private int fill(byte[] buf, int s, int len) throws IOException{ | |
2320 | int i=0; | |
2321 | int foo=s; | |
2322 | while(len>0){ | |
2323 | i=io_in.read(buf, s, len); | |
2324 | if(i<=0){ | |
2325 | throw new IOException("inputstream is closed"); | |
2326 | //return (s-foo)==0 ? i : s-foo; | |
2327 | } | |
2328 | s+=i; | |
2329 | len-=i; | |
2330 | } | |
2331 | return s-foo; | |
2332 | } | |
2333 | private void skip(long foo) throws IOException{ | |
2334 | while(foo>0){ | |
2335 | long bar=io_in.skip(foo); | |
2336 | if(bar<=0) | |
2337 | break; | |
2338 | foo-=bar; | |
2339 | } | |
2340 | } | |
2341 | ||
2342 | class Header{ | |
2343 | int length; | |
2344 | int type; | |
2345 | int rid; | |
2346 | } | |
2347 | private Header header(Buffer buf, Header header) throws IOException{ | |
2348 | buf.rewind(); | |
2349 | int i=fill(buf.buffer, 0, 9); | |
2350 | header.length=buf.getInt()-5; | |
2351 | header.type=buf.getByte()&0xff; | |
2352 | header.rid=buf.getInt(); | |
2353 | return header; | |
2354 | } | |
2355 | ||
2356 | private String remoteAbsolutePath(String path) throws SftpException{ | |
2357 | if(path.charAt(0)=='/') return path; | |
2358 | String cwd=getCwd(); | |
2359 | // if(cwd.equals(getHome())) return path; | |
2360 | if(cwd.endsWith("/")) return cwd+path; | |
2361 | return cwd+"/"+path; | |
2362 | } | |
2363 | ||
2364 | private String localAbsolutePath(String path){ | |
2365 | if(isLocalAbsolutePath(path)) return path; | |
2366 | if(lcwd.endsWith(file_separator)) return lcwd+path; | |
2367 | return lcwd+file_separator+path; | |
2368 | } | |
2369 | ||
2370 | /** | |
2371 | * This method will check if the given string can be expanded to the | |
2372 | * unique string. If it can be expanded to mutiple files, SftpException | |
2373 | * will be thrown. | |
2374 | * @return the returned string is unquoted. | |
2375 | */ | |
2376 | private String isUnique(String path) throws SftpException, Exception{ | |
2377 | Vector v=glob_remote(path); | |
2378 | if(v.size()!=1){ | |
2379 | throw new SftpException(SSH_FX_FAILURE, path+" is not unique: "+v.toString()); | |
2380 | } | |
2381 | return (String)(v.elementAt(0)); | |
2382 | } | |
2383 | ||
2384 | public int getServerVersion() throws SftpException{ | |
2385 | if(!isConnected()){ | |
2386 | throw new SftpException(SSH_FX_FAILURE, "The channel is not connected."); | |
2387 | } | |
2388 | return server_version; | |
2389 | } | |
2390 | ||
2391 | public void setFilenameEncoding(String encoding) throws SftpException{ | |
2392 | int sversion=getServerVersion(); | |
2393 | if(sversion > 3 && | |
2394 | !encoding.equals(UTF8)){ | |
2395 | throw new SftpException(SSH_FX_FAILURE, | |
2396 | "The encoding can not be changed for this sftp server."); | |
2397 | } | |
2398 | if(encoding.equals(UTF8)){ | |
2399 | encoding=UTF8; | |
2400 | } | |
2401 | fEncoding=encoding; | |
2402 | fEncoding_is_utf8=fEncoding.equals(UTF8); | |
2403 | } | |
2404 | ||
2405 | public String getExtension(String key){ | |
2406 | if(extensions==null) | |
2407 | return null; | |
2408 | return (String)extensions.get(key); | |
2409 | } | |
2410 | ||
2411 | public String realpath(String path) throws SftpException{ | |
2412 | try{ | |
2413 | byte[] _path=_realpath(remoteAbsolutePath(path)); | |
2414 | return Util.byte2str(_path, fEncoding); | |
2415 | } | |
2416 | catch(Exception e){ | |
2417 | if(e instanceof SftpException) throw (SftpException)e; | |
2418 | if(e instanceof Throwable) | |
2419 | throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e); | |
2420 | throw new SftpException(SSH_FX_FAILURE, ""); | |
2421 | } | |
2422 | } | |
2423 | ||
2424 | public class LsEntry implements Comparable{ | |
2425 | private String filename; | |
2426 | private String longname; | |
2427 | private SftpATTRS attrs; | |
2428 | LsEntry(String filename, String longname, SftpATTRS attrs){ | |
2429 | setFilename(filename); | |
2430 | setLongname(longname); | |
2431 | setAttrs(attrs); | |
2432 | } | |
2433 | public String getFilename(){return filename;}; | |
2434 | void setFilename(String filename){this.filename = filename;}; | |
2435 | public String getLongname(){return longname;}; | |
2436 | void setLongname(String longname){this.longname = longname;}; | |
2437 | public SftpATTRS getAttrs(){return attrs;}; | |
2438 | void setAttrs(SftpATTRS attrs) {this.attrs = attrs;}; | |
2439 | public String toString(){ return longname; } | |
2440 | public int compareTo(Object o) throws ClassCastException{ | |
2441 | if(o instanceof LsEntry){ | |
2442 | return filename.compareTo(((LsEntry)o).getFilename()); | |
2443 | } | |
2444 | throw new ClassCastException("a decendent of LsEntry must be given."); | |
2445 | } | |
2446 | } | |
2447 | } |