//
// Class: sftp
// Current CLR: 4.0.30319.34209
// System: Microsoft Visual Studio 10.0
// Author: dani
// Created: 4/28/2015 8:53:41 AM
//
// Copyright (c) 2015 Informatikbüro Daniel Schick. All rights reserved.
using log4net;
using System.Diagnostics;
using bsmd.database;
using WinSCP;
using System;
using System.Linq;
using System.Collections.Generic;
namespace bsmd.dakosy
{
///
/// Hilfsklasse zur Kommunikation via SFTP. Wir machen es uns hier leicht und verwenden das beliebte WINSCP im
/// Process-Modus. (http://winscp.net/eng/docs/guide_dotnet)
///
public static class SFtp
{
private static readonly ILog _log = LogManager.GetLogger(typeof(SFtp));
public enum Direction
{
INCOMING,
OUTGOING
}
public static void Put(string filename, string targetDir)
{
Process winscp = new Process();
winscp.StartInfo.FileName = Properties.Settings.Default.WINSCPFullPath;
winscp.StartInfo.Arguments = "/xmllog=\"" + Properties.Settings.Default.SFTPLog + "\"";
winscp.StartInfo.UseShellExecute = false;
winscp.StartInfo.RedirectStandardInput = true;
winscp.StartInfo.RedirectStandardOutput = true;
winscp.StartInfo.CreateNoWindow = true;
winscp.Start();
string dotlessFilename = filename.Substring(1);
// Feed in the scripting commands
winscp.StandardInput.WriteLine("option batch abort");
winscp.StandardInput.WriteLine("option confirm off");
winscp.StandardInput.WriteLine("open " + Properties.Settings.Default.SFTPSessionName);
winscp.StandardInput.WriteLine("lcd " + Properties.Settings.Default.SFTPOutDir);
// winscp.StandardInput.WriteLine("ls");
if (targetDir != null)
winscp.StandardInput.WriteLine("cd " + targetDir);
winscp.StandardInput.WriteLine("put " + filename);
winscp.StandardInput.WriteLine(string.Format("mv {0} {1}", filename, dotlessFilename));
winscp.StandardInput.Close();
// Collect all output
string output = winscp.StandardOutput.ReadToEnd();
_log.Debug(output);
output = winscp.StandardError.ReadToEnd();
if (!output.Trim().IsNullOrEmpty())
_log.Warn(output);
// Wait until WinSCP finishes
winscp.WaitForExit();
/*
// Parse and interpret the XML log
// (Note that in case of fatal failure the log file may not exist at all)
XPathDocument log = new XPathDocument(Properties.Settings.Default.SFTPLog);
XmlNamespaceManager ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("w", @"http://winscp.net/schema/session/1.0");
XPathNavigator nav = log.CreateNavigator();
// Success (0) or error?
if (winscp.ExitCode != 0)
{
Console.WriteLine("Error occured");
// See if there are any messages associated with the error
foreach (XPathNavigator message in nav.Select("//w:message", ns))
{
Console.WriteLine(message.Value);
}
}
else
{
// It can be worth looking for directory listing even in case of
// error as possibly only upload may fail
XPathNodeIterator files = nav.Select("//w:file", ns);
Console.WriteLine(string.Format("There are {0} files and subdirectories:", files.Count));
foreach (XPathNavigator file in files)
{
Console.WriteLine(file.SelectSingleNode("w:filename/@value", ns).Value);
}
}
*/
}
private static Process StartWinSCPProcess()
{
Process winscp = new Process();
winscp.StartInfo.FileName = Properties.Settings.Default.WINSCPFullPath;
winscp.StartInfo.Arguments = "/xmllog=\"" + Properties.Settings.Default.SFTPLog + "\"";
winscp.StartInfo.UseShellExecute = false;
winscp.StartInfo.RedirectStandardInput = true;
winscp.StartInfo.RedirectStandardOutput = true;
winscp.StartInfo.RedirectStandardError = true;
winscp.StartInfo.CreateNoWindow = true;
//winscp.EnableRaisingEvents = true;
//winscp.OutputDataReceived += (s, e) => { if (!e.Data.IsNullOrEmpty()) _log.Debug(e.Data); };
//winscp.ErrorDataReceived += (s, e) => { if (!e.Data.IsNullOrEmpty()) _log.Warn(e.Data); };
winscp.Start();
//winscp.BeginErrorReadLine();
//winscp.BeginOutputReadLine();
return winscp;
}
public static void TransmitAllFiles(string remoteDir, string localDir, string host, string user, string keyPath, string hostkey)
{
try
{
SessionOptions sessionOptions = new SessionOptions();
sessionOptions.Protocol = Protocol.Sftp;
sessionOptions.HostName = host;
sessionOptions.UserName = user;
sessionOptions.SshPrivateKeyPath = keyPath;
sessionOptions.SshHostKeyFingerprint = hostkey;
using (Session session = new Session())
{
session.Open(sessionOptions);
// upload
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
TransferOperationResult transferResult = session.PutFiles(localDir, remoteDir, false, transferOptions);
transferResult.Check();
foreach(TransferEventArgs tea in transferResult.Transfers.Cast())
{
_log.InfoFormat("Upload of {0} successful", tea.FileName);
}
}
}
catch(Exception e)
{
_log.Error(e);
}
}
public static void ReceiveAllFiles(string remoteDir, string localDir, string host, string user, string keyPath, string hostkey)
{
try
{
SessionOptions sessionOptions = new SessionOptions();
sessionOptions.Protocol = Protocol.Sftp;
sessionOptions.HostName = host;
sessionOptions.UserName = user;
sessionOptions.SshPrivateKeyPath = keyPath;
sessionOptions.SshHostKeyFingerprint = hostkey;
using (Session session = new Session())
{
session.Open(sessionOptions);
// upload
TransferOptions transferOptions = new TransferOptions();
transferOptions.TransferMode = TransferMode.Binary;
TransferOperationResult transferResult = session.GetFiles(remoteDir, localDir, false, transferOptions);
transferResult.Check();
foreach (TransferEventArgs tea in transferResult.Transfers.Cast())
{
_log.InfoFormat("Download of {0} successful", tea.FileName);
}
if (transferResult.Transfers.Count > 0)
{
RemovalOperationResult removalResult = session.RemoveFiles(remoteDir + "/*.*");
if ((removalResult.IsSuccess) && (removalResult.Removals.Count > 0))
{
_log.InfoFormat("Removed {0} downloaded files on remote host", removalResult.Removals);
}
if (!removalResult.IsSuccess)
{
_log.WarnFormat("RemoveFiles was not successful, {0} failures", removalResult.Failures.Count);
}
}
}
}
catch (Exception e)
{
_log.Error(e);
}
}
public static void TransmitAll(string remoteDir, string localDir, Direction direction, string openCommand)
{
Process winscp = StartWinSCPProcess();
// Feed in the scripting commands
winscp.StandardInput.WriteLine("option batch abort");
winscp.StandardInput.WriteLine("option confirm off");
winscp.StandardInput.WriteLine(openCommand);
winscp.StandardInput.WriteLine("lcd " + localDir);
// winscp.StandardInput.WriteLine("ls");
if (remoteDir != null)
winscp.StandardInput.WriteLine("cd " + remoteDir);
if(direction == Direction.INCOMING)
winscp.StandardInput.WriteLine("get *.xml");
if(direction == Direction.OUTGOING)
winscp.StandardInput.WriteLine("put *.xml");
winscp.StandardInput.Close();
// Collect all output
string output = winscp.StandardOutput.ReadToEnd();
_log.Debug(output);
output = winscp.StandardError.ReadToEnd();
if (!output.Trim().IsNullOrEmpty())
_log.Warn(output);
// Wait until WinSCP finishes
winscp.WaitForExit();
if(winscp.ExitCode != 0)
{
_log.Warn("WinSCP exited with an error");
}
}
public static void RemoveProcessedFile(string remoteDir, string filename, string openCommand)
{
Process winscp = StartWinSCPProcess();
// Feed in the scripting commands
winscp.StandardInput.WriteLine("option batch abort");
winscp.StandardInput.WriteLine("option confirm off");
winscp.StandardInput.WriteLine(openCommand);
// winscp.StandardInput.WriteLine("ls");
if (remoteDir != null)
winscp.StandardInput.WriteLine("cd " + remoteDir);
winscp.StandardInput.WriteLine("rm " + filename);
winscp.StandardInput.Close();
// Wait until WinSCP finishes
winscp.WaitForExit();
}
}
}