00001 using System;
00002 using Tamir.SharpSsh.jsch;
00003 using System.IO;
00004 using System.Text;
00005 using System.Collections;
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 namespace Tamir.SharpSsh
00039 {
00043 public class Scp : SshTransferProtocolBase
00044 {
00045 private bool m_recursive = false;
00046 private bool m_verbos = false;
00047 private bool m_cancelled = false;
00048
00049 public Scp(string host, string user, string password)
00050 : base(host, user, password)
00051 {
00052 }
00053
00054 public Scp(string host, string user)
00055 : base(host, user)
00056 {
00057 }
00058
00059 protected override string ChannelType
00060 {
00061 get { return "exec"; }
00062 }
00063
00068 protected override void ConnectChannel()
00069 {
00070 }
00071
00075 public bool Recursive
00076 {
00077 get{return m_recursive;}
00078 set{m_recursive=value;}
00079 }
00080
00084 public bool Verbos
00085 {
00086 get{return m_verbos;}
00087 set{m_verbos=value;}
00088 }
00089
00090 public override void Cancel()
00091 {
00092 m_cancelled = true;
00093 }
00094
00099 public override void Mkdir(string dir)
00100 {
00101 SCP_CheckConnectivity();
00102
00103 Channel channel=null;
00104 Stream server = null;
00105 m_cancelled=false;
00106
00107 SCP_ConnectTo(out channel, out server, dir, true);
00108 SCP_EnterIntoDir(server, dir);
00109 channel.disconnect();
00110 }
00111
00112 public override void Put(string fromFilePath, string toFilePath)
00113 {
00114 this.To(fromFilePath, toFilePath);
00115 }
00116
00117 public override void Get(string fromFilePath, string toFilePath)
00118 {
00119 this.From(fromFilePath, toFilePath);
00120 }
00121
00122
00123
00129 public void To(string localPath, string remotePath)
00130 {
00131 this.To(localPath, remotePath, Recursive);
00132 }
00133
00134
00140 public void To(string localPath, string remotePath, bool _recursive)
00141 {
00142 SCP_CheckConnectivity();
00143
00144 Channel channel=null;
00145 Stream server = null;
00146 m_cancelled=false;
00147
00148 try
00149 {
00150
00151 if(File.Exists(localPath))
00152 {
00153 SCP_ConnectTo(out channel, out server, remotePath, _recursive);
00154 SCP_SendFile(server, localPath, remotePath);
00155 channel.disconnect();
00156 }
00157
00158 else if(Directory.Exists(localPath))
00159 {
00160 if(!_recursive)
00161 {
00162 throw new SshTransferException(Path.GetFileName("'"+localPath)+"' is a directory, you should use recursive transfer.");
00163 }
00164 SCP_ConnectTo(out channel, out server, remotePath, true);
00165 ToRecursive(server, localPath, remotePath);
00166 channel.disconnect();
00167 }
00168 else
00169 {
00170 throw new SshTransferException("File not found: "+localPath);
00171 }
00172 }
00173 catch(Exception e)
00174 {
00175 if(Verbos)
00176 Console.WriteLine("Error: "+e.Message);
00177
00178 try{channel.disconnect();}
00179 catch{}
00180 throw e;
00181 }
00182 }
00183
00190 private void ToRecursive(Stream server, string src, string dst)
00191 {
00192 if(Directory.Exists(src))
00193 {
00194 SCP_EnterIntoDir(server, Path.GetFileName(dst));
00195 foreach(string file in Directory.GetFiles(src))
00196 {
00197 SCP_SendFile(server, file, Path.GetFileName( file));
00198 }
00199 if(m_cancelled)
00200 {
00201 return;
00202 }
00203 foreach(string dir in Directory.GetDirectories(src))
00204 {
00205 ToRecursive(server, dir, Path.GetFileName(dir));
00206 }
00207 SCP_EnterIntoParent(server);
00208 }
00209 else if(File.Exists(src))
00210 {
00211 SCP_SendFile(server, src, Path.GetFileName(src));
00212 }
00213 else
00214 {
00215 throw new SshTransferException("File not found: "+src);
00216 }
00217 }
00218
00224 public void From(string remoteFile, string localPath)
00225 {
00226 this.From(remoteFile, localPath, Recursive);
00227 }
00228
00235 public void From(string remoteFile, string localPath, bool _recursive)
00236 {
00237 SCP_CheckConnectivity();
00238
00239 Channel channel=null;
00240 Stream server = null;
00241 m_cancelled=false;
00242 int filesize=0;
00243 String filename=null;
00244 string cmd = null;
00245
00246 try
00247 {
00248 String dir=null;
00249 if(Directory.Exists(localPath))
00250 {
00251 dir= Path.GetFullPath( localPath );
00252 }
00253
00254 SCP_ConnectFrom(out channel, out server, remoteFile, _recursive);
00255
00256 byte[] buf=new byte[1024];
00257
00258
00259 SCP_SendAck(server);
00260 int c=SCP_CheckAck(server);
00261
00262
00263 while((c=='D')||(c=='C')||(c=='E'))
00264 {
00265 if(m_cancelled)
00266 break;
00267
00268 cmd = ""+(char)c;
00269 if(c=='E')
00270 {
00271 c=SCP_CheckAck(server);
00272 dir = Path.GetDirectoryName(dir);
00273 if(Verbos) Console.WriteLine("E");
00274
00275 SCP_SendAck(server);
00276 c=(char)SCP_CheckAck(server);
00277 continue;
00278 }
00279
00280
00281 server.Read(buf, 0, 5);
00282 for(int i=0;i<5;i++)
00283 cmd+=(char)buf[i];
00284
00285
00286 filesize=0;
00287 while(true)
00288 {
00289 server.Read(buf, 0, 1);
00290 if(buf[0]==' ')break;
00291 filesize=filesize*10+(buf[0]-'0');
00292 }
00293
00294
00295 for(int i=0;;i++)
00296 {
00297 server.Read(buf, i, 1);
00298 if(buf[i]==(byte)0x0a)
00299 {
00300 filename=Util.getString(buf, 0, i);
00301 break;
00302 }
00303 }
00304 cmd += " "+filesize+" "+filename;
00305
00306 SCP_SendAck(server);
00307
00308
00309 if(c=='C')
00310 {
00311 if(Verbos) Console.WriteLine("Sending file modes: "+cmd);
00312 SCP_ReceiveFile(server, remoteFile,
00313 dir==null?localPath:dir+"/"+filename,
00314 filesize);
00315
00316 if(m_cancelled)
00317 break;
00318
00319
00320 SCP_SendAck(server);
00321 }
00322
00323 else if(c=='D')
00324 {
00325 if(dir==null)
00326 {
00327 if(File.Exists(localPath)) throw new SshTransferException("'"+localPath+"' is not a directory");
00328 dir = localPath;
00329 Directory.CreateDirectory(dir);
00330 }
00331 if(Verbos) Console.WriteLine("Entering directory: "+cmd);
00332 dir += "/"+filename;
00333 Directory.CreateDirectory(dir);
00334 }
00335
00336 c=SCP_CheckAck(server);
00337 }
00338 channel.disconnect();
00339 }
00340 catch(Exception e)
00341 {
00342 if(Verbos)
00343 Console.WriteLine("Error: "+e.Message);
00344 try
00345 {
00346 channel.disconnect();
00347 }
00348 catch{}
00349 throw e;
00350 }
00351 }
00352
00353 #region SCP private functions
00354
00358 protected void SCP_CheckConnectivity()
00359 {
00360 if(!Connected)
00361 throw new Exception("Channel is down.");
00362 }
00363
00371 protected void SCP_ConnectTo(out Channel channel, out Stream server, string rfile, bool recursive)
00372 {
00373 string scpCommand = "scp -p -t ";
00374 if(recursive) scpCommand += "-r ";
00375 scpCommand += "\""+rfile+"\"";
00376
00377 channel = (ChannelExec)m_session.openChannel(ChannelType);
00378 ((ChannelExec)channel).setCommand(scpCommand);
00379
00380 server =
00381 new Tamir.Streams.CombinedStream
00382 (channel.getInputStream(), channel.getOutputStream());
00383 channel.connect();
00384
00385 SCP_CheckAck(server);
00386 }
00387
00395 protected void SCP_ConnectFrom(out Channel channel, out Stream server, string rfile, bool recursive)
00396 {
00397 string scpCommand = "scp -f ";
00398 if(recursive) scpCommand += "-r ";
00399 scpCommand += "\""+rfile+"\"";
00400
00401 channel = (ChannelExec)m_session.openChannel(ChannelType);
00402 ((ChannelExec)channel).setCommand(scpCommand);
00403
00404 server =
00405 new Tamir.Streams.CombinedStream
00406 (channel.getInputStream(), channel.getOutputStream());
00407 channel.connect();
00408
00409
00410 }
00411
00418 protected void SCP_SendFile(Stream server, string src, string dst)
00419 {
00420 int filesize = 0;
00421 int copied = 0;
00422
00423 filesize=(int)(new FileInfo(src)).Length;
00424
00425 byte[] tmp=new byte[1];
00426
00427
00428
00429 string command="C0644 "+filesize+" "+Path.GetFileName(dst)+"\n";
00430 if(Verbos) Console.WriteLine("Sending file modes: "+command);
00431 SendStartMessage(src, dst, filesize, "Starting transfer.");
00432 byte[] buff = Util.getBytes(command);
00433 server.Write(buff, 0, buff.Length); server.Flush();
00434
00435 if(SCP_CheckAck(server)!=0)
00436 {
00437 throw new SshTransferException("Error openning communication channel.");
00438 }
00439
00440
00441 SendProgressMessage(src, dst, copied, filesize, "Transferring...");
00442 FileStream fis=File.OpenRead(src);
00443 byte[] buf=new byte[1024*10*2];
00444
00445 while(!m_cancelled)
00446 {
00447 int len=fis.Read(buf, 0, buf.Length);
00448 if(len<=0) break;
00449 server.Write(buf, 0, len); server.Flush();
00450 copied += len;
00451 SendProgressMessage(src, dst, copied, filesize, "Transferring...");
00452 }
00453 fis.Close();
00454
00455 if(m_cancelled)
00456 return;
00457
00458
00459 buf[0]=0; server.Write(buf, 0, 1); server.Flush();
00460
00461 SendProgressMessage(src, dst, copied, filesize, "Verifying transfer...");
00462 if(SCP_CheckAck(server)!=0)
00463 {
00464 SendEndMessage(src, dst,copied,filesize, "Transfer ended with an error.");
00465 throw new SshTransferException("Unknow error during file transfer.");
00466 }
00467 SendEndMessage(src, dst, copied, filesize, "Transfer completed successfuly ("+copied+" bytes).");
00468 }
00469
00476 protected void SCP_ReceiveFile(Stream server, string rfile, string lfile, int size)
00477 {
00478 int copied = 0;
00479 SendStartMessage(rfile, lfile, size, "Connected, starting transfer.");
00480
00481 FileStream fos=File.OpenWrite(lfile);
00482 int foo;
00483 int filesize=size;
00484 byte[] buf = new byte[1024];
00485 while(!m_cancelled)
00486 {
00487 if(buf.Length<filesize) foo=buf.Length;
00488 else foo=filesize;
00489 int len=server.Read(buf, 0, foo);
00490 copied += len;
00491 fos.Write(buf, 0, foo);
00492 SendProgressMessage(rfile, lfile, copied, size, "Transferring...");
00493 filesize-=foo;
00494 if(filesize==0) break;
00495 }
00496 fos.Close();
00497 if(m_cancelled)
00498 return;
00499 SCP_CheckAck(server);
00500 SendEndMessage(rfile, lfile, copied, size, "Transfer completed successfuly ("+filesize+" bytes).");
00501 }
00502
00508 protected void SCP_EnterIntoDir(Stream server, string dir)
00509 {
00510 try
00511 {
00512 byte[] tmp=new byte[1];
00513
00514
00515
00516 string command="D0755 0 "+Path.GetFileName(dir)+"\n";
00517 if(Verbos) Console.WriteLine("Enter directory: "+command);
00518
00519 byte[] buff = Util.getBytes(command);
00520 server.Write(buff, 0, buff.Length); server.Flush();
00521
00522 if(SCP_CheckAck(server)!=0)
00523 {
00524 throw new SshTransferException("Error openning communication channel.");
00525 }
00526 }
00527 catch{}
00528 }
00529
00534 protected void SCP_EnterIntoParent(Stream server)
00535 {
00536 try
00537 {
00538 byte[] tmp=new byte[1];
00539
00540
00541
00542 string command="E\n";
00543 if(Verbos) Console.WriteLine(command);
00544
00545 byte[] buff = Util.getBytes(command);
00546 server.Write(buff, 0, buff.Length); server.Flush();
00547
00548 if(SCP_CheckAck(server)!=0)
00549 {
00550 throw new SshTransferException("Error openning communication channel.");
00551 }
00552 }
00553 catch{}
00554 }
00555
00560 private int SCP_CheckAck(Stream ins)
00561 {
00562 int b=ins.ReadByte();
00563
00564
00565
00566
00567 if(b==0) return b;
00568 if(b==-1) return b;
00569
00570 if(b==1 || b==2)
00571 {
00572 StringBuilder sb=new StringBuilder();
00573 int c;
00574 do
00575 {
00576 c=ins.ReadByte();
00577 sb.Append((char)c);
00578 }
00579 while(c!='\n');
00580 if(b==1)
00581 {
00582
00583 throw new SshTransferException(sb.ToString());
00584 }
00585 if(b==2)
00586 {
00587
00588 throw new SshTransferException(sb.ToString());
00589 }
00590 }
00591 return b;
00592 }
00593
00598 private void SCP_SendAck(Stream server)
00599 {
00600 server.WriteByte(0);
00601 server.Flush();
00602 }
00603
00604 #endregion SCP private functions
00605 }
00606 }