using System;
using System.Web;
using System.Net;
using System.IO;
using System.Text;
using Microsoft.SharePoint;
using System.Configuration;
using System.Web.SessionState;
using System.Web.Script.Serialization;
using Renci.SshNet;
using System.Security.Cryptography;
namespace AlkaneSolutions.Layouts.AlkaneSolutions
{
public class FileUpload : IHttpHandler, IRequiresSessionState
{
//important to implement IRequiresSessionState
//IReadOnlySessionState (for read-only access) or IRequiresSessionState (for read-write access)
public bool IsReusable
{
get
{
return false;
}
}
int chunk = 0;
int chunks = 0;
string fileName = "";
string fileExt = "";
string client = "";
string applicationID = "";
string uploadType = "";
//unique session names based on applicationID and UploadType
string ftpUsernameUniqueSessionName = "";
string ftpPasswordUniqueSessionName = "";
string ftpUploadProtocolUniqueSessionName = "";
string ftpUploadPortUniqueSessionName = "";
string ftpServerUniqueSessionName = "";
string ftpDownloadProtocolUniqueSessionName = "";
string ftpDownloadPortUniqueSessionName = "";
string ftpUsername = "";
string ftpPassword = "";
string ftpSalt = "";
string ftpUploadProtocol = "";
int ftpUploadPort = 0;
string ftpServer = "";
string ftpDownloadProtocol = "";
string ftpDownloadPort = "";
public void ProcessRequest(HttpContext context)
{
try
{
if (context.Request.Files.Count > 0)
{
for (int a = 0; a <= context.Request.Files.Count - 1; a++)
{
chunk = context.Request["chunk"] != null ? int.Parse(context.Request["chunk"]) : 0;
chunks = context.Request["chunks"] != null ? int.Parse(context.Request["chunks"]) -1 : 0;
fileName = context.Request["name"] ?? string.Empty;
fileExt = Path.GetExtension(fileName).ToLower();
client = context.Request["client"] ?? string.Empty;
applicationID = context.Request["applicationID"] ?? string.Empty;
uploadType = context.Request["uploadType"] ?? string.Empty;
//set up unique session names per application (useful for concurrent application uploads where applicationID is unique. These sessions are used
//when we upload using chunks (so we dont keep having to read from a data source for every chunk!)
ftpUsernameUniqueSessionName = applicationID + "_" + uploadType + "_ftpUsername";
ftpPasswordUniqueSessionName = applicationID + "_" + uploadType + "_ftpPassword";
ftpUploadProtocolUniqueSessionName = applicationID + "_" + uploadType + "_ftpUploadProtocol";
ftpUploadPortUniqueSessionName = applicationID + "_" + uploadType + "_ftpUploadPort";
ftpServerUniqueSessionName = applicationID + "_" + uploadType + "_ ftpServer";
ftpDownloadPortUniqueSessionName = applicationID + "_" + uploadType + "_ftpDownloadPort";
ftpDownloadProtocolUniqueSessionName = applicationID + "_" + uploadType + "_ftpDownloadProtocol";
if (fileExt != ".zip" && fileExt != ".7z" && fileExt != ".rar" && fileExt != ".iso")
{
AlkaneLogging.WriteToLog("File Upload Error: " + applicationID + " - illegal extension of " + fileExt);
context.Response.ContentType = "application/json";
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.StatusCode = 601;
context.Response.Write("{\"jsonrpc\" : \"2.0\", \"error\" : {\"code\": \"601\", \"message\": " + JsonEncode("Invalid File Type") + "}, \"id\" : \"id\"}");
context.ApplicationInstance.CompleteRequest();
}
if (chunk == 0)
{
//if it's the first chunk get FTP credentials (I've removed lots of code here - I obtain them from a SharePoint query but you may use any other data source)
ftpUsername = "ftpUsername";
ftpSalt = "ftpSalt";
//I retrieve the encrypted password stored in SharePoint and decrypt it here.
ftpPassword = Decrypt(ftpPassword, ftpUsername, ftpSalt, "InitialisationVector");
ftpServer = "ftpServer";
ftpUploadPort = 25;
ftpUploadProtocol = "ftpUploadProtocol";
ftpDownloadProtocol = "ftpDownloadProtocol";
ftpDownloadPort = "ftpDownloadPort";
//store in session, as we don't want to keep searching Sharepoint for each subsequent chunk!
context.Session[ftpUsernameUniqueSessionName] = ftpUsername;
context.Session[ftpPasswordUniqueSessionName] = ftpPassword;
context.Session[ftpUploadProtocolUniqueSessionName] = ftpUploadProtocol;
context.Session[ftpUploadPortUniqueSessionName] = ftpUploadPort;
context.Session[ftpServerUniqueSessionName] = ftpServer;
context.Session[ftpDownloadPortUniqueSessionName] = ftpDownloadPort;
context.Session[ftpDownloadProtocolUniqueSessionName] = ftpDownloadProtocol;
context.Session[ftpDownloadPortUniqueSessionName] = ftpDownloadPort;
}
else
{
//keep session alive for each chunk by resetting it
context.Session[ftpUsernameUniqueSessionName] = (string)(context.Session[ftpUsernameUniqueSessionName]);
context.Session[ftpPasswordUniqueSessionName] = (string)(context.Session[ftpPasswordUniqueSessionName]);
context.Session[ftpUploadProtocolUniqueSessionName] = (string)(context.Session[ftpUploadProtocolUniqueSessionName]);
context.Session[ftpUploadPortUniqueSessionName] = (int)(context.Session[ftpUploadPortUniqueSessionName]);
context.Session[ftpServerUniqueSessionName] = (string)(context.Session[ftpServerUniqueSessionName]);
context.Session[ftpDownloadPortUniqueSessionName] = (string)(context.Session[ftpDownloadPortUniqueSessionName]);
context.Session[ftpDownloadProtocolUniqueSessionName] = (string)(context.Session[ftpDownloadProtocolUniqueSessionName]);
//we'll need to set these vars in case it isn't the first chunk
ftpUsername = (string)(context.Session[ftpUsernameUniqueSessionName]);
ftpPassword = (string)(context.Session[ftpPasswordUniqueSessionName]);
ftpUploadProtocol = (string)(context.Session[ftpUploadProtocolUniqueSessionName]);
ftpUploadPort = (int)(context.Session[ftpUploadPortUniqueSessionName]);
ftpServer = (string)(context.Session[ftpServerUniqueSessionName]);
ftpDownloadPort = (string)(context.Session[ftpDownloadPortUniqueSessionName]);
ftpDownloadProtocol = (string)(context.Session[ftpDownloadProtocolUniqueSessionName]);
}
AlkaneLogging.WriteToLog("Server: " + ftpServer + "\r\nUsername: " + ftpUsername + "\r\nPassword: " + ftpPassword + "\r\nUpload Protocol: " + ftpUploadProtocol + "\r\nUpload Port: " + ftpUploadPort + "\r\nDownload Protocol: " + ftpDownloadPort + "\r\nDownload Port: " + ftpDownloadPort);
//construct upload string
string remoteDirectory = "SOURCE/" + applicationID + "/";
string remoteFilePath = remoteDirectory + fileName;
string SFTPFilePath = ftpUploadProtocol + "://" + ftpServer + ":" + ftpUploadPort + "/SOURCE/" + applicationID + "/" + fileName;
AlkaneLogging.WriteToLog("Attempting to upload: " + SFTPFilePath + " with creds: " + ftpUsername + " " + ftpPassword);
try
{
using (var ftp = new SftpClient(ftpServer, ftpUploadPort, ftpUsername, ftpPassword))
{
//connect to FTP stream
ftp.Connect();
AlkaneLogging.WriteToLog("Connected successfully to: " + ftpServer);
AlkaneLogging.WriteToLog("Attempting to create stream to: " + remoteFilePath);
//if first chunk, create. If subsequent chunk append
using (var destStream = ftp.Open(remoteFilePath, chunk != 0 ? FileMode.Append : FileMode.CreateNew, FileAccess.Write))
{
//if remote folder doesnt exist, create it!
if (!ftp.Exists(remoteDirectory))
{
AlkaneLogging.WriteToLog("Creating directory: " + remoteDirectory);
ftp.CreateDirectory(remoteDirectory);
}
AlkaneLogging.WriteToLog("Writing chunk " + (chunk).ToString() + " of " + chunks.ToString() + " to stream");
Stream streamReader = context.Request.Files[a].InputStream;
byte[] bFile = new byte[streamReader.Length];
streamReader.Read(bFile, 0, (int)streamReader.Length);
streamReader.Close();
streamReader.Dispose();
int offset = 0;
int buffer = (bFile.Length > 2048) ? 2048 : bFile.Length;
while (offset < bFile.Length)
{
destStream.Write(bFile, offset, buffer);
offset += buffer;
buffer = (bFile.Length - offset < buffer) ? (bFile.Length - offset) : buffer;
}
destStream.Flush();
destStream.Close();
AlkaneLogging.WriteToLog("File chunk for " + fileName + " uploaded successfully");
}
//disconnect from SFTP object
ftp.Disconnect();
}
//if no chunks (maybe not supported by browser or not specified) or final chunk
if ((chunks == 0) || (chunk == (chunks)))
{
ftpDownloadPort = (string)(context.Session[ftpDownloadPortUniqueSessionName]);
ftpDownloadProtocol = (string)(context.Session[ftpDownloadProtocolUniqueSessionName]);
string SharepointFTPPath = ftpDownloadProtocol + "://" + ftpServer + ":" + ftpDownloadPort + "?u=" + (string)(context.Session[ftpUsernameUniqueSessionName]) + "&p=" + (string)(context.Session[ftpPasswordUniqueSessionName]) + "&path=" + "/SOURCE/" + applicationID + "/" + fileName;
//update Sharepoint/database with upload path (removed in this code excerpt)
//remove FTP credentials from session
context.Session.Remove(ftpUsernameUniqueSessionName);
context.Session.Remove(ftpPasswordUniqueSessionName);
AlkaneLogging.WriteToLog("File Upload Success: " + applicationID);
context.Response.ContentType = "application/json";
context.Response.ContentEncoding = Encoding.UTF8;
context.Response.StatusCode = 200;
context.Response.Write("{\"jsonrpc\" : \"2.0\", \"result\" : null, \"id\" : \"id\"}");
context.ApplicationInstance.CompleteRequest();
}
}
catch (Exception ex)
{
AlkaneLogging.WriteToLog("File Upload Error: " + applicationID + " " + ex.Message);
context.Response.Write(ex.Message);
context.Response.StatusCode = 500;
context.ApplicationInstance.CompleteRequest();
}
}
}
else
{
AlkaneLogging.WriteToLog("File Upload Error: " + applicationID + " - no files to upload");
context.Response.Write("No files to upload");
context.Response.StatusCode = 500;
context.ApplicationInstance.CompleteRequest();
}
}
catch (WebException e)
{
String status = ((FtpWebResponse)e.Response).StatusDescription;
AlkaneLogging.WriteToLog("File Upload Error: " + applicationID + " - " + status);
context.Response.Clear();
context.Response.Write(status);
context.Response.StatusCode = 500;
context.ApplicationInstance.CompleteRequest();
}
}
public static bool FTPFileExists(string ftpServer, int ftpPort, string ftpUsername, string ftpPassword, string ftpFile)
{
bool fileExists = false;
try
{
using (var ftp = new SftpClient(ftpServer, ftpPort, ftpUsername, ftpPassword))
{
ftp.Connect();
if (ftp.Exists(ftpFile))
{
AlkaneLogging.WriteToLog(ftpFile + " exists");
fileExists = true;
}
else
{
AlkaneLogging.WriteToLog(ftpFile + " does not exist");
fileExists = false;
}
//disconnect from SFTP object
ftp.Disconnect();
return fileExists;
}
}
catch
{
AlkaneLogging.WriteToLog(ftpFile + " does not exist");
return fileExists;
}
}
protected string JsonEncode(object value)
{
var ser = new JavaScriptSerializer();
return ser.Serialize(value);
}
}
}