2016-07-05 20:22:34 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using System.IO;
|
2016-07-06 10:04:02 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Runtime.Remoting.Messaging;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Xml;
|
|
|
|
|
using System.Reflection;
|
2016-07-05 20:22:34 +00:00
|
|
|
|
|
2016-07-09 20:34:09 +00:00
|
|
|
|
using System.Drawing;
|
2016-07-08 16:12:57 +00:00
|
|
|
|
|
2016-07-05 20:22:34 +00:00
|
|
|
|
namespace GalleryShare
|
|
|
|
|
{
|
2016-07-06 19:03:03 +00:00
|
|
|
|
enum OutputFunction
|
|
|
|
|
{
|
|
|
|
|
None,
|
|
|
|
|
SpecialFile,
|
|
|
|
|
DirectoryListing,
|
|
|
|
|
SendFile
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-05 20:22:34 +00:00
|
|
|
|
class GalleryServer
|
|
|
|
|
{
|
2016-07-08 16:12:57 +00:00
|
|
|
|
static MimeSharp.Mime mimeDB = new MimeSharp.Mime();
|
|
|
|
|
|
2016-07-05 20:22:34 +00:00
|
|
|
|
int port;
|
2016-07-06 10:04:02 +00:00
|
|
|
|
string servingDirectory = Environment.CurrentDirectory;
|
2016-07-09 20:34:09 +00:00
|
|
|
|
Size thumbnailSize = new Size(300, 200);
|
2016-07-05 20:22:34 +00:00
|
|
|
|
|
2016-07-06 18:20:05 +00:00
|
|
|
|
HttpListener server = new HttpListener();
|
|
|
|
|
string prefix;
|
|
|
|
|
|
2016-07-08 08:50:53 +00:00
|
|
|
|
Dictionary<string, string> pathReplacements = new Dictionary<string, string>()
|
|
|
|
|
{
|
|
|
|
|
{ "%20", " " }
|
|
|
|
|
};
|
|
|
|
|
|
2016-07-06 10:04:02 +00:00
|
|
|
|
public int Port { get { return port; } }
|
2016-07-06 18:20:05 +00:00
|
|
|
|
public string ServingDirectory { get { return servingDirectory; } }
|
2016-07-05 20:22:34 +00:00
|
|
|
|
|
2016-07-06 18:20:05 +00:00
|
|
|
|
public GalleryServer(string inServingDirectory, int inPort)
|
2016-07-05 20:22:34 +00:00
|
|
|
|
{
|
|
|
|
|
port = inPort;
|
2016-07-06 18:20:05 +00:00
|
|
|
|
servingDirectory = inServingDirectory;
|
2016-07-05 20:22:34 +00:00
|
|
|
|
|
2016-07-08 08:12:59 +00:00
|
|
|
|
string homeDir = Environment.GetEnvironmentVariable("HOME");
|
|
|
|
|
if (homeDir != null)
|
|
|
|
|
servingDirectory = servingDirectory.Replace("~", homeDir);
|
|
|
|
|
|
2016-07-05 20:22:34 +00:00
|
|
|
|
prefix = string.Format("http://*:{0}/", Port);
|
|
|
|
|
server.Prefixes.Add(prefix);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Synchronously starrts the server listening for requests.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void StartSync()
|
|
|
|
|
{
|
|
|
|
|
Task.WaitAll(Start());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Asynchronously starts the server listening for requests.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async Task Start()
|
|
|
|
|
{
|
|
|
|
|
server.Start();
|
|
|
|
|
Console.WriteLine("Listening for requests on {0}.", prefix);
|
2016-07-08 08:12:59 +00:00
|
|
|
|
Console.WriteLine("Serving from {0}. Browser url: http://localhost:{1}/", servingDirectory, Port);
|
2016-07-05 20:22:34 +00:00
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
Utilities.ForgetTask(Handle(await server.GetContextAsync()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Handles the specified Http request.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="cycle">The Http request to handle.</param>
|
|
|
|
|
private async Task Handle(HttpListenerContext cycle)
|
|
|
|
|
{
|
2016-07-06 19:03:03 +00:00
|
|
|
|
OutputFunction outFunction = OutputFunction.None;
|
2016-07-06 10:04:02 +00:00
|
|
|
|
|
2016-07-06 19:03:03 +00:00
|
|
|
|
if (cycle.Request.RawUrl.StartsWith("/!"))
|
|
|
|
|
outFunction = OutputFunction.SpecialFile;
|
2016-07-05 20:22:34 +00:00
|
|
|
|
|
2016-07-06 19:03:03 +00:00
|
|
|
|
string requestedPath = GetFullReqestedPath(cycle.Request.RawUrl);
|
|
|
|
|
if (Directory.Exists(requestedPath))
|
|
|
|
|
outFunction = OutputFunction.DirectoryListing;
|
|
|
|
|
if (File.Exists(requestedPath))
|
|
|
|
|
outFunction = OutputFunction.SendFile;
|
|
|
|
|
|
|
|
|
|
switch(outFunction)
|
2016-07-06 10:04:02 +00:00
|
|
|
|
{
|
2016-07-06 19:03:03 +00:00
|
|
|
|
case OutputFunction.SpecialFile:
|
|
|
|
|
await sendSpecialFile(cycle);
|
|
|
|
|
break;
|
2016-07-06 10:04:02 +00:00
|
|
|
|
|
2016-07-06 19:03:03 +00:00
|
|
|
|
case OutputFunction.DirectoryListing:
|
2016-07-06 19:18:14 +00:00
|
|
|
|
cycle.Response.ContentType = "application/xml";
|
2016-07-06 19:03:03 +00:00
|
|
|
|
await sendDirectoryListing(cycle.Response.OutputStream, cycle.Request.RawUrl, requestedPath);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OutputFunction.SendFile:
|
2016-07-08 16:12:57 +00:00
|
|
|
|
await sendFile(cycle, requestedPath);
|
2016-07-06 19:03:03 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
await sendMessage(cycle, 404, "Error: File or directory '{0}' not found.", requestedPath);
|
|
|
|
|
break;
|
2016-07-06 10:04:02 +00:00
|
|
|
|
}
|
2016-07-05 20:22:34 +00:00
|
|
|
|
|
2016-07-06 10:22:58 +00:00
|
|
|
|
logCycle(cycle);
|
2016-07-05 20:22:34 +00:00
|
|
|
|
cycle.Response.Close();
|
2016-07-06 10:04:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-06 19:03:03 +00:00
|
|
|
|
private string GetFullReqestedPath(string rawUrl)
|
|
|
|
|
{
|
2016-07-08 08:50:53 +00:00
|
|
|
|
string result = Path.GetFullPath(Path.Combine(servingDirectory, "." + rawUrl));
|
|
|
|
|
if(result.IndexOf("?") != -1)
|
|
|
|
|
result = result.Substring(0, result.IndexOf("?"));
|
|
|
|
|
foreach (KeyValuePair<string, string> replacePair in pathReplacements)
|
|
|
|
|
result = result.Replace(replacePair.Key, replacePair.Value);
|
|
|
|
|
return result;
|
2016-07-06 19:03:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-06 10:04:02 +00:00
|
|
|
|
private async Task sendMessage(HttpListenerContext cycle, int statusCode, string message, params object[] paramObjects)
|
|
|
|
|
{
|
|
|
|
|
StreamWriter responseData = new StreamWriter(cycle.Response.OutputStream);
|
|
|
|
|
|
|
|
|
|
cycle.Response.StatusCode = statusCode;
|
|
|
|
|
await responseData.WriteLineAsync(string.Format(message, paramObjects));
|
2016-07-06 19:03:03 +00:00
|
|
|
|
/*responseData.Close();
|
|
|
|
|
cycle.Response.Close();*/
|
2016-07-06 10:04:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void logCycle(HttpListenerContext cycle)
|
|
|
|
|
{
|
2016-07-05 20:22:34 +00:00
|
|
|
|
Console.WriteLine("[{0}] [{1}] [{2}] {3} {4}",
|
|
|
|
|
DateTime.Now.ToString("hh:mm tt"),
|
|
|
|
|
cycle.Request.RemoteEndPoint,
|
|
|
|
|
cycle.Response.StatusCode,
|
|
|
|
|
cycle.Request.HttpMethod,
|
|
|
|
|
cycle.Request.RawUrl
|
|
|
|
|
);
|
|
|
|
|
}
|
2016-07-06 19:03:03 +00:00
|
|
|
|
|
|
|
|
|
private async Task sendDirectoryListing(Stream outgoingData, string rawUrl, string requestedPath)
|
|
|
|
|
{
|
|
|
|
|
List<string> dirFiles = new List<string>(Directory.GetFiles(requestedPath));
|
|
|
|
|
List<string> dirDirectories = new List<string>(Directory.GetDirectories(requestedPath));
|
|
|
|
|
|
|
|
|
|
XmlWriterSettings writerSettings = new XmlWriterSettings();
|
|
|
|
|
writerSettings.Async = true;
|
2016-07-06 20:08:28 +00:00
|
|
|
|
writerSettings.Indent = true;
|
|
|
|
|
writerSettings.IndentChars = "\t";
|
2016-07-06 19:03:03 +00:00
|
|
|
|
XmlWriter xmlData = XmlWriter.Create(outgoingData, writerSettings);
|
|
|
|
|
|
|
|
|
|
await xmlData.WriteStartDocumentAsync();
|
2016-07-06 19:18:14 +00:00
|
|
|
|
await xmlData.WriteProcessingInstructionAsync("xml-stylesheet", "type=\"text/xsl\" href=\"/!Transform-DirListing.xslt\"");
|
2016-07-06 19:03:03 +00:00
|
|
|
|
await xmlData.WriteStartElementAsync(null, "DirectoryListing", null);
|
|
|
|
|
await xmlData.WriteElementStringAsync(null, "CurrentDirectory", null, rawUrl);
|
|
|
|
|
await xmlData.WriteStartElementAsync(null, "Contents", null);
|
|
|
|
|
|
2016-07-08 08:12:59 +00:00
|
|
|
|
foreach (string directoryName in dirDirectories)
|
2016-07-06 19:03:03 +00:00
|
|
|
|
{
|
|
|
|
|
await xmlData.WriteStartElementAsync(null, "ListingEntry", null);
|
|
|
|
|
await xmlData.WriteAttributeStringAsync(null, "Type", null, "Directory");
|
|
|
|
|
|
2016-07-10 19:49:52 +00:00
|
|
|
|
await xmlData.WriteElementStringAsync(null, "Name", null, "/" + directoryName.Substring(servingDirectory.Length));
|
2016-07-08 08:12:59 +00:00
|
|
|
|
await xmlData.WriteElementStringAsync(null, "ItemCount", null, Directory.GetFileSystemEntries(directoryName).Length.ToString());
|
2016-07-06 19:03:03 +00:00
|
|
|
|
|
|
|
|
|
// TODO: Write out thumbnail url
|
|
|
|
|
|
|
|
|
|
await xmlData.WriteEndElementAsync();
|
|
|
|
|
}
|
|
|
|
|
foreach (string filename in dirFiles)
|
|
|
|
|
{
|
|
|
|
|
await xmlData.WriteStartElementAsync(null, "ListingEntry", null);
|
|
|
|
|
await xmlData.WriteAttributeStringAsync(null, "Type", null, "File");
|
|
|
|
|
|
2016-07-10 19:49:52 +00:00
|
|
|
|
await xmlData.WriteElementStringAsync(null, "Name", null, "/" + filename.Substring(servingDirectory.Length));
|
2016-07-06 19:03:03 +00:00
|
|
|
|
|
|
|
|
|
await xmlData.WriteEndElementAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await xmlData.WriteEndDocumentAsync();
|
|
|
|
|
await xmlData.FlushAsync();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task sendSpecialFile(HttpListenerContext cycle)
|
|
|
|
|
{
|
|
|
|
|
string specialFileName = cycle.Request.RawUrl.Substring(2);
|
|
|
|
|
string outputFileName = string.Empty;
|
|
|
|
|
|
|
|
|
|
switch(specialFileName)
|
|
|
|
|
{
|
|
|
|
|
case "Transform-DirListing.xslt":
|
2016-07-08 08:50:53 +00:00
|
|
|
|
cycle.Response.ContentType = "text/xsl";
|
2016-07-08 08:32:06 +00:00
|
|
|
|
outputFileName = @"GalleryShare.Embed.DirectoryListing.xslt";
|
2016-07-06 19:03:03 +00:00
|
|
|
|
break;
|
2016-07-08 08:50:53 +00:00
|
|
|
|
case "Theme.css":
|
|
|
|
|
cycle.Response.ContentType = "text/css";
|
|
|
|
|
outputFileName = @"GalleryShare.Embed.Theme.css";
|
|
|
|
|
break;
|
2016-07-06 19:03:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (outputFileName == string.Empty)
|
|
|
|
|
{
|
|
|
|
|
await sendMessage(cycle, 404, "Error: Unknown special file '{0}' requested.", specialFileName);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*string[] resNames = Assembly.GetExecutingAssembly().GetManifestResourceNames();
|
|
|
|
|
foreach (string resName in resNames)
|
|
|
|
|
Console.WriteLine(resName);*/
|
|
|
|
|
byte[] xsltData = await Utilities.GetEmbeddedResourceContent(outputFileName);
|
|
|
|
|
await cycle.Response.OutputStream.WriteAsync(xsltData, 0, xsltData.Length);
|
|
|
|
|
}
|
2016-07-08 08:29:54 +00:00
|
|
|
|
|
2016-07-08 16:12:57 +00:00
|
|
|
|
private async Task sendFile(HttpListenerContext cycle, string requestedPath)
|
2016-07-08 08:29:54 +00:00
|
|
|
|
{
|
2016-07-08 16:12:57 +00:00
|
|
|
|
if(cycle.Request.QueryString["type"] == "thumbnail")
|
|
|
|
|
{
|
|
|
|
|
// Send a thumbnail!
|
2016-07-09 20:34:09 +00:00
|
|
|
|
Console.WriteLine("Sending thumbnail for '{0}'", requestedPath);
|
|
|
|
|
cycle.Response.ContentType = "image/png";
|
|
|
|
|
ThumbnailGenerator.GenerateThumbnailPng(requestedPath, thumbnailSize, cycle.Response.OutputStream);
|
2016-07-08 16:12:57 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send the raw file
|
|
|
|
|
|
|
|
|
|
cycle.Response.ContentType = mimeDB.Lookup(requestedPath);
|
|
|
|
|
|
|
|
|
|
Stream fileData = File.OpenRead(requestedPath);
|
|
|
|
|
await fileData.CopyToAsync(cycle.Response.OutputStream);
|
2016-07-08 08:29:54 +00:00
|
|
|
|
}
|
2016-07-05 20:22:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|