1 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
2 /*
3 Copyright (c) 2002-2015 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   static private final int LOCAL_MAXIMUM_PACKET_SIZE=32*1024;
39   static private final int LOCAL_WINDOW_SIZE_MAX=(64*LOCAL_MAXIMUM_PACKET_SIZE);
40 
41   private static final byte SSH_FXP_INIT=               1;
42   private static final byte SSH_FXP_VERSION=            2;
43   private static final byte SSH_FXP_OPEN=               3;
44   private static final byte SSH_FXP_CLOSE=              4;
45   private static final byte SSH_FXP_READ=               5;
46   private static final byte SSH_FXP_WRITE=              6;
47   private static final byte SSH_FXP_LSTAT=              7;
48   private static final byte SSH_FXP_FSTAT=              8;
49   private static final byte SSH_FXP_SETSTAT=            9;
50   private static final byte SSH_FXP_FSETSTAT=          10;
51   private static final byte SSH_FXP_OPENDIR=           11;
52   private static final byte SSH_FXP_READDIR=           12;
53   private static final byte SSH_FXP_REMOVE=            13;
54   private static final byte SSH_FXP_MKDIR=             14;
55   private static final byte SSH_FXP_RMDIR=             15;
56   private static final byte SSH_FXP_REALPATH=          16;
57   private static final byte SSH_FXP_STAT=              17;
58   private static final byte SSH_FXP_RENAME=            18;
59   private static final byte SSH_FXP_READLINK=          19;
60   private static final byte SSH_FXP_SYMLINK=           20;
61   private static final byte SSH_FXP_STATUS=           101;
62   private static final byte SSH_FXP_HANDLE=           102;
63   private static final byte SSH_FXP_DATA=             103;
64   private static final byte SSH_FXP_NAME=             104;
65   private static final byte SSH_FXP_ATTRS=            105;
66   private static final byte SSH_FXP_EXTENDED=         (byte)200;
67   private static final byte SSH_FXP_EXTENDED_REPLY=   (byte)201;
68 
69   // pflags
70   private static final int SSH_FXF_READ=           0x00000001;
71   private static final int SSH_FXF_WRITE=          0x00000002;
72   private static final int SSH_FXF_APPEND=         0x00000004;
73   private static final int SSH_FXF_CREAT=          0x00000008;
74   private static final int SSH_FXF_TRUNC=          0x00000010;
75   private static final int SSH_FXF_EXCL=           0x00000020;
76 
77   private static final int SSH_FILEXFER_ATTR_SIZE=         0x00000001;
78   private static final int SSH_FILEXFER_ATTR_UIDGID=       0x00000002;
79   private static final int SSH_FILEXFER_ATTR_PERMISSIONS=  0x00000004;
80   private static final int SSH_FILEXFER_ATTR_ACMODTIME=    0x00000008;
81   private static final int SSH_FILEXFER_ATTR_EXTENDED=     0x80000000;
82 
83   public static final int SSH_FX_OK=                            0;
84   public static final int SSH_FX_EOF=                           1;
85   public static final int SSH_FX_NO_SUCH_FILE=                  2;
86   public static final int SSH_FX_PERMISSION_DENIED=             3;
87   public static final int SSH_FX_FAILURE=                       4;
88   public static final int SSH_FX_BAD_MESSAGE=                   5;
89   public static final int SSH_FX_NO_CONNECTION=                 6;
90   public static final int SSH_FX_CONNECTION_LOST=               7;
91   public static final int SSH_FX_OP_UNSUPPORTED=                8;
92 /*
93    SSH_FX_OK
94       Indicates successful completion of the operation.
95    SSH_FX_EOF
96      indicates end-of-file condition; for SSH_FX_READ it means that no
97        more data is available in the file, and for SSH_FX_READDIR it
98       indicates that no more files are contained in the directory.
99    SSH_FX_NO_SUCH_FILE
100       is returned when a reference is made to a file which should exist
101       but doesn't.
102    SSH_FX_PERMISSION_DENIED
103       is returned when the authenticated user does not have sufficient
104       permissions to perform the operation.
105    SSH_FX_FAILURE
106       is a generic catch-all error message; it should be returned if an
107       error occurs for which there is no more specific error code
108       defined.
109    SSH_FX_BAD_MESSAGE
110       may be returned if a badly formatted packet or protocol
111       incompatibility is detected.
112    SSH_FX_NO_CONNECTION
113       is a pseudo-error which indicates that the client has no
114       connection to the server (it can only be generated locally by the
115       client, and MUST NOT be returned by servers).
116    SSH_FX_CONNECTION_LOST
117       is a pseudo-error which indicates that the connection to the
118       server has been lost (it can only be generated locally by the
119       client, and MUST NOT be returned by servers).
120    SSH_FX_OP_UNSUPPORTED
121       indicates that an attempt was made to perform an operation which
122       is not supported for the server (it may be generated locally by
123       the client if e.g.  the version number exchange indicates that a
124       required feature is not supported by the server, or it may be
125       returned by the server if the server does not implement an
126       operation).
127 */
128   private static final int MAX_MSG_LENGTH = 256* 1024;
129 
130   public static final int OVERWRITE=0;
131   public static final int RESUME=1;
132   public static final int APPEND=2;
133 
134   private boolean interactive=false;
135   private int seq=1;
136   private int[] ackid=new int[1];
137 
138   private Buffer buf;
139   private Packet packet;
140 
141   // The followings will be used in file uploading.
142   private Buffer obuf;
143   private Packet opacket;
144 
145   private int client_version=3;
146   private int server_version=3;
147   private String version=String.valueOf(client_version);
148 
149   private java.util.Hashtable extensions=null;
150   private InputStream io_in=null;
151 
152   private boolean extension_posix_rename = false;
153   private boolean extension_statvfs = false;
154   // private boolean extension_fstatvfs = false;
155   private boolean extension_hardlink = false;
156 
157 /*
158 10. Changes from previous protocol versions
159   The SSH File Transfer Protocol has changed over time, before it's
160    standardization.  The following is a description of the incompatible
161    changes between different versions.
162 10.1 Changes between versions 3 and 2
163    o  The SSH_FXP_READLINK and SSH_FXP_SYMLINK messages were added.
164    o  The SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY messages were added.
165    o  The SSH_FXP_STATUS message was changed to include fields `error
166       message' and `language tag'.
167 10.2 Changes between versions 2 and 1
168    o  The SSH_FXP_RENAME message was added.
169 10.3 Changes between versions 1 and 0
170    o  Implementation changes, no actual protocol changes.
171 */
172 
173   private static final String file_separator=java.io.File.separator;
174   private static final char file_separatorc=java.io.File.separatorChar;
175   private static boolean fs_is_bs=(byte)java.io.File.separatorChar == '\\';
176 
177   private String cwd;
178   private String home;
179   private String lcwd;
180 
181   private static final String UTF8="UTF-8";
182   private String fEncoding=UTF8;
183   private boolean fEncoding_is_utf8=true;
184 
185   private RequestQueue rq = new RequestQueue(16);
186 
187   /**
188    * Specify how many requests may be sent at any one time.
189    * Increasing this value may slightly improve file transfer speed but will
190    * increase memory usage.  The default is 16 requests.
191    *
192    * @param bulk_requests how many requests may be outstanding at any one time.
193    */
setBulkRequests(int bulk_requests)194   public void setBulkRequests(int bulk_requests) throws JSchException {
195     if(bulk_requests>0)
196       rq = new RequestQueue(bulk_requests);
197     else
198       throw new JSchException("setBulkRequests: "+
199                               bulk_requests+" must be greater than 0.");
200   }
201 
202   /**
203    * This method will return the value how many requests may be
204    * sent at any one time.
205    *
206    * @return how many requests may be sent at any one time.
207    */
getBulkRequests()208   public int getBulkRequests(){
209     return rq.size();
210   }
211 
ChannelSftp()212   public ChannelSftp(){
213     super();
214     setLocalWindowSizeMax(LOCAL_WINDOW_SIZE_MAX);
215     setLocalWindowSize(LOCAL_WINDOW_SIZE_MAX);
216     setLocalPacketSize(LOCAL_MAXIMUM_PACKET_SIZE);
217   }
218 
init()219   void init(){
220   }
221 
start()222   public void start() throws JSchException{
223     try{
224 
225       PipedOutputStream pos=new PipedOutputStream();
226       io.setOutputStream(pos);
227       PipedInputStream pis=new MyPipedInputStream(pos, rmpsize);
228       io.setInputStream(pis);
229 
230       io_in=io.in;
231 
232       if(io_in==null){
233         throw new JSchException("channel is down");
234       }
235 
236       Request request=new RequestSftp();
237       request.request(getSession(), this);
238 
239       /*
240       System.err.println("lmpsize: "+lmpsize);
241       System.err.println("lwsize: "+lwsize);
242       System.err.println("rmpsize: "+rmpsize);
243       System.err.println("rwsize: "+rwsize);
244       */
245 
246       buf=new Buffer(lmpsize);
247       packet=new Packet(buf);
248 
249       obuf=new Buffer(rmpsize);
250       opacket=new Packet(obuf);
251 
252       int i=0;
253       int length;
254       int type;
255       byte[] str;
256 
257       // send SSH_FXP_INIT
258       sendINIT();
259 
260       // receive SSH_FXP_VERSION
261       Header header=new Header();
262       header=header(buf, header);
263       length=header.length;
264       if(length > MAX_MSG_LENGTH){
265         throw new SftpException(SSH_FX_FAILURE,
266                                 "Received message is too long: " + length);
267       }
268       type=header.type;             // 2 -> SSH_FXP_VERSION
269       server_version=header.rid;
270       //System.err.println("SFTP protocol server-version="+server_version);
271       extensions=new java.util.Hashtable();
272       if(length>0){
273         // extension data
274         fill(buf, length);
275         byte[] extension_name=null;
276         byte[] extension_data=null;
277         while(length>0){
278           extension_name=buf.getString();
279           length-=(4+extension_name.length);
280           extension_data=buf.getString();
281           length-=(4+extension_data.length);
282           extensions.put(Util.byte2str(extension_name),
283                          Util.byte2str(extension_data));
284         }
285       }
286 
287       if(extensions.get("posix-rename@openssh.com")!=null &&
288          extensions.get("posix-rename@openssh.com").equals("1")){
289         extension_posix_rename = true;
290       }
291 
292       if(extensions.get("statvfs@openssh.com")!=null &&
293          extensions.get("statvfs@openssh.com").equals("2")){
294         extension_statvfs = true;
295       }
296 
297       /*
298       if(extensions.get("fstatvfs@openssh.com")!=null &&
299          extensions.get("fstatvfs@openssh.com").equals("2")){
300         extension_fstatvfs = true;
301       }
302       */
303 
304       if(extensions.get("hardlink@openssh.com")!=null &&
305          extensions.get("hardlink@openssh.com").equals("1")){
306         extension_hardlink = true;
307       }
308 
309       lcwd=new File(".").getCanonicalPath();
310     }
311     catch(Exception e){
312       //System.err.println(e);
313       if(e instanceof JSchException) throw (JSchException)e;
314       if(e instanceof Throwable)
315         throw new JSchException(e.toString(), (Throwable)e);
316       throw new JSchException(e.toString());
317     }
318   }
319 
quit()320   public void quit(){ disconnect();}
exit()321   public void exit(){ disconnect();}
lcd(String path)322   public void lcd(String path) throws SftpException{
323     path=localAbsolutePath(path);
324     if((new File(path)).isDirectory()){
325       try{
326 	path=(new File(path)).getCanonicalPath();
327       }
328       catch(Exception e){}
329       lcwd=path;
330       return;
331     }
332     throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such directory");
333   }
334 
cd(String path)335   public void cd(String path) throws SftpException{
336     try{
337       ((MyPipedInputStream)io_in).updateReadSide();
338 
339       path=remoteAbsolutePath(path);
340       path=isUnique(path);
341 
342       byte[] str=_realpath(path);
343       SftpATTRS attr=_stat(str);
344 
345       if((attr.getFlags()&SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS)==0){
346         throw new SftpException(SSH_FX_FAILURE,
347                                 "Can't change directory: "+path);
348       }
349       if(!attr.isDir()){
350         throw new SftpException(SSH_FX_FAILURE,
351                                 "Can't change directory: "+path);
352       }
353 
354       setCwd(Util.byte2str(str, fEncoding));
355     }
356     catch(Exception e){
357       if(e instanceof SftpException) throw (SftpException)e;
358       if(e instanceof Throwable)
359         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
360       throw new SftpException(SSH_FX_FAILURE, "");
361     }
362   }
363 
put(String src, String dst)364   public void put(String src, String dst) throws SftpException{
365     put(src, dst, null, OVERWRITE);
366   }
put(String src, String dst, int mode)367   public void put(String src, String dst, int mode) throws SftpException{
368     put(src, dst, null, mode);
369   }
put(String src, String dst, SftpProgressMonitor monitor)370   public void put(String src, String dst,
371 		  SftpProgressMonitor monitor) throws SftpException{
372     put(src, dst, monitor, OVERWRITE);
373   }
374 
375   /**
376    * Sends data from <code>src</code> file to <code>dst</code> file.
377    * The <code>mode</code> should be <code>OVERWRITE</code>,
378    * <code>RESUME</code> or <code>APPEND</code>.
379    *
380    * @param src source file
381    * @param dst destination file
382    * @param monitor progress monitor
383    * @param mode how data should be added to dst
384    */
put(String src, String dst, SftpProgressMonitor monitor, int mode)385   public void put(String src, String dst,
386 		  SftpProgressMonitor monitor, int mode) throws SftpException{
387 
388     try{
389       ((MyPipedInputStream)io_in).updateReadSide();
390 
391       src=localAbsolutePath(src);
392       dst=remoteAbsolutePath(dst);
393 
394       Vector v=glob_remote(dst);
395       int vsize=v.size();
396       if(vsize!=1){
397         if(vsize==0){
398           if(isPattern(dst))
399             throw new SftpException(SSH_FX_FAILURE, dst);
400           else
401             dst=Util.unquote(dst);
402         }
403         throw new SftpException(SSH_FX_FAILURE, v.toString());
404       }
405       else{
406         dst=(String)(v.elementAt(0));
407       }
408 
409       boolean isRemoteDir=isRemoteDir(dst);
410 
411       v=glob_local(src);
412       vsize=v.size();
413 
414       StringBuffer dstsb=null;
415       if(isRemoteDir){
416         if(!dst.endsWith("/")){
417 	    dst+="/";
418         }
419         dstsb=new StringBuffer(dst);
420       }
421       else if(vsize>1){
422         throw new SftpException(SSH_FX_FAILURE,
423                                 "Copying multiple files, but the destination is missing or a file.");
424       }
425 
426       for(int j=0; j<vsize; j++){
427 	String _src=(String)(v.elementAt(j));
428 	String _dst=null;
429 	if(isRemoteDir){
430 	  int i=_src.lastIndexOf(file_separatorc);
431           if(fs_is_bs){
432             int ii=_src.lastIndexOf('/');
433             if(ii!=-1 && ii>i)
434               i=ii;
435           }
436 	  if(i==-1) dstsb.append(_src);
437 	  else dstsb.append(_src.substring(i + 1));
438           _dst=dstsb.toString();
439           dstsb.delete(dst.length(), _dst.length());
440 	}
441         else{
442           _dst=dst;
443         }
444         //System.err.println("_dst "+_dst);
445 
446 	long size_of_dst=0;
447 	if(mode==RESUME){
448 	  try{
449 	    SftpATTRS attr=_stat(_dst);
450 	    size_of_dst=attr.getSize();
451 	  }
452 	  catch(Exception eee){
453 	    //System.err.println(eee);
454 	  }
455 	  long size_of_src=new File(_src).length();
456 	  if(size_of_src<size_of_dst){
457 	    throw new SftpException(SSH_FX_FAILURE,
458                                     "failed to resume for "+_dst);
459 	  }
460 	  if(size_of_src==size_of_dst){
461 	    return;
462 	  }
463 	}
464 
465         if(monitor!=null){
466  	  monitor.init(SftpProgressMonitor.PUT, _src, _dst,
467 		       (new File(_src)).length());
468 	  if(mode==RESUME){
469 	    monitor.count(size_of_dst);
470 	  }
471         }
472 	FileInputStream fis=null;
473 	try{
474 	  fis=new FileInputStream(_src);
475 	  _put(fis, _dst, monitor, mode);
476 	}
477 	finally{
478 	  if(fis!=null) {
479 	    fis.close();
480 	  }
481 	}
482       }
483     }
484     catch(Exception e){
485       if(e instanceof SftpException) throw (SftpException)e;
486       if(e instanceof Throwable)
487         throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e);
488       throw new SftpException(SSH_FX_FAILURE, e.toString());
489     }
490   }
put(InputStream src, String dst)491   public void put(InputStream src, String dst) throws SftpException{
492     put(src, dst, null, OVERWRITE);
493   }
put(InputStream src, String dst, int mode)494   public void put(InputStream src, String dst, int mode) throws SftpException{
495     put(src, dst, null, mode);
496   }
put(InputStream src, String dst, SftpProgressMonitor monitor)497   public void put(InputStream src, String dst,
498 		  SftpProgressMonitor monitor) throws SftpException{
499     put(src, dst, monitor, OVERWRITE);
500   }
501 
502   /**
503    * Sends data from the input stream <code>src</code> to <code>dst</code> file.
504    * The <code>mode</code> should be <code>OVERWRITE</code>,
505    * <code>RESUME</code> or <code>APPEND</code>.
506    *
507    * @param src input stream
508    * @param dst destination file
509    * @param monitor progress monitor
510    * @param mode how data should be added to dst
511    */
put(InputStream src, String dst, SftpProgressMonitor monitor, int mode)512   public void put(InputStream src, String dst,
513 		  SftpProgressMonitor monitor, int mode) throws SftpException{
514     try{
515       ((MyPipedInputStream)io_in).updateReadSide();
516 
517       dst=remoteAbsolutePath(dst);
518 
519       Vector v=glob_remote(dst);
520       int vsize=v.size();
521       if(vsize!=1){
522         if(vsize==0){
523           if(isPattern(dst))
524             throw new SftpException(SSH_FX_FAILURE, dst);
525           else
526             dst=Util.unquote(dst);
527         }
528         throw new SftpException(SSH_FX_FAILURE, v.toString());
529       }
530       else{
531         dst=(String)(v.elementAt(0));
532       }
533 
534       if(monitor!=null){
535         monitor.init(SftpProgressMonitor.PUT,
536                      "-", dst,
537                      SftpProgressMonitor.UNKNOWN_SIZE);
538       }
539 
540       _put(src, dst, monitor, mode);
541     }
542     catch(Exception e){
543       if(e instanceof SftpException) {
544         if(((SftpException)e).id == SSH_FX_FAILURE &&
545            isRemoteDir(dst)) {
546           throw new SftpException(SSH_FX_FAILURE, dst+" is a directory");
547         }
548         throw (SftpException)e;
549       }
550       if(e instanceof Throwable)
551         throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e);
552       throw new SftpException(SSH_FX_FAILURE, e.toString());
553     }
554   }
555 
_put(InputStream src, String dst, SftpProgressMonitor monitor, int mode)556   public void _put(InputStream src, String dst,
557                    SftpProgressMonitor monitor, int mode) throws SftpException{
558     try{
559       ((MyPipedInputStream)io_in).updateReadSide();
560 
561       byte[] dstb=Util.str2byte(dst, fEncoding);
562       long skip=0;
563       if(mode==RESUME || mode==APPEND){
564 	try{
565 	  SftpATTRS attr=_stat(dstb);
566 	  skip=attr.getSize();
567 	}
568 	catch(Exception eee){
569 	  //System.err.println(eee);
570 	}
571       }
572       if(mode==RESUME && skip>0){
573 	long skipped=src.skip(skip);
574 	if(skipped<skip){
575 	  throw new SftpException(SSH_FX_FAILURE, "failed to resume for "+dst);
576 	}
577       }
578 
579       if(mode==OVERWRITE){ sendOPENW(dstb); }
580       else{ sendOPENA(dstb); }
581 
582       Header header=new Header();
583       header=header(buf, header);
584       int length=header.length;
585       int type=header.type;
586 
587       fill(buf, length);
588 
589       if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
590 	throw new SftpException(SSH_FX_FAILURE, "invalid type="+type);
591       }
592       if(type==SSH_FXP_STATUS){
593         int i=buf.getInt();
594         throwStatusError(buf, i);
595       }
596       byte[] handle=buf.getString();         // handle
597       byte[] data=null;
598 
599       boolean dontcopy=true;
600 
601       if(!dontcopy){  // This case will not work anymore.
602         data=new byte[obuf.buffer.length
603                       -(5+13+21+handle.length+Session.buffer_margin
604                         )
605         ];
606       }
607 
608       long offset=0;
609       if(mode==RESUME || mode==APPEND){
610 	offset+=skip;
611       }
612 
613       int startid=seq;
614       int ackcount=0;
615       int _s=0;
616       int _datalen=0;
617 
618       if(!dontcopy){  // This case will not work anymore.
619         _datalen=data.length;
620       }
621       else{
622         data=obuf.buffer;
623         _s=5+13+21+handle.length;
624         _datalen=obuf.buffer.length-_s-Session.buffer_margin;
625       }
626 
627       int bulk_requests = rq.size();
628 
629       while(true){
630         int nread=0;
631         int count=0;
632         int s=_s;
633         int datalen=_datalen;
634 
635         do{
636           nread=src.read(data, s, datalen);
637           if(nread>0){
638             s+=nread;
639             datalen-=nread;
640             count+=nread;
641           }
642         }
643         while(datalen>0 && nread>0);
644         if(count<=0)break;
645 
646         int foo=count;
647         while(foo>0){
648           if((seq-1)==startid ||
649              ((seq-startid)-ackcount)>=bulk_requests){
650             while(((seq-startid)-ackcount)>=bulk_requests){
651               if(checkStatus(ackid, header)){
652                 int _ackid = ackid[0];
653                 if(startid>_ackid || _ackid>seq-1){
654                   if(_ackid==seq){
655                     System.err.println("ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid);
656                   }
657                   else{
658                     throw new SftpException(SSH_FX_FAILURE, "ack error: startid="+startid+" seq="+seq+" _ackid="+_ackid);
659                   }
660                 }
661                 ackcount++;
662               }
663               else{
664                 break;
665               }
666             }
667           }
668           foo-=sendWRITE(handle, offset, data, 0, foo);
669         }
670         offset+=count;
671 	if(monitor!=null && !monitor.count(count)){
672           break;
673 	}
674       }
675       int _ackcount=seq-startid;
676       while(_ackcount>ackcount){
677         if(!checkStatus(null, header)){
678           break;
679         }
680         ackcount++;
681       }
682       if(monitor!=null)monitor.end();
683       _sendCLOSE(handle, header);
684     }
685     catch(Exception e){
686       if(e instanceof SftpException) throw (SftpException)e;
687       if(e instanceof Throwable)
688         throw new SftpException(SSH_FX_FAILURE, e.toString(), (Throwable)e);
689       throw new SftpException(SSH_FX_FAILURE, e.toString());
690     }
691   }
692 
put(String dst)693   public OutputStream put(String dst) throws SftpException{
694     return put(dst, (SftpProgressMonitor)null, OVERWRITE);
695   }
put(String dst, final int mode)696   public OutputStream put(String dst, final int mode) throws SftpException{
697     return put(dst, (SftpProgressMonitor)null, mode);
698   }
put(String dst, final SftpProgressMonitor monitor, final int mode)699   public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) throws SftpException{
700     return put(dst, monitor, mode, 0);
701   }
702 
703   /**
704    * Sends data from the output stream to <code>dst</code> file.
705    * The <code>mode</code> should be <code>OVERWRITE</code>,
706    * <code>RESUME</code> or <code>APPEND</code>.
707    *
708    * @param dst destination file
709    * @param monitor progress monitor
710    * @param mode how data should be added to dst
711    * @param offset data will be added at offset
712    * @return output stream, which accepts data to be transferred.
713    */
put(String dst, final SftpProgressMonitor monitor, final int mode, long offset)714   public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, long offset) throws SftpException{
715     try{
716       ((MyPipedInputStream)io_in).updateReadSide();
717 
718       dst=remoteAbsolutePath(dst);
719       dst=isUnique(dst);
720 
721       if(isRemoteDir(dst)){
722 	throw new SftpException(SSH_FX_FAILURE, dst+" is a directory");
723       }
724 
725       byte[] dstb=Util.str2byte(dst, fEncoding);
726 
727       long skip=0;
728       if(mode==RESUME || mode==APPEND){
729 	try{
730 	  SftpATTRS attr=_stat(dstb);
731 	  skip=attr.getSize();
732 	}
733 	catch(Exception eee){
734 	  //System.err.println(eee);
735 	}
736       }
737 
738       if(mode==OVERWRITE){ sendOPENW(dstb); }
739       else{ sendOPENA(dstb); }
740 
741       Header header=new Header();
742       header=header(buf, header);
743       int length=header.length;
744       int type=header.type;
745 
746       fill(buf, length);
747 
748       if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
749 	throw new SftpException(SSH_FX_FAILURE, "");
750       }
751       if(type==SSH_FXP_STATUS){
752         int i=buf.getInt();
753         throwStatusError(buf, i);
754       }
755       final byte[] handle=buf.getString();         // handle
756 
757       if(mode==RESUME || mode==APPEND){
758 	offset+=skip;
759       }
760 
761       final long[] _offset=new long[1];
762       _offset[0]=offset;
763       OutputStream out = new OutputStream(){
764         private boolean init=true;
765         private boolean isClosed=false;
766         private int[] ackid=new int[1];
767         private int startid=0;
768         private int _ackid=0;
769         private int ackcount=0;
770         private int writecount=0;
771         private Header header=new Header();
772 
773         public void write(byte[] d) throws java.io.IOException{
774           write(d, 0, d.length);
775         }
776 
777         public void write(byte[] d, int s, int len) throws java.io.IOException{
778           if(init){
779             startid=seq;
780             _ackid=seq;
781             init=false;
782           }
783 
784           if(isClosed){
785             throw new IOException("stream already closed");
786           }
787 
788           try{
789             int _len=len;
790             while(_len>0){
791               int sent=sendWRITE(handle, _offset[0], d, s, _len);
792               writecount++;
793               _offset[0]+=sent;
794               s+=sent;
795               _len-=sent;
796               if((seq-1)==startid ||
797                  io_in.available()>=1024){
798                 while(io_in.available()>0){
799                   if(checkStatus(ackid, header)){
800                     _ackid=ackid[0];
801                     if(startid>_ackid || _ackid>seq-1){
802                       throw new SftpException(SSH_FX_FAILURE, "");
803                     }
804                     ackcount++;
805                   }
806                   else{
807                     break;
808                   }
809                 }
810               }
811             }
812     	    if(monitor!=null && !monitor.count(len)){
813               close();
814               throw new IOException("canceled");
815 	    }
816           }
817           catch(IOException e){ throw e; }
818           catch(Exception e){ throw new IOException(e.toString());  }
819         }
820 
821         byte[] _data=new byte[1];
822         public void write(int foo) throws java.io.IOException{
823           _data[0]=(byte)foo;
824           write(_data, 0, 1);
825         }
826 
827         public void flush() throws java.io.IOException{
828 
829           if(isClosed){
830             throw new IOException("stream already closed");
831           }
832 
833           if(!init){
834             try{
835               while(writecount>ackcount){
836                 if(!checkStatus(null, header)){
837                   break;
838                 }
839                 ackcount++;
840               }
841             }
842             catch(SftpException e){
843               throw new IOException(e.toString());
844             }
845           }
846         }
847 
848         public void close() throws java.io.IOException{
849           if(isClosed){
850             return;
851           }
852           flush();
853           if(monitor!=null)monitor.end();
854           try{ _sendCLOSE(handle, header); }
855           catch(IOException e){ throw e; }
856           catch(Exception e){
857             throw new IOException(e.toString());
858           }
859           isClosed=true;
860         }
861       };
862       return out;
863     }
864     catch(Exception e){
865       if(e instanceof SftpException) throw (SftpException)e;
866       if(e instanceof Throwable)
867         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
868       throw new SftpException(SSH_FX_FAILURE, "");
869     }
870   }
871 
get(String src, String dst)872   public void get(String src, String dst) throws SftpException{
873     get(src, dst, null, OVERWRITE);
874   }
get(String src, String dst, SftpProgressMonitor monitor)875   public void get(String src, String dst,
876 		  SftpProgressMonitor monitor) throws SftpException{
877     get(src, dst, monitor, OVERWRITE);
878   }
get(String src, String dst, SftpProgressMonitor monitor, int mode)879   public void get(String src, String dst,
880 		  SftpProgressMonitor monitor, int mode) throws SftpException{
881     // System.out.println("get: "+src+" "+dst);
882 
883     boolean _dstExist = false;
884     String _dst=null;
885     try{
886       ((MyPipedInputStream)io_in).updateReadSide();
887 
888       src=remoteAbsolutePath(src);
889       dst=localAbsolutePath(dst);
890 
891       Vector v=glob_remote(src);
892       int vsize=v.size();
893       if(vsize==0){
894         throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such file");
895       }
896 
897       File dstFile=new File(dst);
898       boolean isDstDir=dstFile.isDirectory();
899       StringBuffer dstsb=null;
900       if(isDstDir){
901         if(!dst.endsWith(file_separator)){
902           dst+=file_separator;
903         }
904         dstsb=new StringBuffer(dst);
905       }
906       else if(vsize>1){
907         throw new SftpException(SSH_FX_FAILURE,
908                                 "Copying multiple files, but destination is missing or a file.");
909       }
910 
911       for(int j=0; j<vsize; j++){
912 	String _src=(String)(v.elementAt(j));
913 	SftpATTRS attr=_stat(_src);
914         if(attr.isDir()){
915           throw new SftpException(SSH_FX_FAILURE,
916                                   "not supported to get directory "+_src);
917         }
918 
919 	_dst=null;
920 	if(isDstDir){
921 	  int i=_src.lastIndexOf('/');
922 	  if(i==-1) dstsb.append(_src);
923 	  else dstsb.append(_src.substring(i + 1));
924           _dst=dstsb.toString();
925           dstsb.delete(dst.length(), _dst.length());
926 	}
927         else{
928           _dst=dst;
929         }
930 
931         File _dstFile=new File(_dst);
932 	if(mode==RESUME){
933 	  long size_of_src=attr.getSize();
934 	  long size_of_dst=_dstFile.length();
935 	  if(size_of_dst>size_of_src){
936 	    throw new SftpException(SSH_FX_FAILURE,
937                                     "failed to resume for "+_dst);
938 	  }
939 	  if(size_of_dst==size_of_src){
940 	    return;
941 	  }
942 	}
943 
944 	if(monitor!=null){
945 	  monitor.init(SftpProgressMonitor.GET, _src, _dst, attr.getSize());
946 	  if(mode==RESUME){
947 	    monitor.count(_dstFile.length());
948 	  }
949 	}
950 
951         FileOutputStream fos=null;
952         _dstExist = _dstFile.exists();
953         try{
954           if(mode==OVERWRITE){
955             fos=new FileOutputStream(_dst);
956           }
957           else{
958             fos=new FileOutputStream(_dst, true); // append
959           }
960           // System.err.println("_get: "+_src+", "+_dst);
961           _get(_src, fos, monitor, mode, new File(_dst).length());
962         }
963         finally{
964           if(fos!=null){
965             fos.close();
966           }
967         }
968       }
969     }
970     catch(Exception e){
971       if(!_dstExist && _dst!=null){
972         File _dstFile = new File(_dst);
973         if(_dstFile.exists() && _dstFile.length()==0){
974           _dstFile.delete();
975         }
976       }
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   }
get(String src, OutputStream dst)983   public void get(String src, OutputStream dst) throws SftpException{
984     get(src, dst, null, OVERWRITE, 0);
985   }
get(String src, OutputStream dst, SftpProgressMonitor monitor)986   public void get(String src, OutputStream dst,
987 		  SftpProgressMonitor monitor) throws SftpException{
988     get(src, dst, monitor, OVERWRITE, 0);
989   }
get(String src, OutputStream dst, SftpProgressMonitor monitor, int mode, long skip)990   public void get(String src, OutputStream dst,
991 		   SftpProgressMonitor monitor, int mode, long skip) throws SftpException{
992 //System.err.println("get: "+src+", "+dst);
993     try{
994       ((MyPipedInputStream)io_in).updateReadSide();
995 
996       src=remoteAbsolutePath(src);
997       src=isUnique(src);
998 
999       if(monitor!=null){
1000 	SftpATTRS attr=_stat(src);
1001         monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize());
1002         if(mode==RESUME){
1003           monitor.count(skip);
1004         }
1005       }
1006       _get(src, dst, monitor, mode, skip);
1007     }
1008     catch(Exception e){
1009       if(e instanceof SftpException) throw (SftpException)e;
1010       if(e instanceof Throwable)
1011         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1012       throw new SftpException(SSH_FX_FAILURE, "");
1013     }
1014   }
1015 
_get(String src, OutputStream dst, SftpProgressMonitor monitor, int mode, long skip)1016   private void _get(String src, OutputStream dst,
1017                     SftpProgressMonitor monitor, int mode, long skip) throws SftpException{
1018     //System.err.println("_get: "+src+", "+dst);
1019 
1020     byte[] srcb=Util.str2byte(src, fEncoding);
1021     try{
1022       sendOPENR(srcb);
1023 
1024       Header header=new Header();
1025       header=header(buf, header);
1026       int length=header.length;
1027       int type=header.type;
1028 
1029       fill(buf, length);
1030 
1031       if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
1032 	throw new SftpException(SSH_FX_FAILURE, "");
1033       }
1034 
1035       if(type==SSH_FXP_STATUS){
1036         int i=buf.getInt();
1037         throwStatusError(buf, i);
1038       }
1039 
1040       byte[] handle=buf.getString();         // filename
1041 
1042       long offset=0;
1043       if(mode==RESUME){
1044 	offset+=skip;
1045       }
1046 
1047       int request_max=1;
1048       rq.init();
1049       long request_offset=offset;
1050 
1051       int request_len = buf.buffer.length-13;
1052       if(server_version==0){ request_len=1024; }
1053 
1054       loop:
1055       while(true){
1056 
1057         while(rq.count() < request_max){
1058           sendREAD(handle, request_offset, request_len, rq);
1059           request_offset += request_len;
1060         }
1061 
1062         header=header(buf, header);
1063         length=header.length;
1064         type=header.type;
1065 
1066         RequestQueue.Request rr = null;
1067         try{
1068           rr = rq.get(header.rid);
1069         }
1070         catch(RequestQueue.OutOfOrderException e){
1071           request_offset = e.offset;
1072           skip(header.length);
1073           rq.cancel(header, buf);
1074           continue;
1075         }
1076 
1077         if(type==SSH_FXP_STATUS){
1078           fill(buf, length);
1079           int i=buf.getInt();
1080           if(i==SSH_FX_EOF){
1081             break loop;
1082           }
1083           throwStatusError(buf, i);
1084         }
1085 
1086         if(type!=SSH_FXP_DATA){
1087 	  break loop;
1088         }
1089 
1090         buf.rewind();
1091         fill(buf.buffer, 0, 4); length-=4;
1092         int length_of_data = buf.getInt();   // length of data
1093 
1094         /**
1095          Since sftp protocol version 6, "end-of-file" has been defined,
1096 
1097            byte   SSH_FXP_DATA
1098            uint32 request-id
1099            string data
1100            bool   end-of-file [optional]
1101 
1102          but some sftpd server will send such a field in the sftp protocol 3 ;-(
1103          */
1104         int optional_data = length - length_of_data;
1105 
1106         int foo = length_of_data;
1107         while(foo>0){
1108           int bar=foo;
1109           if(bar>buf.buffer.length){
1110             bar=buf.buffer.length;
1111           }
1112           int data_len = io_in.read(buf.buffer, 0, bar);
1113           if(data_len<0){
1114             break loop;
1115 	  }
1116 
1117           dst.write(buf.buffer, 0, data_len);
1118 
1119           offset+=data_len;
1120           foo-=data_len;
1121 
1122           if(monitor!=null){
1123             if(!monitor.count(data_len)){
1124               skip(foo);
1125               if(optional_data>0){
1126                 skip(optional_data);
1127               }
1128               break loop;
1129             }
1130           }
1131 
1132         }
1133 	//System.err.println("length: "+length);  // length should be 0
1134 
1135         if(optional_data>0){
1136           skip(optional_data);
1137         }
1138 
1139         if(length_of_data<rr.length){  //
1140           rq.cancel(header, buf);
1141           sendREAD(handle, rr.offset+length_of_data, (int)(rr.length-length_of_data), rq);
1142           request_offset=rr.offset+rr.length;
1143         }
1144 
1145         if(request_max < rq.size()){
1146           request_max++;
1147         }
1148       }
1149       dst.flush();
1150 
1151       if(monitor!=null)monitor.end();
1152 
1153       rq.cancel(header, buf);
1154 
1155       _sendCLOSE(handle, header);
1156     }
1157     catch(Exception e){
1158       if(e instanceof SftpException) throw (SftpException)e;
1159       if(e instanceof Throwable)
1160         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1161       throw new SftpException(SSH_FX_FAILURE, "");
1162     }
1163   }
1164 
1165 
1166   private class RequestQueue {
1167     class OutOfOrderException extends Exception {
1168       long offset;
OutOfOrderException(long offset)1169       OutOfOrderException(long offset){
1170         this.offset=offset;
1171       }
1172     }
1173     class Request {
1174       int id;
1175       long offset;
1176       long length;
1177     }
1178 
1179     Request[] rrq=null;
1180     int head, count;
RequestQueue(int size)1181     RequestQueue(int size){
1182       rrq = new Request[size];
1183       for(int i=0; i<rrq.length; i++){
1184         rrq[i]=new Request();
1185       }
1186       init();
1187     }
1188 
init()1189     void init(){
1190       head=count=0;
1191     }
1192 
add(int id, long offset, int length)1193     void add(int id, long offset, int length){
1194       if(count == 0) head = 0;
1195       int tail = head + count;
1196       if(tail>=rrq.length) tail -= rrq.length;
1197       rrq[tail].id=id;
1198       rrq[tail].offset=offset;
1199       rrq[tail].length=length;
1200       count++;
1201     }
1202 
get(int id)1203     Request get(int id) throws OutOfOrderException, SftpException {
1204       count -= 1;
1205       int i = head;
1206       head++;
1207       if(head==rrq.length) head=0;
1208       if(rrq[i].id != id){
1209         long offset = getOffset();
1210         boolean find = false;
1211         for(int j = 0; j<rrq.length; j++){
1212           if(rrq[j].id == id){
1213             find = true;
1214             rrq[j].id = 0;
1215             break;
1216           }
1217         }
1218         if(find)
1219           throw new OutOfOrderException(offset);
1220         throw new SftpException(SSH_FX_FAILURE,
1221                                 "RequestQueue: unknown request id "+id);
1222       }
1223       rrq[i].id = 0;
1224       return rrq[i];
1225     }
1226 
count()1227     int count() {
1228       return count;
1229     }
1230 
size()1231     int size() {
1232       return rrq.length;
1233     }
1234 
cancel(Header header, Buffer buf)1235     void cancel(Header header, Buffer buf) throws IOException {
1236       int _count = count;
1237       for(int i=0; i<_count; i++){
1238         header=header(buf, header);
1239         int length=header.length;
1240         for(int j=0; j<rrq.length; j++){
1241           if(rrq[j].id == header.rid){
1242             rrq[j].id=0;
1243             break;
1244           }
1245         }
1246         skip(length);
1247       }
1248       init();
1249     }
1250 
getOffset()1251     long getOffset(){
1252       long result = Long.MAX_VALUE;
1253 
1254       for(int i=0; i<rrq.length; i++){
1255         if(rrq[i].id == 0)
1256           continue;
1257         if(result>rrq[i].offset)
1258           result=rrq[i].offset;
1259       }
1260 
1261       return result;
1262     }
1263   }
1264 
get(String src)1265   public InputStream get(String src) throws SftpException{
1266     return get(src, null, 0L);
1267   }
get(String src, SftpProgressMonitor monitor)1268   public InputStream get(String src, SftpProgressMonitor monitor) throws SftpException{
1269     return get(src, monitor, 0L);
1270   }
1271 
1272   /**
1273    * @deprecated  This method will be deleted in the future.
1274    */
get(String src, int mode)1275   public InputStream get(String src, int mode) throws SftpException{
1276     return get(src, null, 0L);
1277   }
1278   /**
1279    * @deprecated  This method will be deleted in the future.
1280    */
get(String src, final SftpProgressMonitor monitor, final int mode)1281   public InputStream get(String src, final SftpProgressMonitor monitor, final int mode) throws SftpException{
1282     return get(src, monitor, 0L);
1283   }
get(String src, final SftpProgressMonitor monitor, final long skip)1284   public InputStream get(String src, final SftpProgressMonitor monitor, final long skip) throws SftpException{
1285 
1286     try{
1287       ((MyPipedInputStream)io_in).updateReadSide();
1288 
1289       src=remoteAbsolutePath(src);
1290       src=isUnique(src);
1291 
1292       byte[] srcb=Util.str2byte(src, fEncoding);
1293 
1294       SftpATTRS attr=_stat(srcb);
1295       if(monitor!=null){
1296         monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize());
1297       }
1298 
1299       sendOPENR(srcb);
1300 
1301       Header header=new Header();
1302       header=header(buf, header);
1303       int length=header.length;
1304       int type=header.type;
1305 
1306       fill(buf, length);
1307 
1308       if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
1309 	throw new SftpException(SSH_FX_FAILURE, "");
1310       }
1311       if(type==SSH_FXP_STATUS){
1312         int i=buf.getInt();
1313         throwStatusError(buf, i);
1314       }
1315 
1316       final byte[] handle=buf.getString();         // handle
1317 
1318       rq.init();
1319 
1320       java.io.InputStream in=new java.io.InputStream(){
1321            long offset=skip;
1322            boolean closed=false;
1323            int rest_length=0;
1324            byte[] _data=new byte[1];
1325            byte[] rest_byte=new byte[1024];
1326            Header header=new Header();
1327            int request_max=1;
1328            long request_offset=offset;
1329 
1330            public int read() throws java.io.IOException{
1331              if(closed)return -1;
1332              int i=read(_data, 0, 1);
1333              if (i==-1) { return -1; }
1334              else {
1335                return _data[0]&0xff;
1336              }
1337            }
1338            public int read(byte[] d) throws java.io.IOException{
1339              if(closed)return -1;
1340              return read(d, 0, d.length);
1341            }
1342            public int read(byte[] d, int s, int len) throws java.io.IOException{
1343              if(closed)return -1;
1344              if(d==null){throw new NullPointerException();}
1345              if(s<0 || len <0 || s+len>d.length){
1346                throw new IndexOutOfBoundsException();
1347              }
1348              if(len==0){ return 0; }
1349 
1350              if(rest_length>0){
1351                int foo=rest_length;
1352                if(foo>len) foo=len;
1353                System.arraycopy(rest_byte, 0, d, s, foo);
1354                if(foo!=rest_length){
1355                  System.arraycopy(rest_byte, foo,
1356                                   rest_byte, 0, rest_length-foo);
1357                }
1358 
1359                if(monitor!=null){
1360                  if(!monitor.count(foo)){
1361                    close();
1362                    return -1;
1363                  }
1364                }
1365 
1366                rest_length-=foo;
1367                return foo;
1368              }
1369 
1370              if(buf.buffer.length-13<len){
1371                len=buf.buffer.length-13;
1372              }
1373              if(server_version==0 && len>1024){
1374                len=1024;
1375              }
1376 
1377              if(rq.count()==0) {
1378                int request_len = buf.buffer.length-13;
1379                if(server_version==0){ request_len=1024; }
1380 
1381                while(rq.count() < request_max){
1382                  try{
1383                    sendREAD(handle, request_offset, request_len, rq);
1384                  }
1385                  catch(Exception e){ throw new IOException("error"); }
1386                  request_offset += request_len;
1387                }
1388              }
1389 
1390              header=header(buf, header);
1391              rest_length=header.length;
1392              int type=header.type;
1393              int id=header.rid;
1394 
1395              RequestQueue.Request rr = null;
1396              try{
1397                rr = rq.get(header.rid);
1398              }
1399              catch(RequestQueue.OutOfOrderException e){
1400                request_offset = e.offset;
1401                skip(header.length);
1402                rq.cancel(header, buf);
1403                return 0;
1404              }
1405              catch(SftpException e){
1406                throw new IOException("error: "+e.toString());
1407              }
1408 
1409              if(type!=SSH_FXP_STATUS && type!=SSH_FXP_DATA){
1410                throw new IOException("error");
1411              }
1412              if(type==SSH_FXP_STATUS){
1413                fill(buf, rest_length);
1414                int i=buf.getInt();
1415                rest_length=0;
1416                if(i==SSH_FX_EOF){
1417                  close();
1418                  return -1;
1419                }
1420                //throwStatusError(buf, i);
1421                throw new IOException("error");
1422              }
1423 
1424              buf.rewind();
1425              fill(buf.buffer, 0, 4);
1426              int length_of_data = buf.getInt(); rest_length-=4;
1427 
1428              /**
1429               Since sftp protocol version 6, "end-of-file" has been defined,
1430 
1431                 byte   SSH_FXP_DATA
1432                 uint32 request-id
1433                 string data
1434                 bool   end-of-file [optional]
1435 
1436               but some sftpd server will send such a field in the sftp protocol 3 ;-(
1437               */
1438              int optional_data = rest_length - length_of_data;
1439 
1440              offset += length_of_data;
1441              int foo = length_of_data;
1442              if(foo>0){
1443                int bar=foo;
1444                if(bar>len){
1445                  bar=len;
1446                }
1447                int i=io_in.read(d, s, bar);
1448                if(i<0){
1449                  return -1;
1450                }
1451                foo-=i;
1452                rest_length=foo;
1453 
1454                if(foo>0){
1455                  if(rest_byte.length<foo){
1456                    rest_byte=new byte[foo];
1457                  }
1458                  int _s=0;
1459                  int _len=foo;
1460                  int j;
1461                  while(_len>0){
1462                    j=io_in.read(rest_byte, _s, _len);
1463                    if(j<=0)break;
1464                    _s+=j;
1465                    _len-=j;
1466                  }
1467                }
1468 
1469                if(optional_data>0){
1470                  io_in.skip(optional_data);
1471                }
1472 
1473                if(length_of_data<rr.length){  //
1474                  rq.cancel(header, buf);
1475                  try {
1476                    sendREAD(handle,
1477                             rr.offset+length_of_data,
1478                             (int)(rr.length-length_of_data), rq);
1479                  }
1480                  catch(Exception e){ throw new IOException("error"); }
1481                  request_offset=rr.offset+rr.length;
1482                }
1483 
1484                if(request_max < rq.size()){
1485                  request_max++;
1486                }
1487 
1488                if(monitor!=null){
1489                  if(!monitor.count(i)){
1490                    close();
1491                    return -1;
1492                  }
1493                }
1494 
1495                return i;
1496              }
1497              return 0; // ??
1498            }
1499            public void close() throws IOException{
1500              if(closed)return;
1501              closed=true;
1502              if(monitor!=null)monitor.end();
1503              rq.cancel(header, buf);
1504              try{_sendCLOSE(handle, header);}
1505              catch(Exception e){throw new IOException("error");}
1506            }
1507          };
1508        return in;
1509      }
1510      catch(Exception e){
1511        if(e instanceof SftpException) throw (SftpException)e;
1512        if(e instanceof Throwable)
1513          throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1514        throw new SftpException(SSH_FX_FAILURE, "");
1515      }
1516    }
1517 
ls(String path)1518    public java.util.Vector ls(String path) throws SftpException{
1519      final java.util.Vector v = new Vector();
1520      LsEntrySelector selector = new LsEntrySelector(){
1521        public int select(LsEntry entry){
1522          v.addElement(entry);
1523          return CONTINUE;
1524        }
1525      };
1526      ls(path, selector);
1527      return v;
1528    }
1529 
1530   /**
1531    * List files specified by the remote <code>path</code>.
1532    * Each files and directories will be passed to
1533    * <code>LsEntrySelector#select(LsEntry)</code> method, and if that method
1534    * returns <code>LsEntrySelector#BREAK</code>, the operation will be
1535    * canceled immediately.
1536    *
1537    * @see ChannelSftp.LsEntrySelector
1538    * @since 0.1.47
1539    */
ls(String path, LsEntrySelector selector)1540    public void ls(String path, LsEntrySelector selector) throws SftpException{
1541      //System.out.println("ls: "+path);
1542      try{
1543        ((MyPipedInputStream)io_in).updateReadSide();
1544 
1545        path=remoteAbsolutePath(path);
1546        byte[] pattern=null;
1547        java.util.Vector v=new java.util.Vector();
1548 
1549        int foo=path.lastIndexOf('/');
1550        String dir=path.substring(0, ((foo==0)?1:foo));
1551        String _pattern=path.substring(foo+1);
1552        dir=Util.unquote(dir);
1553 
1554        // If pattern has included '*' or '?', we need to convert
1555        // to UTF-8 string before globbing.
1556        byte[][] _pattern_utf8=new byte[1][];
1557        boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8);
1558 
1559        if(pattern_has_wildcard){
1560          pattern=_pattern_utf8[0];
1561        }
1562        else{
1563          String upath=Util.unquote(path);
1564          //SftpATTRS attr=_lstat(upath);
1565          SftpATTRS attr=_stat(upath);
1566          if(attr.isDir()){
1567            pattern=null;
1568            dir=upath;
1569          }
1570          else{
1571            /*
1572              // If we can generage longname by ourself,
1573              // we don't have to use openDIR.
1574            String filename=Util.unquote(_pattern);
1575            String longname=...
1576            v.addElement(new LsEntry(filename, longname, attr));
1577            return v;
1578            */
1579 
1580            if(fEncoding_is_utf8){
1581              pattern=_pattern_utf8[0];
1582              pattern=Util.unquote(pattern);
1583            }
1584            else{
1585              _pattern=Util.unquote(_pattern);
1586              pattern=Util.str2byte(_pattern, fEncoding);
1587            }
1588 
1589          }
1590        }
1591 
1592        sendOPENDIR(Util.str2byte(dir, fEncoding));
1593 
1594        Header header=new Header();
1595        header=header(buf, header);
1596        int length=header.length;
1597        int type=header.type;
1598 
1599        fill(buf, length);
1600 
1601        if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
1602          throw new SftpException(SSH_FX_FAILURE, "");
1603        }
1604        if(type==SSH_FXP_STATUS){
1605          int i=buf.getInt();
1606          throwStatusError(buf, i);
1607        }
1608 
1609        int cancel = LsEntrySelector.CONTINUE;
1610        byte[] handle=buf.getString();         // handle
1611 
1612        while(cancel==LsEntrySelector.CONTINUE){
1613 
1614          sendREADDIR(handle);
1615 
1616          header=header(buf, header);
1617          length=header.length;
1618          type=header.type;
1619          if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){
1620            throw new SftpException(SSH_FX_FAILURE, "");
1621          }
1622          if(type==SSH_FXP_STATUS){
1623            fill(buf, length);
1624            int i=buf.getInt();
1625            if(i==SSH_FX_EOF)
1626              break;
1627            throwStatusError(buf, i);
1628          }
1629 
1630          buf.rewind();
1631          fill(buf.buffer, 0, 4); length-=4;
1632          int count=buf.getInt();
1633 
1634          byte[] str;
1635          int flags;
1636 
1637          buf.reset();
1638          while(count>0){
1639            if(length>0){
1640              buf.shift();
1641              int j=(buf.buffer.length>(buf.index+length)) ?
1642                length :
1643                (buf.buffer.length-buf.index);
1644              int i=fill(buf.buffer, buf.index, j);
1645              buf.index+=i;
1646              length-=i;
1647            }
1648            byte[] filename=buf.getString();
1649            byte[] longname=null;
1650            if(server_version<=3){
1651              longname=buf.getString();
1652            }
1653            SftpATTRS attrs=SftpATTRS.getATTR(buf);
1654 
1655            if(cancel==LsEntrySelector.BREAK){
1656              count--;
1657              continue;
1658            }
1659 
1660            boolean find=false;
1661            String f=null;
1662            if(pattern==null){
1663              find=true;
1664            }
1665            else if(!pattern_has_wildcard){
1666              find=Util.array_equals(pattern, filename);
1667            }
1668            else{
1669              byte[] _filename=filename;
1670              if(!fEncoding_is_utf8){
1671                f=Util.byte2str(_filename, fEncoding);
1672                _filename=Util.str2byte(f, UTF8);
1673              }
1674              find=Util.glob(pattern, _filename);
1675            }
1676 
1677            if(find){
1678              if(f==null){
1679                f=Util.byte2str(filename, fEncoding);
1680              }
1681              String l=null;
1682              if(longname==null){
1683                // TODO: we need to generate long name from attrs
1684                //       for the sftp protocol 4(and later).
1685                l=attrs.toString()+" "+f;
1686              }
1687              else{
1688                l=Util.byte2str(longname, fEncoding);
1689              }
1690 
1691              cancel = selector.select(new LsEntry(f, l, attrs));
1692            }
1693 
1694            count--;
1695          }
1696        }
1697        _sendCLOSE(handle, header);
1698 
1699        /*
1700        if(v.size()==1 && pattern_has_wildcard){
1701          LsEntry le=(LsEntry)v.elementAt(0);
1702          if(le.getAttrs().isDir()){
1703            String f=le.getFilename();
1704            if(isPattern(f)){
1705              f=Util.quote(f);
1706            }
1707            if(!dir.endsWith("/")){
1708              dir+="/";
1709            }
1710            v=null;
1711            return ls(dir+f);
1712          }
1713        }
1714        */
1715 
1716      }
1717      catch(Exception e){
1718        if(e instanceof SftpException) throw (SftpException)e;
1719        if(e instanceof Throwable)
1720          throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1721        throw new SftpException(SSH_FX_FAILURE, "");
1722      }
1723    }
1724 
readlink(String path)1725    public String readlink(String path) throws SftpException{
1726      try{
1727        if(server_version<3){
1728          throw new SftpException(SSH_FX_OP_UNSUPPORTED,
1729                                  "The remote sshd is too old to support symlink operation.");
1730        }
1731 
1732        ((MyPipedInputStream)io_in).updateReadSide();
1733 
1734        path=remoteAbsolutePath(path);
1735 
1736        path=isUnique(path);
1737 
1738        sendREADLINK(Util.str2byte(path, fEncoding));
1739 
1740        Header header=new Header();
1741        header=header(buf, header);
1742        int length=header.length;
1743        int type=header.type;
1744 
1745        fill(buf, length);
1746 
1747        if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){
1748          throw new SftpException(SSH_FX_FAILURE, "");
1749        }
1750        if(type==SSH_FXP_NAME){
1751          int count=buf.getInt();       // count
1752          byte[] filename=null;
1753          for(int i=0; i<count; i++){
1754            filename=buf.getString();
1755            if(server_version<=3){
1756              byte[] longname=buf.getString();
1757            }
1758            SftpATTRS.getATTR(buf);
1759          }
1760          return Util.byte2str(filename, fEncoding);
1761        }
1762 
1763        int i=buf.getInt();
1764        throwStatusError(buf, i);
1765      }
1766      catch(Exception e){
1767        if(e instanceof SftpException) throw (SftpException)e;
1768        if(e instanceof Throwable)
1769          throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1770        throw new SftpException(SSH_FX_FAILURE, "");
1771      }
1772      return null;
1773    }
1774 
symlink(String oldpath, String newpath)1775    public void symlink(String oldpath, String newpath) throws SftpException{
1776      if(server_version<3){
1777        throw new SftpException(SSH_FX_OP_UNSUPPORTED,
1778                                "The remote sshd is too old to support symlink operation.");
1779      }
1780 
1781      try{
1782        ((MyPipedInputStream)io_in).updateReadSide();
1783 
1784        String _oldpath=remoteAbsolutePath(oldpath);
1785        newpath=remoteAbsolutePath(newpath);
1786 
1787        _oldpath=isUnique(_oldpath);
1788        if(oldpath.charAt(0)!='/'){ // relative path
1789          String cwd=getCwd();
1790          oldpath=_oldpath.substring(cwd.length()+(cwd.endsWith("/")?0:1));
1791        }
1792        else {
1793          oldpath=_oldpath;
1794        }
1795 
1796        if(isPattern(newpath)){
1797          throw new SftpException(SSH_FX_FAILURE, newpath);
1798        }
1799        newpath=Util.unquote(newpath);
1800 
1801        sendSYMLINK(Util.str2byte(oldpath, fEncoding),
1802                    Util.str2byte(newpath, fEncoding));
1803 
1804        Header header=new Header();
1805        header=header(buf, header);
1806        int length=header.length;
1807        int type=header.type;
1808 
1809        fill(buf, length);
1810 
1811        if(type!=SSH_FXP_STATUS){
1812          throw new SftpException(SSH_FX_FAILURE, "");
1813        }
1814 
1815        int i=buf.getInt();
1816        if(i==SSH_FX_OK) return;
1817        throwStatusError(buf, i);
1818      }
1819      catch(Exception e){
1820        if(e instanceof SftpException) throw (SftpException)e;
1821        if(e instanceof Throwable)
1822          throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1823        throw new SftpException(SSH_FX_FAILURE, "");
1824      }
1825    }
1826 
hardlink(String oldpath, String newpath)1827    public void hardlink(String oldpath, String newpath) throws SftpException{
1828      if(!extension_hardlink){
1829        throw new SftpException(SSH_FX_OP_UNSUPPORTED,
1830                                "hardlink@openssh.com is not supported");
1831      }
1832 
1833      try{
1834        ((MyPipedInputStream)io_in).updateReadSide();
1835 
1836        String _oldpath=remoteAbsolutePath(oldpath);
1837        newpath=remoteAbsolutePath(newpath);
1838 
1839        _oldpath=isUnique(_oldpath);
1840        if(oldpath.charAt(0)!='/'){ // relative path
1841          String cwd=getCwd();
1842          oldpath=_oldpath.substring(cwd.length()+(cwd.endsWith("/")?0:1));
1843        }
1844        else {
1845          oldpath=_oldpath;
1846        }
1847 
1848        if(isPattern(newpath)){
1849          throw new SftpException(SSH_FX_FAILURE, newpath);
1850        }
1851        newpath=Util.unquote(newpath);
1852 
1853        sendHARDLINK(Util.str2byte(oldpath, fEncoding),
1854                    Util.str2byte(newpath, fEncoding));
1855 
1856        Header header=new Header();
1857        header=header(buf, header);
1858        int length=header.length;
1859        int type=header.type;
1860 
1861        fill(buf, length);
1862 
1863        if(type!=SSH_FXP_STATUS){
1864          throw new SftpException(SSH_FX_FAILURE, "");
1865        }
1866 
1867        int i=buf.getInt();
1868        if(i==SSH_FX_OK) return;
1869        throwStatusError(buf, i);
1870      }
1871      catch(Exception e){
1872        if(e instanceof SftpException) throw (SftpException)e;
1873        if(e instanceof Throwable)
1874          throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1875        throw new SftpException(SSH_FX_FAILURE, "");
1876      }
1877    }
1878 
rename(String oldpath, String newpath)1879    public void rename(String oldpath, String newpath) throws SftpException{
1880      if(server_version<2){
1881        throw new SftpException(SSH_FX_OP_UNSUPPORTED,
1882                                "The remote sshd is too old to support rename operation.");
1883      }
1884 
1885      try{
1886        ((MyPipedInputStream)io_in).updateReadSide();
1887 
1888        oldpath=remoteAbsolutePath(oldpath);
1889        newpath=remoteAbsolutePath(newpath);
1890 
1891        oldpath=isUnique(oldpath);
1892 
1893        Vector v=glob_remote(newpath);
1894        int vsize=v.size();
1895        if(vsize>=2){
1896          throw new SftpException(SSH_FX_FAILURE, v.toString());
1897        }
1898        if(vsize==1){
1899          newpath=(String)(v.elementAt(0));
1900        }
1901        else{  // vsize==0
1902          if(isPattern(newpath))
1903            throw new SftpException(SSH_FX_FAILURE, newpath);
1904          newpath=Util.unquote(newpath);
1905        }
1906 
1907        sendRENAME(Util.str2byte(oldpath, fEncoding),
1908                   Util.str2byte(newpath, fEncoding));
1909 
1910        Header header=new Header();
1911        header=header(buf, header);
1912        int length=header.length;
1913        int type=header.type;
1914 
1915        fill(buf, length);
1916 
1917        if(type!=SSH_FXP_STATUS){
1918          throw new SftpException(SSH_FX_FAILURE, "");
1919        }
1920 
1921        int i=buf.getInt();
1922        if(i==SSH_FX_OK) return;
1923        throwStatusError(buf, i);
1924     }
1925     catch(Exception e){
1926       if(e instanceof SftpException) throw (SftpException)e;
1927       if(e instanceof Throwable)
1928         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1929       throw new SftpException(SSH_FX_FAILURE, "");
1930     }
1931   }
rm(String path)1932   public void rm(String path) throws SftpException{
1933     try{
1934       ((MyPipedInputStream)io_in).updateReadSide();
1935 
1936       path=remoteAbsolutePath(path);
1937 
1938       Vector v=glob_remote(path);
1939       int vsize=v.size();
1940 
1941       Header header=new Header();
1942 
1943       for(int j=0; j<vsize; j++){
1944 	path=(String)(v.elementAt(j));
1945         sendREMOVE(Util.str2byte(path, fEncoding));
1946 
1947         header=header(buf, header);
1948         int length=header.length;
1949         int type=header.type;
1950 
1951         fill(buf, length);
1952 
1953         if(type!=SSH_FXP_STATUS){
1954 	  throw new SftpException(SSH_FX_FAILURE, "");
1955         }
1956         int i=buf.getInt();
1957 	if(i!=SSH_FX_OK){
1958 	  throwStatusError(buf, i);
1959 	}
1960       }
1961     }
1962     catch(Exception e){
1963       if(e instanceof SftpException) throw (SftpException)e;
1964       if(e instanceof Throwable)
1965         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
1966       throw new SftpException(SSH_FX_FAILURE, "");
1967     }
1968   }
1969 
isRemoteDir(String path)1970   private boolean isRemoteDir(String path){
1971     try{
1972       sendSTAT(Util.str2byte(path, fEncoding));
1973 
1974       Header header=new Header();
1975       header=header(buf, header);
1976       int length=header.length;
1977       int type=header.type;
1978 
1979       fill(buf, length);
1980 
1981       if(type!=SSH_FXP_ATTRS){
1982         return false;
1983       }
1984       SftpATTRS attr=SftpATTRS.getATTR(buf);
1985       return attr.isDir();
1986     }
1987     catch(Exception e){}
1988     return false;
1989   }
1990 
chgrp(int gid, String path)1991   public void chgrp(int gid, String path) throws SftpException{
1992     try{
1993       ((MyPipedInputStream)io_in).updateReadSide();
1994 
1995       path=remoteAbsolutePath(path);
1996 
1997       Vector v=glob_remote(path);
1998       int vsize=v.size();
1999       for(int j=0; j<vsize; j++){
2000 	path=(String)(v.elementAt(j));
2001 
2002         SftpATTRS attr=_stat(path);
2003 
2004 	attr.setFLAGS(0);
2005 	attr.setUIDGID(attr.uid, gid);
2006 	_setStat(path, attr);
2007       }
2008     }
2009     catch(Exception e){
2010       if(e instanceof SftpException) throw (SftpException)e;
2011       if(e instanceof Throwable)
2012         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2013       throw new SftpException(SSH_FX_FAILURE, "");
2014     }
2015   }
2016 
chown(int uid, String path)2017   public void chown(int uid, String path) throws SftpException{
2018     try{
2019       ((MyPipedInputStream)io_in).updateReadSide();
2020 
2021       path=remoteAbsolutePath(path);
2022 
2023       Vector v=glob_remote(path);
2024       int vsize=v.size();
2025       for(int j=0; j<vsize; j++){
2026 	path=(String)(v.elementAt(j));
2027 
2028         SftpATTRS attr=_stat(path);
2029 
2030 	attr.setFLAGS(0);
2031 	attr.setUIDGID(uid, attr.gid);
2032 	_setStat(path, attr);
2033       }
2034     }
2035     catch(Exception e){
2036       if(e instanceof SftpException) throw (SftpException)e;
2037       if(e instanceof Throwable)
2038         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2039       throw new SftpException(SSH_FX_FAILURE, "");
2040     }
2041   }
2042 
chmod(int permissions, String path)2043   public void chmod(int permissions, String path) throws SftpException{
2044     try{
2045       ((MyPipedInputStream)io_in).updateReadSide();
2046 
2047       path=remoteAbsolutePath(path);
2048 
2049       Vector v=glob_remote(path);
2050       int vsize=v.size();
2051       for(int j=0; j<vsize; j++){
2052 	path=(String)(v.elementAt(j));
2053 
2054 	SftpATTRS attr=_stat(path);
2055 
2056 	attr.setFLAGS(0);
2057 	attr.setPERMISSIONS(permissions);
2058 	_setStat(path, attr);
2059       }
2060     }
2061     catch(Exception e){
2062       if(e instanceof SftpException) throw (SftpException)e;
2063       if(e instanceof Throwable)
2064         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2065       throw new SftpException(SSH_FX_FAILURE, "");
2066     }
2067   }
2068 
setMtime(String path, int mtime)2069   public void setMtime(String path, int mtime) throws SftpException{
2070     try{
2071       ((MyPipedInputStream)io_in).updateReadSide();
2072 
2073       path=remoteAbsolutePath(path);
2074 
2075       Vector v=glob_remote(path);
2076       int vsize=v.size();
2077       for(int j=0; j<vsize; j++){
2078 	path=(String)(v.elementAt(j));
2079 
2080         SftpATTRS attr=_stat(path);
2081 
2082 	attr.setFLAGS(0);
2083 	attr.setACMODTIME(attr.getATime(), mtime);
2084 	_setStat(path, attr);
2085       }
2086     }
2087     catch(Exception e){
2088       if(e instanceof SftpException) throw (SftpException)e;
2089       if(e instanceof Throwable)
2090         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2091       throw new SftpException(SSH_FX_FAILURE, "");
2092     }
2093   }
2094 
rmdir(String path)2095   public void rmdir(String path) throws SftpException{
2096     try{
2097       ((MyPipedInputStream)io_in).updateReadSide();
2098 
2099       path=remoteAbsolutePath(path);
2100 
2101       Vector v=glob_remote(path);
2102       int vsize=v.size();
2103 
2104       Header header=new Header();
2105 
2106       for(int j=0; j<vsize; j++){
2107 	path=(String)(v.elementAt(j));
2108 	sendRMDIR(Util.str2byte(path, fEncoding));
2109 
2110         header=header(buf, header);
2111         int length=header.length;
2112         int type=header.type;
2113 
2114         fill(buf, length);
2115 
2116 	if(type!=SSH_FXP_STATUS){
2117 	  throw new SftpException(SSH_FX_FAILURE, "");
2118 	}
2119 
2120 	int i=buf.getInt();
2121 	if(i!=SSH_FX_OK){
2122 	  throwStatusError(buf, i);
2123 	}
2124       }
2125     }
2126     catch(Exception e){
2127       if(e instanceof SftpException) throw (SftpException)e;
2128       if(e instanceof Throwable)
2129         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2130       throw new SftpException(SSH_FX_FAILURE, "");
2131     }
2132   }
2133 
mkdir(String path)2134   public void mkdir(String path) throws SftpException{
2135     try{
2136       ((MyPipedInputStream)io_in).updateReadSide();
2137 
2138       path=remoteAbsolutePath(path);
2139 
2140       sendMKDIR(Util.str2byte(path, fEncoding), null);
2141 
2142       Header header=new Header();
2143       header=header(buf, header);
2144       int length=header.length;
2145       int type=header.type;
2146 
2147       fill(buf, length);
2148 
2149       if(type!=SSH_FXP_STATUS){
2150 	throw new SftpException(SSH_FX_FAILURE, "");
2151       }
2152 
2153       int i=buf.getInt();
2154       if(i==SSH_FX_OK) return;
2155       throwStatusError(buf, i);
2156     }
2157     catch(Exception e){
2158       if(e instanceof SftpException) throw (SftpException)e;
2159       if(e instanceof Throwable)
2160         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2161       throw new SftpException(SSH_FX_FAILURE, "");
2162     }
2163   }
2164 
stat(String path)2165   public SftpATTRS stat(String path) throws SftpException{
2166     try{
2167       ((MyPipedInputStream)io_in).updateReadSide();
2168 
2169       path=remoteAbsolutePath(path);
2170       path=isUnique(path);
2171 
2172       return _stat(path);
2173     }
2174     catch(Exception e){
2175       if(e instanceof SftpException) throw (SftpException)e;
2176       if(e instanceof Throwable)
2177         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2178       throw new SftpException(SSH_FX_FAILURE, "");
2179     }
2180     //return null;
2181   }
2182 
_stat(byte[] path)2183   private SftpATTRS _stat(byte[] path) throws SftpException{
2184     try{
2185 
2186       sendSTAT(path);
2187 
2188       Header header=new Header();
2189       header=header(buf, header);
2190       int length=header.length;
2191       int type=header.type;
2192 
2193       fill(buf, length);
2194 
2195       if(type!=SSH_FXP_ATTRS){
2196 	if(type==SSH_FXP_STATUS){
2197 	  int i=buf.getInt();
2198 	  throwStatusError(buf, i);
2199 	}
2200 	throw new SftpException(SSH_FX_FAILURE, "");
2201       }
2202       SftpATTRS attr=SftpATTRS.getATTR(buf);
2203       return attr;
2204     }
2205     catch(Exception e){
2206       if(e instanceof SftpException) throw (SftpException)e;
2207       if(e instanceof Throwable)
2208         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2209       throw new SftpException(SSH_FX_FAILURE, "");
2210     }
2211     //return null;
2212   }
2213 
_stat(String path)2214   private SftpATTRS _stat(String path) throws SftpException{
2215     return _stat(Util.str2byte(path, fEncoding));
2216   }
2217 
statVFS(String path)2218   public SftpStatVFS statVFS(String path) throws SftpException{
2219     try{
2220       ((MyPipedInputStream)io_in).updateReadSide();
2221 
2222       path=remoteAbsolutePath(path);
2223       path=isUnique(path);
2224 
2225       return _statVFS(path);
2226     }
2227     catch(Exception e){
2228       if(e instanceof SftpException) throw (SftpException)e;
2229       if(e instanceof Throwable)
2230         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2231       throw new SftpException(SSH_FX_FAILURE, "");
2232     }
2233     //return null;
2234   }
2235 
_statVFS(byte[] path)2236   private SftpStatVFS _statVFS(byte[] path) throws SftpException{
2237     if(!extension_statvfs){
2238       throw new SftpException(SSH_FX_OP_UNSUPPORTED,
2239                               "statvfs@openssh.com is not supported");
2240     }
2241 
2242     try{
2243 
2244       sendSTATVFS(path);
2245 
2246       Header header=new Header();
2247       header=header(buf, header);
2248       int length=header.length;
2249       int type=header.type;
2250 
2251       fill(buf, length);
2252 
2253       if(type != (SSH_FXP_EXTENDED_REPLY&0xff)){
2254         if(type==SSH_FXP_STATUS){
2255           int i=buf.getInt();
2256           throwStatusError(buf, i);
2257         }
2258         throw new SftpException(SSH_FX_FAILURE, "");
2259       }
2260       else {
2261         SftpStatVFS stat = SftpStatVFS.getStatVFS(buf);
2262         return stat;
2263       }
2264     }
2265     catch(Exception e){
2266       if(e instanceof SftpException) throw (SftpException)e;
2267       if(e instanceof Throwable)
2268         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2269       throw new SftpException(SSH_FX_FAILURE, "");
2270     }
2271     //return null;
2272   }
2273 
_statVFS(String path)2274   private SftpStatVFS _statVFS(String path) throws SftpException{
2275     return _statVFS(Util.str2byte(path, fEncoding));
2276   }
2277 
lstat(String path)2278   public SftpATTRS lstat(String path) throws SftpException{
2279     try{
2280       ((MyPipedInputStream)io_in).updateReadSide();
2281 
2282       path=remoteAbsolutePath(path);
2283       path=isUnique(path);
2284 
2285       return _lstat(path);
2286     }
2287     catch(Exception e){
2288       if(e instanceof SftpException) throw (SftpException)e;
2289       if(e instanceof Throwable)
2290         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2291       throw new SftpException(SSH_FX_FAILURE, "");
2292     }
2293   }
2294 
_lstat(String path)2295   private SftpATTRS _lstat(String path) throws SftpException{
2296     try{
2297       sendLSTAT(Util.str2byte(path, fEncoding));
2298 
2299       Header header=new Header();
2300       header=header(buf, header);
2301       int length=header.length;
2302       int type=header.type;
2303 
2304       fill(buf, length);
2305 
2306       if(type!=SSH_FXP_ATTRS){
2307 	if(type==SSH_FXP_STATUS){
2308 	  int i=buf.getInt();
2309 	  throwStatusError(buf, i);
2310 	}
2311 	throw new SftpException(SSH_FX_FAILURE, "");
2312       }
2313       SftpATTRS attr=SftpATTRS.getATTR(buf);
2314       return attr;
2315     }
2316     catch(Exception e){
2317       if(e instanceof SftpException) throw (SftpException)e;
2318       if(e instanceof Throwable)
2319         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2320       throw new SftpException(SSH_FX_FAILURE, "");
2321     }
2322   }
2323 
_realpath(String path)2324   private byte[] _realpath(String path) throws SftpException, IOException, Exception{
2325     sendREALPATH(Util.str2byte(path, fEncoding));
2326 
2327     Header header=new Header();
2328     header=header(buf, header);
2329     int length=header.length;
2330     int type=header.type;
2331 
2332     fill(buf, length);
2333 
2334     if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){
2335       throw new SftpException(SSH_FX_FAILURE, "");
2336     }
2337     int i;
2338     if(type==SSH_FXP_STATUS){
2339       i=buf.getInt();
2340       throwStatusError(buf, i);
2341     }
2342     i=buf.getInt();   // count
2343 
2344     byte[] str=null;
2345     while(i-->0){
2346       str=buf.getString();  // absolute path;
2347       if(server_version<=3){
2348         byte[] lname=buf.getString();  // long filename
2349       }
2350       SftpATTRS attr=SftpATTRS.getATTR(buf);  // dummy attribute
2351     }
2352     return str;
2353   }
2354 
setStat(String path, SftpATTRS attr)2355   public void setStat(String path, SftpATTRS attr) throws SftpException{
2356     try{
2357       ((MyPipedInputStream)io_in).updateReadSide();
2358 
2359       path=remoteAbsolutePath(path);
2360 
2361       Vector v=glob_remote(path);
2362       int vsize=v.size();
2363       for(int j=0; j<vsize; j++){
2364 	path=(String)(v.elementAt(j));
2365 	_setStat(path, attr);
2366       }
2367     }
2368     catch(Exception e){
2369       if(e instanceof SftpException) throw (SftpException)e;
2370       if(e instanceof Throwable)
2371         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2372       throw new SftpException(SSH_FX_FAILURE, "");
2373     }
2374   }
_setStat(String path, SftpATTRS attr)2375   private void _setStat(String path, SftpATTRS attr) throws SftpException{
2376     try{
2377       sendSETSTAT(Util.str2byte(path, fEncoding), attr);
2378 
2379       Header header=new Header();
2380       header=header(buf, header);
2381       int length=header.length;
2382       int type=header.type;
2383 
2384       fill(buf, length);
2385 
2386       if(type!=SSH_FXP_STATUS){
2387 	throw new SftpException(SSH_FX_FAILURE, "");
2388       }
2389       int i=buf.getInt();
2390       if(i!=SSH_FX_OK){
2391 	throwStatusError(buf, i);
2392       }
2393     }
2394     catch(Exception e){
2395       if(e instanceof SftpException) throw (SftpException)e;
2396       if(e instanceof Throwable)
2397         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2398       throw new SftpException(SSH_FX_FAILURE, "");
2399     }
2400   }
2401 
pwd()2402   public String pwd() throws SftpException{ return getCwd(); }
lpwd()2403   public String lpwd(){ return lcwd; }
version()2404   public String version(){ return version; }
getHome()2405   public String getHome() throws SftpException {
2406     if(home==null){
2407       try{
2408         ((MyPipedInputStream)io_in).updateReadSide();
2409 
2410         byte[] _home=_realpath("");
2411         home=Util.byte2str(_home, fEncoding);
2412       }
2413       catch(Exception e){
2414         if(e instanceof SftpException) throw (SftpException)e;
2415         if(e instanceof Throwable)
2416           throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2417         throw new SftpException(SSH_FX_FAILURE, "");
2418       }
2419     }
2420     return home;
2421   }
2422 
getCwd()2423   private String getCwd() throws SftpException{
2424     if(cwd==null)
2425       cwd=getHome();
2426     return cwd;
2427   }
2428 
setCwd(String cwd)2429   private void setCwd(String cwd){
2430     this.cwd=cwd;
2431   }
2432 
read(byte[] buf, int s, int l)2433   private void read(byte[] buf, int s, int l) throws IOException, SftpException{
2434     int i=0;
2435     while(l>0){
2436       i=io_in.read(buf, s, l);
2437       if(i<=0){
2438         throw new SftpException(SSH_FX_FAILURE, "");
2439       }
2440       s+=i;
2441       l-=i;
2442     }
2443   }
2444 
checkStatus(int[] ackid, Header header)2445   private boolean checkStatus(int[] ackid, Header header) throws IOException, SftpException{
2446     header=header(buf, header);
2447     int length=header.length;
2448     int type=header.type;
2449     if(ackid!=null)
2450       ackid[0]=header.rid;
2451 
2452     fill(buf, length);
2453 
2454     if(type!=SSH_FXP_STATUS){
2455       throw new SftpException(SSH_FX_FAILURE, "");
2456     }
2457     int i=buf.getInt();
2458     if(i!=SSH_FX_OK){
2459       throwStatusError(buf, i);
2460     }
2461     return true;
2462   }
_sendCLOSE(byte[] handle, Header header)2463   private boolean _sendCLOSE(byte[] handle, Header header) throws Exception{
2464     sendCLOSE(handle);
2465     return checkStatus(null, header);
2466   }
2467 
sendINIT()2468   private void sendINIT() throws Exception{
2469     packet.reset();
2470     putHEAD(SSH_FXP_INIT, 5);
2471     buf.putInt(3);                // version 3
2472     getSession().write(packet, this, 5+4);
2473   }
2474 
sendREALPATH(byte[] path)2475   private void sendREALPATH(byte[] path) throws Exception{
2476     sendPacketPath(SSH_FXP_REALPATH, path);
2477   }
sendSTAT(byte[] path)2478   private void sendSTAT(byte[] path) throws Exception{
2479     sendPacketPath(SSH_FXP_STAT, path);
2480   }
sendSTATVFS(byte[] path)2481   private void sendSTATVFS(byte[] path) throws Exception{
2482     sendPacketPath((byte)0, path, "statvfs@openssh.com");
2483   }
2484   /*
2485   private void sendFSTATVFS(byte[] handle) throws Exception{
2486     sendPacketPath((byte)0, handle, "fstatvfs@openssh.com");
2487   }
2488   */
sendLSTAT(byte[] path)2489   private void sendLSTAT(byte[] path) throws Exception{
2490     sendPacketPath(SSH_FXP_LSTAT, path);
2491   }
sendFSTAT(byte[] handle)2492   private void sendFSTAT(byte[] handle) throws Exception{
2493     sendPacketPath(SSH_FXP_FSTAT, handle);
2494   }
sendSETSTAT(byte[] path, SftpATTRS attr)2495   private void sendSETSTAT(byte[] path, SftpATTRS attr) throws Exception{
2496     packet.reset();
2497     putHEAD(SSH_FXP_SETSTAT, 9+path.length+attr.length());
2498     buf.putInt(seq++);
2499     buf.putString(path);             // path
2500     attr.dump(buf);
2501     getSession().write(packet, this, 9+path.length+attr.length()+4);
2502   }
sendREMOVE(byte[] path)2503   private void sendREMOVE(byte[] path) throws Exception{
2504     sendPacketPath(SSH_FXP_REMOVE, path);
2505   }
sendMKDIR(byte[] path, SftpATTRS attr)2506   private void sendMKDIR(byte[] path, SftpATTRS attr) throws Exception{
2507     packet.reset();
2508     putHEAD(SSH_FXP_MKDIR, 9+path.length+(attr!=null?attr.length():4));
2509     buf.putInt(seq++);
2510     buf.putString(path);             // path
2511     if(attr!=null) attr.dump(buf);
2512     else buf.putInt(0);
2513     getSession().write(packet, this, 9+path.length+(attr!=null?attr.length():4)+4);
2514   }
sendRMDIR(byte[] path)2515   private void sendRMDIR(byte[] path) throws Exception{
2516     sendPacketPath(SSH_FXP_RMDIR, path);
2517   }
sendSYMLINK(byte[] p1, byte[] p2)2518   private void sendSYMLINK(byte[] p1, byte[] p2) throws Exception{
2519     sendPacketPath(SSH_FXP_SYMLINK, p1, p2);
2520   }
sendHARDLINK(byte[] p1, byte[] p2)2521   private void sendHARDLINK(byte[] p1, byte[] p2) throws Exception{
2522     sendPacketPath((byte)0, p1, p2, "hardlink@openssh.com");
2523   }
sendREADLINK(byte[] path)2524   private void sendREADLINK(byte[] path) throws Exception{
2525     sendPacketPath(SSH_FXP_READLINK, path);
2526   }
sendOPENDIR(byte[] path)2527   private void sendOPENDIR(byte[] path) throws Exception{
2528     sendPacketPath(SSH_FXP_OPENDIR, path);
2529   }
sendREADDIR(byte[] path)2530   private void sendREADDIR(byte[] path) throws Exception{
2531     sendPacketPath(SSH_FXP_READDIR, path);
2532   }
sendRENAME(byte[] p1, byte[] p2)2533   private void sendRENAME(byte[] p1, byte[] p2) throws Exception{
2534     sendPacketPath(SSH_FXP_RENAME, p1, p2,
2535                    extension_posix_rename ? "posix-rename@openssh.com" : null);
2536   }
sendCLOSE(byte[] path)2537   private void sendCLOSE(byte[] path) throws Exception{
2538     sendPacketPath(SSH_FXP_CLOSE, path);
2539   }
sendOPENR(byte[] path)2540   private void sendOPENR(byte[] path) throws Exception{
2541     sendOPEN(path, SSH_FXF_READ);
2542   }
sendOPENW(byte[] path)2543   private void sendOPENW(byte[] path) throws Exception{
2544     sendOPEN(path, SSH_FXF_WRITE|SSH_FXF_CREAT|SSH_FXF_TRUNC);
2545   }
sendOPENA(byte[] path)2546   private void sendOPENA(byte[] path) throws Exception{
2547     sendOPEN(path, SSH_FXF_WRITE|/*SSH_FXF_APPEND|*/SSH_FXF_CREAT);
2548   }
sendOPEN(byte[] path, int mode)2549   private void sendOPEN(byte[] path, int mode) throws Exception{
2550     packet.reset();
2551     putHEAD(SSH_FXP_OPEN, 17+path.length);
2552     buf.putInt(seq++);
2553     buf.putString(path);
2554     buf.putInt(mode);
2555     buf.putInt(0);           // attrs
2556     getSession().write(packet, this, 17+path.length+4);
2557   }
sendPacketPath(byte fxp, byte[] path)2558   private void sendPacketPath(byte fxp, byte[] path) throws Exception{
2559     sendPacketPath(fxp, path, (String)null);
2560   }
sendPacketPath(byte fxp, byte[] path, String extension)2561   private void sendPacketPath(byte fxp, byte[] path, String extension) throws Exception{
2562     packet.reset();
2563     int len = 9+path.length;
2564     if(extension == null) {
2565       putHEAD(fxp, len);
2566       buf.putInt(seq++);
2567     }
2568     else {
2569       len+=(4+extension.length());
2570       putHEAD(SSH_FXP_EXTENDED, len);
2571       buf.putInt(seq++);
2572       buf.putString(Util.str2byte(extension));
2573     }
2574     buf.putString(path);             // path
2575     getSession().write(packet, this, len+4);
2576   }
2577 
sendPacketPath(byte fxp, byte[] p1, byte[] p2)2578   private void sendPacketPath(byte fxp, byte[] p1, byte[] p2) throws Exception{
2579     sendPacketPath(fxp, p1, p2, null);
2580   }
sendPacketPath(byte fxp, byte[] p1, byte[] p2, String extension)2581   private void sendPacketPath(byte fxp, byte[] p1, byte[] p2, String extension) throws Exception{
2582     packet.reset();
2583     int len = 13+p1.length+p2.length;
2584     if(extension==null){
2585       putHEAD(fxp, len);
2586       buf.putInt(seq++);
2587     }
2588     else {
2589       len+=(4+extension.length());
2590       putHEAD(SSH_FXP_EXTENDED, len);
2591       buf.putInt(seq++);
2592       buf.putString(Util.str2byte(extension));
2593     }
2594     buf.putString(p1);
2595     buf.putString(p2);
2596     getSession().write(packet, this, len+4);
2597   }
2598 
sendWRITE(byte[] handle, long offset, byte[] data, int start, int length)2599   private int sendWRITE(byte[] handle, long offset,
2600                         byte[] data, int start, int length) throws Exception{
2601     int _length=length;
2602     opacket.reset();
2603     if(obuf.buffer.length<obuf.index+13+21+handle.length+length+Session.buffer_margin){
2604       _length=obuf.buffer.length-(obuf.index+13+21+handle.length+Session.buffer_margin);
2605       // System.err.println("_length="+_length+" length="+length);
2606     }
2607 
2608     putHEAD(obuf, SSH_FXP_WRITE, 21+handle.length+_length);       // 14
2609     obuf.putInt(seq++);                                      //  4
2610     obuf.putString(handle);                                  //  4+handle.length
2611     obuf.putLong(offset);                                    //  8
2612     if(obuf.buffer!=data){
2613       obuf.putString(data, start, _length);                    //  4+_length
2614     }
2615     else{
2616       obuf.putInt(_length);
2617       obuf.skip(_length);
2618     }
2619     getSession().write(opacket, this, 21+handle.length+_length+4);
2620     return _length;
2621   }
sendREAD(byte[] handle, long offset, int length)2622   private void sendREAD(byte[] handle, long offset, int length) throws Exception{
2623     sendREAD(handle, offset, length, null);
2624   }
sendREAD(byte[] handle, long offset, int length, RequestQueue rrq)2625   private void sendREAD(byte[] handle, long offset, int length,
2626                         RequestQueue rrq) throws Exception{
2627     packet.reset();
2628     putHEAD(SSH_FXP_READ, 21+handle.length);
2629     buf.putInt(seq++);
2630     buf.putString(handle);
2631     buf.putLong(offset);
2632     buf.putInt(length);
2633     getSession().write(packet, this, 21+handle.length+4);
2634     if(rrq!=null){
2635       rrq.add(seq-1, offset, length);
2636     }
2637   }
2638 
putHEAD(Buffer buf, byte type, int length)2639   private void putHEAD(Buffer buf, byte type, int length) throws Exception{
2640     buf.putByte((byte)Session.SSH_MSG_CHANNEL_DATA);
2641     buf.putInt(recipient);
2642     buf.putInt(length+4);
2643     buf.putInt(length);
2644     buf.putByte(type);
2645   }
2646 
putHEAD(byte type, int length)2647   private void putHEAD(byte type, int length) throws Exception{
2648     putHEAD(buf, type, length);
2649   }
2650 
glob_remote(String _path)2651   private Vector glob_remote(String _path) throws Exception{
2652     Vector v=new Vector();
2653     int i=0;
2654 
2655     int foo=_path.lastIndexOf('/');
2656     if(foo<0){  // it is not absolute path.
2657       v.addElement(Util.unquote(_path));
2658       return v;
2659     }
2660 
2661     String dir=_path.substring(0, ((foo==0)?1:foo));
2662     String _pattern=_path.substring(foo+1);
2663 
2664     dir=Util.unquote(dir);
2665 
2666     byte[] pattern=null;
2667     byte[][] _pattern_utf8=new byte[1][];
2668     boolean pattern_has_wildcard=isPattern(_pattern, _pattern_utf8);
2669 
2670     if(!pattern_has_wildcard){
2671       if(!dir.equals("/"))
2672         dir+="/";
2673       v.addElement(dir+Util.unquote(_pattern));
2674       return v;
2675     }
2676 
2677     pattern=_pattern_utf8[0];
2678 
2679     sendOPENDIR(Util.str2byte(dir, fEncoding));
2680 
2681     Header header=new Header();
2682     header=header(buf, header);
2683     int length=header.length;
2684     int type=header.type;
2685 
2686     fill(buf, length);
2687 
2688     if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE){
2689       throw new SftpException(SSH_FX_FAILURE, "");
2690     }
2691     if(type==SSH_FXP_STATUS){
2692       i=buf.getInt();
2693       throwStatusError(buf, i);
2694     }
2695 
2696     byte[] handle=buf.getString();         // filename
2697     String pdir=null;                      // parent directory
2698 
2699     while(true){
2700       sendREADDIR(handle);
2701       header=header(buf, header);
2702       length=header.length;
2703       type=header.type;
2704 
2705       if(type!=SSH_FXP_STATUS && type!=SSH_FXP_NAME){
2706 	throw new SftpException(SSH_FX_FAILURE, "");
2707       }
2708       if(type==SSH_FXP_STATUS){
2709         fill(buf, length);
2710 	break;
2711       }
2712 
2713       buf.rewind();
2714       fill(buf.buffer, 0, 4); length-=4;
2715       int count=buf.getInt();
2716 
2717       byte[] str;
2718       int flags;
2719 
2720       buf.reset();
2721       while(count>0){
2722 	if(length>0){
2723 	  buf.shift();
2724           int j=(buf.buffer.length>(buf.index+length)) ? length : (buf.buffer.length-buf.index);
2725 	  i=io_in.read(buf.buffer, buf.index, j);
2726 	  if(i<=0)break;
2727 	  buf.index+=i;
2728 	  length-=i;
2729 	}
2730 
2731 	byte[] filename=buf.getString();
2732 	//System.err.println("filename: "+new String(filename));
2733         if(server_version<=3){
2734           str=buf.getString();  // longname
2735         }
2736 	SftpATTRS attrs=SftpATTRS.getATTR(buf);
2737 
2738         byte[] _filename=filename;
2739         String f=null;
2740         boolean found=false;
2741 
2742         if(!fEncoding_is_utf8){
2743           f=Util.byte2str(filename, fEncoding);
2744           _filename=Util.str2byte(f, UTF8);
2745         }
2746         found=Util.glob(pattern, _filename);
2747 
2748 	if(found){
2749           if(f==null){
2750             f=Util.byte2str(filename, fEncoding);
2751           }
2752           if(pdir==null){
2753             pdir=dir;
2754             if(!pdir.endsWith("/")){
2755               pdir+="/";
2756             }
2757           }
2758 	  v.addElement(pdir+f);
2759 	}
2760 	count--;
2761       }
2762     }
2763     if(_sendCLOSE(handle, header))
2764       return v;
2765     return null;
2766   }
2767 
isPattern(byte[] path)2768   private boolean isPattern(byte[] path){
2769     int length=path.length;
2770     int i=0;
2771     while(i<length){
2772       if(path[i]=='*' || path[i]=='?')
2773         return true;
2774       if(path[i]=='\\' && (i+1)<length)
2775         i++;
2776       i++;
2777     }
2778     return false;
2779   }
2780 
glob_local(String _path)2781   private Vector glob_local(String _path) throws Exception{
2782 //System.err.println("glob_local: "+_path);
2783     Vector v=new Vector();
2784     byte[] path=Util.str2byte(_path, UTF8);
2785     int i=path.length-1;
2786     while(i>=0){
2787       if(path[i]!='*' && path[i]!='?'){
2788         i--;
2789         continue;
2790       }
2791       if(!fs_is_bs &&
2792          i>0 && path[i-1]=='\\'){
2793         i--;
2794         if(i>0 && path[i-1]=='\\'){
2795           i--;
2796           i--;
2797           continue;
2798         }
2799       }
2800       break;
2801     }
2802 
2803     if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;}
2804 
2805     while(i>=0){
2806       if(path[i]==file_separatorc ||
2807          (fs_is_bs && path[i]=='/')){ // On Windows, '/' is also the separator.
2808         break;
2809       }
2810       i--;
2811     }
2812 
2813     if(i<0){ v.addElement(fs_is_bs ? _path : Util.unquote(_path)); return v;}
2814 
2815     byte[] dir;
2816     if(i==0){dir=new byte[]{(byte)file_separatorc};}
2817     else{
2818       dir=new byte[i];
2819       System.arraycopy(path, 0, dir, 0, i);
2820     }
2821 
2822     byte[] pattern=new byte[path.length-i-1];
2823     System.arraycopy(path, i+1, pattern, 0, pattern.length);
2824 
2825 //System.err.println("dir: "+new String(dir)+" pattern: "+new String(pattern));
2826     try{
2827       String[] children=(new File(Util.byte2str(dir, UTF8))).list();
2828       String pdir=Util.byte2str(dir)+file_separator;
2829       for(int j=0; j<children.length; j++){
2830 //System.err.println("children: "+children[j]);
2831 	if(Util.glob(pattern, Util.str2byte(children[j], UTF8))){
2832 	  v.addElement(pdir+children[j]);
2833 	}
2834       }
2835     }
2836     catch(Exception e){
2837     }
2838     return v;
2839   }
2840 
throwStatusError(Buffer buf, int i)2841   private void throwStatusError(Buffer buf, int i) throws SftpException{
2842     if(server_version>=3 &&   // WindRiver's sftp will send invalid
2843        buf.getLength()>=4){   // SSH_FXP_STATUS packet.
2844       byte[] str=buf.getString();
2845       //byte[] tag=buf.getString();
2846       throw new SftpException(i, Util.byte2str(str, UTF8));
2847     }
2848     else{
2849       throw new SftpException(i, "Failure");
2850     }
2851   }
2852 
isLocalAbsolutePath(String path)2853   private static boolean isLocalAbsolutePath(String path){
2854     return (new File(path)).isAbsolute();
2855   }
2856 
disconnect()2857   public void disconnect(){
2858     super.disconnect();
2859   }
2860 
isPattern(String path, byte[][] utf8)2861   private boolean isPattern(String path, byte[][] utf8){
2862     byte[] _path=Util.str2byte(path, UTF8);
2863     if(utf8!=null)
2864       utf8[0]=_path;
2865     return isPattern(_path);
2866   }
2867 
isPattern(String path)2868   private boolean isPattern(String path){
2869     return isPattern(path, null);
2870   }
2871 
fill(Buffer buf, int len)2872   private void fill(Buffer buf, int len)  throws IOException{
2873     buf.reset();
2874     fill(buf.buffer, 0, len);
2875     buf.skip(len);
2876   }
2877 
fill(byte[] buf, int s, int len)2878   private int fill(byte[] buf, int s, int len) throws IOException{
2879     int i=0;
2880     int foo=s;
2881     while(len>0){
2882       i=io_in.read(buf, s, len);
2883       if(i<=0){
2884         throw new IOException("inputstream is closed");
2885         //return (s-foo)==0 ? i : s-foo;
2886       }
2887       s+=i;
2888       len-=i;
2889     }
2890     return s-foo;
2891   }
skip(long foo)2892   private void skip(long foo) throws IOException{
2893     while(foo>0){
2894       long bar=io_in.skip(foo);
2895       if(bar<=0)
2896         break;
2897       foo-=bar;
2898     }
2899   }
2900 
2901   class Header{
2902     int length;
2903     int type;
2904     int rid;
2905   }
header(Buffer buf, Header header)2906   private Header header(Buffer buf, Header header) throws IOException{
2907     buf.rewind();
2908     int i=fill(buf.buffer, 0, 9);
2909     header.length=buf.getInt()-5;
2910     header.type=buf.getByte()&0xff;
2911     header.rid=buf.getInt();
2912     return header;
2913   }
2914 
remoteAbsolutePath(String path)2915   private String remoteAbsolutePath(String path) throws SftpException{
2916     if(path.charAt(0)=='/') return path;
2917     String cwd=getCwd();
2918 //    if(cwd.equals(getHome())) return path;
2919     if(cwd.endsWith("/")) return cwd+path;
2920     return cwd+"/"+path;
2921   }
2922 
localAbsolutePath(String path)2923   private String localAbsolutePath(String path){
2924     if(isLocalAbsolutePath(path)) return path;
2925     if(lcwd.endsWith(file_separator)) return lcwd+path;
2926     return lcwd+file_separator+path;
2927   }
2928 
2929   /**
2930    * This method will check if the given string can be expanded to the
2931    * unique string.  If it can be expanded to mutiple files, SftpException
2932    * will be thrown.
2933    * @return the returned string is unquoted.
2934    */
isUnique(String path)2935   private String isUnique(String path) throws SftpException, Exception{
2936     Vector v=glob_remote(path);
2937     if(v.size()!=1){
2938       throw new SftpException(SSH_FX_FAILURE, path+" is not unique: "+v.toString());
2939     }
2940     return (String)(v.elementAt(0));
2941   }
2942 
getServerVersion()2943   public int getServerVersion() throws SftpException{
2944     if(!isConnected()){
2945       throw new SftpException(SSH_FX_FAILURE, "The channel is not connected.");
2946     }
2947     return server_version;
2948   }
2949 
setFilenameEncoding(String encoding)2950   public void setFilenameEncoding(String encoding) throws SftpException{
2951     int sversion=getServerVersion();
2952     if(3 <= sversion && sversion <= 5 &&
2953        !encoding.equals(UTF8)){
2954       throw new SftpException(SSH_FX_FAILURE,
2955                               "The encoding can not be changed for this sftp server.");
2956     }
2957     if(encoding.equals(UTF8)){
2958       encoding=UTF8;
2959     }
2960     fEncoding=encoding;
2961     fEncoding_is_utf8=fEncoding.equals(UTF8);
2962   }
2963 
getExtension(String key)2964   public String getExtension(String key){
2965     if(extensions==null)
2966       return null;
2967     return (String)extensions.get(key);
2968   }
2969 
realpath(String path)2970   public String realpath(String path) throws SftpException{
2971     try{
2972       byte[] _path=_realpath(remoteAbsolutePath(path));
2973       return Util.byte2str(_path, fEncoding);
2974     }
2975     catch(Exception e){
2976       if(e instanceof SftpException) throw (SftpException)e;
2977       if(e instanceof Throwable)
2978         throw new SftpException(SSH_FX_FAILURE, "", (Throwable)e);
2979       throw new SftpException(SSH_FX_FAILURE, "");
2980     }
2981   }
2982 
2983   public class LsEntry implements Comparable{
2984     private  String filename;
2985     private  String longname;
2986     private  SftpATTRS attrs;
LsEntry(String filename, String longname, SftpATTRS attrs)2987     LsEntry(String filename, String longname, SftpATTRS attrs){
2988       setFilename(filename);
2989       setLongname(longname);
2990       setAttrs(attrs);
2991     }
getFilename()2992     public String getFilename(){return filename;};
setFilename(String filename)2993     void setFilename(String filename){this.filename = filename;};
getLongname()2994     public String getLongname(){return longname;};
setLongname(String longname)2995     void setLongname(String longname){this.longname = longname;};
getAttrs()2996     public SftpATTRS getAttrs(){return attrs;};
setAttrs(SftpATTRS attrs)2997     void setAttrs(SftpATTRS attrs) {this.attrs = attrs;};
toString()2998     public String toString(){ return longname; }
compareTo(Object o)2999     public int compareTo(Object o) throws ClassCastException{
3000       if(o instanceof LsEntry){
3001         return filename.compareTo(((LsEntry)o).getFilename());
3002       }
3003       throw new ClassCastException("a decendent of LsEntry must be given.");
3004     }
3005   }
3006 
3007   /**
3008    * This interface will be passed as an argument for <code>ls</code> method.
3009    *
3010    * @see ChannelSftp.LsEntry
3011    * @see #ls(String, ChannelSftp.LsEntrySelector)
3012    * @since 0.1.47
3013    */
3014   public interface LsEntrySelector {
3015     public final int CONTINUE = 0;
3016     public final int BREAK = 1;
3017 
3018     /**
3019      * <p> The <code>select</code> method will be invoked in <code>ls</code>
3020      * method for each file entry. If this method returns BREAK,
3021      * <code>ls</code> will be canceled.
3022      *
3023      * @param entry one of entry from ls
3024      * @return if BREAK is returned, the 'ls' operation will be canceled.
3025      */
select(LsEntry entry)3026     public int select(LsEntry entry);
3027   }
3028 }
3029