diff --git a/Builder/IISMainHandler/build.txt b/Builder/IISMainHandler/build.txt
index a9d8b73..fefb598 100644
--- a/Builder/IISMainHandler/build.txt
+++ b/Builder/IISMainHandler/build.txt
@@ -1 +1 @@
-257
\ No newline at end of file
+284
\ No newline at end of file
diff --git a/Builder/IISUploadHandler/build.txt b/Builder/IISUploadHandler/build.txt
new file mode 100644
index 0000000..19c7bdb
--- /dev/null
+++ b/Builder/IISUploadHandler/build.txt
@@ -0,0 +1 @@
+16
\ No newline at end of file
diff --git a/Builder/IISUploadHandler/postbuild.bat b/Builder/IISUploadHandler/postbuild.bat
new file mode 100644
index 0000000..9c9b7a9
--- /dev/null
+++ b/Builder/IISUploadHandler/postbuild.bat
@@ -0,0 +1 @@
+@echo off
diff --git a/Builder/IISUploadHandler/prebuild.bat b/Builder/IISUploadHandler/prebuild.bat
new file mode 100644
index 0000000..9c9b7a9
--- /dev/null
+++ b/Builder/IISUploadHandler/prebuild.bat
@@ -0,0 +1 @@
+@echo off
diff --git a/Builder/IISUploadHandler/product.wxs b/Builder/IISUploadHandler/product.wxs
new file mode 100644
index 0000000..e9743dc
--- /dev/null
+++ b/Builder/IISUploadHandler/product.wxs
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Common/Common.csproj b/Common/Common.csproj
index af70483..b2c3365 100644
--- a/Common/Common.csproj
+++ b/Common/Common.csproj
@@ -64,11 +64,13 @@
+
+
diff --git a/Common/Config.cs b/Common/Config.cs
index db4cd92..6b3c45b 100644
--- a/Common/Config.cs
+++ b/Common/Config.cs
@@ -3,10 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
+using FLocal.Core;
namespace FLocal.Common {
- public class Config : FLocal.Core.Config {
+ public class Config : Config {
public readonly string InitTime;
@@ -20,6 +21,10 @@ namespace FLocal.Common {
public readonly string SaltPasswords;
+ public readonly string SaltUploader;
+
+ public readonly string UploaderUrl;
+
protected Config(NameValueCollection data) : base(data) {
this.InitTime = DateTime.Now.ToLongTimeString();
this.mainConnection = new MySQLConnector.Connection(data["ConnectionString"], MySQLConnector.PostgresDBTraits.instance);
@@ -27,6 +32,8 @@ namespace FLocal.Common {
this.DirSeparator = System.IO.Path.DirectorySeparatorChar.ToString();
this.SaltMigration = data["SaltMigration"];
this.SaltPasswords = data["SaltPasswords"];
+ this.SaltUploader = data["SaltUploader"];
+ this.UploaderUrl = data["UploaderUrl"];
}
public static void Init(NameValueCollection data) {
@@ -44,8 +51,17 @@ namespace FLocal.Common {
public static void Transactional(Action action) {
using(Core.DB.Transaction transaction = Core.DB.IDBConnectionExtensions.beginTransaction(instance.mainConnection)) {
- action(transaction);
- transaction.Commit();
+ bool success = false;
+ try {
+ action(transaction);
+ success = true;
+ transaction.Commit();
+ } catch(FLocalException) {
+ if(!success) {
+ transaction.Rollback();
+ }
+ throw;
+ }
}
}
diff --git a/Common/UploadManager.cs b/Common/UploadManager.cs
new file mode 100644
index 0000000..72d1d26
--- /dev/null
+++ b/Common/UploadManager.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using FLocal.Core;
+using FLocal.Common.dataobjects;
+using FLocal.Core.DB;
+using FLocal.Core.DB.conditions;
+using System.Net;
+
+namespace FLocal.Common {
+ public static class UploadManager {
+
+ private class _AlreadyUploadedException : FLocalException {
+
+ public _AlreadyUploadedException() : base("Already uploaded") {
+ }
+
+ }
+
+ public class AlreadyUploadedException : FLocalException {
+
+ public readonly int uploadId;
+
+ public AlreadyUploadedException(int uploadId) : base("Already uploaded " + uploadId) {
+ this.uploadId = uploadId;
+ }
+
+ }
+
+ private const int MAX_UPLOAD_FILESIZE = 1024*1024;
+
+ private static HashSet allowedExtensions = new HashSet() {
+ "jpg",
+ "gif",
+ "png",
+ };
+
+ public static void UploadFile(Stream fileStream, string _extension, DateTime uploadDate, User uploader, int? id) {
+
+ string extension = _extension.ToLower();
+
+ if(!allowedExtensions.Contains(extension)) throw new FLocalException("Unsupported extension");
+ if(fileStream.Length > MAX_UPLOAD_FILESIZE) throw new FLocalException("File is too big");
+
+ byte[] data = new byte[fileStream.Length];
+ if(fileStream.Read(data, 0, (int)fileStream.Length) != fileStream.Length) {
+ throw new FLocalException("File is incomplete");
+ }
+
+ string file_md5 = Util.md5(fileStream);
+ AbstractCondition condition = new ComparisonCondition(
+ Upload.TableSpec.instance.getColumnSpec(Upload.TableSpec.FIELD_HASH),
+ ComparisonType.EQUAL,
+ file_md5
+ );
+ try {
+ if(Config.instance.mainConnection.GetCountByConditions(Upload.TableSpec.instance, condition, new JoinSpec[0]) > 0) {
+ throw new _AlreadyUploadedException();
+ }
+ Config.Transactional(transaction => {
+ Config.instance.mainConnection.lockTable(transaction, Upload.TableSpec.instance);
+ /*if(Config.instance.mainConnection.GetCountByConditions(Upload.TableSpec.instance, condition, new JoinSpec[0]) > 0) {
+ throw new _AlreadyUploadedException();
+ }*/
+ //TODO: ???
+
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Config.instance.UploaderUrl + "Upload/?extension=" + extension + "&signature=" + Util.md5(file_md5 + " " + Config.instance.SaltUploader));
+ request.Method = "POST";
+ request.ContentLength = data.Length;
+ Stream requestStream = request.GetRequestStream();
+ requestStream.Write(data, 0, data.Length);
+ requestStream.Close();
+
+ HttpWebResponse response;
+ try {
+ response = (HttpWebResponse)request.GetResponse();
+ } catch(WebException e) {
+ response = (HttpWebResponse)e.Response;
+ }
+ using(StreamReader reader = new StreamReader(response.GetResponseStream())) {
+ string result = reader.ReadToEnd();
+ if(result != "OK") {
+ throw new CriticalException("Cannot upload file to upload service: " + result);
+ }
+ }
+
+ Dictionary row = new Dictionary();
+ if(id.HasValue) row[Upload.TableSpec.FIELD_ID] = id.ToString();
+ row[Upload.TableSpec.FIELD_HASH] = file_md5;
+ row[Upload.TableSpec.FIELD_EXTENSION] = extension;
+ row[Upload.TableSpec.FIELD_UPLOADDATE] = uploadDate.ToUTCString();
+ row[Upload.TableSpec.FIELD_USERID] = uploader.id.ToString();
+ row[Upload.TableSpec.FIELD_SIZE] = data.Length.ToString();
+ Config.instance.mainConnection.insert(transaction, Upload.TableSpec.instance, row);
+ });
+ } catch(_AlreadyUploadedException) {
+ throw new AlreadyUploadedException(
+ int.Parse(
+ Config.instance.mainConnection.LoadIdsByConditions(
+ Upload.TableSpec.instance,
+ condition,
+ Diapasone.unlimited,
+ new JoinSpec[0]
+ )[0]
+ )
+ );
+ }
+ }
+
+ }
+}
diff --git a/Common/dataobjects/Upload.cs b/Common/dataobjects/Upload.cs
new file mode 100644
index 0000000..9093fa5
--- /dev/null
+++ b/Common/dataobjects/Upload.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Xml.Linq;
+using FLocal.Core;
+using FLocal.Core.DB;
+using FLocal.Core.DB.conditions;
+
+namespace FLocal.Common.dataobjects {
+ public class Upload : SqlObject {
+
+ public class TableSpec : ISqlObjectTableSpec {
+ public const string TABLE = "Uploads";
+ public const string FIELD_ID = "Id";
+ public const string FIELD_HASH = "MD5";
+ public const string FIELD_EXTENSION = "Extension";
+ public const string FIELD_SIZE = "Size";
+ public const string FIELD_UPLOADDATE = "UploadDate";
+ public const string FIELD_USERID = "UserId";
+ public static readonly TableSpec instance = new TableSpec();
+ public string name { get { return TABLE; } }
+ public string idName { get { return FIELD_ID; } }
+ public void refreshSqlObject(int id) { Refresh(id); }
+ }
+
+ protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } }
+
+ private string _hash;
+ public string hash {
+ get {
+ this.LoadIfNotLoaded();
+ return this._hash;
+ }
+ }
+
+ private string _extension;
+ public string extension {
+ get {
+ this.LoadIfNotLoaded();
+ return this._extension;
+ }
+ }
+
+ private int _size;
+ public int size {
+ get {
+ this.LoadIfNotLoaded();
+ return this._size;
+ }
+ }
+
+ private DateTime _uploadDate;
+ public DateTime uploadDate {
+ get {
+ this.LoadIfNotLoaded();
+ return this._uploadDate;
+ }
+ }
+
+ private int? _userId;
+ public int? userId {
+ get {
+ this.LoadIfNotLoaded();
+ return this._userId;
+ }
+ }
+ public User user {
+ get {
+ return User.LoadById(this.userId.Value);
+ }
+ }
+
+ protected override void doFromHash(Dictionary data) {
+ this._hash = data[TableSpec.FIELD_HASH];
+ this._extension = data[TableSpec.FIELD_EXTENSION];
+ this._size = int.Parse(data[TableSpec.FIELD_SIZE]);
+ this._uploadDate = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_UPLOADDATE]).Value;
+ this._userId = Util.ParseInt(data[TableSpec.FIELD_USERID]);
+ }
+
+ }
+}
diff --git a/Core/Util.cs b/Core/Util.cs
index cf69d3c..17e27e5 100644
--- a/Core/Util.cs
+++ b/Core/Util.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using System.IO;
namespace FLocal.Core {
@@ -165,6 +166,34 @@ namespace FLocal.Core {
return sBuilder.ToString();
}
+ ///
+ /// Code sample from http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5.aspx
+ ///
+ ///
+ ///
+ public static string md5(Stream inputStream) {
+ inputStream.Position = 0;
+
+ System.Security.Cryptography.HashAlgorithm md5Hasher = System.Security.Cryptography.MD5.Create();
+
+ // Convert the input string to a byte array and compute the hash.
+ byte[] data = md5Hasher.ComputeHash(inputStream);
+
+ // Create a new Stringbuilder to collect the bytes
+ // and create a string.
+ StringBuilder sBuilder = new StringBuilder();
+
+ // Loop through each byte of the hashed data
+ // and format each one as a hexadecimal string.
+ for (int i = 0; i < data.Length; i++)
+ {
+ sBuilder.Append(data[i].ToString("x2"));
+ }
+
+ // Return the hexadecimal string.
+ return sBuilder.ToString();
+ }
+
public static bool throws(Action action) where TException : Exception {
try {
action();
@@ -174,6 +203,24 @@ namespace FLocal.Core {
}
}
+ private static Dictionary extension2mime = new Dictionary();
+ public static string getMimeByExtension(string extension) {
+ if(!extension.StartsWith(".")) extension = "." + extension;
+ if(!extension2mime.ContainsKey(extension)) {
+ lock(extension2mime) {
+ if(!extension2mime.ContainsKey(extension)) {
+ Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(extension);
+ if (regKey != null && regKey.GetValue("Content Type") != null) {
+ extension2mime[extension] = regKey.GetValue("Content Type").ToString();
+ } else {
+ return null;
+ }
+ }
+ }
+ }
+ return extension2mime[extension];
+ }
+
}
}
diff --git a/FLocal.sln b/FLocal.sln
index a835f4b..d4fff68 100644
--- a/FLocal.sln
+++ b/FLocal.sln
@@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Importer", "Importer\Import
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImportConsole", "ImportConsole\ImportConsole.csproj", "{208B84AF-A5DD-4C20-83D5-160EA243BA43}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IISUploadHandler", "IISUploadHandler\IISUploadHandler.csproj", "{09F13185-3B2A-4FCB-AB6A-750112FFF920}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -73,6 +75,12 @@ Global
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|Any CPU.Build.0 = Release|Any CPU
{208B84AF-A5DD-4C20-83D5-160EA243BA43}.Release|x86.ActiveCfg = Release|Any CPU
+ {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|Any CPU.Build.0 = Release|Any CPU
+ {09F13185-3B2A-4FCB-AB6A-750112FFF920}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/IISMainHandler/HandlersFactory.cs b/IISMainHandler/HandlersFactory.cs
index 8d30371..425da9e 100644
--- a/IISMainHandler/HandlersFactory.cs
+++ b/IISMainHandler/HandlersFactory.cs
@@ -14,6 +14,13 @@ namespace FLocal.IISHandler {
// throw new FLocalException("Malformed url");
// }
if(context.requestParts.Length < 1) return new handlers.RootHandler();
+
+ #region legacy
+ if(context.httprequest.Path.ToLower().StartsWith("/user/upload/")) {
+ return new handlers.response.LegacyUploadHandler();
+ }
+ #endregion
+
switch(context.requestParts[0].ToLower()) {
case "boards":
return new handlers.BoardsHandler();
@@ -33,6 +40,8 @@ namespace FLocal.IISHandler {
return new handlers.response.UserListHandler();
case "user":
return new handlers.response.UserInfoHandler();
+ case "uploads":
+ return new handlers.response.UploadHandler();
case "static":
return new handlers.StaticHandler(context.requestParts);
case "do":
diff --git a/IISMainHandler/IISMainHandler.csproj b/IISMainHandler/IISMainHandler.csproj
index 7b8a8b4..fd9821b 100644
--- a/IISMainHandler/IISMainHandler.csproj
+++ b/IISMainHandler/IISMainHandler.csproj
@@ -50,6 +50,7 @@
+
@@ -62,8 +63,10 @@
+
+
diff --git a/IISMainHandler/MainHandler.cs b/IISMainHandler/MainHandler.cs
index 8758d1e..6d00219 100644
--- a/IISMainHandler/MainHandler.cs
+++ b/IISMainHandler/MainHandler.cs
@@ -12,7 +12,7 @@ namespace FLocal.IISHandler {
get { return true; }
}
- public void ProcessRequest(HttpContext httpcontext) {
+ private void doProcessRequest(HttpContext httpcontext) {
Uri referer = httpcontext.Request.UrlReferrer;
if(referer != null && referer.PathAndQuery.StartsWith("/static")) {
@@ -32,5 +32,13 @@ namespace FLocal.IISHandler {
handler.Handle(context);
}
+ public void ProcessRequest(HttpContext context) {
+ try {
+ this.doProcessRequest(context);
+ } catch(RedirectException e) {
+ context.Response.Redirect(e.newUrl);
+ }
+ }
+
}
}
diff --git a/IISMainHandler/exceptions/RedirectException.cs b/IISMainHandler/exceptions/RedirectException.cs
new file mode 100644
index 0000000..9af2144
--- /dev/null
+++ b/IISMainHandler/exceptions/RedirectException.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace FLocal.IISHandler {
+ class RedirectException : FLocal.Core.FLocalException {
+
+ public readonly string newUrl;
+
+ public RedirectException(string newUrl) : base("Redirect to " + newUrl + " is being performed") {
+ this.newUrl = newUrl;
+ }
+
+ }
+}
diff --git a/IISMainHandler/handlers/StaticHandler.cs b/IISMainHandler/handlers/StaticHandler.cs
index c0ce399..04c4236 100644
--- a/IISMainHandler/handlers/StaticHandler.cs
+++ b/IISMainHandler/handlers/StaticHandler.cs
@@ -5,7 +5,7 @@ using System.Text;
using System.Web;
using System.Text.RegularExpressions;
using System.IO;
-using Microsoft.Win32;
+using FLocal.Core;
namespace FLocal.IISHandler.handlers {
class StaticHandler : ISpecificHandler {
@@ -16,23 +16,6 @@ namespace FLocal.IISHandler.handlers {
this.requestParts = requestParts;
}
- private static Dictionary extension2mime = new Dictionary();
- private static string getMimeByExtension(string extension) {
- if(!extension2mime.ContainsKey(extension)) {
- lock(extension2mime) {
- if(!extension2mime.ContainsKey(extension)) {
- RegistryKey regKey = Registry.ClassesRoot.OpenSubKey(extension);
- if (regKey != null && regKey.GetValue("Content Type") != null) {
- extension2mime[extension] = regKey.GetValue("Content Type").ToString();
- } else {
- return null;
- }
- }
- }
- }
- return extension2mime[extension];
- }
-
public void Handle(WebContext context) {
if(this.requestParts.Length < 2) {
throw new HttpException(403, "listing not allowed");
@@ -56,7 +39,7 @@ namespace FLocal.IISHandler.handlers {
throw new HttpException(403, "forbidden");
}
- string mime = getMimeByExtension(fileinfo.Extension);
+ string mime = Util.getMimeByExtension(fileinfo.Extension);
if(mime != null) {
context.httpresponse.ContentType = mime;
} else {
@@ -66,7 +49,7 @@ namespace FLocal.IISHandler.handlers {
context.httpresponse.CacheControl = HttpCacheability.Public.ToString();
context.httpresponse.Expires = 1440;
- context.httpresponse.WriteFile(fileinfo.FullName);
+ context.httpresponse.TransmitFile(fileinfo.FullName);
}
}
diff --git a/IISMainHandler/handlers/response/LegacyUploadHandler.cs b/IISMainHandler/handlers/response/LegacyUploadHandler.cs
new file mode 100644
index 0000000..6d3a329
--- /dev/null
+++ b/IISMainHandler/handlers/response/LegacyUploadHandler.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using FLocal.Core;
+using FLocal.Common;
+using System.Xml.Linq;
+using FLocal.Common.dataobjects;
+
+namespace FLocal.IISHandler.handlers.response {
+ class LegacyUploadHandler : AbstractGetHandler {
+
+ protected override string templateName {
+ get {
+ return null;
+ }
+ }
+
+ protected override System.Xml.Linq.XElement[] getSpecificData(WebContext context) {
+ if(context.requestParts.Length != 3) throw new FLocalException("wrong url");
+ string[] parts = context.requestParts[2].Split('.');
+ if(parts.Length != 2) throw new FLocalException("wrong url");
+ if(parts[0].PHPSubstring(0, 4).ToLower() != "file") throw new FLocalException("wrong url");
+ int rawFileNum = int.Parse(parts[0].PHPSubstring(4));
+ int fileNum;
+ switch(parts[1].ToLower()) {
+ case "jpg":
+ fileNum = rawFileNum;
+ break;
+ case "gif":
+ fileNum = 500000 + rawFileNum;
+ break;
+ case "png":
+ fileNum = 600000 + rawFileNum;
+ break;
+ default:
+ throw new FLocalException("wrong url");
+ }
+ throw new RedirectException("/Uploads/" + fileNum + "/");
+ }
+
+ }
+}
diff --git a/IISMainHandler/handlers/response/UploadHandler.cs b/IISMainHandler/handlers/response/UploadHandler.cs
new file mode 100644
index 0000000..4bea604
--- /dev/null
+++ b/IISMainHandler/handlers/response/UploadHandler.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using FLocal.Core;
+using FLocal.Common;
+using System.Xml.Linq;
+using FLocal.Common.dataobjects;
+
+namespace FLocal.IISHandler.handlers.response {
+ class UploadHandler : AbstractGetHandler {
+
+ protected override string templateName {
+ get {
+ return null;
+ }
+ }
+
+ protected override System.Xml.Linq.XElement[] getSpecificData(WebContext context) {
+ if(context.requestParts.Length != 2) throw new FLocalException("wrong url");
+ Upload upload = Upload.LoadById(int.Parse(context.requestParts[1]));
+ throw new RedirectException(Config.instance.UploaderUrl + "Data/" + upload.hash + "." + upload.extension);
+ }
+
+ }
+}
diff --git a/IISUploadHandler/Config.cs b/IISUploadHandler/Config.cs
new file mode 100644
index 0000000..dee9498
--- /dev/null
+++ b/IISUploadHandler/Config.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Configuration;
+
+namespace FLocal.IISUploadHandler {
+ class Config {
+
+ public static readonly Config instance = new Config();
+
+ public readonly string salt;
+ public readonly string storageDir;
+
+ private Config() {
+ var appSettings = ConfigurationManager.AppSettings;
+ this.salt = appSettings["salt"];
+ this.storageDir = appSettings["storageDir"];
+ }
+
+ }
+}
diff --git a/IISUploadHandler/IISUploadHandler.csproj b/IISUploadHandler/IISUploadHandler.csproj
new file mode 100644
index 0000000..c5c8e05
--- /dev/null
+++ b/IISUploadHandler/IISUploadHandler.csproj
@@ -0,0 +1,68 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {09F13185-3B2A-4FCB-AB6A-750112FFF920}
+ Library
+ Properties
+ FLocal.IISUploadHandler
+ IISUploadHandler
+ v3.5
+ 512
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+ 3.5
+
+
+
+ 3.5
+
+
+ 3.5
+
+
+
+
+
+
+
+
+
+
+
+ {6F532626-E9F8-498E-9683-1538E7CD62CB}
+ Core
+
+
+
+
+
\ No newline at end of file
diff --git a/IISUploadHandler/Properties/AssemblyInfo.cs b/IISUploadHandler/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..2a0c3d7
--- /dev/null
+++ b/IISUploadHandler/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("IISUploadHandler")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("IISUploadHandler")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("15bb5131-77d1-4703-bded-bbf9a7ffae50")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/IISUploadHandler/UploadHandler.cs b/IISUploadHandler/UploadHandler.cs
new file mode 100644
index 0000000..c76b4d4
--- /dev/null
+++ b/IISUploadHandler/UploadHandler.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web;
+using System.IO;
+using FLocal.Core;
+
+namespace FLocal.IISUploadHandler {
+ class UploadHandler : IHttpHandler {
+
+ public bool IsReusable {
+ get {
+ return true;
+ }
+ }
+
+ public void ProcessRequest(HttpContext httpcontext) {
+ if(httpcontext.Request.Path.ToLower() == "/upload/") {
+ this.ProcessUpload(httpcontext);
+ } else if(httpcontext.Request.Path.ToLower().StartsWith("/data/")) {
+ this.ProcessRetrieve(httpcontext);
+ } else {
+ throw new HttpException(403, "wrong url");
+ }
+ }
+
+ private static string getFilePath(string md5, string extension) {
+ foreach(char chr in (md5 + extension)) {
+ if(!Char.IsLetterOrDigit(chr)) throw new HttpException(403, "wrong md5 or extension");
+ }
+ return Config.instance.storageDir + md5.PHPSubstring(0, 2) + Path.DirectorySeparatorChar + md5.PHPSubstring(2, 2) + Path.DirectorySeparatorChar + md5.PHPSubstring(4) + "." + extension;
+ }
+
+ private static void CreateDirectoryIfNotExists(DirectoryInfo directoryInfo) {
+ if(!directoryInfo.Exists) {
+ CreateDirectoryIfNotExists(directoryInfo.Parent);
+ directoryInfo.Create();
+ }
+ }
+
+ private void ProcessUpload(HttpContext context) {
+ byte[] data = new byte[context.Request.InputStream.Length];
+ if(context.Request.InputStream.Read(data, 0, (int)context.Request.InputStream.Length) != context.Request.InputStream.Length) {
+ throw new FLocalException("File is not uploaded correctly");
+ }
+
+ string file_md5 = Util.md5(context.Request.InputStream);
+
+ string md5 = Util.md5(file_md5 + " " + Config.instance.salt);
+ if(md5 != context.Request.QueryString["signature"]) {
+ throw new HttpException(403, "signature mismatch");
+ }
+
+ string filePath = getFilePath(file_md5, context.Request.QueryString["extension"]);
+ CreateDirectoryIfNotExists((new FileInfo(filePath)).Directory);
+ using(FileStream stream = new FileStream(filePath, FileMode.CreateNew)) {
+ stream.Write(data, 0, data.Length);
+ }
+
+ context.Response.Write("OK");
+ }
+
+ private void ProcessRetrieve(HttpContext context) {
+ string[] requestParts = context.Request.Path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+ if(requestParts.Length != 2) throw new HttpException(403, "wrong url");
+
+ string[] fileParts = requestParts[1].Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
+ if(fileParts.Length != 2) throw new HttpException(403, "wrong url");
+
+ string mime = Util.getMimeByExtension(fileParts[1]);
+ if(mime == null) throw new FLocalException("unknown extension '" + fileParts[1] + "'");
+ context.Response.ContentType = mime;
+
+ context.Response.TransmitFile(getFilePath(fileParts[0], fileParts[1]));
+ }
+
+ }
+}
diff --git a/ImportConsole/Program.cs b/ImportConsole/Program.cs
index e86c10d..34483ec 100644
--- a/ImportConsole/Program.cs
+++ b/ImportConsole/Program.cs
@@ -8,6 +8,8 @@ using FLocal.Common.dataobjects;
using FLocal.Core;
using System.Configuration;
using NConsoler;
+using System.IO;
+using FLocal.Common;
namespace FLocal.ImportConsole {
class Program {
@@ -66,7 +68,63 @@ namespace FLocal.ImportConsole {
[Action]
public static void ProcessUpload(string pathToUpload) {
- throw new NotImplementedException();
+ User uploader = User.LoadByName("Guest 127.0.0.1");
+ DirectoryInfo directoryInfo = new DirectoryInfo(pathToUpload);
+ int i=0;
+ foreach(FileSystemInfo _info in directoryInfo.GetFiles()) {
+ if(i%100 == 0) {
+ Console.Write("[" + (int)(i/100) + "]");
+ }
+ FileInfo info = _info as FileInfo;
+ //Console.WriteLine("Processing " + info.FullName);
+ if(!info.Name.StartsWith("file")) {
+ Console.Write("!");
+ } else {
+ string[] parts = info.Name.Split('.');
+ if(parts.Length != 2) throw new FLocalException("wrong file name");
+ int raw = int.Parse(parts[0].PHPSubstring(4));
+ int id;
+ switch(parts[1].ToLower()) {
+ case "jpg":
+ id = raw;
+ break;
+ case "gif":
+ id = 500000 + raw;
+ break;
+ case "png":
+ id = 600000 + raw;
+ break;
+ default:
+ throw new FLocalException("wrong extension");
+ }
+ if(info != null) {
+ try {
+ Upload.LoadById(id);
+ Console.Write("-");
+ } catch(NotFoundInDBException) {
+ try {
+ UploadManager.UploadFile(
+ info.OpenRead(),
+ parts[1],
+ info.LastWriteTime,
+ uploader,
+ id
+ );
+ } catch(UploadManager.AlreadyUploadedException e) {
+ Console.WriteLine(id + " md5 is equal to that of " + e.uploadId);
+ Console.ReadLine();
+ } catch(Exception e) {
+ Console.WriteLine(e.GetType().FullName + ": " + e.Message);
+ Console.WriteLine(e.StackTrace);
+ throw;
+ }
+ Console.Write("+");
+ //Console.WriteLine("Processed " + info.FullName);
+ }
+ }
+ }
+ i++;
+ }
}
}
}
diff --git a/build-all.bat b/build-all.bat
index bb3a5f2..a49d4b9 100644
--- a/build-all.bat
+++ b/build-all.bat
@@ -2,4 +2,6 @@
@rem Note that this script will only produce a .msi packet of your binaries and data. It will not compile your sources.
cd Builder
Builder IISMainHandler
-move /Y IISMainHandler\product.msi ..\IISMainHandler.msi
\ No newline at end of file
+move /Y IISMainHandler\product.msi ..\IISMainHandler.msi
+Builder IISUploadHandler
+move /Y IISUploadHandler\product.msi ..\IISUploadHandler.msi
\ No newline at end of file