📄 channelsftp.cs
字号:
#region Licences
// Copyright (C) 2005 Sebastian Faltoni <sebastian@dotnetfireball.net>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#endregion Licences
using System;
using System.Net;
using System.IO;
using System.Collections;
using System.Threading;
using System.Runtime.CompilerServices;
using Fireball.Streams;
namespace Fireball.Ssh.jsch
{
/* -*-mode:java; c-basic-offset:2; -*- */
/*
Copyright (c) 2002,2003,2004 ymnk, JCraft,Inc. All rights reserved.
Redistribution and use ins source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions ins binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer ins
the documentation and/or other materials provided with the distribution.
3. The names of the authors may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
public class ChannelSftp : ChannelSession
{
private const byte SSH_FXP_INIT= 1;
private const byte SSH_FXP_VERSION= 2;
private const byte SSH_FXP_OPEN= 3;
private const byte SSH_FXP_CLOSE= 4;
private const byte SSH_FXP_READ= 5;
private const byte SSH_FXP_WRITE= 6;
private const byte SSH_FXP_LSTAT= 7;
private const byte SSH_FXP_FSTAT= 8;
private const byte SSH_FXP_SETSTAT= 9;
private const byte SSH_FXP_FSETSTAT= 10;
private const byte SSH_FXP_OPENDIR= 11;
private const byte SSH_FXP_READDIR= 12;
private const byte SSH_FXP_REMOVE= 13;
private const byte SSH_FXP_MKDIR= 14;
private const byte SSH_FXP_RMDIR= 15;
private const byte SSH_FXP_REALPATH= 16;
private const byte SSH_FXP_STAT= 17;
private const byte SSH_FXP_RENAME= 18;
private const byte SSH_FXP_READLINK= 19;
private const byte SSH_FXP_SYMLINK= 20;
private const byte SSH_FXP_STATUS= 101;
private const byte SSH_FXP_HANDLE= 102;
private const byte SSH_FXP_DATA= 103;
private const byte SSH_FXP_NAME= 104;
private const byte SSH_FXP_ATTRS= 105;
private const byte SSH_FXP_EXTENDED= (byte)200;
private const byte SSH_FXP_EXTENDED_REPLY= (byte)201;
// pflags
private const int SSH_FXF_READ= 0x00000001;
private const int SSH_FXF_WRITE= 0x00000002;
private const int SSH_FXF_APPEND= 0x00000004;
private const int SSH_FXF_CREAT= 0x00000008;
private const int SSH_FXF_TRUNC= 0x00000010;
private const int SSH_FXF_EXCL= 0x00000020;
private const int SSH_FILEXFER_ATTR_SIZE= 0x00000001;
private const int SSH_FILEXFER_ATTR_UIDGID= 0x00000002;
private const int SSH_FILEXFER_ATTR_PERMISSIONS= 0x00000004;
private const int SSH_FILEXFER_ATTR_ACMODTIME= 0x00000008;
private const uint SSH_FILEXFER_ATTR_EXTENDED= 0x80000000;
public const int SSH_FX_OK= 0;
public const int SSH_FX_EOF= 1;
public const int SSH_FX_NO_SUCH_FILE= 2;
public const int SSH_FX_PERMISSION_DENIED= 3;
public const int SSH_FX_FAILURE= 4;
public const int SSH_FX_BAD_MESSAGE= 5;
public const int SSH_FX_NO_CONNECTION= 6;
public const int SSH_FX_CONNECTION_LOST= 7;
public const int SSH_FX_OP_UNSUPPORTED= 8;
/*
SSH_FX_OK
Indicates successful completion of the operation.
SSH_FX_EOF
indicates end-of-file condition; for SSH_FX_READ it means that no
more data is available in the file, and for SSH_FX_READDIR it
indicates that no more files are contained in the directory.
SSH_FX_NO_SUCH_FILE
is returned when a reference is made to a file which should exist
but doesn't.
SSH_FX_PERMISSION_DENIED
is returned when the authenticated user does not have sufficient
permissions to perform the operation.
SSH_FX_FAILURE
is a generic catch-all error message; it should be returned if an
error occurs for which there is no more specific error code
defined.
SSH_FX_BAD_MESSAGE
may be returned if a badly formatted packet or protocol
incompatibility is detected.
SSH_FX_NO_CONNECTION
is a pseudo-error which indicates that the client has no
connection to the server (it can only be generated locally by the
client, and MUST NOT be returned by servers).
SSH_FX_CONNECTION_LOST
is a pseudo-error which indicates that the connection to the
server has been lost (it can only be generated locally by the
client, and MUST NOT be returned by servers).
SSH_FX_OP_UNSUPPORTED
indicates that an attempt was made to perform an operation which
is not supported for the server (it may be generated locally by
the client if e.g. the version number exchange indicates that a
required feature is not supported by the server, or it may be
returned by the server if the server does not implement an
operation).
*/
public const int OVERWRITE=0;
public const int RESUME=1;
public const int APPEND=2;
// private bool interactive=true;
private bool interactive=false;
private int count=1;
private Buffer buf=null;
private Packet packet;
private String _version="3";
private int server_version=3;
/*
10. Changes from previous protocol versions
The SSH File Transfer Protocol has changed over time, before it's
standardization. The following is a description of the incompatible
changes between different versions.
10.1 Changes between versions 3 and 2
o The SSH_FXP_READLINK and SSH_FXP_SYMLINK messages were added.
o The SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY messages were added.
o The SSH_FXP_STATUS message was changed to include fields `error
message' and `language tag'.
10.2 Changes between versions 2 and 1
o The SSH_FXP_RENAME message was added.
10.3 Changes between versions 1 and 0
o Implementation changes, no actual protocol changes.
*/
private String file_separator=Path.DirectorySeparatorChar.ToString();
private char file_separatorc=Path.DirectorySeparatorChar;
private String cwd;
private String home;
private String lcwd;
public ChannelSftp()
{
packet=new Packet(buf);
}
public override void init()
{
/*
io.setInputStream(session.in);
io.setOutputStream(session.out);
*/
}
public override void start()
{
try
{
PipedOutputStream pos=new PipedOutputStream();
io.setOutputStream(pos);
// PipedInputStream pis=new PipedInputStream(pos);
PipedInputStream pis=new MyPipedInputStream(pos, 32*1024);
io.setInputStream(pis);
Request request=new RequestSftp();
request.request(session, this);
// thread=Thread.currentThread();
// buf=new Buffer();
buf=new Buffer(rmpsize);
packet=new Packet(buf);
int i=0;
int j=0;
int length;
int type;
byte[] str;
// send SSH_FXP_INIT
sendINIT();
// receive SSH_FXP_VERSION
buf.rewind();
i=io.ins.Read(buf.buffer, 0, buf.buffer.Length);
//System.out.println(io+" "+io.ins+" "+io.out);
length=buf.getInt();
type=buf.getByte(); // 2 -> SSH_FXP_VERSION
server_version=buf.getInt();
//System.out.println("SFTP protocol server-version="+server_version);
// send SSH_FXP_REALPATH
sendREALPATH(Util.getBytes("."));
// receive SSH_FXP_NAME
buf.rewind();
i=io.ins.Read(buf.buffer, 0, buf.buffer.Length);
length=buf.getInt();
type=buf.getByte(); // 104 -> SSH_FXP_NAME
buf.getInt(); //
i=buf.getInt(); // count
str=buf.getString(); // filename
home=cwd=Util.getString(str);
str=buf.getString(); // logname
// SftpATTRS.getATTR(buf); // attrs
lcwd=Path.GetFullPath(".");
//thread=new Thread(this);
//thread.setName("Sftp for "+session.host);
//thread.start();
}
catch(Exception e)
{
//System.out.println(e);
if(e is JSchException) throw (JSchException)e;
throw new JSchException(e.ToString());
}
}
public void quit(){ disconnect();}
public void exit(){ disconnect();}
public void lcd(String path)
{
// if(!path.StartsWith("/")){ path=lcwd+file_separator+path; }
if(!isLocalAbsolutePath(path)){ path=lcwd+file_separator+path; }
if(Directory.Exists(path))
{
try
{
path=Path.GetFullPath(path);
}
catch(Exception e){}
lcwd=path;
return;
}
throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such directory");
}
/*
cd /tmp
c->s REALPATH
s->c NAME
c->s STAT
s->c ATTR
*/
public void cd(String path)
{
try
{
if(!path.StartsWith("/")){ path=cwd+"/"+path; }
ArrayList v=glob_remote(path);
if(v.Count!=1)
{
throw new SftpException(SSH_FX_FAILURE, v.ToString());
}
path=(String)(v[0]);
sendREALPATH(Util.getBytes(path));
buf.rewind();
int i=io.ins.Read(buf.buffer, 0, buf.buffer.Length);
int length=buf.getInt();
int type=buf.getByte();
if(type!=101 && type!=104)
{
throw new SftpException(SSH_FX_FAILURE, "");
}
if(type==101)
{
buf.getInt();
i=buf.getInt();
throwStatusError(buf, i);
// byte[] str=buf.getString();
// throw new SftpException(i, Util.getString(str));
}
buf.getInt();
i=buf.getInt();
byte[] str=buf.getString();
if(str!=null && str[0]!='/')
{
str=Util.getBytes(cwd+"/"+Util.getString(str));
}
cwd=Util.getString(str);
str=buf.getString(); // logname
i=buf.getInt(); // attrs
}
catch(Exception e)
{
if(e is SftpException) throw (SftpException)e;
throw new SftpException(SSH_FX_FAILURE, "");
}
}
/*
put foo
c->s OPEN
s->c HANDLE
c->s WRITE
s->c STATUS
c->s CLOSE
s->c STATUS
*/
public void put(String src, String dst)
{
put(src, dst, null, OVERWRITE);
}
public void put(String src, String dst, int mode)
{
put(src, dst, null, mode);
}
public void put(String src, String dst,
SftpProgressMonitor monitor)
{
put(src, dst, monitor, OVERWRITE);
}
public void put(String src, String dst,
SftpProgressMonitor monitor, int mode)
{
// if(!src.StartsWith("/")){ src=lcwd+file_separator+src; }
if(!isLocalAbsolutePath(src)){ src=lcwd+file_separator+src; }
if(!dst.StartsWith("/")){ dst=cwd+"/"+dst; }
//System.out.println("src: "+src+", "+dst);
try
{
ArrayList v=glob_remote(dst);
if(v.Count!=1)
{
throw new SftpException(SSH_FX_FAILURE, v.ToString());
}
dst=(String)(v[0]);
bool _isRemoteDir=isRemoteDir(dst);
v=glob_local(src);
//System.out.println("glob_local: "+v+" dst="+dst);
for(int j=0; j<v.Count; j++)
{
String _src=(String)(v[j]);
String _dst=dst;
if(_isRemoteDir)
{
if(!_dst.EndsWith("/"))
{
_dst+="/";
}
int i=_src.LastIndexOf(file_separatorc);
if(i==-1) _dst+=_src;
else _dst+=_src.Substring(i+1);
}
//System.out.println("_dst "+_dst);
long size_of_dst=0;
if(mode==RESUME)
{
try
{
SftpATTRS attr=stat(_dst);
size_of_dst=attr.getSize();
}
catch(Exception eee)
{
//System.out.println(eee);
}
long size_of_src=new FileInfo(_src).Length;
if(size_of_src<size_of_dst)
{
throw new SftpException(SSH_FX_FAILURE, "failed to resume for "+_dst);
}
if(size_of_src==size_of_dst)
{
return;
}
}
if(monitor!=null)
{
monitor.init(SftpProgressMonitor.PUT, _src, _dst,
(new FileInfo(_src)).Length);
if(mode==RESUME)
{
monitor.count(size_of_dst);
}
}
FileStream fis=null;
try
{
fis=File.OpenRead(_src);
put(fis, _dst, monitor, mode);
}
finally
{
if(fis!=null)
{
// try{
fis.Close();
// }catch(Exception ee){};
}
}
}
}
catch(Exception e)
{
if(e is SftpException) throw (SftpException)e;
throw new SftpException(SSH_FX_FAILURE, e.ToString());
}
}
public void put(Stream src, String dst)
{
put(src, dst, null, OVERWRITE);
}
public void put(Stream src, String dst, int mode)
{
put(src, dst, null, mode);
}
public void put(Stream src, String dst,
SftpProgressMonitor monitor)
{
put(src, dst, monitor, OVERWRITE);
}
public void put(Stream src, String dst,
SftpProgressMonitor monitor, int mode)
{
try
{
if(!dst.StartsWith("/")){ dst=cwd+"/"+dst; }
ArrayList v=glob_remote(dst);
if(v.Count!=1)
{
throw new SftpException(SSH_FX_FAILURE, v.ToString());
}
dst=(String)(v[0]);
if(isRemoteDir(dst))
{
throw new SftpException(SSH_FX_FAILURE, dst+" is a directory");
}
long skip=0;
if(mode==RESUME || mode==APPEND)
{
try
{
SftpATTRS attr=stat(dst);
skip=attr.getSize();
}
catch(Exception eee)
{
//System.out.println(eee);
}
}
if(mode==RESUME && skip>0)
{
long skipped=src.Seek(skip, SeekOrigin.Current);
if(skipped<skip)
{
throw new SftpException(SSH_FX_FAILURE, "failed to resume for "+dst);
}
}
if(mode==OVERWRITE)
{
sendOPENW(Util.getBytes(dst));
}
else
{
sendOPENA(Util.getBytes(dst));
}
buf.rewind();
int i=io.ins.Read(buf.buffer, 0, buf.buffer.Length);
int length=buf.getInt();
int type=buf.getByte();
if(type!=SSH_FXP_STATUS && type!=SSH_FXP_HANDLE)
{
throw new SftpException(SSH_FX_FAILURE, "");
}
if(type==SSH_FXP_STATUS)
{
buf.getInt();
i=buf.getInt();
throwStatusError(buf, i);
}
buf.getInt();
byte[] handle=buf.getString(); // filename
byte[] data=new byte[buf.buffer.Length-1024];
long offset=0;
if(mode==RESUME || mode==APPEND)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -