diff --git a/README.md b/README.md index 62ef52a..ccc2b72 100644 --- a/README.md +++ b/README.md @@ -1,4 +1 @@ -internetpack -============ - -Internet Pack \ No newline at end of file +RemObjects Internet Pack diff --git a/Samples/C#/FTP Sync/App.ico b/Samples/C#/FTP Sync/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/C#/FTP Sync/App.ico differ diff --git a/Samples/C#/FTP Sync/AssemblyInfo.cs b/Samples/C#/FTP Sync/AssemblyInfo.cs new file mode 100644 index 0000000..4fb1f66 --- /dev/null +++ b/Samples/C#/FTP Sync/AssemblyInfo.cs @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Samples + + (c)opyright RemObjects Software, Inc. 2003-2004. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("RemObjects Internet Pack for .NET - HttpFResponses Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RemObjects Software")] +[assembly: AssemblyProduct("RemObjects Internet Pack for .NET")] +[assembly: AssemblyCopyright("Copyright RemObjects Software 2003-2004. All Rights Reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("1.0.15.541")] +[assembly: CLSCompliant(true)] +#if REMOBJECTS_SIGN_ASSEMBLY +[assembly: AssemblyKeyName("RemObjectsSoftware")] +#endif diff --git a/Samples/C#/FTP Sync/FtpSync.2008.csproj b/Samples/C#/FTP Sync/FtpSync.2008.csproj new file mode 100644 index 0000000..8dde44c --- /dev/null +++ b/Samples/C#/FTP Sync/FtpSync.2008.csproj @@ -0,0 +1,108 @@ + + + Local + 8.0.50727 + 2.0 + {F95BF882-461F-4072-90C4-979893E0CD59} + Debug + AnyCPU + App.ico + + + FtpSync + + + JScript + Grid + IE50 + false + Exe + FtpSync + OnBuildSuccess + + + + + + + 2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + + + System + + + System.Data + + + System.XML + + + + + + Code + + + Code + + + Code + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/FTP Sync/FtpSync.2008.sln b/Samples/C#/FTP Sync/FtpSync.2008.sln new file mode 100644 index 0000000..8b99682 --- /dev/null +++ b/Samples/C#/FTP Sync/FtpSync.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FtpSync", "FtpSync.2008.csproj", "{F95BF882-461F-4072-90C4-979893E0CD59}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F95BF882-461F-4072-90C4-979893E0CD59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F95BF882-461F-4072-90C4-979893E0CD59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F95BF882-461F-4072-90C4-979893E0CD59}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F95BF882-461F-4072-90C4-979893E0CD59}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/FTP Sync/FtpSync.2010.csproj b/Samples/C#/FTP Sync/FtpSync.2010.csproj new file mode 100644 index 0000000..57494f8 --- /dev/null +++ b/Samples/C#/FTP Sync/FtpSync.2010.csproj @@ -0,0 +1,111 @@ + + + Local + 10.0.20506 + 2.0 + {F95BF882-461F-4072-90C4-979893E0CD59} + Debug + AnyCPU + App.ico + + + FtpSync + + + JScript + Grid + IE50 + false + Exe + FtpSync + OnBuildSuccess + + + + + + + 3.5 + v2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + AllRules.ruleset + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + AllRules.ruleset + + + + + + System + + + System.Data + + + System.XML + + + + + + Code + + + Code + + + Code + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/FTP Sync/FtpSync.2010.sln b/Samples/C#/FTP Sync/FtpSync.2010.sln new file mode 100644 index 0000000..fa95bcd --- /dev/null +++ b/Samples/C#/FTP Sync/FtpSync.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FtpSync", "FtpSync.2010.csproj", "{F95BF882-461F-4072-90C4-979893E0CD59}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F95BF882-461F-4072-90C4-979893E0CD59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F95BF882-461F-4072-90C4-979893E0CD59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F95BF882-461F-4072-90C4-979893E0CD59}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F95BF882-461F-4072-90C4-979893E0CD59}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/FTP Sync/FtpSync.Sample.html b/Samples/C#/FTP Sync/FtpSync.Sample.html new file mode 100644 index 0000000..c7fe236 --- /dev/null +++ b/Samples/C#/FTP Sync/FtpSync.Sample.html @@ -0,0 +1,57 @@ + + + + + + + + +
+

+ FtpSync Sample +

+ +
+

Purpose

+

+ This sample shows how to use the FtpClient class to gain access to an ftp + site and download files from there.
+The +FtpSync application compares files on the local computer against those on the ftp folder and downloads files that are new or have changed.
+ +FtpSync is a console application with following uasge:

+

+
+ +FtpSync localdir user:pass@server/remotedir [-local] [-remote] [-nodelete] [-passive] [-l] [-r]

+

+ +
+ +where:
+ + + + + + + + + + + + + +
localdir:local directory - long file names with spaces should be quoted
user/pass:ftp username & password
server:ftp server (i.e., ftp.whatever.com)
remotedir:directory on the remote server
-nodelete:forbid to delete missing files
-passive:turn on 'Passive' mode
-r:recursive synchronization (through subdirectories)
-l:log - show FTP commands
-local:Local mode of sync (Master = ftp; slave = localhost). By default
-remote:Remote mode of sync (Master = localhost; slave = ftp)
+
+

+ + +

Getting started

+ + + + \ No newline at end of file diff --git a/Samples/C#/FTP Sync/FtpSyncMain.cs b/Samples/C#/FTP Sync/FtpSyncMain.cs new file mode 100644 index 0000000..8ab5804 --- /dev/null +++ b/Samples/C#/FTP Sync/FtpSyncMain.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; + +namespace FtpSync +{ + /// + /// Summary description for Class1. + /// + class FtpSyncMain + { + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + FtpSyncWorker lWorker = new FtpSyncWorker(); + + if (lWorker.CheckArgs(args)) + { + lWorker.Sync(); + } + } + } +} diff --git a/Samples/C#/FTP Sync/FtpSyncWorker.cs b/Samples/C#/FTP Sync/FtpSyncWorker.cs new file mode 100644 index 0000000..19f6922 --- /dev/null +++ b/Samples/C#/FTP Sync/FtpSyncWorker.cs @@ -0,0 +1,517 @@ +using System; +using System.IO; +using System.Collections.Specialized; +using System.Text.RegularExpressions; +using RemObjects.InternetPack.Ftp; +using System.Collections.Generic; + +namespace FtpSync +{ + /// + /// Summary description for FtpSyncWorker. + /// + public class FtpSyncWorker + { + public enum SyncMode {Local, Remote} + + public FtpSyncWorker() + { + // + // TODO: Add constructor logic here + // + } + + #region private fields... + private string fLocalDirectory = ""; + private string fServer = ""; + private string fServerDirectory = ""; + private string fUserName = ""; + private string fPassword = ""; + private bool fDoNotDeleteMissingItems = false; + private bool fRecursiveSync = false; + private bool fPassiveMode = false; + private bool fDoShowFtpClientLog = false; + private SyncMode fSyncMode = SyncMode.Local; + + private FtpClient fFtpClient; + #endregion + + public void ServerLog(object Sender, RemObjects.InternetPack.CommandBased.ClientLogArgs ea) + { + if (fDoShowFtpClientLog) + { + String lIcon = " "; + switch (ea.Direction) + { + case RemObjects.InternetPack.CommandBased.LogDirection.Receive: + lIcon = "<"; + break; + case RemObjects.InternetPack.CommandBased.LogDirection.Send: + lIcon = ">"; + break; + case RemObjects.InternetPack.CommandBased.LogDirection.Status: + lIcon = "!"; + break; + } + + Console.WriteLine("[{0}] {1}", lIcon, ea.Text); + } + } + public void ClientLog(String aMessage) + { + Console.WriteLine("[i] {0}", aMessage); + } + + private void _Ftp_OnTransferProgress(object aSender, RemObjects.InternetPack.Events.TransferProgressEventArgs ea) + { + Console.Write("."); + } + + public void Sync() + { + try + { + fFtpClient = new FtpClient(); + fFtpClient.HostName = fServer; + fFtpClient.UserName = fUserName; + fFtpClient.Password = fPassword; + fFtpClient.Passive = fPassiveMode; + fFtpClient.Port = 21; + + fFtpClient.OnLog += new RemObjects.InternetPack.CommandBased.ClientLogEvent(ServerLog); + fFtpClient.OnTransferProgress +=new RemObjects.InternetPack.Events.TransferProgressEventHandler(_Ftp_OnTransferProgress); + ClientLog("Connecting to " + fServer); + fFtpClient.Open(); + try + { + fFtpClient.Login(); + + SyncDirectory(fLocalDirectory, "/" + fServerDirectory); + } + finally + { + ClientLog("Disconnecting"); + fFtpClient.Quit(); + fFtpClient.Close(); + } + + } + catch (Exception ex) + { + ClientLog(String.Format("Error syncing directory ({0})", ex.Message)); + if (ex.StackTrace != null && fDoShowFtpClientLog) + { + ClientLog(ex.StackTrace); + } + + } + ClientLog("Press enter to continue..."); + Console.ReadLine(); + } + + public void SyncDirectory(string aLocalDirectory, string aRemoteDirectory) + { + string lOriginalLocalDirectory = Directory.GetCurrentDirectory(); + string lOriginalRemoteDirectory = fFtpClient.GetCurrentDirectory(); + + try + { + #region Step into synchronized directories + ClientLog(String.Format("Local change directory to {0}", aLocalDirectory)); + Directory.SetCurrentDirectory(aLocalDirectory); + + ClientLog(String.Format("Remote change directory to {0}", aRemoteDirectory)); + fFtpClient.ChangeDirectory(aRemoteDirectory); + #endregion + + #region Retrieve content + ClientLog("Retrieving directory contents"); + fFtpClient.List(); + #endregion + + #region Get Local Folders List + Dictionary lLocalFolders = new Dictionary(); + if (fRecursiveSync) + { + foreach (String lName in Directory.GetDirectories(aLocalDirectory)) + { + DirectoryInfo d = new DirectoryInfo(lName); + lLocalFolders.Add(d.Name, d); + } + } + #endregion + + #region Get Local Files List + Dictionary lLocalFiles = new Dictionary(); + foreach (String lName in Directory.GetFiles(aLocalDirectory)) + { + FileInfo f = new FileInfo(lName); + lLocalFiles.Add(f.Name, f); + } + #endregion + + #region Get Remote Files and Directories List + Dictionary lRemoteFolders = new Dictionary(); + Dictionary lRemoteFiles = new Dictionary(); + foreach(FtpListingItem lRemoteItem in fFtpClient.CurrentDirectoryContents) + { + if (lRemoteItem.Directory) + { + if (fRecursiveSync) + { + if (lRemoteItem.FileName != "..") + lRemoteFolders.Add(lRemoteItem.FileName, lRemoteItem); + } + } + else + lRemoteFiles.Add(lRemoteItem.FileName, lRemoteItem); + } + #endregion + + #region Synchronization... + switch (fSyncMode) + { + #region Local Synchronization. Master: ftp; slave: local; + case SyncMode.Local: + + #region Folders synchronization + foreach (String lName in lRemoteFolders.Keys) + { + + ClientLog(String.Format("Synchronizing folder '{0}'...", lName)); + String lLocalItemName = Path.Combine(aLocalDirectory, lName); + + if (!Directory.Exists(lLocalItemName)) Directory.CreateDirectory(lLocalItemName); + SyncDirectory(lLocalItemName, lName); + ClientLog(String.Format("Folder '{0}' has been synchronized", lName)); + lLocalFolders.Remove(lName); + } + #region Delete local folders that doesn't exists on FTP + if (!fDoNotDeleteMissingItems) + { + foreach (DirectoryInfo toDelete in lLocalFolders.Values) + { + toDelete.Delete(true);//delete recursive + } + lLocalFolders.Clear(); + } + #endregion + #endregion + + #region Files synchronization + foreach (String lName in lRemoteFiles.Keys) + { + FtpListingItem lRemoteItem = lRemoteFiles[lName]; + Boolean lNeedSync = true; + if (lLocalFiles.ContainsKey(lName)) + { + FileInfo lLocalItem = lLocalFiles[lName]; + lNeedSync = ( + (lLocalItem.Length != lRemoteItem.Size) || + (lLocalItem.LastWriteTime != lRemoteItem.FileDate)); + } + ClientLog( + String.Format( + "File {0} {1}", + lRemoteItem.FileName, + lNeedSync ? "requires synchronization." : "doesn't require synchronization.")); + if (lNeedSync) + { + ClientLog(String.Format("Downloading {0}...", lRemoteItem.FileName)); + using (Stream lStream = File.Open(Path.Combine(aLocalDirectory, lName), FileMode.Create)) + { + fFtpClient.Retrieve(lRemoteItem, lStream); + lStream.Close(); + File.SetLastWriteTime(lRemoteItem.FileName, lRemoteItem.FileDate); + } + ClientLog(String.Format("File {0} has been downloaded.", lRemoteItem.FileName)); + } + lLocalFiles.Remove(lName); + } + + #region Delete local files that doesn't exists on FTP + if (!fDoNotDeleteMissingItems) + { + foreach (FileInfo toDelete in lLocalFiles.Values) + { + toDelete.Delete(); + } + lLocalFiles.Clear(); + } + #endregion + + #endregion + break; + #endregion + + #region Remote Synchronization. Master: local; slave: ftp; + case SyncMode.Remote: + + #region Folders Synchronization. + foreach (String lName in lLocalFolders.Keys) + { + ClientLog(String.Format("Synchronizing folder '{0}'...", lName)); + String lLocalItemName = Path.Combine(aLocalDirectory, lName); + + if (!lRemoteFolders.ContainsKey(lName)) fFtpClient.MakeDirectory(lName); + SyncDirectory(lLocalItemName, lName); + ClientLog(String.Format("Folder '{0}' has been synchronized", lName)); + lRemoteFolders.Remove(lName); + } + #region Delete FTP folders that doesn't exists locally + if (!fDoNotDeleteMissingItems) + { + foreach (FtpListingItem toDelete in lRemoteFolders.Values) + { + fFtpClient.RemoveDirectory(toDelete.FileName); + } + lRemoteFolders.Clear(); + } + #endregion + #endregion + + #region Files Synchronization. + foreach (String lName in lLocalFiles.Keys) + { + String lLocalItemName = Path.Combine(aLocalDirectory, lName); + + Boolean lNeedSync = true; + if (lRemoteFiles.ContainsKey(lName)) + { + FileInfo lLocalItem = new FileInfo(lLocalItemName); + FtpListingItem lRemoteItem = lRemoteFiles[lName]; + lNeedSync = ( + (lLocalItem.Length != lRemoteItem.Size) || + (lLocalItem.LastWriteTime != lRemoteItem.FileDate)); + } + ClientLog( + String.Format( + "File {0} {1}", + lName, + lNeedSync ? "requires synchronization." : "doesn't require synchronization.")); + if (lNeedSync) + { + ClientLog(String.Format("Uploading {0}...", lName)); + if (File.Exists(lLocalItemName)) + using (FileStream fs = new FileStream(lLocalItemName, FileMode.Open, FileAccess.Read)) + { + fFtpClient.Store(lName, fs); + } + ClientLog(String.Format("File {0} has been uploaded.", lName)); + } + lRemoteFiles.Remove(lName); + } + + #region Delete FTP files that doesn't exist locally + if (!fDoNotDeleteMissingItems) + { + foreach (FtpListingItem toDelete in lRemoteFiles.Values) + { + fFtpClient.Delete(toDelete.FileName); + } + lRemoteFiles.Clear(); + } + #endregion + + #endregion + break; + #endregion + } + #endregion + } + finally + { + #region Step out of synchronized directories + Directory.SetCurrentDirectory(lOriginalLocalDirectory); + ClientLog(String.Format("Local change directory to {0}", lOriginalLocalDirectory)); + fFtpClient.ChangeDirectory(lOriginalRemoteDirectory); + ClientLog(String.Format("Remote change directory to {0}", lOriginalRemoteDirectory)); + #endregion + } + + } + + public bool CheckArgs(string[] args) + { + int lCount = args.Length; + + bool lBadParam = false; + + if (lCount > 0) + fLocalDirectory = args[0]; + else + lBadParam = true; + + if (lCount > 1) + { + Match lMatch = Regex.Match(args[1],@"(?\S+):(?\S+)@(?[^/\s]+)/(?\S*)"); + if (lMatch.Success) + { + fUserName = lMatch.Groups["user"].Value; + fPassword = lMatch.Groups["pass"].Value; + fServer = lMatch.Groups["server"].Value; + fServerDirectory = lMatch.Groups["dir"].Value; + } + else + { + lBadParam = true; + Console.WriteLine("Invalid server parameters"); + Console.WriteLine(""); + } + } + else + lBadParam = true; + + for (int i = 2; i < lCount; i++) + { + switch (args[i].ToLower()) + { + case "-local": + fSyncMode = SyncMode.Local; + break; + + case "-remote": + fSyncMode = SyncMode.Remote; + break; + + case "-nodelete": + fDoNotDeleteMissingItems = true; + break; + + case "-passive": + fPassiveMode = true; + break; + + case "-r": + fRecursiveSync = true; + break; + + case "-l": + fDoShowFtpClientLog = true; + break; + + case "-help": + lBadParam = true; + break; + case "/help": + lBadParam = true; + break; + + default: + Console.WriteLine("Invalid command line paramter \""+args[i]+"\""); + Console.WriteLine(""); + lBadParam = true; + break; + } + } + + if (lBadParam) + { + Console.WriteLine("RemObjects Internet Pack - FtpSync Sample"); + Console.WriteLine(""); + Console.WriteLine(" Usage: FtpSync directory user:pass@server/remote [-local] [-remote] [-nodelete] [-passive] [-l] [-r]"); + Console.WriteLine(); + Console.WriteLine(" Compares files on local computer against those on the remote server, "); + Console.WriteLine(" downloading those that are new or have changed."); + Console.WriteLine(); + Console.WriteLine(" directory : local directory - long file names with spaces should be quoted"); + Console.WriteLine(" user/pass : ftp username & password"); + Console.WriteLine(" server : ftp server (i.e., ftp.whatever.com"); + Console.WriteLine(" remote : directory on remote server"); + Console.WriteLine(); + Console.WriteLine(" -nodelete : forbid to delete missing files"); + Console.WriteLine(" -passive : turn on 'Passive mode'"); + Console.WriteLine(" -r : recursive synchronization (through subdirectories)"); + Console.WriteLine(" -l : log - show FTP commands"); + Console.WriteLine(" -local : Local mode of sync (Master = ftp; slave = localhost). By default"); + Console.WriteLine(" -remote : Remote mode of sync (Master = localhost; slave = ftp)"); + Console.WriteLine(); + Console.WriteLine("Press enter to exit."); + Console.ReadLine(); + } + return !lBadParam; + } + + #region public properties... + public string LocalDirectory + { + get + { + return fLocalDirectory; + } + set + { + if (fLocalDirectory == value) + return; + fLocalDirectory = value; + } + } + public string Server + { + get + { + return fServer; + } + set + { + if (fServer == value) + return; + fServer = value; + } + } + public string ServerDirectory + { + get + { + return fServerDirectory; + } + set + { + if (fServerDirectory == value) + return; + fServerDirectory = value; + } + } + public bool DeleteMissing + { + get + { + return fDoNotDeleteMissingItems; + } + set + { + if (fDoNotDeleteMissingItems == value) + return; + fDoNotDeleteMissingItems = value; + } + } + + public bool Subdirectories + { + get + { + return fRecursiveSync; + } + set + { + if (fRecursiveSync == value) + return; + fRecursiveSync = value; + } + } + public bool Passive + { + get + { + return fPassiveMode; + } + set + { + if (fPassiveMode == value) + return; + fPassiveMode = value; + } + } + #endregion + } +} diff --git a/Samples/C#/HTTP Responses/App.ico b/Samples/C#/HTTP Responses/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/C#/HTTP Responses/App.ico differ diff --git a/Samples/C#/HTTP Responses/AssemblyInfo.cs b/Samples/C#/HTTP Responses/AssemblyInfo.cs new file mode 100644 index 0000000..45b8ee9 --- /dev/null +++ b/Samples/C#/HTTP Responses/AssemblyInfo.cs @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Samples + + (c)opyright RemObjects Software, Inc. 2003-2004. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Reflection; + +[assembly: AssemblyTitle("RemObjects Internet Pack for .NET - FtpSync Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RemObjects Software")] +[assembly: AssemblyProduct("RemObjects Internet Pack for .NET")] +[assembly: AssemblyCopyright("Copyright RemObjects Software 2003-2004. All Rights Reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("1.0.15.541")] +[assembly: CLSCompliant(true)] +#if REMOBJECTS_SIGN_ASSEMBLY +[assembly: AssemblyKeyName("RemObjectsSoftware")] +#endif diff --git a/Samples/C#/HTTP Responses/HttpResponses.2008.csproj b/Samples/C#/HTTP Responses/HttpResponses.2008.csproj new file mode 100644 index 0000000..6cf6c5d --- /dev/null +++ b/Samples/C#/HTTP Responses/HttpResponses.2008.csproj @@ -0,0 +1,113 @@ + + + Local + 8.0.50727 + 2.0 + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD} + Debug + AnyCPU + App.ico + + + HttpResponses + + + JScript + Grid + IE50 + false + WinExe + HttpResponses + OnBuildSuccess + HttpResponses.MainForm + + + + + 2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Form + + + MainForm.cs + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/HTTP Responses/HttpResponses.2008.sln b/Samples/C#/HTTP Responses/HttpResponses.2008.sln new file mode 100644 index 0000000..24624de --- /dev/null +++ b/Samples/C#/HTTP Responses/HttpResponses.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpResponses", "HttpResponses.2008.csproj", "{49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/HTTP Responses/HttpResponses.2010.csproj b/Samples/C#/HTTP Responses/HttpResponses.2010.csproj new file mode 100644 index 0000000..af83793 --- /dev/null +++ b/Samples/C#/HTTP Responses/HttpResponses.2010.csproj @@ -0,0 +1,116 @@ + + + Local + 10.0.20506 + 2.0 + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD} + Debug + AnyCPU + App.ico + + + HttpResponses + + + JScript + Grid + IE50 + false + WinExe + HttpResponses + OnBuildSuccess + HttpResponses.MainForm + + + + + 3.5 + v2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + AllRules.ruleset + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + AllRules.ruleset + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Form + + + MainForm.cs + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/HTTP Responses/HttpResponses.2010.sln b/Samples/C#/HTTP Responses/HttpResponses.2010.sln new file mode 100644 index 0000000..8dd5def --- /dev/null +++ b/Samples/C#/HTTP Responses/HttpResponses.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpResponses", "HttpResponses.2010.csproj", "{49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {49A6BF8D-6D7C-4C3B-A7F4-4E14B6F0EAAD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/HTTP Responses/HttpResponses.Sample.html b/Samples/C#/HTTP Responses/HttpResponses.Sample.html new file mode 100644 index 0000000..3fa0428 --- /dev/null +++ b/Samples/C#/HTTP Responses/HttpResponses.Sample.html @@ -0,0 +1,47 @@ + + + + + + + + + +
+

+ HTTP Responses Sample +

+ +
+

Purpose

+

+This sample shows how to create and configure a simple HTTP server. +

+ +

Examine the Code

+
    +
  • +See the httpServer_OnHttpRequest handler, where all possible RequestPaths + are processed. +The options possible are as follows: +
      +
    • / or /home - load home page with all available links.
    • +
    • /file - download the HttpResponses application.
    • +
    • /bytes - download random.bin file that contains random generated data.
    • +
    • /error - shows a custom error with code 555.
    • +
    +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run the application.
  • +
  • Click the link on the top of the application window to open the home page of our server in your web browser.
  • +
  • Try all the links on the home page.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/C#/HTTP Responses/MainForm.cs b/Samples/C#/HTTP Responses/MainForm.cs new file mode 100644 index 0000000..4a454cf --- /dev/null +++ b/Samples/C#/HTTP Responses/MainForm.cs @@ -0,0 +1,195 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Windows.Forms; + +namespace HttpResponses +{ + /// + /// Summary description for Form1. + /// + public class MainForm : System.Windows.Forms.Form + { + private RemObjects.InternetPack.Http.HttpServer httpServer; + private System.Windows.Forms.LinkLabel llblinkLabel1; + private System.Windows.Forms.ListBox lb_Log; + private System.ComponentModel.IContainer components; + + public MainForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.httpServer = new RemObjects.InternetPack.Http.HttpServer(this.components); + this.llblinkLabel1 = new System.Windows.Forms.LinkLabel(); + this.lb_Log = new System.Windows.Forms.ListBox(); + this.SuspendLayout(); + // + // httpServer + // + this.httpServer.Port = 81; + this.httpServer.ValidateRequests = false; + this.httpServer.OnHttpRequest += new RemObjects.InternetPack.Http.OnHttpRequestHandler(this.httpServer_OnHttpRequest); + // + // llblinkLabel1 + // + this.llblinkLabel1.Location = new System.Drawing.Point(8, 8); + this.llblinkLabel1.Name = "llblinkLabel1"; + this.llblinkLabel1.Size = new System.Drawing.Size(100, 23); + this.llblinkLabel1.TabIndex = 0; + this.llblinkLabel1.TabStop = true; + this.llblinkLabel1.Text = "http://localhost:81"; + this.llblinkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.llblinkLabel1_LinkClicked); + // + // lb_Log + // + this.lb_Log.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lb_Log.IntegralHeight = false; + this.lb_Log.Location = new System.Drawing.Point(11, 32); + this.lb_Log.Name = "lb_Log"; + this.lb_Log.Size = new System.Drawing.Size(371, 228); + this.lb_Log.TabIndex = 1; + // + // MainForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(394, 272); + this.Controls.Add(this.lb_Log); + this.Controls.Add(this.llblinkLabel1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(410, 310); + this.Name = "MainForm"; + this.Text = "Internet Pack HTTP Response Sample"; + this.Load += new System.EventHandler(this.Form1_Load); + this.Closed += new System.EventHandler(this.Form1_Closed); + this.ResumeLayout(false); + + } + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.Run(new MainForm()); + } + + const string sWelcome = + "Internet Pack HTTP Responses Test App" + + "

" + + "Valid links:" + + "
" + + "/home show this page" + + "
" + + "/file send back a file (this .exe)" + + "
" + + "/bytes send back a buffer of random bytes" + + "
" + + "/error Display a custom error"; + + private void httpServer_OnHttpRequest(object aSender, RemObjects.InternetPack.Http.OnHttpRequestArgs ea) + { + string lRequestPath = ea.Request.Header.RequestPath; + Invoke(new MethodInvoker(delegate() + { + lb_Log.Items.Add(lRequestPath); + })); + + + switch (ea.Request.Header.RequestPath) + { + case "/": + case "/home": + ea.Response.ContentString = sWelcome; + ea.Response.Header.SetHeaderValue("Content-Type", "text/html"); + break; + + case "/bytes": + byte[] lBuffer = new byte[256]; + Random lRandom = new Random(); + lRandom.NextBytes(lBuffer); + ea.Response.ContentBytes = lBuffer; + ea.Response.Header.SetHeaderValue("Content-Disposition", "filename=random.bin"); + ea.Response.Header.SetHeaderValue("Content-Type", "application/binary"); + break; + + case "/error": + ea.Response.SendError(555, "Custom Error", "A custom error message"); + break; + + case "/file": + string lExeName = this.GetType().Assembly.Location; + try + { + ea.Response.ContentStream = new FileStream(lExeName, + FileMode.Open, FileAccess.Read, FileShare.Read); + ea.Response.Header.SetHeaderValue("Content-Disposition", + String.Format("filename=\"{0}\"", Path.GetFileName(lExeName))); + ea.Response.Header.SetHeaderValue("Content-Type", "application/binary"); + ea.Response.CloseStream = true; /* default, anyway */ + } + catch (Exception e) + { + ea.Response.SendError(404, String.Format("File {0} not found", lExeName), e); + } + break; + + default: + ea.Response.SendError(404, "requested path not found"); + break; + } + + } + + private void llblinkLabel1_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) + { + Process.Start(llblinkLabel1.Text); + } + + private void Form1_Load(object sender, System.EventArgs e) + { + httpServer.Active = true; + } + + private void Form1_Closed(object sender, System.EventArgs e) + { + httpServer.Active = false; + } + } +} \ No newline at end of file diff --git a/Samples/C#/HTTP Responses/MainForm.resx b/Samples/C#/HTTP Responses/MainForm.resx new file mode 100644 index 0000000..cf05be8 --- /dev/null +++ b/Samples/C#/HTTP Responses/MainForm.resx @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + AAABAAYAMDAAAAEACACoDgAAZgAAACAgAAABAAgAqAgAAA4PAAAQEAAAAQAIAGgFAAC2FwAAMDAAAAEA + IACoJQAAHh0AACAgAAABACAAqBAAAMZCAAAQEAAAAQAgAGgEAABuUwAAKAAAADAAAABgAAAAAQAIAAAA + AAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAAFBQUACQkJAA0NDQAUFAsAEhINABwcCQAaGg8AHh4OABIS + EgAVFRUAGhoUABwcFgAZGRkAHR0dACQkBwAqKgcAJiYIACYmDwAoKAkALCwJAD4+AwAxMQoAPDwIACcn + EAAkJBcALi4QAC0tFAAhIRoAIyMcACQkHQAtLR4AMDARAD8/FAAzMxgANjYZADk5GwA8PBgAOzscAD09 + HgAhISEAJSUlAC0tJQApKSkALS0tADQ0JAA0NCsAPT0rADExMQA1NTUAOjoxAD8/NgA5OTkAPDw8AEJC + AgBDQwQARkYHAElJAABNTQAASkoGAE5OBQBHRwwASUkJAElJDQBMTA0AUVEAAFVVAABRUQYAVVUHAFlZ + AABdXQAAWVkHAF9fCABDQxYASUkQAElJGQBOThsAVlYQAFRUFABbWxwAYWEAAGVlAABnZwQAaWkAAG5u + AABlZQkAcXEAAHV1AABycgUAdXUFAHp6AAB9fQAAdHQLAH5+DQBkZBAAb28UAGRkGgBiYh8AdXUVAEFB + IABERCIARkYkAE5OIgBJSSUATEwnAE5OKABcXCMAUFAqAFJSLABUVC8ARUUyAEtLOQBPTz0AVlYxAF9f + MgBrayAAZGQtAHR0IAB0dDcAcnI/AH5+OQBBQUEARUVFAE1NRQBJSUkATU1NAF5eRQBSUkoAUlJSAFVV + VQBYV1AAWVlZAF1dXQBjY0EAZGRMAGpqSQB8e0EAe3tMAGhoUQBiYlsAaWlbAGpqXABtbV8AdnZZAHp6 + XQBhYWEAZGRkAGpqZABoaGgAdXVoAHFxcQB+fn4AgoIAAIWFAACCggYAiYkAAI6OAACKigYAgYEMAJGR + AACWlgAAmZkBAJ2dBgCEhBMAh4cXAIeHGACKihwAnp4UAKGhDAClpRIAqakXAKalHwCtrB0Ah4cjAI6O + IQCRkSUAmJgjAJiYJACUlCoAl5cvAImJMgCYmDAAm5s0AJKSPwCenjgArKwrALCwIwC0tCkAuLgvAKGh + PQCpqDwAsLAwALy7NAC/vzoAhoZPAIuLTACbm0wAiopfAJ6eUACZmV4ApKRCAKenRwCsrEIAqKhHAKur + SwCrq0wAtLNBAK6uUACpqVQAp6dcALKxVQC6uVUAtbVaAL29WgCGhmQAg4NpAIiIbgCWlmQAlpZuAJOT + dAC7u2MApqZwALy7dACzsnwAv75+AMPDQADHx0YAy8pMAM/OUQDT0lcA1tZcAMLCbQDa2WIA3t1oANnY + bADGxXIA0dF4AOLhbgDm5XQA4uF6AOvqfACBgYEAhoaGAIyMjACRkZEAlpaWAJ+fnwC6uoEAoaGhAKys + rADCwoMA6+qIAPHwhQD19IsA8/KSAPn4kQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACSMQ0KCgoKCgMDK4MAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACDKA0oDQ0NDQoKCgMKAwMDA3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAeigoKCgoKA0NDQoKCgoDAwMDAwADKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJIrKysrKCgoKCgN + DQ0KCgoKCgMDAwMDAAN6AAAAAAAAAAAAAAAAAAAAAAAAAAAANSsrKysrKygtIUBHR0REPDwXEwcDAwMD + AwAACgAAAAAAAAAAAAAAAAAAAAAAAAAxMTErMSsrS1VXV1dUVFJSUFBQQkI4EwMDAwMDAAAAAAAAAAAA + AAAAAAAAAAAA8TExMSsxK0taWlpaV1dXVFRSUlBQUEJQQjgPAwIDAwADgwAAAAAAAAAAAAAAAAD0NTEx + MTEvXJ2ZmZlaWldXVFRUUlJQUEJCQkJCFQQCAgMAA5YAAAAAAAAAAAAAAAA1NTUxMWidnZ2dmZlaWlpX + V1RUUlJSUFBQUEJCPDwGAwADAAAAAAAAAAAAAAAAAHw1NTU1aqCgoJ2dnZmZWlpaWldUVFRSUlBQQkJC + Qjw8DwMDAwAKAAAAAAAAAAAAlnp6NTVsoqKgoJ2gnZmZmZlaWldXV1RSUlJQUFBQQkI8PAYDAAMAegAA + AAAAAAAAeno1em+nqKKgoKCdnZ2dmZlaWlpXV1RUVFJSUFBCUEJCQjgEAwMDAAAAAAAAAACDenp6NbCq + qaiioqCgoJ2dnZmZWlpaV1dXVFJSUlBQQlBCQjw4AgMAAygAAAAAAAB8fHp6drqsqqmooqKgoJ2gnZmZ + mZlaWldXVFRUUlJQUEJCQkI8EAMDAwMAAAAAAJJ8fHp7v7u6rKqpqKKgoKCdnZ2dmZlaWlpXV1dUUlJS + UFBQQkJCPAQDAwArAAAAAHx8fHyIwLy7uqyqqaiioqCgoJ2dnZmZWlpaV1dUVFRSUBs4UFBCQhADAwMD + AAAAAIN8g3zOwcC8u7qsqqmooqCgoJ2gnZmZmZlaWldXV1RUUjgNG0JQQjwDAwMDAAAAl3x8fIbi4sDA + vLu6rKqpqKKgoKCdnZ2dmZlaWlpXV1RUVFIZDQ04UEIHBAMDegAAg4ODfMLj4uLBwLy7uqyqqaiioqCg + oJ2dnZmZWlpaV1dUVFQ9DQ0KGUITCgMDAwAAg3yDg9Dk4+LhwcB4ubqsqqmooqKgoJ2gnZmZmZlaWldX + VFRSHA0NCg0SCgoDAwAAg4N8g9Pl5OPiyn53vLu6rK1xbGtraGhoZGRkZCcnJycnIyMiHA0NDQ0KCgQJ + AwAAg4ODg+bm5eTDfHy+wLy7urFvNTU1a2hoaGhkZGRkJycnIyMjKCgNDQ0KCgoKCQAAg4ODg+jo1Yp8 + fIfiwcC8u7qsrnQ1M2GdoJ2dnZ2ZmVpaWldHKCgoDSJQCgoKCgAAg4ODg+nHg3yDfMbi4cHAvLu6rKqn + dW40aF+gnZ2ZmZlaWlpLKCgcSlJSCgoKCgAAg4ODg4yDg4N8g8/PyMi9uLazs7CupqRzbjEzT5mdmZmZ + mVcoKCdSVFRSDQ0KCgAAkpKDg4ODfIODfIB8fHx8e3p6ejV5NTU1NTExMTGdnZ2ZmU4rT1dXV1RQDSgK + DQAAkoOSg4yDg4N9g9LS0s/JyL24trOzr66mpKSeXZmgnZ2dmUtXWlpXV1RHKA0NDQAAkpKDkt3ei4OD + g9Hm5ePj4uLBwLu7uqyqqaiioqCdoJ2dmZmZWlpaV1dKKCgNDQAA8ZKSktf879eDgI/o5uXk4+LhwcDA + u7qsqqmooqKgoKCdnZmZmZlaWlcnDSgofAAA9pKSg5L7/PDrioPc6Obl5OPi4sDAvLu6rKqpqKKgoKCd + nZ2dmZlaWlcoKCgolgAAAJKSkpLf/fzw79nF6Ojm5eTj4uLBwLy7uqyqqaiioKCgoJ2dnZmZWk8pKSgo + AAAAAJaSkpKV//388PDr6eno5uXk4+LhwcC8u7qsqqmooqKgoKCdnZmZmS0pKSgxAAAAAACUkpKS+v/9 + /PDu7+3p6Obl5OPi4sDAvLu6rKqpqKKgoKCdoJ2dVSsrKykAAAAAAACUlJKSk/7//fz78O7t6Ojm5eTj + 4uLBwLy7uqyqqaiioKCgnZ2ZLisrKysAAAAAAAAAlJSSktv///388PDu7eno5uXk4+LhwcC8u7qsqqmo + oqKgoKBoLjErLvEAAAAAAAAAlpSUk5Lf///9/PDw7u3p6Obl5OPi4sDAvLu6rKqpqKKgoGEuMDArNQAA + AAAAAAAAAJSUlJSS+v///fzw8O7t6ejm5eTj4uLBwLy7uqyqqaiiXzMwMTAxAAAAAAAAAAAAAPmUlJSU + kvf///388PDu7eno5uXk4+LhwcC8u7qsqql1NTQxMTDyAAAAAAAAAAAAAADylJSUlJLb/v/9/PDw7u3p + 6Obl5OPi4sDAvLu6q3I1NTQ0MYMAAAAAAAAAAAAAAAAA8pSUlJSSlfr//fzw8O7t6ejm5eTj4uLBwLy0 + b3o1NTU0gwAAAAAAAAAAAAAAAAAAAPKUlJSUkpLY4P388PDu7eno5uXk4+Lht4V6ejV6NTWEAAAAAAAA + AAAAAAAAAAAAAAD4lJSUlJKSkpPa3uzw7u3p6ObTxomAe3p6eno1efEAAAAAAAAAAAAAAAAAAAAAAAAA + AJaUlJSSkpGRg4ODg5CQg4N8g3x8fHx8enp8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4lJSSkpKSkpGD + g4ODg3yDfHyDfHx8evIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiUkpKSg5KRg4ODg4ODg3x9fIDy + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSWkpKRkYODg4N9g4PyAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + wAP//wAA//4AAH//AAD/+AAAH/8AAP/gAAAH/wAA/8AAAAP/AAD/gAAAAf8AAP4AAAAAfwAA/AAAAAA/ + AAD8AAAAAD8AAPgAAAAAHwAA8AAAAAAPAADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADAAAAAAAMAAMAA + AAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAA + AAAAAwAAwAAAAAADAADgAAAAAAcAAOAAAAAABwAA8AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA+AAAAAAf + AAD8AAAAAD8AAP4AAAAAfwAA/wAAAAD/AAD/gAAAAf8AAP/gAAAH/wAA//AAAA//AAD//AAAP/8AAP// + gAH//wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAEAAAAAAAAAAAAAAABAAAAAQAAAAAAAAYG + BgAJCQkADAwMABAQCwASEg0AGBgJABISEgAUFBQAHBwWABkZGQAeHh4ALy8GACoqCgAuLgkAKysPADc3 + DQAqKhEALS0TACgoFQAmJhkANTUYADY2HwA4OBoAOzscAD4+HgAhISEAJCQkACkpKQAuLi4AODghADEx + MQA1NTUAOjoxAD8/NgA5OTkAPj4+AENDAgBHRwEAQkIFAEZGBQBLSwAAT08AAE1NBgBTUwAAV1cBAFJS + BgBYWAAAXFwAAFFRCABSUg8AV1cNAF1dCABERBIAQEAaAE9PHABQUBMAXV0dAGBgAABkZAAAZ2cEAGho + AABtbQAAa2sEAGVlCQBsbAoAcXEAAHV1AAB3dwUAenoAAH19AABycg8AYGAYAHNzFAB5eREAfHsfAEJC + IQBFRSIASUklAExMJwBPTykASUktAFhYKABcXC4AREQ7AEtLOQBrayAAfX0tAHBwMQBBQUEARkZGAExM + QwBJSUkATU1NAFpaQQBUVEwAWlpJAFJSUgBVVVUAWFhYAF1dXQBubk4Afn5PAGFhWgBmZlgAYWFhAGVl + ZQBtbWcAaGhoAG5ubgB7e2cAcnJyAHZ2dgB7e3sAgYEAAIaGAACDgwYAi4sAAI+PAACFhQoAk5MAAJeX + AACcnAUAj48WAJaVFACiog4AqKcWAK2tHwCHhyMAgoI/AJSUNgCYmDwArKwpALOzJwC5uDAAtrY5AL6+ + OACKilUAlpZQAJKSVQCbmlUAlpZaAJ+eWwCqqkoAvLxBALi4XgCEhGEAiIdlAIGBbgCLimkAnp5lAIuK + cQCbmnQAnp54AKyrYgC9vGUApqZwAK6uegC9vXIAxMNBAMnJSQDPzlIA1dRaAMLBbADLym8A0tFiANra + YwDg32sA5uV0AOLhewDr6nwAg4ODAIWFhQCIiIgAj4+PAJaWlgChoaEA4+KHAOnojwDx8IUA9vaNAPz7 + lQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAG9aCggICApcAAAAAAAAAAAAAAAAAAAAAAAAAAAAbyAaChoKCggICAMDAyAAAAAAAAAA + AAAAAAAAAAAAAB0dGhoaGgoKCggICAMDAwBaAAAAAAAAAAAAAAAAAGMdHR0aHjI0Qj87OyonDQQDAwAI + AAAAAAAAAAAAAABjICAdTEdyckJCPz87OjotKgwDAwMAAAAAAAAAAAAAaSMgIEh1c3JyckJCPz87Ojot + LScGAwADAAAAAAAAAHEjIyNJeHV1c3JyQkJCPz87OjotLSoEAwACAAAAAAAAWlojS3p6eHV1c3JyckJC + Pzs7OjotLSoEAwIgAAAAAGNaWlh+fXp6eHV1c3JyckJCPz87Oi0tLScGAAMAAAAAXFpbhH9+fXp6eHV1 + c3JyQnI/Pzs7OzotLQwDAxoAAG1cXIGGhX9+fXp6eHV1c3JyQkJCQj8tKi0tKgQDAwAAYVxhkIiGhX9+ + fXp6eHV1c3NyckJCPz8REC0tDQQDbwBhYWWgn4iGhX9+fXp6eHV1c3JyckJCPy0KCicqBwcjAGFhi6Gg + n4eChX9+fXp6eHV1c3JyQkJCPxQKChEIBwOwY2GOoqGPYIOGhX9VIyNQT09MTB5MGBgVGgoKCggICKxj + Y6OliVxgn4aGhX+AU1NKeHV1dXNycjsaGhQxEQgIrGNjlmhhYYqgn4iGhX9+fFZROXN1c3JyOBo1Pz8T + CAgAaWNjY2NhYWFcXFtaWiMjIyMjIHR1c3IeQEJCPwoKCgBpaZWVY2GNoqGgn4aGhYV+fXp4eHV1c3Jy + ckI/GgoKAGlpbrOeZ2imoqGgn4iGhX9+fXp4eHV1c3JyQjQaGmEAbWlpsbOpkpqmoqGgn4iGhX9+fXp6 + eHV1c3JyGBoaAACwaWmZtLOqpKemoqGgn4iGhX9+fXp4eHV1c0IdGxsAAABtaWmytLOqqKemoqGgn4iG + hX9+fXp6eHV1Nx0bZAAAAK5tbW61tLOqqKemoqGgn4iGhX9+fXp4eEkdHR0AAAAAAG1pbZm1tLOqqKem + oqGgn4iGhX9+fXp0Ix8dbQAAAAAAAG1paZm1tLOqqKemoqGgn4iGhX9+fCMjH1wAAAAAAAAAsG1taZey + tLOqqKemoqGgn4iGhVdUIyNbAAAAAAAAAAAAsG1pbWudsbOqqKemoqGgn4NeWiNaWgAAAAAAAAAAAAAA + AG1paWlpbpyeo5uRimZfXFpaWmcAAAAAAAAAAAAAAAAAAK5tbWlpY2NjY2FhYVxcXGEAAAAAAAAAAAAA + AAAAAAAAAACubWlpaWNjY2NhYW0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACurK6srgAAAAAAAAAAAAAA + AAD/8A///4AB//8AAH/8AAA/+AAAH/AAAA/gAAAH4AAAA8AAAAPAAAABgAAAAYAAAACAAAAAgAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAAGAAAABwAAAAcAAAAPgAAAD8AAAB/AAAA/4AAAf/gAAP/8A + AP//wAP///wf/ygAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAABAAAAAAAACgoKAA4O + CQAPDw8AGxsLABQUFAAYGBgAHR0dAC4uDAA8PAQANjYJADQ0DgA9PQsAKioTACIiGwArKysAMTEiADQ0 + NAA7OzEAODg4AD4+PgBAQAMAREQCAEREDgBNTQoASUkMAFNTBABZWQAARUUSAEFBFABWVhAAYWEAAGlp + AABtbQIAZmYJAG1tCgBycgAAe3sAAHV1CwB9fQ0AZGQUAHh4GwBYWCEAX18nAFlZKgBFRTwAc3MqAHt7 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + YP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xMTP9LS0v/SUlJ/0dH + R/9GRkafRUVFDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpqah9paWmfaGho/2Zm + Zv9kZGT/Y2Nj/2FhYf9gYGD/Xl5e/1xcXP9bW1v/WVlZ/1hYWP9WVlb/VFRU/1NTU/9RUVH/T09P/05O + Tv9MTEz/S0tLn0lJSR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAaGhoD2dnZ19mZmavZGRk72NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZWf9YWFj/VlZW/1RU + VP9TU1PvUVFRr09PT19PT08PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjYy9iYmJPYWFhf19fX39eXl5/XFxcf1pa + Wn9ZWVl/V1dXT1ZWVi8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD//wAA//8AAP/8AAA//wAA//AAAA// + AAD/wAAAA/8AAP+AAAAB/wAA/wAAAAD/AAD+AAAAAH8AAPwAAAAAPwAA+AAAAAAfAADwAAAAAA8AAPAA + AAAADwAA4AAAAAAHAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAABwAA4AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAP4A + AAAAfwAA/4AAAAH/AAD/wAAAA/8AAP/gAAAH/wAA//gAAB//AAD//gAAf/8AAP//4Af//wAAKAAAACAA + AABAAAAAAQAgAAAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISEPHx8fXx0dHZ8aGhrPGBgY/xUVFf8TExP/ERER/w4ODu8MDAy/CgoKfwgI + CC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApKSkfJiYmnyQkJO8hISH/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/w4O + Dv8MDAz/CQkJ/wcHB88GBgZfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAALS0tfysrK/8oKCj/JiYm/yQkJP8hISH/Hx8f/x0dHf8aGhr/GBgY/xUV + Ff8TExP/ERER/w4ODv8MDAz/CQkJ/wcHB/8FBQW/BAQELwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAANTU1DzIyMs8vLy//LS0t/ysrK/8oKCj/NjYf/1JSD/9dXQj/bW0A/2lp + AP9kZAD/YGAA/1NTAv9CQgX/KioK/xISDf8MDAz/CQkJ/wcHB/8FBQXvAwMDXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5OQ83NzfPNDQ0/zIyMv8vLy//Q0Mi/2xsCv9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/T08A/y8vBv8MDAz/CQkJ/wcHB/8FBQX/AwMDXwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + 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 + + + \ No newline at end of file diff --git a/Samples/C#/HTTP Spy/App.ico b/Samples/C#/HTTP Spy/App.ico new file mode 100644 index 0000000..919c5b8 Binary files /dev/null and b/Samples/C#/HTTP Spy/App.ico differ diff --git a/Samples/C#/HTTP Spy/AssemblyInfo.cs b/Samples/C#/HTTP Spy/AssemblyInfo.cs new file mode 100644 index 0000000..5f5c750 --- /dev/null +++ b/Samples/C#/HTTP Spy/AssemblyInfo.cs @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Samples + + (c)opyright RemObjects Software, Inc. 2003-2004. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("RemObjects Internet Pack for .NET - HttpSpy Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RemObjects Software")] +[assembly: AssemblyProduct("RemObjects Internet Pack for .NET")] +[assembly: AssemblyCopyright("Copyright RemObjects Software 2003-2004. All Rights Reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("1.0.15.541")] +[assembly: CLSCompliant(true)] +#if REMOBJECTS_SIGN_ASSEMBLY +[assembly: AssemblyKeyName("RemObjectsSoftware")] +#endif diff --git a/Samples/C#/HTTP Spy/HTTPSpy.2008.csproj b/Samples/C#/HTTP Spy/HTTPSpy.2008.csproj new file mode 100644 index 0000000..ce03f82 --- /dev/null +++ b/Samples/C#/HTTP Spy/HTTPSpy.2008.csproj @@ -0,0 +1,115 @@ + + + Local + 8.0.50727 + 2.0 + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5} + Debug + AnyCPU + App.ico + + + HTTPSpy + + + JScript + Grid + IE50 + false + WinExe + HTTPSpy + OnBuildSuccess + + + + + + + 2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE;DEBUGSERVER + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY;DEBUGSERVER + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Form + + + MainForm.cs + Designer + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/HTTP Spy/HTTPSpy.2008.sln b/Samples/C#/HTTP Spy/HTTPSpy.2008.sln new file mode 100644 index 0000000..4f0a289 --- /dev/null +++ b/Samples/C#/HTTP Spy/HTTPSpy.2008.sln @@ -0,0 +1,30 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTTPSpy", "HTTPSpy.2008.csproj", "{E2A1390A-0AFA-4ACF-9DA3-917E832901F5}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + IDE|Any CPU = IDE|Any CPU + Release|Any CPU = Release|Any CPU + RO|Any CPU = RO|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.IDE|Any CPU.ActiveCfg = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.IDE|Any CPU.Build.0 = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Release|Any CPU.Build.0 = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.RO|Any CPU.ActiveCfg = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.RO|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/HTTP Spy/HTTPSpy.2010.csproj b/Samples/C#/HTTP Spy/HTTPSpy.2010.csproj new file mode 100644 index 0000000..2e3fce7 --- /dev/null +++ b/Samples/C#/HTTP Spy/HTTPSpy.2010.csproj @@ -0,0 +1,118 @@ + + + Local + 10.0.20506 + 2.0 + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5} + Debug + AnyCPU + App.ico + + + HTTPSpy + + + JScript + Grid + IE50 + false + WinExe + HTTPSpy + OnBuildSuccess + + + + + + + 3.5 + v2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE;DEBUGSERVER + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + AllRules.ruleset + + + ..\..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY;DEBUGSERVER + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + AllRules.ruleset + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Form + + + MainForm.cs + Designer + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/HTTP Spy/HTTPSpy.2010.sln b/Samples/C#/HTTP Spy/HTTPSpy.2010.sln new file mode 100644 index 0000000..e76da60 --- /dev/null +++ b/Samples/C#/HTTP Spy/HTTPSpy.2010.sln @@ -0,0 +1,30 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTTPSpy", "HTTPSpy.2010.csproj", "{E2A1390A-0AFA-4ACF-9DA3-917E832901F5}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + IDE|Any CPU = IDE|Any CPU + Release|Any CPU = Release|Any CPU + RO|Any CPU = RO|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.IDE|Any CPU.ActiveCfg = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.IDE|Any CPU.Build.0 = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Release|Any CPU.Build.0 = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.RO|Any CPU.ActiveCfg = Release|Any CPU + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.RO|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/HTTP Spy/HTTPSpy.Sample.html b/Samples/C#/HTTP Spy/HTTPSpy.Sample.html new file mode 100644 index 0000000..781cf29 --- /dev/null +++ b/Samples/C#/HTTP Spy/HTTPSpy.Sample.html @@ -0,0 +1,41 @@ + + + + + + + + +
+

+ HTTPSpy Sample +

+ +
+

Purpose

+

+This example demonstrates how HTTP works. +With help of the HttpClient components, we can send a  HttpClientRequest and retrieve back a HttpClientResponse. The application shows us the request and response header parameters and the context of the response in text and hex view. + Also, you have the ability to add your custom request header parameter. +

+ +

Examine the Code

+
    +
  • +See the btnSubmit_Click handler. This is the main function where we create and configure the HttpClientRequest and send it to the server with the help of the + HttpClient component. +In order to get response from the server we call the Dispatch function with our HttpClientRequest instance as a parameter. +
  • +
+ + +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run the  application.
  • +
  • Using an existing URL, click on the "Submit" button. See the response of the request on the Result page.
  • +
+ + + diff --git a/Samples/C#/HTTP Spy/MainForm.cs b/Samples/C#/HTTP Spy/MainForm.cs new file mode 100644 index 0000000..3679394 --- /dev/null +++ b/Samples/C#/HTTP Spy/MainForm.cs @@ -0,0 +1,644 @@ +using System; +using System.Collections.Specialized; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using RemObjects.InternetPack.Http; + +namespace HTTPSpy +{ + /// + /// Summary description for Form1. + /// + public class MainForm : System.Windows.Forms.Form + { + private System.Windows.Forms.TextBox edUrl; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TextBox edResult; + private NameValueCollection Headers = new NameValueCollection(); + private NameValueCollection Content = new NameValueCollection(); + private System.Windows.Forms.RadioButton rbText; + private System.Windows.Forms.RadioButton rbHex; + + private string _LastResultString; + private byte[] _LastResultBytes; + private int _LastLength = 0; + private System.Windows.Forms.TabPage tabPage2; + private System.Data.DataSet dataSet1; + private System.Data.DataColumn dataColumn1; + private System.Data.DataColumn dataColumn2; + private System.Windows.Forms.DataGrid dataGrid1; + private System.Data.DataTable tblHeaders; + private System.Data.DataTable tblParams; + private System.Data.DataColumn dataColumn3; + private System.Data.DataColumn dataColumn4; + private System.Windows.Forms.DataGrid dataGrid2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.RadioButton rbPost; + private System.Windows.Forms.RadioButton rbGet; + private System.Data.DataView dvHeaders; + private System.Data.DataView dvParams; + + private int hexWidth = 16; + private System.Windows.Forms.DataGrid dataGrid3; + private System.Windows.Forms.Splitter splitter1; + private System.Data.DataTable tblResponseHeaders; + private System.Data.DataColumn dataColumn5; + private System.Data.DataColumn dataColumn6; + private RemObjects.InternetPack.Http.HttpClient httpClient1; + private System.Windows.Forms.CheckBox cbKeepAlive; + private System.Windows.Forms.Panel pnlpanel2; + private System.Windows.Forms.Splitter splitter2; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Panel pnlpanel1; + private System.Windows.Forms.Button btnSubmit; + + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + + public MainForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.edUrl = new System.Windows.Forms.TextBox(); + this.btnSubmit = new System.Windows.Forms.Button(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.splitter2 = new System.Windows.Forms.Splitter(); + this.dataGrid2 = new System.Windows.Forms.DataGrid(); + this.dvParams = new System.Data.DataView(); + this.tblParams = new System.Data.DataTable(); + this.dataColumn3 = new System.Data.DataColumn(); + this.dataColumn4 = new System.Data.DataColumn(); + this.dataGrid1 = new System.Windows.Forms.DataGrid(); + this.dvHeaders = new System.Data.DataView(); + this.tblHeaders = new System.Data.DataTable(); + this.dataColumn1 = new System.Data.DataColumn(); + this.dataColumn2 = new System.Data.DataColumn(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.edResult = new System.Windows.Forms.TextBox(); + this.pnlpanel1 = new System.Windows.Forms.Panel(); + this.rbHex = new System.Windows.Forms.RadioButton(); + this.rbText = new System.Windows.Forms.RadioButton(); + this.splitter1 = new System.Windows.Forms.Splitter(); + this.dataGrid3 = new System.Windows.Forms.DataGrid(); + this.tblResponseHeaders = new System.Data.DataTable(); + this.dataColumn5 = new System.Data.DataColumn(); + this.dataColumn6 = new System.Data.DataColumn(); + this.dataSet1 = new System.Data.DataSet(); + this.label1 = new System.Windows.Forms.Label(); + this.rbPost = new System.Windows.Forms.RadioButton(); + this.rbGet = new System.Windows.Forms.RadioButton(); + this.httpClient1 = new RemObjects.InternetPack.Http.HttpClient(); + this.cbKeepAlive = new System.Windows.Forms.CheckBox(); + this.pnlpanel2 = new System.Windows.Forms.Panel(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.tabControl1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dataGrid2)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.dvParams)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.tblParams)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.dvHeaders)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.tblHeaders)).BeginInit(); + this.tabPage1.SuspendLayout(); + this.pnlpanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dataGrid3)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.tblResponseHeaders)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.dataSet1)).BeginInit(); + this.pnlpanel2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // edUrl + // + this.edUrl.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.edUrl.Location = new System.Drawing.Point(52, 12); + this.edUrl.Name = "edUrl"; + this.edUrl.Size = new System.Drawing.Size(513, 20); + this.edUrl.TabIndex = 0; + this.edUrl.Text = "http://www.remobjects.com"; + // + // btnSubmit + // + this.btnSubmit.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnSubmit.Location = new System.Drawing.Point(586, 11); + this.btnSubmit.Name = "btnSubmit"; + this.btnSubmit.Size = new System.Drawing.Size(72, 24); + this.btnSubmit.TabIndex = 2; + this.btnSubmit.Text = "Submit"; + this.btnSubmit.Click += new System.EventHandler(this.btnSubmit_Click); + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.Location = new System.Drawing.Point(5, 76); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(659, 501); + this.tabControl1.TabIndex = 3; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.splitter2); + this.tabPage2.Controls.Add(this.dataGrid2); + this.tabPage2.Controls.Add(this.dataGrid1); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(5); + this.tabPage2.Size = new System.Drawing.Size(651, 475); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Parameters"; + // + // splitter2 + // + this.splitter2.Dock = System.Windows.Forms.DockStyle.Bottom; + this.splitter2.Location = new System.Drawing.Point(5, 284); + this.splitter2.Name = "splitter2"; + this.splitter2.Size = new System.Drawing.Size(641, 5); + this.splitter2.TabIndex = 2; + this.splitter2.TabStop = false; + // + // dataGrid2 + // + this.dataGrid2.CaptionText = "Request Content"; + this.dataGrid2.DataMember = ""; + this.dataGrid2.DataSource = this.dvParams; + this.dataGrid2.Dock = System.Windows.Forms.DockStyle.Bottom; + this.dataGrid2.HeaderForeColor = System.Drawing.SystemColors.ControlText; + this.dataGrid2.Location = new System.Drawing.Point(5, 289); + this.dataGrid2.Name = "dataGrid2"; + this.dataGrid2.PreferredColumnWidth = 300; + this.dataGrid2.Size = new System.Drawing.Size(641, 181); + this.dataGrid2.TabIndex = 1; + // + // dvParams + // + this.dvParams.Table = this.tblParams; + // + // tblParams + // + this.tblParams.Columns.AddRange(new System.Data.DataColumn[] { + this.dataColumn3, + this.dataColumn4}); + this.tblParams.TableName = "Params"; + // + // dataColumn3 + // + this.dataColumn3.ColumnName = "Name"; + // + // dataColumn4 + // + this.dataColumn4.ColumnName = "Value"; + // + // dataGrid1 + // + this.dataGrid1.CaptionText = "Request Headers"; + this.dataGrid1.DataMember = ""; + this.dataGrid1.DataSource = this.dvHeaders; + this.dataGrid1.Dock = System.Windows.Forms.DockStyle.Fill; + this.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText; + this.dataGrid1.Location = new System.Drawing.Point(5, 5); + this.dataGrid1.Name = "dataGrid1"; + this.dataGrid1.PreferredColumnWidth = 300; + this.dataGrid1.Size = new System.Drawing.Size(641, 465); + this.dataGrid1.TabIndex = 0; + // + // dvHeaders + // + this.dvHeaders.Table = this.tblHeaders; + // + // tblHeaders + // + this.tblHeaders.Columns.AddRange(new System.Data.DataColumn[] { + this.dataColumn1, + this.dataColumn2}); + this.tblHeaders.TableName = "Headers"; + // + // dataColumn1 + // + this.dataColumn1.ColumnName = "Name"; + // + // dataColumn2 + // + this.dataColumn2.ColumnName = "Value"; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.edResult); + this.tabPage1.Controls.Add(this.pnlpanel1); + this.tabPage1.Controls.Add(this.splitter1); + this.tabPage1.Controls.Add(this.dataGrid3); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(5); + this.tabPage1.Size = new System.Drawing.Size(650, 470); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Result"; + // + // edResult + // + this.edResult.Dock = System.Windows.Forms.DockStyle.Fill; + this.edResult.Location = new System.Drawing.Point(5, 213); + this.edResult.Multiline = true; + this.edResult.Name = "edResult"; + this.edResult.ReadOnly = true; + this.edResult.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.edResult.Size = new System.Drawing.Size(640, 225); + this.edResult.TabIndex = 4; + // + // pnlpanel1 + // + this.pnlpanel1.Controls.Add(this.rbHex); + this.pnlpanel1.Controls.Add(this.rbText); + this.pnlpanel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pnlpanel1.Location = new System.Drawing.Point(5, 438); + this.pnlpanel1.Name = "pnlpanel1"; + this.pnlpanel1.Size = new System.Drawing.Size(640, 27); + this.pnlpanel1.TabIndex = 10; + // + // rbHex + // + this.rbHex.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.rbHex.Location = new System.Drawing.Point(68, 4); + this.rbHex.Name = "rbHex"; + this.rbHex.Size = new System.Drawing.Size(60, 20); + this.rbHex.TabIndex = 6; + this.rbHex.Text = "Hex"; + // + // rbText + // + this.rbText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.rbText.Checked = true; + this.rbText.Location = new System.Drawing.Point(8, 4); + this.rbText.Name = "rbText"; + this.rbText.Size = new System.Drawing.Size(60, 20); + this.rbText.TabIndex = 5; + this.rbText.TabStop = true; + this.rbText.Text = "Text"; + this.rbText.CheckedChanged += new System.EventHandler(this.rbText_CheckedChanged); + // + // splitter1 + // + this.splitter1.Dock = System.Windows.Forms.DockStyle.Top; + this.splitter1.Location = new System.Drawing.Point(5, 208); + this.splitter1.Name = "splitter1"; + this.splitter1.Size = new System.Drawing.Size(640, 5); + this.splitter1.TabIndex = 8; + this.splitter1.TabStop = false; + // + // dataGrid3 + // + this.dataGrid3.CaptionText = "Result Headers"; + this.dataGrid3.DataMember = ""; + this.dataGrid3.DataSource = this.tblResponseHeaders; + this.dataGrid3.Dock = System.Windows.Forms.DockStyle.Top; + this.dataGrid3.HeaderForeColor = System.Drawing.SystemColors.ControlText; + this.dataGrid3.Location = new System.Drawing.Point(5, 5); + this.dataGrid3.Name = "dataGrid3"; + this.dataGrid3.PreferredColumnWidth = 300; + this.dataGrid3.ReadOnly = true; + this.dataGrid3.Size = new System.Drawing.Size(640, 203); + this.dataGrid3.TabIndex = 7; + // + // tblResponseHeaders + // + this.tblResponseHeaders.Columns.AddRange(new System.Data.DataColumn[] { + this.dataColumn5, + this.dataColumn6}); + this.tblResponseHeaders.TableName = "ResponseHeaders"; + // + // dataColumn5 + // + this.dataColumn5.ColumnName = "Name"; + // + // dataColumn6 + // + this.dataColumn6.ColumnName = "Value"; + // + // dataSet1 + // + this.dataSet1.DataSetName = "NewDataSet"; + this.dataSet1.Locale = new System.Globalization.CultureInfo("en-US"); + this.dataSet1.Tables.AddRange(new System.Data.DataTable[] { + this.tblHeaders, + this.tblParams, + this.tblResponseHeaders}); + // + // label1 + // + this.label1.Location = new System.Drawing.Point(12, 16); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(32, 23); + this.label1.TabIndex = 4; + this.label1.Text = "URL:"; + // + // rbPost + // + this.rbPost.Location = new System.Drawing.Point(100, 36); + this.rbPost.Name = "rbPost"; + this.rbPost.Size = new System.Drawing.Size(48, 20); + this.rbPost.TabIndex = 8; + this.rbPost.Text = "Post"; + // + // rbGet + // + this.rbGet.Checked = true; + this.rbGet.Location = new System.Drawing.Point(52, 36); + this.rbGet.Name = "rbGet"; + this.rbGet.Size = new System.Drawing.Size(40, 20); + this.rbGet.TabIndex = 7; + this.rbGet.TabStop = true; + this.rbGet.Text = "Get"; + // + // httpClient1 + // + this.httpClient1.ConnectionClass = null; + this.httpClient1.ConnectionFactory = null; + this.httpClient1.CustomConnectionPool = null; + this.httpClient1.HostAddress = null; + this.httpClient1.HostName = null; + this.httpClient1.Password = ""; + this.httpClient1.Port = 0; + this.httpClient1.Url = null; + this.httpClient1.UserName = ""; + // + // cbKeepAlive + // + this.cbKeepAlive.Checked = true; + this.cbKeepAlive.CheckState = System.Windows.Forms.CheckState.Checked; + this.cbKeepAlive.Location = new System.Drawing.Point(160, 36); + this.cbKeepAlive.Name = "cbKeepAlive"; + this.cbKeepAlive.Size = new System.Drawing.Size(104, 20); + this.cbKeepAlive.TabIndex = 9; + this.cbKeepAlive.Text = "Keep Alive"; + // + // pnlpanel2 + // + this.pnlpanel2.Controls.Add(this.pictureBox1); + this.pnlpanel2.Controls.Add(this.edUrl); + this.pnlpanel2.Controls.Add(this.rbGet); + this.pnlpanel2.Controls.Add(this.label1); + this.pnlpanel2.Controls.Add(this.rbPost); + this.pnlpanel2.Controls.Add(this.btnSubmit); + this.pnlpanel2.Controls.Add(this.cbKeepAlive); + this.pnlpanel2.Dock = System.Windows.Forms.DockStyle.Top; + this.pnlpanel2.Location = new System.Drawing.Point(5, 5); + this.pnlpanel2.Name = "pnlpanel2"; + this.pnlpanel2.Size = new System.Drawing.Size(659, 71); + this.pnlpanel2.TabIndex = 10; + // + // pictureBox1 + // + this.pictureBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image"))); + this.pictureBox1.Location = new System.Drawing.Point(538, 40); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(120, 30); + this.pictureBox1.TabIndex = 10; + this.pictureBox1.TabStop = false; + // + // MainForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(669, 582); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.pnlpanel2); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(685, 620); + this.Name = "MainForm"; + this.Padding = new System.Windows.Forms.Padding(5); + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "RemObjects Internet Pack for .NET - HTTP Spy"; + this.Load += new System.EventHandler(this.Form1_Load); + this.tabControl1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dataGrid2)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.dvParams)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.tblParams)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.dvHeaders)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.tblHeaders)).EndInit(); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.pnlpanel1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dataGrid3)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.tblResponseHeaders)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.dataSet1)).EndInit(); + this.pnlpanel2.ResumeLayout(false); + this.pnlpanel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.Run(new MainForm()); + } + + private void AddHeader(string Name, string Value) + { + DataRow aRow = tblHeaders.NewRow(); + aRow["Name"] = Name; + aRow["Value"] = Value; + tblHeaders.Rows.Add(aRow); + } + + private void AddResponseHeader(string Name, string Value) + { + DataRow aRow = tblResponseHeaders.NewRow(); + aRow["Name"] = Name; + aRow["Value"] = Value; + tblResponseHeaders.Rows.Add(aRow); + } + + private void btnSubmit_Click(object sender, System.EventArgs e) + { + HttpClientRequest lRequest = new HttpClientRequest(); + if (rbPost.Checked) // don't set .Get, it's the default + { + lRequest.RequestType = RequestType.Post; + + string lParams = ""; + for (int i = 0; i < tblParams.Rows.Count; i++) + { + DataRow aRow = tblParams.Rows[i]; + + lParams += string.Format("{0}={1}\r\n", aRow["Name"].ToString(), aRow["Value"].ToString()); + } + lRequest.ContentString = lParams; + } + + lRequest.Url.Parse(edUrl.Text); + + // set headers + for (int i = 0; i < tblHeaders.Rows.Count; i++) + { + DataRow aRow = tblHeaders.Rows[i]; + lRequest.Header.SetHeaderValue(aRow["Name"].ToString(), aRow["Value"].ToString()); + } + + httpClient1.KeepAlive = cbKeepAlive.Checked; + lRequest.KeepAlive = httpClient1.KeepAlive; + + tblResponseHeaders.Clear(); + edResult.Text = ""; + + tabControl1.SelectedIndex = 1; + Application.DoEvents(); + + try + { + HttpClientResponse lResponse = httpClient1.Dispatch(lRequest); + ShowResponse(lResponse); + } + catch (HttpException ex) + { + ShowResponse(ex.Response); + } + catch (Exception ex) + { + _LastResultString = "Error retrieving response: " + ex.Message; + _LastResultBytes = new UnicodeEncoding().GetBytes(_LastResultString); + _LastLength = _LastResultBytes.Length; + SetResultText(); + } + } + + public void ShowResponse(HttpClientResponse aResponse) + { + _LastResultString = aResponse.ContentString; + _LastResultBytes = aResponse.ContentBytes; + try + { + _LastLength = aResponse.ContentLength; + } + catch + { + _LastLength = _LastResultBytes.Length; + } + + AddResponseHeader(aResponse.Header.FirstHeader, ""); + foreach (HttpHeader aHeader in aResponse.Header) + { + AddResponseHeader(aHeader.Name, aHeader.Value); + if (aHeader.Name == "Set-Cookie") + { + if (MessageBox.Show("Keep Cookie for future requests?", "Internet Pack", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + DataRow aRow = tblHeaders.NewRow(); + aRow["Name"] = "Cookie"; + aRow["Value"] = aHeader.Value; + tblHeaders.Rows.Add(aRow); + } + } + } + + SetResultText(); + } + + private void SetResultText() + { + if (_LastLength != 0) + { + if (rbText.Checked) + { + edResult.Text = _LastResultString; + edResult.Font = new Font("Courier New", (float)8.25); + } + else + { + string lHex = ""; + string lChars = ""; + + for (int i = 0; i < _LastLength; i++) + { + if (i % hexWidth == 0) + { + if (i > 0) + { + lHex += "| " + lChars + "\r\n"; + lChars = ""; + } + lHex += i.ToString("X8") + ": "; + } + + lHex += _LastResultBytes[i].ToString("X2") + ' '; + if (_LastResultBytes[i] < 32) + lChars += "."; + else + lChars += (char)(_LastResultBytes[i]); + } + + if (_LastLength % hexWidth > 0) + { + for (int i = _LastLength % hexWidth; i < hexWidth; i++) + { + lHex += " "; + } + lHex += "| " + lChars; + } + + edResult.Text = lHex; + edResult.Font = new Font("Courier New", (float)8.25); + } + } + } + + private void rbText_CheckedChanged(object sender, System.EventArgs e) + { + SetResultText(); + } + + private void Form1_Load(object sender, System.EventArgs e) + { + AddHeader("Accept", httpClient1.Accept); + AddHeader("User-Agent", httpClient1.UserAgent); + } + } +} \ No newline at end of file diff --git a/Samples/C#/HTTP Spy/MainForm.resx b/Samples/C#/HTTP Spy/MainForm.resx new file mode 100644 index 0000000..b7a309e --- /dev/null +++ b/Samples/C#/HTTP Spy/MainForm.resx @@ -0,0 +1,594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 117, 17 + + + 221, 17 + + + 316, 17 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + + + AAABAAYAMDAAAAEACACoDgAAZgAAACAgAAABAAgAqAgAAA4PAAAQEAAAAQAIAGgFAAC2FwAAMDAAAAEA + IACoJQAAHh0AACAgAAABACAAqBAAAMZCAAAQEAAAAQAgAGgEAABuUwAAKAAAADAAAABgAAAAAQAIAAAA + AAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAAFBQUACQkJAA0NDQAUFAsAEhINABwcCQAaGg8AHh4OABIS + EgAVFRUAGhoUABwcFgAZGRkAHR0dACQkBwAqKgcAJiYIACYmDwAoKAkALCwJAD4+AwAxMQoAPDwIACcn + EAAkJBcALi4QAC0tFAAhIRoAIyMcACQkHQAtLR4AMDARAD8/FAAzMxgANjYZADk5GwA8PBgAOzscAD09 + HgAhISEAJSUlAC0tJQApKSkALS0tADQ0JAA0NCsAPT0rADExMQA1NTUAOjoxAD8/NgA5OTkAPDw8AEJC + AgBDQwQARkYHAElJAABNTQAASkoGAE5OBQBHRwwASUkJAElJDQBMTA0AUVEAAFVVAABRUQYAVVUHAFlZ + AABdXQAAWVkHAF9fCABDQxYASUkQAElJGQBOThsAVlYQAFRUFABbWxwAYWEAAGVlAABnZwQAaWkAAG5u + AABlZQkAcXEAAHV1AABycgUAdXUFAHp6AAB9fQAAdHQLAH5+DQBkZBAAb28UAGRkGgBiYh8AdXUVAEFB + IABERCIARkYkAE5OIgBJSSUATEwnAE5OKABcXCMAUFAqAFJSLABUVC8ARUUyAEtLOQBPTz0AVlYxAF9f + MgBrayAAZGQtAHR0IAB0dDcAcnI/AH5+OQBBQUEARUVFAE1NRQBJSUkATU1NAF5eRQBSUkoAUlJSAFVV + VQBYV1AAWVlZAF1dXQBjY0EAZGRMAGpqSQB8e0EAe3tMAGhoUQBiYlsAaWlbAGpqXABtbV8AdnZZAHp6 + XQBhYWEAZGRkAGpqZABoaGgAdXVoAHFxcQB+fn4AgoIAAIWFAACCggYAiYkAAI6OAACKigYAgYEMAJGR + AACWlgAAmZkBAJ2dBgCEhBMAh4cXAIeHGACKihwAnp4UAKGhDAClpRIAqakXAKalHwCtrB0Ah4cjAI6O + IQCRkSUAmJgjAJiYJACUlCoAl5cvAImJMgCYmDAAm5s0AJKSPwCenjgArKwrALCwIwC0tCkAuLgvAKGh + PQCpqDwAsLAwALy7NAC/vzoAhoZPAIuLTACbm0wAiopfAJ6eUACZmV4ApKRCAKenRwCsrEIAqKhHAKur + SwCrq0wAtLNBAK6uUACpqVQAp6dcALKxVQC6uVUAtbVaAL29WgCGhmQAg4NpAIiIbgCWlmQAlpZuAJOT + dAC7u2MApqZwALy7dACzsnwAv75+AMPDQADHx0YAy8pMAM/OUQDT0lcA1tZcAMLCbQDa2WIA3t1oANnY + bADGxXIA0dF4AOLhbgDm5XQA4uF6AOvqfACBgYEAhoaGAIyMjACRkZEAlpaWAJ+fnwC6uoEAoaGhAKys + rADCwoMA6+qIAPHwhQD19IsA8/KSAPn4kQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACSMQ0KCgoKCgMDK4MAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACDKA0oDQ0NDQoKCgMKAwMDA3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAeigoKCgoKA0NDQoKCgoDAwMDAwADKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJIrKysrKCgoKCgN + DQ0KCgoKCgMDAwMDAAN6AAAAAAAAAAAAAAAAAAAAAAAAAAAANSsrKysrKygtIUBHR0REPDwXEwcDAwMD + AwAACgAAAAAAAAAAAAAAAAAAAAAAAAAxMTErMSsrS1VXV1dUVFJSUFBQQkI4EwMDAwMDAAAAAAAAAAAA + AAAAAAAAAAAA8TExMSsxK0taWlpaV1dXVFRSUlBQUEJQQjgPAwIDAwADgwAAAAAAAAAAAAAAAAD0NTEx + MTEvXJ2ZmZlaWldXVFRUUlJQUEJCQkJCFQQCAgMAA5YAAAAAAAAAAAAAAAA1NTUxMWidnZ2dmZlaWlpX + V1RUUlJSUFBQUEJCPDwGAwADAAAAAAAAAAAAAAAAAHw1NTU1aqCgoJ2dnZmZWlpaWldUVFRSUlBQQkJC + Qjw8DwMDAwAKAAAAAAAAAAAAlnp6NTVsoqKgoJ2gnZmZmZlaWldXV1RSUlJQUFBQQkI8PAYDAAMAegAA + AAAAAAAAeno1em+nqKKgoKCdnZ2dmZlaWlpXV1RUVFJSUFBCUEJCQjgEAwMDAAAAAAAAAACDenp6NbCq + qaiioqCgoJ2dnZmZWlpaV1dXVFJSUlBQQlBCQjw4AgMAAygAAAAAAAB8fHp6drqsqqmooqKgoJ2gnZmZ + mZlaWldXVFRUUlJQUEJCQkI8EAMDAwMAAAAAAJJ8fHp7v7u6rKqpqKKgoKCdnZ2dmZlaWlpXV1dUUlJS + UFBQQkJCPAQDAwArAAAAAHx8fHyIwLy7uqyqqaiioqCgoJ2dnZmZWlpaV1dUVFRSUBs4UFBCQhADAwMD + AAAAAIN8g3zOwcC8u7qsqqmooqCgoJ2gnZmZmZlaWldXV1RUUjgNG0JQQjwDAwMDAAAAl3x8fIbi4sDA + vLu6rKqpqKKgoKCdnZ2dmZlaWlpXV1RUVFIZDQ04UEIHBAMDegAAg4ODfMLj4uLBwLy7uqyqqaiioqCg + oJ2dnZmZWlpaV1dUVFQ9DQ0KGUITCgMDAwAAg3yDg9Dk4+LhwcB4ubqsqqmooqKgoJ2gnZmZmZlaWldX + VFRSHA0NCg0SCgoDAwAAg4N8g9Pl5OPiyn53vLu6rK1xbGtraGhoZGRkZCcnJycnIyMiHA0NDQ0KCgQJ + AwAAg4ODg+bm5eTDfHy+wLy7urFvNTU1a2hoaGhkZGRkJycnIyMjKCgNDQ0KCgoKCQAAg4ODg+jo1Yp8 + fIfiwcC8u7qsrnQ1M2GdoJ2dnZ2ZmVpaWldHKCgoDSJQCgoKCgAAg4ODg+nHg3yDfMbi4cHAvLu6rKqn + dW40aF+gnZ2ZmZlaWlpLKCgcSlJSCgoKCgAAg4ODg4yDg4N8g8/PyMi9uLazs7CupqRzbjEzT5mdmZmZ + mVcoKCdSVFRSDQ0KCgAAkpKDg4ODfIODfIB8fHx8e3p6ejV5NTU1NTExMTGdnZ2ZmU4rT1dXV1RQDSgK + DQAAkoOSg4yDg4N9g9LS0s/JyL24trOzr66mpKSeXZmgnZ2dmUtXWlpXV1RHKA0NDQAAkpKDkt3ei4OD + g9Hm5ePj4uLBwLu7uqyqqaiioqCdoJ2dmZmZWlpaV1dKKCgNDQAA8ZKSktf879eDgI/o5uXk4+LhwcDA + u7qsqqmooqKgoKCdnZmZmZlaWlcnDSgofAAA9pKSg5L7/PDrioPc6Obl5OPi4sDAvLu6rKqpqKKgoKCd + nZ2dmZlaWlcoKCgolgAAAJKSkpLf/fzw79nF6Ojm5eTj4uLBwLy7uqyqqaiioKCgoJ2dnZmZWk8pKSgo + AAAAAJaSkpKV//388PDr6eno5uXk4+LhwcC8u7qsqqmooqKgoKCdnZmZmS0pKSgxAAAAAACUkpKS+v/9 + /PDu7+3p6Obl5OPi4sDAvLu6rKqpqKKgoKCdoJ2dVSsrKykAAAAAAACUlJKSk/7//fz78O7t6Ojm5eTj + 4uLBwLy7uqyqqaiioKCgnZ2ZLisrKysAAAAAAAAAlJSSktv///388PDu7eno5uXk4+LhwcC8u7qsqqmo + oqKgoKBoLjErLvEAAAAAAAAAlpSUk5Lf///9/PDw7u3p6Obl5OPi4sDAvLu6rKqpqKKgoGEuMDArNQAA + AAAAAAAAAJSUlJSS+v///fzw8O7t6ejm5eTj4uLBwLy7uqyqqaiiXzMwMTAxAAAAAAAAAAAAAPmUlJSU + kvf///388PDu7eno5uXk4+LhwcC8u7qsqql1NTQxMTDyAAAAAAAAAAAAAADylJSUlJLb/v/9/PDw7u3p + 6Obl5OPi4sDAvLu6q3I1NTQ0MYMAAAAAAAAAAAAAAAAA8pSUlJSSlfr//fzw8O7t6ejm5eTj4uLBwLy0 + b3o1NTU0gwAAAAAAAAAAAAAAAAAAAPKUlJSUkpLY4P388PDu7eno5uXk4+Lht4V6ejV6NTWEAAAAAAAA + AAAAAAAAAAAAAAD4lJSUlJKSkpPa3uzw7u3p6ObTxomAe3p6eno1efEAAAAAAAAAAAAAAAAAAAAAAAAA + AJaUlJSSkpGRg4ODg5CQg4N8g3x8fHx8enp8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4lJSSkpKSkpGD + g4ODg3yDfHyDfHx8evIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiUkpKSg5KRg4ODg4ODg3x9fIDy + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSWkpKRkYODg4N9g4PyAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + wAP//wAA//4AAH//AAD/+AAAH/8AAP/gAAAH/wAA/8AAAAP/AAD/gAAAAf8AAP4AAAAAfwAA/AAAAAA/ + AAD8AAAAAD8AAPgAAAAAHwAA8AAAAAAPAADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADAAAAAAAMAAMAA + AAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAA + AAAAAwAAwAAAAAADAADgAAAAAAcAAOAAAAAABwAA8AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA+AAAAAAf + AAD8AAAAAD8AAP4AAAAAfwAA/wAAAAD/AAD/gAAAAf8AAP/gAAAH/wAA//AAAA//AAD//AAAP/8AAP// + gAH//wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAEAAAAAAAAAAAAAAABAAAAAQAAAAAAAAYG + BgAJCQkADAwMABAQCwASEg0AGBgJABISEgAUFBQAHBwWABkZGQAeHh4ALy8GACoqCgAuLgkAKysPADc3 + DQAqKhEALS0TACgoFQAmJhkANTUYADY2HwA4OBoAOzscAD4+HgAhISEAJCQkACkpKQAuLi4AODghADEx + MQA1NTUAOjoxAD8/NgA5OTkAPj4+AENDAgBHRwEAQkIFAEZGBQBLSwAAT08AAE1NBgBTUwAAV1cBAFJS + BgBYWAAAXFwAAFFRCABSUg8AV1cNAF1dCABERBIAQEAaAE9PHABQUBMAXV0dAGBgAABkZAAAZ2cEAGho + AABtbQAAa2sEAGVlCQBsbAoAcXEAAHV1AAB3dwUAenoAAH19AABycg8AYGAYAHNzFAB5eREAfHsfAEJC + IQBFRSIASUklAExMJwBPTykASUktAFhYKABcXC4AREQ7AEtLOQBrayAAfX0tAHBwMQBBQUEARkZGAExM + QwBJSUkATU1NAFpaQQBUVEwAWlpJAFJSUgBVVVUAWFhYAF1dXQBubk4Afn5PAGFhWgBmZlgAYWFhAGVl + ZQBtbWcAaGhoAG5ubgB7e2cAcnJyAHZ2dgB7e3sAgYEAAIaGAACDgwYAi4sAAI+PAACFhQoAk5MAAJeX + AACcnAUAj48WAJaVFACiog4AqKcWAK2tHwCHhyMAgoI/AJSUNgCYmDwArKwpALOzJwC5uDAAtrY5AL6+ + OACKilUAlpZQAJKSVQCbmlUAlpZaAJ+eWwCqqkoAvLxBALi4XgCEhGEAiIdlAIGBbgCLimkAnp5lAIuK + cQCbmnQAnp54AKyrYgC9vGUApqZwAK6uegC9vXIAxMNBAMnJSQDPzlIA1dRaAMLBbADLym8A0tFiANra + YwDg32sA5uV0AOLhewDr6nwAg4ODAIWFhQCIiIgAj4+PAJaWlgChoaEA4+KHAOnojwDx8IUA9vaNAPz7 + lQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAG9aCggICApcAAAAAAAAAAAAAAAAAAAAAAAAAAAAbyAaChoKCggICAMDAyAAAAAAAAAA + AAAAAAAAAAAAAB0dGhoaGgoKCggICAMDAwBaAAAAAAAAAAAAAAAAAGMdHR0aHjI0Qj87OyonDQQDAwAI + AAAAAAAAAAAAAABjICAdTEdyckJCPz87OjotKgwDAwMAAAAAAAAAAAAAaSMgIEh1c3JyckJCPz87Ojot + LScGAwADAAAAAAAAAHEjIyNJeHV1c3JyQkJCPz87OjotLSoEAwACAAAAAAAAWlojS3p6eHV1c3JyckJC + Pzs7OjotLSoEAwIgAAAAAGNaWlh+fXp6eHV1c3JyckJCPz87Oi0tLScGAAMAAAAAXFpbhH9+fXp6eHV1 + c3JyQnI/Pzs7OzotLQwDAxoAAG1cXIGGhX9+fXp6eHV1c3JyQkJCQj8tKi0tKgQDAwAAYVxhkIiGhX9+ + fXp6eHV1c3NyckJCPz8REC0tDQQDbwBhYWWgn4iGhX9+fXp6eHV1c3JyckJCPy0KCicqBwcjAGFhi6Gg + n4eChX9+fXp6eHV1c3JyQkJCPxQKChEIBwOwY2GOoqGPYIOGhX9VIyNQT09MTB5MGBgVGgoKCggICKxj + Y6OliVxgn4aGhX+AU1NKeHV1dXNycjsaGhQxEQgIrGNjlmhhYYqgn4iGhX9+fFZROXN1c3JyOBo1Pz8T + CAgAaWNjY2NhYWFcXFtaWiMjIyMjIHR1c3IeQEJCPwoKCgBpaZWVY2GNoqGgn4aGhYV+fXp4eHV1c3Jy + ckI/GgoKAGlpbrOeZ2imoqGgn4iGhX9+fXp4eHV1c3JyQjQaGmEAbWlpsbOpkpqmoqGgn4iGhX9+fXp6 + eHV1c3JyGBoaAACwaWmZtLOqpKemoqGgn4iGhX9+fXp4eHV1c0IdGxsAAABtaWmytLOqqKemoqGgn4iG + hX9+fXp6eHV1Nx0bZAAAAK5tbW61tLOqqKemoqGgn4iGhX9+fXp4eEkdHR0AAAAAAG1pbZm1tLOqqKem + oqGgn4iGhX9+fXp0Ix8dbQAAAAAAAG1paZm1tLOqqKemoqGgn4iGhX9+fCMjH1wAAAAAAAAAsG1taZey + tLOqqKemoqGgn4iGhVdUIyNbAAAAAAAAAAAAsG1pbWudsbOqqKemoqGgn4NeWiNaWgAAAAAAAAAAAAAA + AG1paWlpbpyeo5uRimZfXFpaWmcAAAAAAAAAAAAAAAAAAK5tbWlpY2NjY2FhYVxcXGEAAAAAAAAAAAAA + AAAAAAAAAACubWlpaWNjY2NhYW0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACurK6srgAAAAAAAAAAAAAA + AAD/8A///4AB//8AAH/8AAA/+AAAH/AAAA/gAAAH4AAAA8AAAAPAAAABgAAAAYAAAACAAAAAgAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAAGAAAABwAAAAcAAAAPgAAAD8AAAB/AAAA/4AAAf/gAAP/8A + AP//wAP///wf/ygAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAABAAAAAAAACgoKAA4O + CQAPDw8AGxsLABQUFAAYGBgAHR0dAC4uDAA8PAQANjYJADQ0DgA9PQsAKioTACIiGwArKysAMTEiADQ0 + NAA7OzEAODg4AD4+PgBAQAMAREQCAEREDgBNTQoASUkMAFNTBABZWQAARUUSAEFBFABWVhAAYWEAAGlp + AABtbQIAZmYJAG1tCgBycgAAe3sAAHV1CwB9fQ0AZGQUAHh4GwBYWCEAX18nAFlZKgBFRTwAc3MqAHt7 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + 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 + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + OS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trf2pqav9oaGj/ZmZm/2Rk + ZP9hYWH/e3tn/6amcP+9vXH/wsFs/728Zf+4uF7/m5pV/35+T/9UVEz/SUlJ/0dHR/9FRUX/QkJC/0BA + QM8+Pj4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trL2pq + ar9oaGj/ZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dH + R+9ERER/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGpqak9oaGivZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05O + Tt9LS0t/SkpKDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZ2cPZWVlT2NjY39hYWG/Xl5ev1xcXL9aWlq/V1dXn1VV + VW9SUlIvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAP//wAA//4A + AD/4AAAf8AAAD+AAAAfgAAADwAAAA8AAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAcAAAAHAAAAD4AAAA/AAAAf4AAAP/AAAH/4AAH//gAD//+AH/ygA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJycnPyIi + Iq8dHR3vGBgY/xQUFP8PDw//CwsLvwcHB18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUPMDAwrysr + K/8xMSL/RUUS/0REDv89PQv/NjYJ/xsbC/8KCgr/BgYGzwMDAy8AAAAAAAAAAAAAAAA/Pz8POTk5zzU1 + Nf9kZBT/g4MA/3t7AP9ycgD/aWkA/2FhAP9ZWQD/PDwE/w4OCf8GBgbvAwMDLwAAAAAAAAAAQ0NDjz4+ + Pv94eBv/lJQA/4uLAP+DgwD/e3sA/3JyAP9paQD/YWEA/1lZAP9ERAL/Dg4J/wYGBs8AAAAATExMH0hI + SP97ezb/qakY/56eCP+UlAD/i4sA/4ODAP97ewD/cnIA/2lpAP9hYQD/WVkA/0BAA/8KCgr/BwcHX1BQ + UI9MTEz/uLg7/7S0Kv+pqRj/np4I/5SUAP+LiwD/g4MA/3t7AP9ycgD/aWkA/1NTBP9ZWQD/GxsL/woK + Cs9VVVW/cnJT/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uLAP+DgwD/e3sA/3JyAP9JSQz/KioT/y4u + DP8PDw//W1tb75KSX//OzVz/e3tM/8C/O/+0tCr/e3sr/19fJ/99fQ3/dXUL/21tCv9mZgn/QUEU/yIi + G/80NA7/FBQU/19fX99+fmL/X19X/2ppUv+Likr/g4NA/3t7Nf9zcyr/WVkq/1hYIf+LiwD/g4MA/1ZW + EP9tbQL/TU0K/xgYGP9kZGS/hYVq/66tcf+BgVz/1tVc/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uL + AP+DgwD/e3sA/0VFEv8dHR3/Z2dnb2RkZP/k44j/29p7/9nYbP/W1Vz/y8pL/8C/O/+0tCr/qakY/56e + CP+UlAD/i4sA/4ODAP8xMSL/IiIir2trax9paWn/lZR1//j3j//t7H//4eFu/9bVXP/Lykv/wL87/7S0 + Kv+pqRj/np4I/5SUAP9kZBT/Kysr/ygoKF8AAAAAa2trf2lpaf/FxIb/+PeP/+3sf//h4W7/1tVc/8vK + S//Avzv/tLQq/6mpGP+LixH/Ozsx/zAwML8AAAAAAAAAAAAAAABra2ufaWlp/6iofP/u7Yz/7ex//+Hh + bv/W1Vz/y8pL/8C/O/97ezb/RUU8/zk5Oc81NTUPAAAAAAAAAAAAAAAAAAAAAGtra39paWn/ZGRk/4WF + av+jomv/m5tg/3p6VP9UVEz/SEhI/0NDQ68/Pz8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trH2ho + aI9kZGS/X19f/1tbW/9WVlbPUVFRn0xMTD8AAAAAAAAAAAAAAAAAAAAA8A8AAMADAACAAQAAgAEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAMABAADgAwAA8A8AAA== + + + \ No newline at end of file diff --git a/Samples/C#/SMTP Client/App.ico b/Samples/C#/SMTP Client/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/C#/SMTP Client/App.ico differ diff --git a/Samples/C#/SMTP Client/AssemblyInfo.cs b/Samples/C#/SMTP Client/AssemblyInfo.cs new file mode 100644 index 0000000..177a4f0 --- /dev/null +++ b/Samples/C#/SMTP Client/AssemblyInfo.cs @@ -0,0 +1,58 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// +// 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("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// +// 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 Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.*")] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] diff --git a/Samples/C#/SMTP Client/MainForm.cs b/Samples/C#/SMTP Client/MainForm.cs new file mode 100644 index 0000000..a4a1c9e --- /dev/null +++ b/Samples/C#/SMTP Client/MainForm.cs @@ -0,0 +1,504 @@ +using System; +using System.Windows.Forms; + +namespace SMTP_Client_Sample +{ + /// + /// Summary description for Form1. + /// + public class MainForm : System.Windows.Forms.Form + { + private System.Windows.Forms.TextBox txtUserName; + private System.Windows.Forms.TextBox txtPassword; + private System.Windows.Forms.Label lblSenderName; + private System.Windows.Forms.TextBox txtSenderName; + private System.Windows.Forms.Label lblSMTPServer; + private System.Windows.Forms.TextBox txtSMTPServer; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Label lblRO; + private System.Windows.Forms.Label lblIP; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TextBox txtSenderAddress; + private System.Windows.Forms.TextBox txtSubject; + private System.Windows.Forms.TextBox txtMessage; + private System.Windows.Forms.TextBox txtTo; + private System.Windows.Forms.TextBox txtCC; + private System.Windows.Forms.TextBox txtBCC; + private RemObjects.InternetPack.Email.SmtpClient smtpClient; + private System.Windows.Forms.CheckBox chkUseAuth; + /// + /// Required designer variable. + /// + private System.ComponentModel.Container components = null; + private System.Windows.Forms.Label lblUserName; + private System.Windows.Forms.Label lblPassword; + private System.Windows.Forms.Button btnSendEMail; + private System.Windows.Forms.Label lblFromEmail; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.GroupBox groupBox3; + private RemObjects.InternetPack.Messages.MailMessage msg; + + public MainForm() + { + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + msg = new RemObjects.InternetPack.Messages.MailMessage(); + } + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.chkUseAuth = new System.Windows.Forms.CheckBox(); + this.lblUserName = new System.Windows.Forms.Label(); + this.lblPassword = new System.Windows.Forms.Label(); + this.txtUserName = new System.Windows.Forms.TextBox(); + this.txtPassword = new System.Windows.Forms.TextBox(); + this.lblSenderName = new System.Windows.Forms.Label(); + this.txtSenderName = new System.Windows.Forms.TextBox(); + this.txtSenderAddress = new System.Windows.Forms.TextBox(); + this.lblFromEmail = new System.Windows.Forms.Label(); + this.lblSMTPServer = new System.Windows.Forms.Label(); + this.txtSMTPServer = new System.Windows.Forms.TextBox(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.lblRO = new System.Windows.Forms.Label(); + this.lblIP = new System.Windows.Forms.Label(); + this.txtSubject = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.txtMessage = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.btnSendEMail = new System.Windows.Forms.Button(); + this.txtTo = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.txtCC = new System.Windows.Forms.TextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.txtBCC = new System.Windows.Forms.TextBox(); + this.label8 = new System.Windows.Forms.Label(); + this.smtpClient = new RemObjects.InternetPack.Email.SmtpClient(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.SuspendLayout(); + // + // chkUseAuth + // + this.chkUseAuth.Location = new System.Drawing.Point(16, 48); + this.chkUseAuth.Name = "chkUseAuth"; + this.chkUseAuth.Size = new System.Drawing.Size(279, 24); + this.chkUseAuth.TabIndex = 2; + this.chkUseAuth.Text = "My outgoing server (SMTP) requires authentication"; + this.chkUseAuth.CheckedChanged += new System.EventHandler(this.chkUseAuth_CheckedChanged); + // + // lblUserName + // + this.lblUserName.AutoSize = true; + this.lblUserName.Enabled = false; + this.lblUserName.Location = new System.Drawing.Point(19, 27); + this.lblUserName.Name = "lblUserName"; + this.lblUserName.Size = new System.Drawing.Size(63, 13); + this.lblUserName.TabIndex = 0; + this.lblUserName.Text = "User Name:"; + // + // lblPassword + // + this.lblPassword.AutoSize = true; + this.lblPassword.Enabled = false; + this.lblPassword.Location = new System.Drawing.Point(26, 51); + this.lblPassword.Name = "lblPassword"; + this.lblPassword.Size = new System.Drawing.Size(56, 13); + this.lblPassword.TabIndex = 2; + this.lblPassword.Text = "Password:"; + // + // txtUserName + // + this.txtUserName.Enabled = false; + this.txtUserName.Location = new System.Drawing.Point(88, 24); + this.txtUserName.Name = "txtUserName"; + this.txtUserName.Size = new System.Drawing.Size(232, 20); + this.txtUserName.TabIndex = 1; + // + // txtPassword + // + this.txtPassword.Enabled = false; + this.txtPassword.Location = new System.Drawing.Point(88, 48); + this.txtPassword.Name = "txtPassword"; + this.txtPassword.PasswordChar = '*'; + this.txtPassword.Size = new System.Drawing.Size(232, 20); + this.txtPassword.TabIndex = 3; + // + // lblSenderName + // + this.lblSenderName.AutoSize = true; + this.lblSenderName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.lblSenderName.Location = new System.Drawing.Point(34, 19); + this.lblSenderName.Name = "lblSenderName"; + this.lblSenderName.Size = new System.Drawing.Size(64, 13); + this.lblSenderName.TabIndex = 0; + this.lblSenderName.Text = "From Name:"; + // + // txtSenderName + // + this.txtSenderName.Location = new System.Drawing.Point(104, 16); + this.txtSenderName.Name = "txtSenderName"; + this.txtSenderName.Size = new System.Drawing.Size(232, 20); + this.txtSenderName.TabIndex = 1; + this.txtSenderName.Text = "John Doe"; + // + // txtSenderAddress + // + this.txtSenderAddress.Location = new System.Drawing.Point(104, 40); + this.txtSenderAddress.Name = "txtSenderAddress"; + this.txtSenderAddress.Size = new System.Drawing.Size(232, 20); + this.txtSenderAddress.TabIndex = 3; + this.txtSenderAddress.Text = "alexanderk@remobjects.com"; + // + // lblFromEmail + // + this.lblFromEmail.AutoSize = true; + this.lblFromEmail.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.lblFromEmail.Location = new System.Drawing.Point(65, 43); + this.lblFromEmail.Name = "lblFromEmail"; + this.lblFromEmail.Size = new System.Drawing.Size(33, 13); + this.lblFromEmail.TabIndex = 2; + this.lblFromEmail.Text = "From:"; + // + // lblSMTPServer + // + this.lblSMTPServer.Location = new System.Drawing.Point(6, 19); + this.lblSMTPServer.Name = "lblSMTPServer"; + this.lblSMTPServer.Size = new System.Drawing.Size(160, 15); + this.lblSMTPServer.TabIndex = 0; + this.lblSMTPServer.Text = "Outgoing Mail Server (SMTP):"; + // + // txtSMTPServer + // + this.txtSMTPServer.Location = new System.Drawing.Point(168, 16); + this.txtSMTPServer.Name = "txtSMTPServer"; + this.txtSMTPServer.Size = new System.Drawing.Size(168, 20); + this.txtSMTPServer.TabIndex = 1; + this.txtSMTPServer.Text = "ws7.elitedev.com"; + // + // pictureBox1 + // + this.pictureBox1.BackColor = System.Drawing.SystemColors.ActiveBorder; + this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image"))); + this.pictureBox1.Location = new System.Drawing.Point(502, 16); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(88, 80); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.pictureBox1.TabIndex = 14; + this.pictureBox1.TabStop = false; + // + // lblRO + // + this.lblRO.AutoSize = true; + this.lblRO.Font = new System.Drawing.Font("Verdana", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.lblRO.Location = new System.Drawing.Point(465, 104); + this.lblRO.Name = "lblRO"; + this.lblRO.Size = new System.Drawing.Size(124, 23); + this.lblRO.TabIndex = 3; + this.lblRO.Text = "RemObjects"; + // + // lblIP + // + this.lblIP.AutoSize = true; + this.lblIP.Font = new System.Drawing.Font("Verdana", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.lblIP.Location = new System.Drawing.Point(416, 136); + this.lblIP.Name = "lblIP"; + this.lblIP.Size = new System.Drawing.Size(173, 25); + this.lblIP.TabIndex = 2; + this.lblIP.Text = "Internet Pack"; + // + // txtSubject + // + this.txtSubject.Location = new System.Drawing.Point(104, 88); + this.txtSubject.Name = "txtSubject"; + this.txtSubject.Size = new System.Drawing.Size(464, 20); + this.txtSubject.TabIndex = 11; + this.txtSubject.Text = ""; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.label3.Location = new System.Drawing.Point(52, 91); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(46, 13); + this.label3.TabIndex = 10; + this.label3.Text = "Subject:"; + // + // txtMessage + // + this.txtMessage.Location = new System.Drawing.Point(104, 120); + this.txtMessage.Multiline = true; + this.txtMessage.Name = "txtMessage"; + this.txtMessage.Size = new System.Drawing.Size(464, 136); + this.txtMessage.TabIndex = 13; + this.txtMessage.Text = ""; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.label5.Location = new System.Drawing.Point(45, 123); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(53, 13); + this.label5.TabIndex = 12; + this.label5.Text = "Message:"; + // + // btnSendEMail + // + this.btnSendEMail.Location = new System.Drawing.Point(456, 264); + this.btnSendEMail.Name = "btnSendEMail"; + this.btnSendEMail.Size = new System.Drawing.Size(112, 23); + this.btnSendEMail.TabIndex = 14; + this.btnSendEMail.Text = "Send Email"; + this.btnSendEMail.Click += new System.EventHandler(this.btnSendEMail_Click); + // + // txtTo + // + this.txtTo.Location = new System.Drawing.Point(392, 16); + this.txtTo.Name = "txtTo"; + this.txtTo.Size = new System.Drawing.Size(176, 20); + this.txtTo.TabIndex = 5; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.label6.Location = new System.Drawing.Point(366, 19); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(23, 13); + this.label6.TabIndex = 4; + this.label6.Text = "To:"; + // + // txtCC + // + this.txtCC.Location = new System.Drawing.Point(392, 40); + this.txtCC.Name = "txtCC"; + this.txtCC.Size = new System.Drawing.Size(176, 20); + this.txtCC.TabIndex = 7; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.label7.Location = new System.Drawing.Point(365, 43); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(24, 13); + this.label7.TabIndex = 6; + this.label7.Text = "CC:"; + // + // txtBCC + // + this.txtBCC.Location = new System.Drawing.Point(392, 64); + this.txtBCC.Name = "txtBCC"; + this.txtBCC.Size = new System.Drawing.Size(176, 20); + this.txtBCC.TabIndex = 9; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.label8.Location = new System.Drawing.Point(358, 67); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(31, 13); + this.label8.TabIndex = 8; + this.label8.Text = "BCC:"; + // + // smtpClient + // + this.smtpClient.AuthPassword = null; + this.smtpClient.AuthUser = null; + this.smtpClient.ConnectionClass = null; + this.smtpClient.ConnectionFactory = null; + this.smtpClient.HeloDomain = "remobjects.com"; + this.smtpClient.HostAddress = null; + this.smtpClient.HostName = ""; + this.smtpClient.Port = 25; + this.smtpClient.UseAuth = false; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.lblSMTPServer); + this.groupBox1.Controls.Add(this.txtSMTPServer); + this.groupBox1.Controls.Add(this.groupBox2); + this.groupBox1.Controls.Add(this.chkUseAuth); + this.groupBox1.Location = new System.Drawing.Point(8, 8); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(360, 160); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Server Information"; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.txtPassword); + this.groupBox2.Controls.Add(this.txtUserName); + this.groupBox2.Controls.Add(this.lblPassword); + this.groupBox2.Controls.Add(this.lblUserName); + this.groupBox2.Location = new System.Drawing.Point(16, 72); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(336, 80); + this.groupBox2.TabIndex = 3; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Login Information"; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.lblSenderName); + this.groupBox3.Controls.Add(this.lblFromEmail); + this.groupBox3.Controls.Add(this.txtMessage); + this.groupBox3.Controls.Add(this.txtSenderAddress); + this.groupBox3.Controls.Add(this.label5); + this.groupBox3.Controls.Add(this.txtSenderName); + this.groupBox3.Controls.Add(this.txtTo); + this.groupBox3.Controls.Add(this.label6); + this.groupBox3.Controls.Add(this.label3); + this.groupBox3.Controls.Add(this.txtCC); + this.groupBox3.Controls.Add(this.txtSubject); + this.groupBox3.Controls.Add(this.label7); + this.groupBox3.Controls.Add(this.txtBCC); + this.groupBox3.Controls.Add(this.label8); + this.groupBox3.Controls.Add(this.btnSendEMail); + this.groupBox3.Location = new System.Drawing.Point(8, 176); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(584, 296); + this.groupBox3.TabIndex = 1; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "EMail"; + // + // MainForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(600, 478); + this.Controls.Add(this.groupBox3); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.lblIP); + this.Controls.Add(this.lblRO); + this.Controls.Add(this.pictureBox1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "SMTP Client Sample"; + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.Run(new MainForm()); + } + + private void chkUseAuth_CheckedChanged(object sender, System.EventArgs e) + { + lblUserName.Enabled = chkUseAuth.Checked; + txtUserName.Enabled = chkUseAuth.Checked; + lblPassword.Enabled = chkUseAuth.Checked; + txtPassword.Enabled = chkUseAuth.Checked; + } + + private String getRequiredValue(TextBox ctrl, String errorMessage) + { + if (ctrl == null) return null; + String result = ctrl.Text.Trim(); + if (result.Length == 0) + { + ctrl.Focus(); + throw new Exception(errorMessage); + } + return result; + } + + private void btnSendEMail_Click(object sender, System.EventArgs e) + { + // Clear lists before sending + msg.To.Clear(); + msg.Cc.Clear(); + msg.Bcc.Clear(); + try + { + msg.To.Add(getRequiredValue(txtTo, "Value of field 'To' is required")); + + String cc = txtCC.Text.Trim(); + if (cc.Length > 0) msg.Cc.Add(cc); + + String bcc = txtBCC.Text.Trim(); + if (bcc.Length > 0) msg.Bcc.Add(bcc); + + + smtpClient.HostName = getRequiredValue(txtSMTPServer, "Host Name is required"); + smtpClient.HeloDomain = "remobjects.com"; + + smtpClient.UseAuth = chkUseAuth.Checked; + smtpClient.AuthUser = txtUserName.Text; + smtpClient.AuthPassword = txtPassword.Text; + + msg.From.Name = getRequiredValue(txtSenderName, "From field value is required"); + msg.From.Address = getRequiredValue(txtSenderAddress, "EMail field value is required"); + msg.Subject = getRequiredValue(txtSubject, "Subject of letter is required"); + msg.Contents = getRequiredValue(txtMessage, "Content of letter is required"); + + + smtpClient.Open(); + smtpClient.SendMessage(msg); + smtpClient.Close(); + MessageBox.Show("Email has been sent successfully!", "SMTP Client Sample"); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Error during sending letter.", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } +} diff --git a/Samples/C#/SMTP Client/MainForm.resx b/Samples/C#/SMTP Client/MainForm.resx new file mode 100644 index 0000000..5b171cf --- /dev/null +++ b/Samples/C#/SMTP Client/MainForm.resx @@ -0,0 +1,736 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + iVBORw0KGgoAAAANSUhEUgAAAMsAAADICAIAAADJDYLKAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8 + YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAlwSFlzAAAevwAA + Hr8BQ/1smgAANPxJREFUeF7tnQl4VNXZx+f7uq+pba221S/a2tqv/Vq0VltrNXbRVquCiAtucUFQXFhU + BEHCoiAIJGyyE1ZDEAgh7FsgCfsSAoQQlgQS1oQkhCVh7/ebOczx5N47d+69M5ON+z736UNjMsud3/zP + e97zLp5q19w7EMk74Inkg7uP7d6BapewLyAoLy/f6rMZfouPj//oo48+rGm9FevVq9eAAQOSfTZt2rQt + PuNxXLLkHbhyCduzZ8+KFStmzpzZt2/f7t27v+i3F/wW67Pn/facz55V7BmfPe2zln57ym/vvvtuXFxc + UlLSkiVLsrOzr1jmriDCDh48CFJTp07t0aPHy357yWdhx+tJvz3hs8d99vbbbw8ePHjx4sXFxcVXDnCN + nDCoWrly5dixY997771WfqsTvFoo9thjjyGHCQkJVwJtjZMwVsDPPvusZ8+erVu3fsVn9QovCGvut0cf + fbR9+/ajRo3auXNnoxS2RkXY3r17AatLly6AJaz+4wVhzfzG16DxodYYCDt06NCiRYvef//9Nj5roHg1 + 9dsjjzzCOs4WpKioqBGoWsMmbPv27cOHD3/VZ40GLwh72GcPPfQQ0ZCMjIwGzVmDJKyiooL7jmgJthor + XhAmjK3uggULysrKGiJqDYww2Jo9e3bHjh0lW40er3/77MEHHyTeMXHixAbHWYMhDLZSU1Nh67XXXrsC + 8YIw7IEHHiDoMWHChAbEWQMgTLBFuBK2rnC8IOxfPiPekZiY2CA4q++EEZOErbZt27p4Sbwg7J8+I6Y2 + ffr0eu6c1V/C2Cd+8MEHsOXiBVt6vCDsfp9xNrphw4Z6y1l9JIz41ogRI15//XUXL+F7meB1n986dOiw + f//+eshZvSOMZfGdd95x8ZKuvRW8/uEzQrbjxo2rb5DVI8I48yEZC7ZcvBzgBWF/9xlHTzt27Kg/nNUX + wpAuwVb9wYt4ujByDD9XDOdaWP/+/ckAI7cMI4jA/k5zpK2eOaqHQmrUXoZVZdwrFLz+5jfSSeoJZHVP + GF7XoEGD6hAvzjFJYp08eTJxc779+/bt+49TO3r06LZt29LS0sh37datGz64ONSuZbz+6rO33nqrPnhm + dUzYxo0bhddVy+rFcjxr1iyevaSkxClOlv7u1KlTa9euJdO1a9euSFctqJfA616foYvLly+vWzGrS8L4 + oku2amFxRFSmTJmCSllC4z//KTt/fs+ZM7u5qs+sOHFi/vHj8yoquOaWV6SVly+tOL6zqmrn6aq806dL + z52z+JgUAYwePRp1idDiqOIlIIuJiSHVsQ4hqxvCCNOrK2NE8SKoRm6PuVYdOn9+U3X10lOnJlVUjCor + 63To8DsHD7194GDHAwfaFxe3Kyp6a3/RG/v2v16477WCwlf3FrTZs7f17j2tdu1+edeuF/PzX9iZH5u3 + s01+/keF+wbtL5px9Oj6ysrCqioT7I4cOcIBKzUl4swxLL6XIV4Qds8993B2fuzYsTrhrA4IY8/Ipx5p + 9eIkALBKS0sNP+aKCxfzzp5dcOrU+OOVH5SWdi0pef9oSZcjR987fMQZXs/tyHs2d8cz23Nbbtv+1LZt + T27d+nhOTostWz7YvXtccfHaiopT588bvhJQS0lJ4W5YjHuJwITcOUrX3gQvCLv77rt5/Nzc3NqHrLYJ + y8zMVB2vSKjXmDFj8vLyDD/OnefOLaqqGnK8smdZeY9jZd1Lj0UUr+bZ2Y9uzm62afMjGzc9vGHjm9tz + R+3fv7q83PC1kfk9cOBAdqPyUEhG7WVY1TFeEIb95S9/4YS3liGrVcLAS5Wu8OLFAQDrzunTp/Wf39Zz + 52acrhpQeaJ3xfFe5RV1gtdD6zc8uH79A+vW/Wvt2ubrN/TfvTurrEz/Uk+ePMmulgq6SOAFYRiJ2rUJ + We0RRs5JhPBCFGFX/2ntv3BhXnV1/MmTfSpPfHS8sp7gdf+aNfetXv33Vav/lrXq4TVr+ubnb66o0L94 + lnjqkcKoXgKvu3xGkK/WIKslwiKEF1WveraqL13aeO7cqNNV/U6e/PhE/cXrr5lZMRmZ92Rk3L1yZYs1 + a6cVF584p/XV4IwS4NAXRxUvCPvzn/9MIVbtQFYbhEUCL+SQNVHz1T9+8dKCs2eHnq7qf+pUA8LrrhUr + /pyefufy5f9YubJnbu5B3SaUqhBituJQyLprL30vPV4Qduedd5IQVQsbzMgSpo9KhMX3omRN429VXrq0 + 8Oy5gThbp083ULz+uGzZHUuX3r5k6W2Ll8Rt26bhDP+MFOow4gVhf/rTnxDISEMWQcLAS55khytq369f + P05CVOmCrUXnzg2qqm40eP1+8eJbFi1qsnBht61bD9TcuBw+fJgsnaCBiaDqJfASFmnIIkhYeBfHN954 + g9Nxla0zly6tPH8+ofpMo8TrdwsW/Hb+/N/Mm9c3N7ey5pkBp/AcCYiQvYjai7CqiHvZwgvC/vjHP3J+ + GjklixRh4cWLLAZN7DT7woWRZ842erx+PXfur9LS/rBgQeLeveq3i0WzXbt2YcELwu644w6SRCLk+EeE + sPDipZGukkuXks6dH3zF4HXznLRfpM65KTW1ZWZW7vHjKmeIGYdOoaiXwEsYOUiRgCz8hBE1Dlfcq1On + Thqva92Fi0POnrsC8fr57Nk3pqTcMGtWfM2T+127dtGdytniqOJ1u89YK8IOWZgJC2PUfujQoeqGsfTS + pWnnzl/heEXPnHn9jBn3L1myXQnSnjhxgh2VOBQKFJhQXXvhe+nx+oPPSGoKL2ThJIwj7XCpl2Zl3HLx + 4jAXLx9e133++U+nf/7j5Omj8/PVFZOc29Dxus1na9asCSNkYSOMVNWwHGm/+eabmzdvlveODeP8Cxdc + vIR6SbyuTU7+0bRpz2dkHD97Vt4r7hunmeJciJiqCKtaVy+BF8bOlEzdcEEWHsL0oS9nhWjgpTperIyT + zrt4zTLE6+qkpB9+9tkts2dvVZI1cMvIzggFLwj7/e9/T5NQNu9hgSw8hGk2j87wor2q6njlXbw01sVr + lhleP5g69aopU6KTk6fs2SOVDLeM1Axn6iXwwm699VaiIfWFMI13Hxa81l+8ONzFywJe35s8+buTJn1n + 4qSPsrM1kMmovblrLxdHFS8Iu+WWWxCO0CELVcPw7kMv5dCo1zIXL19gwmRxFOol8fr2hInfnDDhFSWF + CSUj+UdAZrJzNMELwjAKC0KELCTCNO5X6OqFX+/iJeJedvH6RmLi18ePfzkjo+LMGSlmBBRDwatJkyYk + 3IbokIVEmFosFBa8pl9wF0fneH113LivjB1726xZEjKUjINtGbUXYVUR9wqqXuAlrE+fPqHImHPC6I0T + YsaEujiiXi5eoaiXwOvLY8b89+jRt8yYoULGwTaQOcPrdz4jEdIxZA4JY32U9ULO1AsBlztHFy95KORs + cVTx+q9RozyjRjX5/HMNZA7US+CFEf5wvFY6JEyuj87w0sS9ZruLYwi+lx4vz8iRnhEjmi5YIB0yZqOQ + VWZrcZR4/dZnnTt3diZjTgiT66MzvGgWr4ZVXdc+XIujUC+Bl+fTT7lily2TkDFzhGwf6X7JuJcITAiT + vpcGr//z2apVqxxA5oQwkbnqGK+srCz5trPcwEQE1Evg5Rk+3DNsWLuMDHm358yZIwhzgNdvfvMbCp9q + gzDRhskxXrRQk2+YqL0bVnUcmAi0OKp4QZhn6NBEJeGHTlXO8IIwjNlydiGzp2E4+MRXHePF5lHideCS + i1eogQmxc9QvjkK9BF6eIUO40ouL5Z1n1hMro/XFUbAljOhaQUGBLcjsEcYxgmO8SLSXmdCUb7hnjrWg + XgIvz+DBUSNGFFZWCsgIkpFMZtH3UvH6tc+YoxgpwsjPcYwX3r2ak5Psnjk6jdpbXxwlXp6EBK4mU6ZI + GaN3tRXXXo/X//osJyfHOmQ2NIy+HY4bj6vuV+YFN53Q4aGQY7w88fGeQYPapadLyEiYNt85BsILwjgn + CD9hRCgc46UGV/devOSmE4YrrKoGJvS+F4ujUC+Bl/caODBl9265VtLvCcgMAxMmeP3KZwQELEJmVcMQ + MMdTOYjEiHdF7H60mwxtlK0q0gkNMybEkXaI6iXw8gwYEDV0aEV1tfg4WCud4XXzzTdzEhVOwhAwx3hR + 0CGVed75C24phz4ZutbwgjDPJ580TUmRnwg19EAmovYirBpUvcBLGHmBViCzpGFCwBxMRGP/KA8f914M + WyHa8JJS680vA3Un1LSPU/t7qQ2Y1A45soWJ2mNCNgGQVdqijFbWOYb9zNHB4uhly4eXp39/rpRduwRk + lZWVnDk6wOuXv/zl008/HR7C2EI6w4s6PlkyxPo4Khx1jt0OHV5/6vS6U6cs9lZ18RKLo4qXp1+/qIQE + uVbSwsiueoGXMCsyFlzDxo8f70C9wIvmXlKNM85fCL2Mlml3VRcv8pg9Dx6y0rrXxcsQL8/HH3O1W7JE + fjovvPCC9cVR4vWLX/yC8HtQGQtCGEF8Z3hBmGymWnLxUoh4fXD48Laqy/7p7upqFy+LO0e9egm8PH37 + evr0yT5yREC2fv16SZgIq4q4FyZ2jtL3UvGCsJtuuok0enPIghCGhDqbpY0LKb8iM86eC6WFyYSyciFd + woYcORq08birXibqJfDyfPRRzOTJ8q6STwVkdvGCsKC9FIMQJqe1t/EZA1qEcbaFMaRJGCF7YUgXRvd2 + KWDFFy86xivuyBEKQ+Vd4B/Hzp938dKcORrGvczVC7y814cfpvtH7Bw4cMABXhDG2YBzDSNeIgTMLl4f + f/yxxGLcmbPO+ntNLK8hXeIBJ5ceMx+b4KqXFfUSeHl6944eMkR+Ul26dLG+OMIW9nOfkY5qApmZhg0f + PtwBXqqA5V644ACvHkePbq8pXeIunL540cVLzZgIRb3Ay3v16pW4ZYu4vciYRd9LxQvCKBB3QhhBCmd4 + qQI2tvqM3eaXzH1RvS4mT8kv2dyKCpOhL6562VIvgZenZ8/ohARVxoK69hq8fuYzE38/oIZRXmJ3cUS9 + VAHbfv6CLbx6Hi3ZXv1FrR9vmykpNGGT7//tffsDzRRy8XKGF4R5evRI9NeLr1u3znznaIjXjTfeyDiw + QDIWkDCa+tty7QVeBEgkEMlnzljvDD1ZJ13koiG/QCYecPWJky5emoQc9UjbomsvF0cvWz68PHFxTUaM + kJ8aleIiNqEPTATCC8JoLGWPMEpTHOBF4E7OTyi6cMEiXr1KSnOVMmUhXRw3gZeaE9t1f5HhRDRXvUJR + L/DyXt27pxcUCMjoUGcXLwi74YYbsrOzDSEz1rCFCxdaD0wI9QIv0nvkKeSCM2et9LWffPy4xusS0oU9 + 8cQT6f58pvyqKhcvka2qT8gJRb0EXp4PPmg6daqUMUpFhIARUxVhVRP1EnhhgdrAGhNGGM1i3EviBWEM + RROv8vjFi0Hx+rD0mEa6iCzzaBIv6k3kex5w8KB+nqOrXmFRL/DyXt26Ffr7kDGT2i5e0dHRdLiwqmEs + kQ7wgjBZBZl19qz50Jepxys10kUNjGBLqBc2b948QVjRmTMuXpFTL4GXp2vXOH9xZXFxsS31Ai9hhjtK + Aw1buXKllai9ql7gxcRQKTkjT50ONLJqYFl5wdkaM4uRLo4BNHjxgDJOkXjkqGYarate4VUv8OKK/uQT + +Qk+/PDDFhdHiRf/MJwraEAYOYNBD4U0eMXGxk71L+RHLlwIhNf8k6f00vXUU09p8Hr88ccTExPFu2WE + totXhHwvqV5ewt5/39OlS7p/LgT334rvpeL1P//zPyiFfqE0IIw55+Znjnq8IEwukXOrqvXzHAeVVxTU + HJ3CkRQvyBAvCDt69KggLPVYmTpL21WvSKiXwIsrdvp0cdtZKOWhEFF7EVbFqRcmXHsNXhB2/fXXByeM + WcAO8OrYsaMU2EG0d685LnT+KWPpCoTXsGHDxKOdvnDhzT175Kh2F6+I4uXp3DlKKZlmoRTHjtbxgjBc + LA1kWg1bsGCBScaEoXrRl3aKvxZv3/nzKl4JFRWFOukiBQO2AuHVokULygIEYVmVlS5eMqwaRQ23LltV + zfeSGROGYVUZmNAvjl4B69zZ8957nk6dUvw3nwYCdvG67rrr9FNFtIQNGTIkUEJOILwgbIe/M8Liqmo5 + 7HjBqdPVSl4XnjsbRsGWCV6EVaQcvru34IWd+bF5O131Qr3iN22KW7VK5tqTDB12vCCsXWqquP/EvW2p + F3hh9FoPomFkohnme5ngBWGSiaGVJxjVnlBx3ES6TPBCwDgaE4+26eRJFy9Z54h6RY8ezW3JPnq0SWJi + hPDyvPtudJ8+8tOkmtKK78XiKPDCyM4wI4xImAO8EvyH8xUXL4LXwtNVeulq2bJlUPUCL0pO5Nvru7/I + VS9Z5yhKOWSNUFxmpkyGDsviiHqBl/d6553CsjLxKRC0Curaq3j91Ge0j1Ahq7FKoh/6bFVz9eKglJMs + 8YIKz53nkojwDzaMPKBFvCBs+fLl4s/3V1e7eGnwogotJilJ3t7C48djpkwJO16et9+OX7lSPAu9IMx3 + jnq8IIy4VUDCmFiuSYYOiheESSdMZQuviypL2LKOF6utjLKOPnTI9b0Md46Apd7n+HXropA3fzqhmjFh + 0bVX1Qu8PB07Nh0/XjwFrphJYMIQr5/85Cddu3YNSBjJg2quvRW8aJKhvmHxb6SL1dYWXghYcnKy+POS + c+dcvAIFJtotXaq54YUVFTGTJol0QpmQ4xgvCIvu3Vs+BRXhhnGvQHhB2KOPPhqQMFqYyFIOi3hxSqq+ + 4ZKSEild1tULvNiDyCjrlCNHns3d8cz23Jbbtj+1bduTW7c+npPTYsuWK7lKW7r2UYMGyWLaGmK2dm0U + u0tfvlcoeHk6dPC0by9dMY5bJGHEVEVY1QSvH//4xyT/BCTMLl4I2GSlImr+/PlSuuzipba3iC8q+rCw + sHdBYa+Cgp5798bt4drTfffuD3bt7pa/6/38/Pd37uycx5XXaUfeu7k73snNfXt7bsft2zts29Z+69Z2 + W7e+lZPz5pacN7K3vJ6d3Xbz5tc2b35106Y2Gze23rjxFfR1/fqXOQxdt+7FtWtfWLM2ds2a51evfm7V + 6mdXrXomK+vpzKyWmZlPZWQ8mZHxxMqVj69Y0SJ9xWPp6c2XL3902fJmy5Y9snTpI0uWPMS1ePG/Fy1+ + YNGiBxYu/OeCBVz3zZ9/37x5f583729z5/41Le3etLSYOXNiUlMvX7Nnx3ClpMTMmsUVPWGCs4ScxJwc + /brh9YMRM7aZ/oQc87iXfnFEvQReXClbt4qniI+PF4RZxAvCMIRGQvaFp88EG1GIZlG9wAsjhutd10pK + evfuTSMDsTLaxQsBo3ej4V1r3D+Mz852cOYY7T/z4OZQbigLBy8zsXp1FKVE/owJeeaohlXN8fK0axfn + 75ROFrtdvK699toVK1YYE2YXLwjDzRfHAKHgxfCHxk2Sybtrt2KFg2RoWeeYkpJCnaM8ZxNPRKZXzNix + ImNCnjnKqH1QvDxvvRXjr3JjXq4t9QIvjDMeA8JmzJhhS73AiyZS+GGwFQpezZs3p+COoayBDHE1MTo+ + mtsWUyP319yI7pjbJlPbGMAIPQoamNyhhlU1LUwCRe2b+ndFNM+hey9xTvxrzaY+ftWqKNx/35G2Lbwg + LDouTn4rrPheYnEUeF1zzTV9+/YNSBiJWcJIl8CIIAgjKoGJlVEYeGGh4wVhGDcIa+a3pn57xGecwmIP + KfZvnz3oN9r5YaRZCmMYsbD7fUYjeGH/8NvfffY3vzEvA2OggTQmEWP3+Iy+usLCMqpdTjqmY6/4FGOm + T3dwpI3XJf6cyVayzlF1Zy+L2ejRdvHyvPmm5403JGEMQzV37TV4QRjFvQaEwZ2LlyAs0ngJyFC3y4Ql + Jzs40o5bsUL8uaiklXWOfDe1YpaZGYX7r0TtRdxLde3xvZAu7+XDiyvb3yCdZCp5KCSi9hhRCUywpaoX + eGG8BgPC+Cq46lVreDE2QS6UMYIwf/s4i2eOUUrrBmoaNHWOZDCozh/Rhxim0fgOhazg5Xn99XR/F7sO + HToIwiziBWEsOwaEkQZdV4sj74FAsGpskYShtxhjm1Sjpbs0YngYvcpUo2xTGG9KGBls0ng6ae19xkxr + 1cjBFEYegDCq61RTxx6y+Mp5xxbHhaKRMiUYDuitahcvEfdSK2n1DZj4tqxdu1blLD4jI4qU/GDqBV6e + tm3j5s4Vf0uA0xZeP/KZAWF1hReu19ixY9Ub0bD+TcTI+rBjzpIZLqS+wcRt25zh5a2k/fRT+VC4lIZ1 + jkSR2A3IX/OKGRND/HEv/eIo8PK89ppKmHX1EnhdffXVBoQ5cO0JsU6cONE834t4vTCCXpjw6zWuPdsF + tT9FwyJs7ty5QWdpI1rkxsllUb5BUnGiqIL09Va1uDhqovaykpY600BV2jy7RswSOc0k5VDne0m8IKyd + P6Oa4lkrvheLo8QrIGF2d46i5F+WaMtCNHxDTLJljhcahmO4TJlTt/XECe9VeSKnslJcW7iOH+fKFldF + xWb/tam8YlN5OddGcZWVbfBf68vK1h87xrXOd60tLRXXGnGVlKz2X6uOHhVXFtcRriOZ/ivj8GGuHH9C + i57+pKQkk8VRL1riETjAbrd8eYh4EVaNnTlTPCBCJSppAxWi0SZCFbOKqqqmY8aorr2Kl+fVV2MGDhSP + vHr16qCuvQavH/7whzKsfzmmz9fLLl5Il2wqgQLxHRJ1jg7wgjCKA+SH91Zubv3pDM2h0BT/lAMVL3oT + UYHM18MQL1xd3Be9aPEIidu3O3PtA505wop4Yfim5nWOBFyIoKrvIiUnJwr3n82jf3FEvcBLQ5j5zlGP + F4RRry8WysuEEbR0EPeShIkXLeaG2FUvEfniIyGsKh5nSWnpg+vXP7Bu3b/Wrq2rxuPRycmdN2zYd/Kk + XrRYFtlbBPK92F7IZgjq33pFKz09Ern2cf5sC1FJG7TOUStmp083Ze6pz/eSeHnatGFdF6+fwaUmgQlD + vAISZjesqiGMV4OYUWpnxfcSi6PEC8IYjCre0snz51ts3FhXeD2zYuXUPXv0YCFaOPWBdo68ETocGYtW + bq43puqfyuEgMGGeMRHdv798tcTArdQ53nXXXVox27Ilij2mT73Ay9O6tUpYoLhXILx+8IMfGGiYXbzU + 1ku7arYs5Bibr7KJa6/HS0Ttj/i7I4/ct6+W1evW1NR+W7fuNxIt+hsQniC0LaMSqmvPO+WgV08k0/Y4 + c4xiVq1/ppCDsKrFfK9Ef/CWAQbW6xzpTnJcyWesQMwY3+zDy/PKK9GdO8s3ZRhWNcErIGF2D4WkhiUc + Phx/6PCxmoVrZBQa7hwD4cWZEDIg3tXhM2fuW73676tW/y1rVaSncjyfkTFPGfApbyuiRQkCh06wpceL + d2EoWpwzJiJaM2ZoRlZFDi8OhWJGjZIvm52j9TpHBoLgyNfwzLKzo9hjvvKKp1UrlTB55iii9uZ4QRgJ + 8Vo/zC5eqoaBF+3j2hcULvUflokXh5gR57R+5shjynfVPS8vonjdnpb2ybZt+5UunvKpES3iq9x9TI8X + oV1D0couKYldvDiK0LluIlpE8RJ1jtn+o3QHdY5xcXFaMaM28+WX5Q2xi9f3v/99M8KsH2nL0tlBBw/J + /l79i4tpNqF+LUidsH6kvcQ/q4KoRITU68WsrAUHDujXtV27diFanJfjpujx4i0QmJDH1fLPvaK1Y0eT + pCRn6YThKuWIVbItHNQ5su5rxWzzZvEei4qKRMaERfUCLzPCrONFYEISxr5cbcDUdtfuRTUDSIgZhzNW + MiYQD/nhPbl+/T0ZGXevXBmWkVV/mj9/YG5ukU60gAbR4kgDsIRp8GLzRYaOnsjs0tLYpUujSF7wDzuu + E/USR9pRH3wgwxa4hnbrHEVMlXJoVczEW2YvaRevgITZwouohCRswIED+g45HxXuKzl7Vv1g0AARmDBP + yCF0Iv5q/pEjYcGr44YNC/2LiPp6EC02sGT7kJajx4vFHfU1Fq28vCbTpoW98XiIpRwcO4p3h3/soM5R + hFXvuOMOkFLvkkqYGrUnJCEMr0uYUC/sqquu0q6S5eXldvFSCetffMCwQ06rHXnzSo+pL5cmZpxnm+d7 + EasUf3Li/Pl/ZWX9OT39zuXLHQzc+11aWvyOHZU1Kfc+7IkTnHeRbmmS70VgQi9a4idIV/qBA19cxcXp + mquoKF1z7d+frrn27SNP9YursDBdfxUUcCj0xbV3L92Xalx79qQrV7ay9BMPd1CIJuNe5AFIMZOEWcfL + gDDcfgfphFLDhh48aNLC5N38XYX+uLP4kFJTU7kFMqFQn04owxYJu3c7xiu35raD50W0SFIiVzFoOiEH + xoEIaxA/J78+UAMmi+mEHEDxPZSrpC28vve97xmcfDvIVpUVjrOPHQvaY2LaoUPqZwNDJOQAmWG2qmzm + c6iqyoF63TBrFuoln+7kyZPcLOpcrGerEh1sECSZvEjmktotRNPHvciH4NbZxSsgYXYrhSRhKfi8Fjrk + tNuxY+/p0+pNYfYbOwZ9MjTRWvlr7+bk3L5k6W2Ll9iaRlvs9+gpVOHxHSRDi8HT5tNbzRveqgX0aqWg + zCU2TPmUOeuaGLhwY+RSIxRBVkjItiCy8YwkzEEphwPXnpURgy0sKirKQMM4N7VbiMZwU8HBxhMnrFdp + T6kZKUDMeGog0OTay/m66UdL7OLFLG0JqDO8gibkiExojGxV7FafMahMGF1rhDke1e5gbIKDHhMOjrQD + ufYqXjBtQBhnCHbrHGWvrx2nTtmq0m67deuemlEDqrI4zVRLOdQ+PP/OyLhl0aImCxdan6UtCXOgXi5e + VqL26s5RxQsBQywMCPvkk0+sNGBS0wllnSNRCQdNACYWFWk8M8LlaqUQdWjiF6bs22cLr59O//y4fwtJ + DnTYK4Vc9TLBKyBhxH4cZKtKRJz1mGi1OTu7okYzGRZHTjNFIRrQi8c/ce7cXUuW/nb+/N/Mm/fruXN/ + lZZ285y0X6TOuSk19eezZ9+YkoJrHz1zJovjdZ9/Dl4/Tp4+TSkiD1TwaFLpGKjOUf05DWACGS3czY1G + WiZGSqq5kRlhbgToMcbeWtw5Oojai7iXRr3AC1Pnf3/RVUAQpm88bp7vJbOfe+4tcNzCZPCePYS+1K0f + E41EnSPbQPHzrjk51vG6Njn5tjlpUsYa+q7Q2euHsNDxIkJuElY1xOu73/0usR6DVZIlyS5eRL0p1Bbv + f3hRcSgdcpqvWUMmtHorCfRxFDhp0iTxwwOnT1tUL/D60bRpVycl3Tt/gWE2jrMPrMH9lYM6RzVjggSN + kSNHkhZqxbUX0oWBF4ZXbUAY0Uh16IvFZGhZOTP9yJHQGzANys9nQVTF7FOlnIYOOUEXR4nXDz/77AdT + p141Zcrdc+c+SHucRYv+5euQc7+vQ84/AnXIke1x/B1yYmbO9F4zZnivzz/3XtOne6/kZO81bZr3Skry + Xp995r2mTvVeU6Z4r8mTvdekSd5r4kTvNWEC7XEuX+PHx3CNG0ePicvXmDExo0d7r1GjvNfIkZevESNi + Pv3Uew0f7r2GDfNeQ4d6ryFDvNfgwd4rISHF77lyXG23jFbFC+kSAxI+4zYaHQoFUi/w+s53vkPVhQFh + /MhBKQe5IgKI7SdPhqW/18OZmZRyGArGksOHzX0vPV7fmzz5u5MmfWfipG9PmPjNCRO+kZj49fHjvzpu + 3FfGjv3ymDH/PXr0f5FAzEXKDcl3ZAtyUexF7goXZYz14Ejbehkt7g9ZhOLWOSijFWFVEhiRLnn/OeLT + nzma4wVhAfuH4aDZLeUgY0K+mkc3ZzfbtPmRjZse3rAxxFKO/nl5lTVTgMSzxCxeHMi1v8LxopSjnX+e + MEeKdqu0BV4EezW5FeQoaI60g+JFL6CAhPXr18/i4qjme0nC2u/ICwteIjDxz/R0CtE0YjZ+zx7DnaOL + F6Uchf7b5aBKG+kSp5Aas4sXAkYcICBhLLoOKoWksz+mqCh09dLEvbpkb1HFjH+TMaEJTLh4gVfsxIkC + DkQIFbFVpa2XLvFQWVlZ5nEv6doDlrBvf/vbpNMFJIxOWrbKaEVGIVyKF7SmvCLExdEwrHrb/PmLlVPz + tzduVONeLl6iEC09P198ChwWW8dLL13publc4qFGjBgh872CLo4CL4zu9wEJKysrc1CIhjMoXhBVaJGr + c2y9dm2lbzDl9ooKGVZ18RJ4xQwaJFc3jrwsNgEgb6JGev6pU+0mTvQ89VRhSYl4NEo3TMKqIjChqhd4 + fetb3yIoEZAw/sOrr75qpQmAWudIOqF8e69v2xa5OsffzpkjUlUfS19B1N7FS5bRyiCFxR4TFO5qvC50 + K5rOYU89Fa1ksTvAi6N0Fa8var7lT4k+OKhzlOWdsw4djnSd40urVo3Oz3fxknhFd+0qv+GoQ9AeE3St + 0koX7fhbtgQvz5NPtpswQTxaZmZmoEOhQOqFgOHHByGMY8FAHXJM6hxlzjEZE7VW50jUXoZVr6i4l6bH + RKK/4DFoC5M//OEPmhz8lA0bolq1knhBWHZhoSCMtm3WfS/YEkaxVhDC6H1gt4yWPFXS6+TX6JmNmyJa + 5+iql9pjIqpDBxllJTZpku/VrVs3jXQ1HTjQy5ZfvcAr6oUX5OdIZptMJ9QcCul9L4HXN7/5TVIBghDG + f27Tpo2D1r2UeIgXN6ygIEJ1jq7vpe+QE5eWJm47x0SB8DKTLgUvzxNPtPOPV+c40gFeJGFr8DLww/gR + Qme9SltUc5Brz5x68VYPV1fHZGSGsc7R3TmK1r16vOgxIaOsZIMadsgxkC76aDz9tEa9wMvz+ONyiaSE + QiRDW1cvBAwv0BJhxNkcNB6nOEwK7EubNoWlztGNe8nO0IZ4xfolh+WPTsGaDjnG0kVDigB4RbdtKz9B + cvzt4gVhtF2yRBhRMetNANRKIVmWPv/w4bBUabvqZaJedMgpLC0VTNCjRYMX6cdarwvpeuaZQHh5WrSI + 97cGZhfpAK9vfOMbzD6zRBi/xKQPk51joDpHtZL2/sxMZ3WO+mxV17XXt49T+3tBGIXashCNSRSaDWPi + ypVRNGwyxQvCKvyVE1RI2FocUS/w4jXo8TL2w/gpgX8rPSb0UzlkSmrvHTuc1Tm6Z44mvVVl+zi6L6Xv + 3CkEjORkiZfMOxf/ieh8zIcfetkKhlesfxoXykelkJpOaLJzhC2BF0bEygZhxCw0UXuLQ19IbhTvjUra + O5YudVDn6Ppe5r6Xvn0ck3QgjP+VKQjiI4hfuPCydAXDy/PYY+nbt4u/IuXTAV5f//rXNYdFxhmIKoMs + lEFbmIhKWrXOkVN66S2+s2WLgzpH1/cy9728veNeeSXR37yEBRG8aKUub/tl6WIA4LPPWlEv8Irp3l3j + 45tE7UXcS1Uv8Lr99tsNBSzgKsl/4ITL2cgqWUlL73FbVdquellRL03/S9gykC46ZVrGyytg/mILmiAb + JuSII20ZVtXgBWGBlkgzwthROpuIRs2j/EIwjdZunaMbVjUMTIjFUTS/jEtNVRVL/tvrdSFdzz1nC6/o + Nm3kI+BY21Uv8MIMd5FC1b6oZtOrHHtDZwP3ZCUtGffWq7RFnaO7czTcOUq8ot58Ux4TqZx5vS6kyyZe + nubNU/yzjwhSOMOLg+xAS2QQwihYNWnAZDLPkXx/+eaZpW2rzlEUorlH2mrjcYkXvVVjx43TCFj2/v0x + ffp42bKPV0y3bvLRcKkN871MFkfU62tf+9rMmTMdEsafEakP1IDJfFyolLG8ykq7dY4uXoHwgjAZZRVk + xKWkeJ5/3hlenkcflR5YRkaGM7xINTPBK4iG8Z/B09k0Wo7P5ZeDXHsHdY5XckKOGvcSvhdscTUdOlTe + VaSrCePiQ8Crad++qoBZjHsJx0uoF0Yf65AIw9837O9lZdgxu1HxBijfINfepMeE63uZ+14CL89LL6Xn + 5X0hXbGxoeDladas8OhR8WhpaWnO8IIwEx8/uKcvfoMQnD7uZWWWNs3TZIg/cc8et87R0769+cA9k8UR + vGL69YOGy9IVMl5xSUlSwGigIiuFggYmpHqBF06UuYAFXyX5DRKP9O3jLI5qZ2DbFzvh5cvdM0fNLO2g + gQmpXp4XX0zMzPR6XbAVMl7RrVvLU0ga2zrD66tf/WqgOL6KnVm0Qv4ep10mO0c65Ajj4EIYh68YU1ux + w4cPC8hyK467Z44WzxxV3wv1Ai+uKMqKwoEX66N08Pft20d1uCxEMw+rquoFXlYEzJKGCRkz3zkGwgvC + aKAgZWxQbq57KGTlUEi69hIvzwsvhAuvdsrMa/p5OcPLooBZJYzf69+/vxXfS1Uv8Pqrz+i8LSG7b/Fi + N6xqa3H0Clj48Ip+5RW5PgoHX5TR2lIv8KLHcVAPzKqnL34PGbPoe4nFUeJ17733ElGTayX1tL+cNcsN + q2rGhaqHQqrvFV68PE2bZvtbQ1ZQ2Hzddc7w+spXvmLFA7NHGL9N3xTR/FJ0JzTxvVS8IAxjxoSUsaSC + Ajdqr06jrTW81P0j66NjvKwLmI1VUsgY+fsO8IIwhh6qa+Uba9aI9nFuWLXW8IpRCneHDx/uGC9bAmaP + MH6bVH+76iXwwug6jrQKJaPD6j3z5rl41Rpe0a1aSfeLwWTUhTvwvWALo6jJogdme5XkDwjxM5PCZOco + XHuxMgqTeEEYJedy1BkdVq+fNu0K6U5Yh649vldUy5bZ/mpW3C9mGzrGi56uQYP4Gv4sxcPUv2E8rGHc + S+97afASTe2pVZcOWU5Z2ZXQ/LJu8fI88kji0qXyngv3y+7OUagXNm7cOFsCZnuVFI9OiEsTVrWIl5iI + xquUb3jy7t2Nu7dqvcKLYZqh4MWnbBcvh4TRpdgw7hVocdRPRGMmrYRs0q5djbV1b53jFZuQIO8z4+5C + wevLX/4yMyhqiTCeBh3Sx70Mfa9AA/ek188teGnlysbXGbpe4SW8e8eLI3jZdfAli7b9MPmXrVq1srU4 + ioG0ctgxUQ8VshdXrGhMjccbGV4Mn3OgXk72kurT7Nixw3znGHRcKJAdUhq0xqanN46+9vUKLzaPNA4O + Rb0cr4+hEsbfU21s3ffSj2q/8847GdKpjmqPXb68oY9NqG94cZNDxMvx+hgGwngIWjbq415B1Yu3jdHT + FtNAFrd+fcOdytH48AplfQwPYceOHSORXw2r2sJLQEbPY1XJEvPyGuLQlzrHK87fdp7NE4tj6OrFuIb8 + /HzHHlh4CONR2MSKQ6FQptGyaeDNyK11yt69UYwbYqzQ4MGehATvFR/voT8RF70hBwzwXp984unf33v1 + 6+f5+GPv1bevh7ouqlK56AjSu7f36tXL07On9+rRwxMX5726d/dQQ8HVrZuna1fv9f77ni5dvFfnzp73 + 3vN06uS93n3X+kyhOsdLDauSVxg6Xl/60pc4Sg4RL4fxMP2zjhkzJhS8aAyEaSDLLimJHj/exUt0J6T7 + kvd67DHv1bw5hWikqnqvmodCfEVDD0zg2oMX/k/oeIWNMB6IafChDztmc5qeni6VrOLMGVIyXPUywatJ + +/aFR47IO0bZY4hxL4EXvnVY8AonYThkpA2JoJca9+KcFVNde+F7/dFnQr0wmrdgNIbE5Biby5Vbq1e7 + i6Oheqn50Nyr0KP2Aq+f/exndo+3TXB0HnHVP2hubi6Z1iHiJYa0U+ep+v7pRUVRTHt0fS//4hj19NOy + 34T4HlICHeKhkMCLOSDOTocCQRZOwngOIDOMewndCqpeAi9hZAEc9M2YEVZRXd2UWi7XtW/WjFxCWUzL + ncGvDyUhh4wJ2BJ4hcu7V2kLM2E8dGpqKpA5WBxVvAjDYOweli9fLiHjHym7dkWxr7xSd45RzzwTP2eO + ekOo5gglnVCD19ixY8PlfoXhXNLkpQCZXd9Lj9etPrvlllsoc1JXTMSs3dKlV2BgommfPqp0EfHq1KlT + KMnQGrxoXx12vMLp6WteHL35ZNTe1uIo1EviBWEY9cCMM1G/u+n79jGq/QqJe1GiLWtoxU1gz0grgDDi + xSTASOAVQcJ46B49etj1vQzxauI3ip0qKytVzhJzcqLZATTesGrUs89qlkUhXY7rHGW2qup7RQ6vyBIm + ITMJTJgsjkK9JF4McsLYqy5btkyFjH/HrVgRRVi/cUXto557Lm7aNFm+Id4yXpfoYtIg1Ctsp0bm6krc + IVDcyy5eEMY0FIyOCXn+PkdypxmXnh7NyVLDPxQyZIsNI66C4xYmVGnXvnrVEmE8DZDpw6qO8fo/n/FV + ZvrzgQMHNHqWuHlzNAeXDfPMMfq11+LT0jS6xbLI4GLH/b1gqw7xivgqKeVt8uTJatQ+dLwgTNiwYcM0 + zhnMpRcUxM6c2YCOtJv265eybp3m28J4DlovXX/99ZHAK1zHjkH3B+GPhwV6SsaFiEOhMOJF9iaGQFIk + p9eziqqq+KysJiRo1NeMCcahMa9KjUEIyKi16du3L2w5660q+l+aqFck4l61FNM3J3rJkiXk+UjCzHeO + wrWXvpdcHKV6Cbz+12+Mv2MqIrndGiXg/xaWl8dnZjbBS6sfCTlNOnVinLYc5qi+YEaHisFVzhqPC7ZM + 8OJQqDbxqr1VUpLHAAsBWdjxgrCbfcZEJhoc65dOL2plZYkbNsQmJUWRE9axo6dDB2/ny9CaX4r2cUEb + MEXR53fAgPh585iroP8OsCBOnTqVXE5nM4XU1r3meIX3zDHoElkHhPGUpaWlTz75pD6sqglM2FIviReE + /dJnQExCkRyBo/9Qsw8cSFy3Lnbq1Cacdb71VijdCQPh1aRLl9iRI+PnzzeUK/GSmJOAaMlxaJFTL4LY + oSesWkFK8zu154dpnpjpwOJQyDDuFSJeEEafd+ymm27iWQhRmqAmPuns4uKUnJy4efNiJ02KiY9vQpbs + a6+Zt+5V1Sv67bcZm9A0ISFu1qzEjIx0o8VapZwJQviOYlq7s3GhDNzTNB43US9iqmFMyLHFWZ0Rxqtk + LSPVRx9WDSNeEIb93Gc8ESku48ePJwFEL2mBfsKIFyY5Glx5efQe18xPMH9Y/PcRI0ZQlHDDDTdcddVV + Dka160dWaXqrGgYmatnxqi8aJl4Hji0NecT66MC11y+OqnqpeEEYiXXYjT7juVq3bs1EsTVr1hh6bNYR + NPlNvCuGpnPYBVU8KV42Blu1hhfvt/Ydr/pFmHDL2JnXJl582KgIFu030o0YTMFB6qBBg5hWjqlDsi3S + BkwYmSBdu3ZlcCKrM7U6wgRbtYxXHa6MKmR1uUqqr4NJnHzM4lDISmDCsXrp8QIzHG2M+BNGd1Nh5F1h + NEtj9hjWXDF6QQqjeuWaa675kc+u9hkdtoTVIV489YwZM2x5S5H75fpCmBAznN96hReE/cRnYpA2dq3P + oEpafcOrWbNmdeXUG2JajwgTrw8xQzZMwqq1pl4NDi+8rvojXZK2ekeYELPBgwcDmT5q7+IVKGpPd4l6 + JV31mjDx4goKCtq2baseCrl4GeKFL2i9u33k/K1Aj1wfNUx9rezOaJ0CWy5eerw4t+Cot/ahsfWM9Z0w + 8WamT59OObg4dhSHQhbjXnZ3jg3F9yK856Bpry0ywvXLDYMwyRnV7lc4XoSRGwpb4lNrSISJV5ycnMxs + VHHmaBK1b3zqxZrYsNhqqISJ101KAsc+8sxRcyjUyPAi1rt06dJwLVu1/DgNT8PUG7R3796BAwdSGq6e + OTYavAjqtm/fvj7vE63A2rAJk++QBv0dO3YUp9qaM0fzQ6H66dpzSEXiiZXPr/7/TiMhTNzokpKSpKSk + l19+WR5pNyy8WrRoQXJR/QycOka5UREm74JAjSWGU85AR9r1RL046+Qr0fjAkp9F4yRM/cJlZ2eTUcO6 + o2ZM1DlenL327NmTZhyOtaGh/GHjJ0xDG1mmlAoy+auWMyZo4cEUlYSEBLoiNBQ4wvI6ryzCNLeMSYZ0 + pvz444+ff/55kl7CmJBDfw2sV69eEyZMuNKQ0tzkK5oww+/o5s2b6VaMUW+tGqNbH3roIQrOMEK+H9Y0 + OulhGzduDMv3vjE9yP8D62HvPiT5lLoAAAAASUVORK5CYII= + + + + 17, 17 + + + + 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 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + YP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xMTP9LS0v/SUlJ/0dH + R/9GRkafRUVFDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpqah9paWmfaGho/2Zm + Zv9kZGT/Y2Nj/2FhYf9gYGD/Xl5e/1xcXP9bW1v/WVlZ/1hYWP9WVlb/VFRU/1NTU/9RUVH/T09P/05O + Tv9MTEz/S0tLn0lJSR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAaGhoD2dnZ19mZmavZGRk72NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZWf9YWFj/VlZW/1RU + VP9TU1PvUVFRr09PT19PT08PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjYy9iYmJPYWFhf19fX39eXl5/XFxcf1pa + Wn9ZWVl/V1dXT1ZWVi8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD//wAA//8AAP/8AAA//wAA//AAAA// + AAD/wAAAA/8AAP+AAAAB/wAA/wAAAAD/AAD+AAAAAH8AAPwAAAAAPwAA+AAAAAAfAADwAAAAAA8AAPAA + AAAADwAA4AAAAAAHAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAABwAA4AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAP4A + AAAAfwAA/4AAAAH/AAD/wAAAA/8AAP/gAAAH/wAA//gAAB//AAD//gAAf/8AAP//4Af//wAAKAAAACAA + AABAAAAAAQAgAAAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISEPHx8fXx0dHZ8aGhrPGBgY/xUVFf8TExP/ERER/w4ODu8MDAy/CgoKfwgI + CC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApKSkfJiYmnyQkJO8hISH/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/w4O + Dv8MDAz/CQkJ/wcHB88GBgZfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAALS0tfysrK/8oKCj/JiYm/yQkJP8hISH/Hx8f/x0dHf8aGhr/GBgY/xUV + Ff8TExP/ERER/w4ODv8MDAz/CQkJ/wcHB/8FBQW/BAQELwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAANTU1DzIyMs8vLy//LS0t/ysrK/8oKCj/NjYf/1JSD/9dXQj/bW0A/2lp + AP9kZAD/YGAA/1NTAv9CQgX/KioK/xISDf8MDAz/CQkJ/wcHB/8FBQXvAwMDXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5OQ83NzfPNDQ0/zIyMv8vLy//Q0Mi/2xsCv9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/T08A/y8vBv8MDAz/CQkJ/wcHB/8FBQX/AwMDXwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + 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 + + + \ No newline at end of file diff --git a/Samples/C#/SMTP Client/SMTP Client.2008.csproj b/Samples/C#/SMTP Client/SMTP Client.2008.csproj new file mode 100644 index 0000000..8a61f23 --- /dev/null +++ b/Samples/C#/SMTP Client/SMTP Client.2008.csproj @@ -0,0 +1,114 @@ + + + Local + 8.0.50727 + 2.0 + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A} + Debug + AnyCPU + App.ico + + + SMTP Client Sample + + + JScript + Grid + IE50 + false + WinExe + SMTP_Client_Sample + OnBuildSuccess + + + + + + + 2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE;SUPPORT_SMTP_AUTH + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + bin\Release\ + false + 285212672 + false + + + TRACE + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Form + + + MainForm.cs + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/SMTP Client/SMTP Client.2008.sln b/Samples/C#/SMTP Client/SMTP Client.2008.sln new file mode 100644 index 0000000..e85158b --- /dev/null +++ b/Samples/C#/SMTP Client/SMTP Client.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SMTP Client", "SMTP Client.2008.csproj", "{87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/SMTP Client/SMTP Client.2010.csproj b/Samples/C#/SMTP Client/SMTP Client.2010.csproj new file mode 100644 index 0000000..e0e857b --- /dev/null +++ b/Samples/C#/SMTP Client/SMTP Client.2010.csproj @@ -0,0 +1,117 @@ + + + Local + 10.0.20506 + 2.0 + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A} + Debug + AnyCPU + App.ico + + + SMTP Client Sample + + + JScript + Grid + IE50 + false + WinExe + SMTP_Client_Sample + OnBuildSuccess + + + + + + + 3.5 + v2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE;SUPPORT_SMTP_AUTH + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + AllRules.ruleset + + + bin\Release\ + false + 285212672 + false + + + TRACE + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + AllRules.ruleset + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Form + + + MainForm.cs + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/SMTP Client/SMTP Client.2010.sln b/Samples/C#/SMTP Client/SMTP Client.2010.sln new file mode 100644 index 0000000..4c7e245 --- /dev/null +++ b/Samples/C#/SMTP Client/SMTP Client.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SMTP Client", "SMTP Client.2010.csproj", "{87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87FD38B4-EDF1-48CE-ADBA-64E9C0D1DF1A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/SMTP Client/SMTP Client.Sample.html b/Samples/C#/SMTP Client/SMTP Client.Sample.html new file mode 100644 index 0000000..4e6a04b --- /dev/null +++ b/Samples/C#/SMTP Client/SMTP Client.Sample.html @@ -0,0 +1,38 @@ + + + + + + + + + +

SMTP Client Sample

+ +

Purpose

+ +

+ The sample shows how we can use the SMTP client component for sending emails. +

+ + +

Examine the code

+
    +
  • Sending emails is pretty simple (see MainForm's btnSendEMail_Click handler). + We create and configure an instance of MailMessage.
  • +
  • Next we put the SmtpClient component onto the form and configure it.
  • +
  • After configuring these components we can send the message (smtpClient.SendMessage(msg);). +
+ +

Getting started

+
    +
  • Compile the solution.
  • +
  • Run the Smtp client application.
  • +
  • Specify the outgoing mail server (if your mail server requires authentication you + will need to specify login and password).
  • +
  • Create an email and send it
  • +
+ + + + diff --git a/Samples/C#/Sample Server/App.ico b/Samples/C#/Sample Server/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/C#/Sample Server/App.ico differ diff --git a/Samples/C#/Sample Server/AssemblyInfo.cs b/Samples/C#/Sample Server/AssemblyInfo.cs new file mode 100644 index 0000000..489393a --- /dev/null +++ b/Samples/C#/Sample Server/AssemblyInfo.cs @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Samples + + (c)opyright RemObjects Software, Inc. 2003-2004. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("RemObjects Internet Pack for .NET - SampleServer Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RemObjects Software")] +[assembly: AssemblyProduct("RemObjects Internet Pack for .NET")] +[assembly: AssemblyCopyright("Copyright RemObjects Software 2003-2004. All Rights Reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("1.0.1.48")] +[assembly: CLSCompliant(true)] +#if REMOBJECTS_SIGN_ASSEMBLY +[assembly: AssemblyKeyName("RemObjectsSoftware")] +#endif diff --git a/Samples/C#/Sample Server/HttpRoot/Styles.css b/Samples/C#/Sample Server/HttpRoot/Styles.css new file mode 100644 index 0000000..c8e0628 --- /dev/null +++ b/Samples/C#/Sample Server/HttpRoot/Styles.css @@ -0,0 +1,103 @@ +body +{ + background-color: #f7f7f7; + margin-top: 15px; + margin-bottom: 15px; + margin-left: 15px; + margin-right: 15px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + font-family: tahoma, verdana, sans-serif; + font-size: 10pt; + width: 700px; + color: #000000; +} +p +{ + padding-top: 0; + padding-bottom: 0; + padding-left: 0; + padding-right: 0.5em; +} +ul +{ + padding-top: 0; + padding-bottom: 0; + list-style-type: disc; +} +li +{ + padding-top: 0; + padding-bottom: 0; +} +img +{ + margin: 5px; + border-width: 0; +} +table +{ + background-color: #f7f7f7; + margin: 15px; + padding: 0px; + font-size: 10pt; +} +tr +{ + background-color: #f7f7f7; + margin: 15px; + padding: 0px; + font-size: 10pt; +} +td, th +{ + background-color: #f7f7f7; + margin: 0; + padding: 5px; + font-size: 10pt; +} +td ul +{ + padding-left: 2em; +} + +img:left { margin-left: 0; } +img:right { margin-right: 0; } +p.h1 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:13pt; + font-weight:bold; +} +p.h2 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:11pt; + font-weight:bold; +} +p.h3 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:10pt; + font-weight:bold; +} +pre +{ + margin-top:0px; + margin-bottom:0px; + margin-left:0px; + margin-right:0px; +} +.spaced +{ + letter-spacing:1px; + color:#000060; +} diff --git a/Samples/C#/Sample Server/HttpRoot/index.html b/Samples/C#/Sample Server/HttpRoot/index.html new file mode 100644 index 0000000..ffaee1e --- /dev/null +++ b/Samples/C#/Sample Server/HttpRoot/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + +

+ Sample Server +

+ + +

Purpose

+

+This example shows how easy it is to create a simple custom Http server. The + +SimpleHttpServer class is a file-based HTTP Server that will make all files in (and below) the specified RootPath folder available via HTTP. + + +

Examine the Code

+
    +
  • look at the ActivateServers procedure to see how to create and configure the HTTP server. The +RootPath property should reference the folder in our file system which we want to expose. +
  • +
  • +See the OnHttpRequest handler. There we can catch and process any incoming requests that come to the server. +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run application.
  • +
  • Configure server properties and activate the servers.
  • +
  • Click link on the main window to open home page of our server in web browser.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/C#/Sample Server/HttpRoot/ip.png b/Samples/C#/Sample Server/HttpRoot/ip.png new file mode 100644 index 0000000..ea014ca Binary files /dev/null and b/Samples/C#/Sample Server/HttpRoot/ip.png differ diff --git a/Samples/C#/Sample Server/MainForm.cs b/Samples/C#/Sample Server/MainForm.cs new file mode 100644 index 0000000..fa37187 --- /dev/null +++ b/Samples/C#/Sample Server/MainForm.cs @@ -0,0 +1,384 @@ +using System; +using System.IO; +using System.Windows.Forms; +using RemObjects.InternetPack.Http; +using RemObjects.InternetPack.StandardServers; + +namespace StandardServersTest +{ + public class MainForm : System.Windows.Forms.Form + { + private System.ComponentModel.Container components = null; + + public MainForm() + { + InitializeComponent(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.GroupBox1 = new System.Windows.Forms.GroupBox(); + this.lblUrl = new System.Windows.Forms.Label(); + this.nudCount = new System.Windows.Forms.NumericUpDown(); + this.lblCount = new System.Windows.Forms.Label(); + this.lblServerName = new System.Windows.Forms.Label(); + this.txtServerName = new System.Windows.Forms.TextBox(); + this.txtRoot = new System.Windows.Forms.TextBox(); + this.lblRoot = new System.Windows.Forms.Label(); + this.nudPort = new System.Windows.Forms.NumericUpDown(); + this.lblPort = new System.Windows.Forms.Label(); + this.lblLink = new System.Windows.Forms.LinkLabel(); + this.btnAction = new System.Windows.Forms.Button(); + this.txtLog = new System.Windows.Forms.TextBox(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.Label2 = new System.Windows.Forms.Label(); + this.GroupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nudCount)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudPort)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // GroupBox1 + // + this.GroupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.GroupBox1.Controls.Add(this.lblUrl); + this.GroupBox1.Controls.Add(this.nudCount); + this.GroupBox1.Controls.Add(this.lblCount); + this.GroupBox1.Controls.Add(this.lblServerName); + this.GroupBox1.Controls.Add(this.txtServerName); + this.GroupBox1.Controls.Add(this.txtRoot); + this.GroupBox1.Controls.Add(this.lblRoot); + this.GroupBox1.Controls.Add(this.nudPort); + this.GroupBox1.Controls.Add(this.lblPort); + this.GroupBox1.Controls.Add(this.lblLink); + this.GroupBox1.Controls.Add(this.btnAction); + this.GroupBox1.Location = new System.Drawing.Point(9, 48); + this.GroupBox1.Name = "GroupBox1"; + this.GroupBox1.Size = new System.Drawing.Size(526, 144); + this.GroupBox1.TabIndex = 18; + this.GroupBox1.TabStop = false; + this.GroupBox1.Text = "HttpServer"; + // + // lblUrl + // + this.lblUrl.AutoSize = true; + this.lblUrl.Enabled = false; + this.lblUrl.Location = new System.Drawing.Point(99, 120); + this.lblUrl.Name = "lblUrl"; + this.lblUrl.Size = new System.Drawing.Size(32, 13); + this.lblUrl.TabIndex = 8; + this.lblUrl.Text = "URL:"; + // + // nudCount + // + this.nudCount.Location = new System.Drawing.Point(139, 88); + this.nudCount.Name = "nudCount"; + this.nudCount.Size = new System.Drawing.Size(48, 20); + this.nudCount.TabIndex = 7; + this.nudCount.Value = new decimal(new int[] { + 5, + 0, + 0, + 0}); + // + // lblCount + // + this.lblCount.AutoSize = true; + this.lblCount.Location = new System.Drawing.Point(16, 90); + this.lblCount.Name = "lblCount"; + this.lblCount.Size = new System.Drawing.Size(115, 13); + this.lblCount.TabIndex = 6; + this.lblCount.Text = "Listener Thread Count:"; + // + // lblServerName + // + this.lblServerName.AutoSize = true; + this.lblServerName.Location = new System.Drawing.Point(59, 67); + this.lblServerName.Name = "lblServerName"; + this.lblServerName.Size = new System.Drawing.Size(72, 13); + this.lblServerName.TabIndex = 4; + this.lblServerName.Text = "&Server Name:"; + // + // txtServerName + // + this.txtServerName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtServerName.Location = new System.Drawing.Point(139, 64); + this.txtServerName.Name = "txtServerName"; + this.txtServerName.Size = new System.Drawing.Size(374, 20); + this.txtServerName.TabIndex = 5; + // + // txtRoot + // + this.txtRoot.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtRoot.Location = new System.Drawing.Point(139, 40); + this.txtRoot.Name = "txtRoot"; + this.txtRoot.Size = new System.Drawing.Size(374, 20); + this.txtRoot.TabIndex = 3; + // + // lblRoot + // + this.lblRoot.AutoSize = true; + this.lblRoot.Location = new System.Drawing.Point(76, 43); + this.lblRoot.Name = "lblRoot"; + this.lblRoot.Size = new System.Drawing.Size(55, 13); + this.lblRoot.TabIndex = 2; + this.lblRoot.Text = "&RootPath:"; + // + // nudPort + // + this.nudPort.Location = new System.Drawing.Point(139, 16); + this.nudPort.Name = "nudPort"; + this.nudPort.Size = new System.Drawing.Size(48, 20); + this.nudPort.TabIndex = 1; + this.nudPort.Value = new decimal(new int[] { + 83, + 0, + 0, + 0}); + this.nudPort.ValueChanged += new System.EventHandler(this.nudPort_ValueChanged); + // + // lblPort + // + this.lblPort.AutoSize = true; + this.lblPort.Location = new System.Drawing.Point(102, 18); + this.lblPort.Name = "lblPort"; + this.lblPort.Size = new System.Drawing.Size(29, 13); + this.lblPort.TabIndex = 0; + this.lblPort.Text = "&Port:"; + // + // lblLink + // + this.lblLink.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.lblLink.Enabled = false; + this.lblLink.Location = new System.Drawing.Point(139, 120); + this.lblLink.Name = "lblLink"; + this.lblLink.Size = new System.Drawing.Size(166, 16); + this.lblLink.TabIndex = 9; + this.lblLink.TabStop = true; + this.lblLink.Text = "http://localhost:82/index.html"; + this.lblLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lblLink_LinkClicked); + // + // btnAction + // + this.btnAction.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnAction.Location = new System.Drawing.Point(401, 115); + this.btnAction.Name = "btnAction"; + this.btnAction.Size = new System.Drawing.Size(112, 23); + this.btnAction.TabIndex = 10; + this.btnAction.Text = "Activate Servers"; + this.btnAction.Click += new System.EventHandler(this.btnAction_Click); + // + // txtLog + // + this.txtLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtLog.Location = new System.Drawing.Point(9, 224); + this.txtLog.Multiline = true; + this.txtLog.Name = "txtLog"; + this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtLog.Size = new System.Drawing.Size(526, 220); + this.txtLog.TabIndex = 20; + this.txtLog.WordWrap = false; + // + // pictureBox1 + // + this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image"))); + this.pictureBox1.Location = new System.Drawing.Point(9, 8); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(120, 30); + this.pictureBox1.TabIndex = 21; + this.pictureBox1.TabStop = false; + // + // Label2 + // + this.Label2.Location = new System.Drawing.Point(9, 200); + this.Label2.Name = "Label2"; + this.Label2.Size = new System.Drawing.Size(32, 16); + this.Label2.TabIndex = 19; + this.Label2.Text = "Log"; + // + // MainForm + // + this.AllowDrop = true; + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(544, 452); + this.Controls.Add(this.GroupBox1); + this.Controls.Add(this.txtLog); + this.Controls.Add(this.pictureBox1); + this.Controls.Add(this.Label2); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(550, 480); + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Internet Pack Sample Server"; + this.Load += new System.EventHandler(this.MainForm_Load); + this.Closed += new System.EventHandler(this.MainForm_Closed); + this.GroupBox1.ResumeLayout(false); + this.GroupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.nudCount)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.nudPort)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + #endregion + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + + Application.Run(new MainForm()); + } + + private EchoServer fEchoServer; + internal System.Windows.Forms.GroupBox GroupBox1; + internal System.Windows.Forms.Label lblUrl; + internal System.Windows.Forms.NumericUpDown nudCount; + internal System.Windows.Forms.Label lblCount; + internal System.Windows.Forms.Label lblServerName; + internal System.Windows.Forms.TextBox txtServerName; + internal System.Windows.Forms.TextBox txtRoot; + internal System.Windows.Forms.Label lblRoot; + internal System.Windows.Forms.NumericUpDown nudPort; + internal System.Windows.Forms.Label lblPort; + internal System.Windows.Forms.LinkLabel lblLink; + internal System.Windows.Forms.Button btnAction; + internal System.Windows.Forms.TextBox txtLog; + internal System.Windows.Forms.PictureBox pictureBox1; + internal System.Windows.Forms.Label Label2; + private SimpleHttpServer fHttpServer; + + + public void OnHttpRequest(object aSender, OnHttpRequestArgs ea) + { + AddLog(String.Format("Request to {0}", ea.Request.Header.RequestPath)); + } + + private void MainForm_Load(object sender, System.EventArgs e) + { + txtRoot.Text = Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location), "HttpRoot"); + txtServerName.Text = "Internet Pack HTTP Server"; + nudPort.Value = 82; + nudCount.Value = 5; + } + + delegate void TextCallback(string text); + private void AddLog(String line) + { + if (this.txtLog.InvokeRequired) + { + TextCallback d = new TextCallback(AddLog); + this.Invoke(d, new object[] { + Environment.NewLine + String.Format("{0}: {1}", DateTime.Now.ToLongTimeString(), line) }); + } + else + { + txtLog.AppendText(Environment.NewLine + String.Format("{0}: {1}", DateTime.Now.ToLongTimeString(), line)); + } + } + + private void MainForm_Closed(object sender, System.EventArgs e) + { + DeactivateServers(); + } + + private void nudPort_ValueChanged(object sender, System.EventArgs e) + { + lblLink.Text = String.Format("http://localhost:{0}/index.html", nudPort.Value); + } + + private void lblLink_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) + { + if (File.Exists(fHttpServer.RootPath + "\\index.html")) + System.Diagnostics.Process.Start(lblLink.Text); + else + MessageBox.Show(fHttpServer.RootPath + "\\index.html can not be opened, because it does not exists.", "Warning", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + private void ActivateServers() + { + AddLog("Trying to activate servers..."); + fEchoServer = new EchoServer(); + try + { + fEchoServer.Open(); + AddLog("EchoServer is active."); + } + catch (Exception ex) + { + AddLog("Can't activate EchoServer. An exception occured: " + ex.Message); + } + + fHttpServer = new SimpleHttpServer(); + fHttpServer.Port = Convert.ToInt32(nudPort.Value); + fHttpServer.RootPath = txtRoot.Text; + fHttpServer.ServerName = txtServerName.Text; + if (this.fHttpServer.BindingV4 != null) + this.fHttpServer.BindingV4.ListenerThreadCount = Convert.ToInt32(this.nudCount.Value); + fHttpServer.OnHttpRequest += new OnHttpRequestHandler(OnHttpRequest); + fHttpServer.Open(); + AddLog(String.Format("SimpleHttpServer is active on {0} port.", fHttpServer.Port)); + SetEnable(false); + AddLog("Servers activated."); + btnAction.Text = "Deactivate Servers"; + } + + private void DeactivateServers() + { + AddLog("Trying to deactivate servers..."); + if (fEchoServer != null) fEchoServer.Close(); + AddLog("EchoServer is closed."); + if (fHttpServer != null) fHttpServer.Close(); + AddLog("HttpServer is closed."); + SetEnable(true); + AddLog("Servers is deactivated"); + btnAction.Text = "Activate Servers"; + } + + private void SetEnable(Boolean mode) + { + lblPort.Enabled = mode; + nudPort.Enabled = mode; + lblServerName.Enabled = mode; + txtServerName.Enabled = mode; + lblRoot.Enabled = mode; + txtRoot.Enabled = mode; + lblUrl.Enabled = !mode; + lblLink.Enabled = !mode; + } + + private void btnAction_Click(object sender, System.EventArgs e) + { + if (btnAction.Text == "Activate Servers") + ActivateServers(); + else + DeactivateServers(); + } + } +} diff --git a/Samples/C#/Sample Server/MainForm.resx b/Samples/C#/Sample Server/MainForm.resx new file mode 100644 index 0000000..725885f --- /dev/null +++ b/Samples/C#/Sample Server/MainForm.resx @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + + + AAABAAMAMDAAAAEAIACoJQAANgAAACAgAAABACAAqBAAAN4lAAAQEAAAAQAgAGgEAACGNgAAKAAAADAA + AABgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1lcy5XY4MNKVD8AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACY5Rg4hJS+ONDhCuzs/S7skJzTAHiEwnjU5SGyEkaUYAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADSrmUBAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4lMFdSXWrGabHCdm+XuZJ6i67DZG+I1Rwd + KOgXGCLNGRoidSMnMxcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAODxVFJCYwix0fJ4sbHCWLICErjBkZIYEnKjN1IiMrdh0dJF8ODRBgPUBHYllc + YGQ/PDhTPzYpMdGtcQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsfKGB1hZLJN9z0ayif + +XUpVvx7Lz6Atz1EV+o6P0/rMDRA4xcYIrQREhldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkJi+WhYyVvZWYkJSNjXyRgYBvk4GCeKd+goO/eX2AyGZq + bslscHPLaGtuzWhrb89oa3DRPT9GyyIgI2TRrHcBAAAAAAAAAAAAAAAAyZjHAceW0AEAAAAAAAAAABET + Gl1+jJnDNdz0aSif+X4pVvx/KVD8fx01jqI9R2PULDNF2ScrN+MiJTDHDA4WgxAiPA4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbGh0kR0pTtKKgiJvRszVl0bM1btGz + NXDRszVyv6tQibupWJGvpG6krKJ5q6CcjMO8pneUjIqHxCYmLbvQqn4GAAAAAAAAAADKmsIDyZjHFMaV + 0Bi9mdkHnq3jARUXHy9ncn3AM9z0YCif+X0pV/yAKVD8gCpZ/YA7a+2KH0OaljVBXsIiJTLUQkhW1w4R + Gp8WOGESAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRoTJzU2 + O7aVknalyq8/etGzNX7RszV/0bM1f9GzNX/Rszt/0bJIgNKxV4DSr2R/tqODmU1OVdAbFhVSz6iLAgAA + AADKmsQGSkNTeCUkMMUzND/GMDI+wCEkLsI8QErlSoCQrSWP44MpV/yAKVD8gCpZ/YArYv2AK2r9fjRv + 4YEzQmK8Jyw51URNXskUHS53L5L+BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAA8MByUkJSm1jo+DuNGzNXDRszV+0bM1f9GzNYDRszuA0bJIgNKxV4DSr2SA0a1yf4SD + hcwnJiyqz6eQCwAAAAAjIypIPkBNyJCKor6nmMOPlp69lIihtoc4Q07EOkBM80VNXPE/T4TDJkrmhCpZ + /X8rYv1/K2r9fyxz/X5FgeSJSFJlzzc+Ss4lKTXGDilJIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZGBYlKy0ztIeIfbbKsD900bM1fdGzNX/RszuA0bJIgNKx + V4DSr2SA0a1yf7GgjaBDREzSQzc2RTEnKQ8WFx+lf4CQxcCVzX67mtx+oqrifIG+6W9d0+5dMqW5ZCo/ + VKtES1vvSFBm60pZg8o5Y9iGK2r9eSxz/X0te/14TInea3eKoZdPWmu+DyE5OgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKikrGUNHUJyEhn2zxas7c9Gz + NXzRszt/0bJIgNKxV4DSr2SA0a1ygNCqgH9wb3fTJiInmykmLGhWW2i+rZa1gMaV0nu7mtyAoqrif4G+ + 6X5d0u55Odv0ZSii+UVCXrdPSVJqs0VMXO0/SmbULVe4cyxz/WIte/1pLYT9STpusEtEV3CLETJXIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExN + Sw0/Q02baWpks6mfbJnRszt80bJIf9KxV4DSr2SA0a1ygNCqgICkl5GtNzdB2i4wO8mMj52zyZjGZ8aV + 0X67mtyAoqvigIG/6YBd0u5/ONv0fCig+XIpV/xdKVD8SFFqs4BFT2ndLDBA5DlXkWxnluFnlbPbg5yv + xXheg68iL5T+AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAA3NjUMRkpUmnF1c7e5o0d00bJIe9KwV3/Sr2SA0a1ygNCqgIDJppCFVllj7Ftf + a9+flqeYypnFdcaV0X+7mtyAoqvigIG/6YBd0+6AONv0gCif+X8pV/x9KVD8eCpZ/XU0ZvR9UmGCzj1D + UuVCSlx/fZi+Hy6L/QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJSMhDENIUppdYmnGn4pEetKwV3rSrmR/0a1ygNCq + gIDPp4+AbGZrx01RXvKckqSzyZnFfMaV0X+7mtyAoqvigIG/6YBd0+6AONv0gCif+YApV/yAKVD8fypZ + /X8rYv1/NG70fWZ6nLoWGCG1Bg8dGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8wNgxBRlCOQUNJvaOO + U3rSrmR60axyf9CqgIDPp4+AsZGNjjE0QfV2eYfmxJjChcaV0YC7mtyAoqvigIG/6YBd0+6AONv0gCif + +YApV/yAKVD8gCpZ/YArYv2AK2r9fkV75IZfbYTGDA4YlC6J/QUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAP0NOd11fY7bEpmZo0axyetCqgH/Pp4+AzqScgE5HUc5MT13xqJaxrsaV0YC7mtyAoqvigIG/ + 6YBd0+6AONv0gCif+YApV/yAKVD8gCpZ/YArYv2AK2r9fyxz/Xpsh7K3MjlIzRArUDQui/0CAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0NEjofISqXICEpagwLDS9obW4MAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAEJHUneFhIOk0a1zYtCqgHzPp49/zqScgIdqcZg4Okjwj42g08aV + 0YC7mtyAoqvigIG/6YBd0+6AONv0gCif+YApV/yAKVD8gCpZ/YArYv2AMGrwgzdSi6k4QVXQS158vBM1 + Y08ui/0FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABscJZd3fYa/cXZ8xEdK + UMQjJCvBKiszkUdKUGEZFg8m0bM1A9GzNQEAAAAAAAAAANKvXwFRVFtZVVNWxUpEQ8VqXFmvknd1l82h + qYBDOkjEdnaH26+Vv5y7mtyAoqvigIG/6YBd0+6AONv0gCif+YApV/yAKVD8gCpZ/YArYv1/NG70eyMr + PchXZX3HP1qDpSNlvS8uif0DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8O + ESMxNDyyi4yFsaSffJ6WmZLMgYWE0GhsbsxBQ0jHHx8kuB4dH4gdGhROVEcfE9GwWQImIB03Q0ZP2G5z + fu5lanXwSEtY7j4+St8tKjbVMjE92XNqg766mtx/oqvigIG/6YBd0+6AONv0gCif+YApV/yAKVD8gCpZ + /YArYv19OVSToCgtPc1jep+2Pm2zZC6G/RIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAwMjYkISEmqISHgrvKsEBxtqdel6uicaqupGyioZpyqoB/cb9WWFrPJygtyRMR + EX4aGBqIOTxG7ENHUvQ+QU31UFVi9W10gPV5fovsKyw332JgcdS6mtx/oqvigIG+6X9d0u5+ONv0fCif + +XwpVvx9KVD8fypZ/X9Db+WKHyU4yjpHYrp1kLidLYH9Hy6F/QMAAAAAAAAAAC+g/gEwpf4DMKf+AgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBgKDR8gJptzdnbBvqpTftGzNXvRszV/0bM1ftGz + NX3Aq1SNopt4rUFCR9AwMTjboZySqpWHeqWqnZitfnuC0Vpdaeo7PUrzREhU9WZoeuN+cZmnoqvigIG+ + 6X5e0u5zOdv0Wyif+VUpVvxfKVD8bCpZ/XUsN1nDIiY30n+No9JAVn1hFR0wQE1TYV4VGCNgExYghBYb + JpQcIi6vFRollQodLhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYkIQw3OkSPZGhpv52T + YpbGrDuA0bM1f9GzNYDRszt/0bJIfpmVhcMqLDTzppZ/odCqgH3Pp49/zqScgL2dp5HAnLOLcmF4tTg3 + ROk/Q1H1X2h723GlyYtf0u5qMHmLXxUiNIYeJ0uAJSxOpy03WrhCSFbuOT1K9VRcbeJCSVnDUlxsw1xn + eMZpd4nKcYGVzmaBnsBsk7a3XGt+zw8WIoEwrf4DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADRszUBQUVOd2Zqbb6XiUiI0bM1etGzNX/Rszt/0bJIf8uuXYVUUEvRaWlt2rqji5XPp49+zqScf82h + qYDLnbeAypnFgMaV0YBgVHK3Oj1L7EdRX+tNb4G8IScz3GVue+tsdYXjaXSLyWl8rZduhbyYZH+4oXGL + ubBpjMGkY43GomOQyKZTlNmWL5n+eS+g/n0wpP5+ZJ3JqCw0Qs0PJjk7MbH+AQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADg7RnZITFTEb2tPqamcXpvErU6D0bJIdtKxV3a9nlt8Liwv0oaH + i9C9opSFzqSce82gqX/LnbeAXUddqIJjjJW6mtx9kpjLhkVSY9I/RlT1NDtH9UlQX/BwhceNKVH8ZCpZ + /XArYv11K2r9eSxz/Xwte/19LYT9fi6M/X8vk/5/L5n+gC+g/oAwpf6AOKj1hF91i9EWIC+hMbT+DQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0Njs9SUlEbWBeTnqppY58urWef354 + ZXnSrmQ+fGdIVykqMb18gIjCup+ed82gqXRWRVKsQ0ZT8z5BTfBqWoGZoqvifoG+6X9GYnHHJio0+Tk/ + TPVHTl3xSGTOiSpZ/XkrYv1/K2r9gCxz/YAte/2ALYT9gC6M/YAvk/6AL5n+gC+g/oAwpf6AMKr+f16h + zqcvOkrVFj1XWDK3/gcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0bM1A9Gz + NQfRszsI0bJICNKxVwjSrmQI0a1yCVVGOhMqLDeZeXyFuoqAiowsLTjbOj1K82JndvCXfbOIoqvif3Wr + 04UuQlHJTVxq5D1EUfghJC/1S1Fg70xu1oErYv14K2r9fyxz/YAte/2ALYT9gC6M/YAvk/6AL5n+gC+g + /oAwpf6AMKr+gDGv/n9fhaLDGyU1vjG5/h8xuv4DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQwSXhkZIeEmKDP0TlJd9ZaX + qNiymtKGoqvif4G+6X9WvtiCQGBvxUBKWekzOEf1MjdG7E5Xa9RVeNOFK2r9eSxz/X8te/2ALYT9gC6M + /YAvk/6AL5n+gC+g/oAwpf6AMKr+gDGv/oBAru2LSFdo2A4pO4Exu/4RAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHx8mSCUm + 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 + /QMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoSfwDKVH8CCpa/Q5OddsfMF/JKCxz + /Sote/0yLYH9Hi6E/QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAK2v9ASx0/QQte/0HLYL9Bi6F/QEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///////wAA////////AAD///////8AAP///////wAA////////AAD////j//8AAP// + /wD//wAA//v/AD//AACAAP8AH/8AAIAAcwAH/wAAgABgAAP/AADAACAAAf8AAOAAIAAB/wAA8AAAAAH/ + AAD4AAAAAf8AAPwAAAAB/wAA/gAAAAf/AAD/AAAAD/8AAP+AAAAH/wAA/+AAAAP/AAAH8AAAA/8AAAAw + AAAD/wAAAAAAAAf/AACAAAAABj8AAMAAAAAAHwAA4AAAAAAPAADwAAAAAAcAAPwAAAAABwAA/gAAAAAD + AAD/AAAAAAEAAP//gAAAAQAA//+AAAAAAAD//wAAAAAAAP//BAAAAAAA//4OAOAAAAD//A8Af4AAAP/4 + HwA//AAA//gfgB//AAD/+B+AD/8AAP/8H8AP/wAA//8/4Af/AAD////wB/8AAP////8H/wAA//////// + AAD///////8AAP///////wAA////////AAD///////8AACgAAAAgAAAAQAAAAAEAIAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALXr9Ai2C/QIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////+D/7//AP+AH4B/gAgAH8AA + AB/gAAAf8AAAH/gAAD/8AAB//gAAPwcAAD8AAAA/gAAAB8AAAAPgAAAB8AAAAPwAAAD/8AAA/+AAAP/i + AAD/wwHg/4eA//+HgH//x8B////gP////n////////////////8oAAAAEAAAACAAAAABACAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAClX + /AIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAwAwMAQAUEAUEMCgRDGxYNNwAAAAAAAAAAZc/tARJE + T40SK2+rAgYQqgIHDiEAAAAAAAAAAAAAAAAAAAAADQsDVVVJFaLRszV20rJHek5BJrFPQDEZalBrIRUR + GFkKLTWiJFLehyFOxI0EDBvWBREeNgAAAAAAAAAAAAAAAAAAAAAEAwFRZlcZm9KyR3/RrW5/MCYifDAk + MJKtpN99PJuyewcSLq8PJVuuBxQs1AMJEHcAAAAAAAAAAAAAAAAAAAAAAAAAAAoJAjJmVyWb0a1uf0U3 + L790WHGWrKTfgEvR8X8oYPx6FTKAogEFCoQuj/4BAAAAAAAAAAAAAAAAFREFI9GzNQEAAAAACQgDMlZH + L6K2koSHQzNCv6yk34BL0fGAKWD8gCtl/X0HFSm/Lor9BAAAAAAAAAAAAAAAAAsJApdRRRScSj8SewwK + BGUMCgaYAAAA/w0KDO+spN9/S9Hxfilf/H4TK3CsBxYrmi6K/QEwpf4CAAAAAAAAAAAAAAAAIx4IhqCJ + KIqiijWNKyQYyc6ll35qUWSnJyYzzxU1PasLGEOYBhAo0Q0lSZ0XS3+ADS9JshVMbhwAAAAAAAAAAAAA + AAAYFAZIGBQHbCIcEXI4LSyaNyoyxHFymZUPHCHeAwcW4yZa3oUtfv1/L5b+gDCm/n8QPFaQMbn+BAAA + AAAAAAAAAAAAAAAAAAAAAAAADwsMMwAAAN86OEypEy81zQEDC9gMH0txFDpzkxhOhacqkt+GHWuYnw43 + SloAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAcAGBQUgh7voBgcfJM0KGUCrBg4lJS2B/QIvl/4LBRIbRwwu + QWsJJTKLAAAAAAAAAAAAAAAAAAAAAAUEAmIDAwKyAAAAAAAAAAADDxJ4JFHehA0fUKMMIEYWAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAz6eSAwAAAAAAAAAAAAAAAAYNKV8MHkuIBQ0cjwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC19 + /QEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//AAD/vwAABg8AAAAHGF+AB1m5wAdIfyAHNX8AAzyAgAFPgMAA + YoD4AICK+ABK0fMPOST7jxqM/++Uuf//23w= + + + \ No newline at end of file diff --git a/Samples/C#/Sample Server/SampleServer.2008.csproj b/Samples/C#/Sample Server/SampleServer.2008.csproj new file mode 100644 index 0000000..a0cba1d --- /dev/null +++ b/Samples/C#/Sample Server/SampleServer.2008.csproj @@ -0,0 +1,123 @@ + + + Local + 9.0.30729 + 2.0 + {81E7BAAA-3289-4806-8B37-25DD62A197C5} + Debug + AnyCPU + App.ico + + + SampleServer + + + JScript + Grid + IE50 + false + WinExe + SampleServer + OnBuildSuccess + + + + + + + 2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Always + + + Always + + + Always + + + Code + + + Form + + + MainForm.cs + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/Sample Server/SampleServer.2008.sln b/Samples/C#/Sample Server/SampleServer.2008.sln new file mode 100644 index 0000000..a0e5904 --- /dev/null +++ b/Samples/C#/Sample Server/SampleServer.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleServer", "SampleServer.2008.csproj", "{81E7BAAA-3289-4806-8B37-25DD62A197C5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {81E7BAAA-3289-4806-8B37-25DD62A197C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81E7BAAA-3289-4806-8B37-25DD62A197C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81E7BAAA-3289-4806-8B37-25DD62A197C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81E7BAAA-3289-4806-8B37-25DD62A197C5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/Sample Server/SampleServer.2010.csproj b/Samples/C#/Sample Server/SampleServer.2010.csproj new file mode 100644 index 0000000..880bf95 --- /dev/null +++ b/Samples/C#/Sample Server/SampleServer.2010.csproj @@ -0,0 +1,126 @@ + + + Local + 10.0.20506 + 2.0 + {81E7BAAA-3289-4806-8B37-25DD62A197C5} + Debug + AnyCPU + App.ico + + + SampleServer + + + JScript + Grid + IE50 + false + WinExe + SampleServer + OnBuildSuccess + + + + + + + 3.5 + v2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + AllRules.ruleset + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + AllRules.ruleset + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Always + + + Always + + + Always + + + Code + + + Form + + + MainForm.cs + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/Sample Server/SampleServer.2010.sln b/Samples/C#/Sample Server/SampleServer.2010.sln new file mode 100644 index 0000000..31b25e3 --- /dev/null +++ b/Samples/C#/Sample Server/SampleServer.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleServer", "SampleServer.2010.csproj", "{81E7BAAA-3289-4806-8B37-25DD62A197C5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {81E7BAAA-3289-4806-8B37-25DD62A197C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81E7BAAA-3289-4806-8B37-25DD62A197C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81E7BAAA-3289-4806-8B37-25DD62A197C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81E7BAAA-3289-4806-8B37-25DD62A197C5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/Sample Server/SampleServer.Form1.resources b/Samples/C#/Sample Server/SampleServer.Form1.resources new file mode 100644 index 0000000..5c80bd6 Binary files /dev/null and b/Samples/C#/Sample Server/SampleServer.Form1.resources differ diff --git a/Samples/C#/Sample Server/SampleServer.Sample.html b/Samples/C#/Sample Server/SampleServer.Sample.html new file mode 100644 index 0000000..2323841 --- /dev/null +++ b/Samples/C#/Sample Server/SampleServer.Sample.html @@ -0,0 +1,44 @@ + + + + + + + + + + +

+ Sample Server +

+ + +

Purpose

+

+This example shows how easy it is to create a simple custom Http server. The + +SimpleHttpServer class is a file-based HTTP Server that will make all files in (and below) the specified RootPath folder available via HTTP. + + +

Examine the Code

+
    +
  • look at the ActivateServers procedure to see how to create and configure the HTTP server. The +RootPath property should reference the folder in our file system which we want to expose. +
  • +
  • +See the OnHttpRequest handler. There we can catch and process any incoming requests that come to the server. +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run application.
  • +
  • Configure server properties and activate the servers.
  • +
  • Click link on the main window to open home page of our server in web browser.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/C#/Virtual FTP/App.ico b/Samples/C#/Virtual FTP/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/C#/Virtual FTP/App.ico differ diff --git a/Samples/C#/Virtual FTP/AssemblyInfo.cs b/Samples/C#/Virtual FTP/AssemblyInfo.cs new file mode 100644 index 0000000..1dcb484 --- /dev/null +++ b/Samples/C#/Virtual FTP/AssemblyInfo.cs @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Samples + + (c)opyright RemObjects Software, Inc. 2003-2004. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("RemObjects Internet Pack for .NET - VirtualFTP Sample")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RemObjects Software")] +[assembly: AssemblyProduct("RemObjects Internet Pack for .NET")] +[assembly: AssemblyCopyright("Copyright RemObjects Software 2003-2004. All Rights Reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("1.0.1.48")] +[assembly: CLSCompliant(true)] +#if REMOBJECTS_SIGN_ASSEMBLY +[assembly: AssemblyKeyName("RemObjectsSoftware")] +#endif diff --git a/Samples/C#/Virtual FTP/Main.cs b/Samples/C#/Virtual FTP/Main.cs new file mode 100644 index 0000000..6e7c859 --- /dev/null +++ b/Samples/C#/Virtual FTP/Main.cs @@ -0,0 +1,59 @@ +using System; +using System.IO; +using System.Configuration; +using RemObjects.InternetPack.Ftp.VirtualFtp; +using RemObjects.DebugServer; + +namespace VirtualFtp +{ + public class CmdMain + { + public static int Main(string[] args) + { + StartServer(); + Console.WriteLine("VirtualFTP running under "+Environment.OSVersion.ToString()); + Console.ReadLine(); + Console.WriteLine("Shutting down..."); + StopServer(); + Console.WriteLine("Down..."); + + return 0; + } + + static VirtualFolder fRootFolder; + static IFtpUserManager fUserManager = null; + static VirtualFtpServer fFtpServer; + + public static void StartServer() + { + string lDiskFolder = Path.GetDirectoryName(typeof(CmdMain).Assembly.Location)+@"\FtpRoot";; + + fRootFolder = new VirtualFolder(null, "[ROOT]"); + fRootFolder.Add(new VirtualFolder(null, "virtual")); + fRootFolder.Add(new DiscFolder(null, "drive-c", @"c:\")); + fRootFolder.Add(new DiscFolder(null, "disc", lDiskFolder)); + fRootFolder.Add(new EmptyFile(null, "=== Welcome to the FTP ===")); + + fUserManager = new UserManager(); + ((UserManager)fUserManager).AddUser("test", "test"); + + fFtpServer = new VirtualFtpServer(); + fFtpServer.Port = 2222; + fFtpServer.Timeout = 60*1000; /* 1 minute */ + fFtpServer.Binding.ListenerThreadCount = 10; + fFtpServer.RootFolder = fRootFolder; + fFtpServer.UserManager = fUserManager; + fFtpServer.ServerName = "VirtualFTP Sample - powered by RemObjects Internet Pack for .NET"; + + fFtpServer.Open(); + + Debug.Write("VirtualFTP 0.3 BETA - started up"); + } + + public static void StopServer() + { + fFtpServer.Close(); + } + + } +} diff --git a/Samples/C#/Virtual FTP/MainForm.cs b/Samples/C#/Virtual FTP/MainForm.cs new file mode 100644 index 0000000..0d2056f --- /dev/null +++ b/Samples/C#/Virtual FTP/MainForm.cs @@ -0,0 +1,227 @@ +using System; +using System.IO; +using System.Windows.Forms; +using RemObjects.InternetPack.Ftp.VirtualFtp; + +namespace VirtualFtp +{ + public class MainForm : System.Windows.Forms.Form + { + const int port = 4444; + internal System.Windows.Forms.Button btnStartStop; + internal System.Windows.Forms.GroupBox GroupBox1; + internal System.Windows.Forms.TextBox txtLog; + internal System.Windows.Forms.LinkLabel llShortcut; + internal System.Windows.Forms.Label label1; + internal System.Windows.Forms.PictureBox pictureBox1; + + private VirtualFolder fRootFolder; + private IFtpUserManager fUserManager = null; + private VirtualFtpServer fFtpServer; + + #region Boilerplate stuff + private System.ComponentModel.Container components = null; + + public MainForm() + { + InitializeComponent(); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (components != null) + { + components.Dispose(); + } + } + base.Dispose(disposing); + + } + + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.btnStartStop = new System.Windows.Forms.Button(); + this.GroupBox1 = new System.Windows.Forms.GroupBox(); + this.txtLog = new System.Windows.Forms.TextBox(); + this.llShortcut = new System.Windows.Forms.LinkLabel(); + this.label1 = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.GroupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // btnStartStop + // + this.btnStartStop.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btnStartStop.Location = new System.Drawing.Point(231, 8); + this.btnStartStop.Name = "btnStartStop"; + this.btnStartStop.Size = new System.Drawing.Size(75, 23); + this.btnStartStop.TabIndex = 18; + this.btnStartStop.Text = "Start"; + this.btnStartStop.Click += new System.EventHandler(this.btnStartStop_Click); + // + // GroupBox1 + // + this.GroupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.GroupBox1.Controls.Add(this.txtLog); + this.GroupBox1.Location = new System.Drawing.Point(8, 103); + this.GroupBox1.Name = "GroupBox1"; + this.GroupBox1.Size = new System.Drawing.Size(298, 132); + this.GroupBox1.TabIndex = 17; + this.GroupBox1.TabStop = false; + this.GroupBox1.Text = "Log"; + // + // txtLog + // + this.txtLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtLog.Location = new System.Drawing.Point(8, 16); + this.txtLog.Multiline = true; + this.txtLog.Name = "txtLog"; + this.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.txtLog.Size = new System.Drawing.Size(282, 108); + this.txtLog.TabIndex = 6; + this.txtLog.WordWrap = false; + // + // llShortcut + // + this.llShortcut.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.llShortcut.Location = new System.Drawing.Point(4, 62); + this.llShortcut.Name = "llShortcut"; + this.llShortcut.Size = new System.Drawing.Size(300, 33); + this.llShortcut.TabIndex = 15; + this.llShortcut.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + this.llShortcut.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.llShortcut_LinkClicked); + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label1.Location = new System.Drawing.Point(3, 38); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(309, 24); + this.label1.TabIndex = 16; + this.label1.Text = "In order to login on ftp please use login: test; password: test."; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // pictureBox1 + // + this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image"))); + this.pictureBox1.Location = new System.Drawing.Point(8, 7); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(120, 30); + this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; + this.pictureBox1.TabIndex = 14; + this.pictureBox1.TabStop = false; + // + // MainForm + // + this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); + this.ClientSize = new System.Drawing.Size(314, 242); + this.Controls.Add(this.btnStartStop); + this.Controls.Add(this.GroupBox1); + this.Controls.Add(this.llShortcut); + this.Controls.Add(this.label1); + this.Controls.Add(this.pictureBox1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(330, 280); + this.Name = "MainForm"; + this.Tag = ""; + this.Text = "Virtual FTP"; + this.Closed += new System.EventHandler(this.MainForm_Closed); + this.GroupBox1.ResumeLayout(false); + this.GroupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.Run(new MainForm()); + } + #endregion + + private void btnStartStop_Click(object sender, System.EventArgs e) + { + if (btnStartStop.Text == "Start") + { + addToLog("Starting Virtual FTP on " + port.ToString() + " port..."); + StartServer(); + llShortcut.Text = String.Format("ftp://localhost:{0}/", port); + addToLog("Virtual FTP is running under " + Environment.OSVersion.ToString()); + btnStartStop.Text = "Stop"; + } + else + { + addToLog("Shutting down Virtual FTP ..."); + StopServer(); + llShortcut.Text = ""; + addToLog("Virtual FTP is stopped."); + btnStartStop.Text = "Start"; + } + } + + private void llShortcut_LinkClicked(object sender, System.Windows.Forms.LinkLabelLinkClickedEventArgs e) + { + if (llShortcut.Text != "") System.Diagnostics.Process.Start(llShortcut.Text); + } + + private void MainForm_Closed(object sender, System.EventArgs e) + { + StopServer(); + } + + private void StartServer() + { + string lDiskFolder = Path.GetDirectoryName(typeof(MainForm).Assembly.Location) + @"\FtpRoot"; ; + + fRootFolder = new VirtualFolder(null, "[ROOT]"); + fRootFolder.Add(new VirtualFolder(null, "virtual")); + fRootFolder.Add(new DiscFolder(null, "drive-c", @"c:\")); + fRootFolder.Add(new DiscFolder(null, "disc", lDiskFolder)); + fRootFolder.Add(new EmptyFile(null, "=== Welcome to the FTP ===")); + + fUserManager = new UserManager(); + ((UserManager)fUserManager).AddUser("test", "test"); + + fFtpServer = new VirtualFtpServer(); + fFtpServer.Port = port; + fFtpServer.Timeout = 60 * 1000; /* 1 minute */ + if (this.fFtpServer.BindingV4 != null) + this.fFtpServer.BindingV4.ListenerThreadCount = 10; + fFtpServer.RootFolder = fRootFolder; + fFtpServer.UserManager = fUserManager; + fFtpServer.ServerName = "VirtualFTP Sample - powered by RemObjects Internet Pack for .NET"; + + fFtpServer.Open(); + + addToLog("VirtualFTP 0.3 BETA - started up"); + } + + private void StopServer() + { + if (fFtpServer != null) + fFtpServer.Close(); + } + + private void addToLog(String line) + { + txtLog.Text = txtLog.Text + + System.DateTime.Now.ToLongTimeString() + + ": " + + line + + Environment.NewLine; + } + } +} \ No newline at end of file diff --git a/Samples/C#/Virtual FTP/MainForm.resx b/Samples/C#/Virtual FTP/MainForm.resx new file mode 100644 index 0000000..a726801 --- /dev/null +++ b/Samples/C#/Virtual FTP/MainForm.resx @@ -0,0 +1,582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 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 + CQAPDw8AGxsLABQUFAAYGBgAHR0dAC4uDAA8PAQANjYJADQ0DgA9PQsAKioTACIiGwArKysAMTEiADQ0 + NAA7OzEAODg4AD4+PgBAQAMAREQCAEREDgBNTQoASUkMAFNTBABZWQAARUUSAEFBFABWVhAAYWEAAGlp + AABtbQIAZmYJAG1tCgBycgAAe3sAAHV1CwB9fQ0AZGQUAHh4GwBYWCEAX18nAFlZKgBFRTwAc3MqAHt7 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + 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 + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + OS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trf2pqav9oaGj/ZmZm/2Rk + ZP9hYWH/e3tn/6amcP+9vXH/wsFs/728Zf+4uF7/m5pV/35+T/9UVEz/SUlJ/0dHR/9FRUX/QkJC/0BA + QM8+Pj4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trL2pq + ar9oaGj/ZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dH + R+9ERER/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGpqak9oaGivZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05O + Tt9LS0t/SkpKDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZ2cPZWVlT2NjY39hYWG/Xl5ev1xcXL9aWlq/V1dXn1VV + VW9SUlIvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAP//wAA//4A + AD/4AAAf8AAAD+AAAAfgAAADwAAAA8AAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAcAAAAHAAAAD4AAAA/AAAAf4AAAP/AAAH/4AAH//gAD//+AH/ygA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJycnPyIi + Iq8dHR3vGBgY/xQUFP8PDw//CwsLvwcHB18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUPMDAwrysr + K/8xMSL/RUUS/0REDv89PQv/NjYJ/xsbC/8KCgr/BgYGzwMDAy8AAAAAAAAAAAAAAAA/Pz8POTk5zzU1 + Nf9kZBT/g4MA/3t7AP9ycgD/aWkA/2FhAP9ZWQD/PDwE/w4OCf8GBgbvAwMDLwAAAAAAAAAAQ0NDjz4+ + Pv94eBv/lJQA/4uLAP+DgwD/e3sA/3JyAP9paQD/YWEA/1lZAP9ERAL/Dg4J/wYGBs8AAAAATExMH0hI + SP97ezb/qakY/56eCP+UlAD/i4sA/4ODAP97ewD/cnIA/2lpAP9hYQD/WVkA/0BAA/8KCgr/BwcHX1BQ + UI9MTEz/uLg7/7S0Kv+pqRj/np4I/5SUAP+LiwD/g4MA/3t7AP9ycgD/aWkA/1NTBP9ZWQD/GxsL/woK + Cs9VVVW/cnJT/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uLAP+DgwD/e3sA/3JyAP9JSQz/KioT/y4u + DP8PDw//W1tb75KSX//OzVz/e3tM/8C/O/+0tCr/e3sr/19fJ/99fQ3/dXUL/21tCv9mZgn/QUEU/yIi + G/80NA7/FBQU/19fX99+fmL/X19X/2ppUv+Likr/g4NA/3t7Nf9zcyr/WVkq/1hYIf+LiwD/g4MA/1ZW + EP9tbQL/TU0K/xgYGP9kZGS/hYVq/66tcf+BgVz/1tVc/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uL + AP+DgwD/e3sA/0VFEv8dHR3/Z2dnb2RkZP/k44j/29p7/9nYbP/W1Vz/y8pL/8C/O/+0tCr/qakY/56e + CP+UlAD/i4sA/4ODAP8xMSL/IiIir2trax9paWn/lZR1//j3j//t7H//4eFu/9bVXP/Lykv/wL87/7S0 + Kv+pqRj/np4I/5SUAP9kZBT/Kysr/ygoKF8AAAAAa2trf2lpaf/FxIb/+PeP/+3sf//h4W7/1tVc/8vK + S//Avzv/tLQq/6mpGP+LixH/Ozsx/zAwML8AAAAAAAAAAAAAAABra2ufaWlp/6iofP/u7Yz/7ex//+Hh + bv/W1Vz/y8pL/8C/O/97ezb/RUU8/zk5Oc81NTUPAAAAAAAAAAAAAAAAAAAAAGtra39paWn/ZGRk/4WF + av+jomv/m5tg/3p6VP9UVEz/SEhI/0NDQ68/Pz8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trH2ho + aI9kZGS/X19f/1tbW/9WVlbPUVFRn0xMTD8AAAAAAAAAAAAAAAAAAAAA8A8AAMADAACAAQAAgAEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAMABAADgAwAA8A8AAA== + + + \ No newline at end of file diff --git a/Samples/C#/Virtual FTP/SecureFile.cs b/Samples/C#/Virtual FTP/SecureFile.cs new file mode 100644 index 0000000..2406a18 --- /dev/null +++ b/Samples/C#/Virtual FTP/SecureFile.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using RemObjects.InternetPack.Core; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + + public class SecureFile : FtpFile + { + public SecureFile(IFtpFolder aParent, string aName, SecureStorage aStorage) : base(aParent, aName) { fStorage = aStorage; SecureFileName = fStorage.GetNewFilename(); WorldRead = true; } private SecureStorage fStorage; private int fSize; public override int Size { get { return fSize; } set { fSize = value; } } private string fSecureFileName; public string SecureFileName { get { return fSecureFileName; } set { fSecureFileName = value; } } public override void Invalidate() + { + base.Invalidate (); + File.Delete(fSecureFileName); + } + public override void GetFile(Stream aToStream) { if (!File.Exists(SecureFileName)) + throw new Exception("Error retrieving file from secure storage: file does not exist."); + + Stream lStream = fStorage.GetFile(SecureFileName, 0); + try + { + + byte[] lBuffer = new byte[BUFFER_SIZE]; + + int lBytesRead = lStream.Read(lBuffer, 0, BUFFER_SIZE); + while (lBytesRead > 0) { aToStream.Write(lBuffer,0,lBytesRead); lBytesRead = lStream.Read(lBuffer, 0, BUFFER_SIZE); + } + } + finally + { + lStream.Close(); + } + + //return new FileStream(SecureFileName,FileMode.Open, FileAccess.Read, FileShare.Read); + } const int BUFFER_SIZE = 64*1024; + + public override void CreateFile(Stream aStream) { if (File.Exists(SecureFileName)) + throw new Exception("Error adding file to secure storage: file already exist."); + + //Stream lStream = new FileStream(SecureFileName,FileMode.CreateNew, FileAccess.Write, FileShare.None); + Stream lStream = fStorage.CreateFile(SecureFileName); + try + { + + byte[] lBuffer = new byte[BUFFER_SIZE]; + + int lBytesRead = aStream.Read(lBuffer, 0, BUFFER_SIZE); + while (lBytesRead > 0) { lStream.Write(lBuffer,0,lBytesRead); Size += lBytesRead; lBytesRead = aStream.Read(lBuffer, 0, BUFFER_SIZE); + } + /* ToDo: eliminate one call to receive by checking for lBytesRead == BUFFER_SIZE? */ + lStream.Close(); + Complete = true; + } + catch (ConnectionClosedException) + { + lStream.Close(); + Complete = true; + } + catch (Exception) + { + lStream.Close(); + File.Delete(SecureFileName); + //lSession.CurrentFolder.DeleteFile(ea.FileName,lSession); + throw; + } + + } } + +} diff --git a/Samples/C#/Virtual FTP/SecureFolder.cs b/Samples/C#/Virtual FTP/SecureFolder.cs new file mode 100644 index 0000000..c34e91a --- /dev/null +++ b/Samples/C#/Virtual FTP/SecureFolder.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + + /* + * - Keep files open + * - use different Key and IV per file + * + * + * + */ + + public class SecureFolder: VirtualFolder + { + public SecureFolder(IFtpFolder aParent, string aName, SecureStorage aStorage) : base(aParent, aName) + { + fStorage = aStorage; + WorldRead = true; + WorldWrite = true; + OwningUser = "system"; + OwningGroup = "system"; + } + + public SecureFolder(IFtpFolder aParent, string aName, SecureStorage aStorage, bool aPrivcate) : this(aParent, aName, aStorage) + { + fStorage = aStorage; + GroupRead = false; + GroupWrite = false; + WorldRead = false; + WorldWrite = false; + OwningUser = "system"; + OwningGroup = "system"; + } + + protected override IFtpFolder DoCreateFolder(string aFolderName, VirtualFtpSession aSession) { lock(this) + { + if (HasSubfolder(aFolderName)) + throw new FtpException(String.Format("Cannot create folder named \"{0}\", a folder with this name already exists.",aFolderName)); if (!AllowMkDir(aSession)) + throw new FtpException(550, String.Format("Cannot create folder named \"{0}\", permission denied.",aFolderName)); IFtpFolder lFolder = new SecureFolder(this, aFolderName, fStorage); lFolder.OwningUser = aSession.Username; Add(lFolder); return lFolder; } } + protected override IFtpFile DoCreateFile(string aFilename, VirtualFtpSession aSession) { if (HasFile(aFilename)) + throw new FtpException(String.Format("Cannot create file \"{0}\", a file with this name already exists.",aFilename)); if (!AllowMkDir(aSession)) + throw new FtpException(550, String.Format("Cannot create file \"{0}\", permission denied.",aFilename)); SecureFile lFile = new SecureFile(this, aFilename, fStorage); lFile.OwningUser = aSession.Username; Add(lFile); return lFile; } + private SecureStorage fStorage; } + +} diff --git a/Samples/C#/Virtual FTP/SecureStorage.cs b/Samples/C#/Virtual FTP/SecureStorage.cs new file mode 100644 index 0000000..c369d92 --- /dev/null +++ b/Samples/C#/Virtual FTP/SecureStorage.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; +using System.Security.Cryptography; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + + public class SecureStorage + { + public SecureStorage(string aBaseFolder) + { + fBaseFolder = aBaseFolder; + fCrypter = new RijndaelManaged(); fCrypter.BlockSize = 128; SelectRandomStorageFolder(); + } + + ~SecureStorage() + { + FreeStorage(); + } + + #region Properties + private string fStorageFolder; public string StorageFolder { get { return fStorageFolder; } } + + private string fBaseFolder; public string BaseFolder { get { return fBaseFolder; } } #endregion + + RijndaelManaged fCrypter; + + public void ClearStorage() + { + FreeStorage(); SelectRandomStorageFolder(); } + public void FreeStorage() + { + if (Directory.Exists(fBaseFolder)) + { + Directory.Delete(fBaseFolder,true); } + } + private void SelectRandomStorageFolder() { lock (this) { FreeStorage(); Directory.CreateDirectory(fBaseFolder); fStorageFolder = Path.Combine(fBaseFolder,Guid.NewGuid().ToString("D")); Directory.CreateDirectory(fStorageFolder); fCrypter.GenerateKey(); fCrypter.GenerateIV(); } } + + public string GetNewFilename() + { + lock (this) + { + return Path.Combine(fStorageFolder,Guid.NewGuid().ToString("D"))+".data"; } + } + + private Stream WrapReadStream(Stream aStream) + { + return new CryptoStream(aStream, fCrypter.CreateDecryptor(), CryptoStreamMode.Read); + } + + private Stream WrapWriteStream(Stream aStream) + { + return new CryptoStream(aStream, fCrypter.CreateEncryptor(), CryptoStreamMode.Write); + } + + public Stream CreateFile(string aFilename) + { + Stream lStream = new FileStream(aFilename,FileMode.Create,FileAccess.Write, FileShare.None); + return WrapWriteStream(lStream); + } + + public Stream GetFile(string aFilename, int aOffset) + { + Stream lStream = new FileStream(aFilename,FileMode.Open,FileAccess.ReadWrite, FileShare.Read); + return WrapReadStream(lStream); + } + + } + +} diff --git a/Samples/C#/Virtual FTP/SiteCommand.cs b/Samples/C#/Virtual FTP/SiteCommand.cs new file mode 100644 index 0000000..cbdff4c --- /dev/null +++ b/Samples/C#/Virtual FTP/SiteCommand.cs @@ -0,0 +1,237 @@ +using System; +using ByteFX.Data.MySqlClient; +using System.Collections; +using RemObjects.InternetPack.Core; +using RemObjects.InternetPack.Ftp; +using spbServ.VirtualFtp; +using RemObjects.InternetPack.Ftp.VirtualFtp; + + +namespace spbServ.VirtualFtp +{ + /// + /// Summary description for SiteCommand. + /// + public class SiteCommand + { + private Hashtable fSubCommands = new Hashtable(); + + public SiteCommand(FtpServer ftp) + { + ftp.AddCustomCommand("SITE", new OnCommandHandler(Cmd_SITE)); + AddCustomCommand("ADDUSER", new OnCommandHandler(Cmd_Site_AddUser)); + AddCustomCommand("CHANGE", new OnCommandHandler(Cmd_Site_Change)); + AddCustomCommand("DELUSER", new OnCommandHandler(Cmd_Site_DelUser)); + AddCustomCommand("PASSWD", new OnCommandHandler(Cmd_Site_Passwd)); + AddCustomCommand("HELP", new OnCommandHandler(Cmd_Site_Help)); + } + + public void AddCustomCommand(String name, OnCommandHandler handler) + { + fSubCommands.Add(name, handler); + } + + public static void Cmd_Site_AddUser(Object o, CommandEventArgs ea) + { + if (!((VirtualFtpSession)ea.Session).IsSuperUser) + { + ea.Connection.WriteLine("500 Unknown command"); + return; + } + if (ea.Parameters.Length < 2) + { + ea.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + + MySqlConnection lConnection = SpbServUserManager.GetConnection(); + try + { + MySqlCommand lCommand = lConnection.CreateCommand(); + lCommand.CommandText = String.Format("insert into login (username, passwordhash, admin) values ('{0}', '', 0)", ea.Parameters[1].Replace("'", "''")); + try + { + lCommand.ExecuteNonQuery(); + ea.Connection.WriteLine("501 User Added."); + } + catch + { + ea.Connection.WriteLine("501 Unable to add user."); + } + } + finally + { + SpbServUserManager.ReleaseConnection(lConnection); + } + } + + public static void Cmd_Site_DelUser(Object o, CommandEventArgs ea) + { + if (!((VirtualFtpSession)ea.Session).IsSuperUser) + { + ea.Connection.WriteLine("500 Unknown command"); + return; + } + if (ea.Parameters.Length < 2) + { + ea.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + if (ea.Parameters[1] == ((VirtualFtpSession)ea.Session).Username) + { + ea.Connection.WriteLine("501 You cannot delete yourself."); + return; + } + MySqlConnection lConnection = SpbServUserManager.GetConnection(); + try + { + MySqlCommand lCommand = lConnection.CreateCommand(); + lCommand.CommandText = String.Format("delete from login where username='{0}'", ea.Parameters[1].Replace("'", "''")); + try + { + lCommand.ExecuteNonQuery(); + ea.Connection.WriteLine("501 User deleted."); + } + catch + { + ea.Connection.WriteLine("501 Unable to delete user."); + } + + } + finally + { + SpbServUserManager.ReleaseConnection(lConnection); + } + } + public static void Cmd_Site_Passwd(Object o, CommandEventArgs ea) + { + if (!((VirtualFtpSession)ea.Session).IsSuperUser) + { + ea.Connection.WriteLine("500 Unknown command"); + return; + } + if (ea.Parameters.Length < 3) + { + ea.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + MySqlConnection lConnection = SpbServUserManager.GetConnection(); + try + { + MySqlCommand lCommand = lConnection.CreateCommand(); + lCommand.CommandText = String.Format("update login set passwordhash='{1}' where username='{0}'", ea.Parameters[1].Replace("'", "''"), SpbServUserManager.GenerateHash(ea.Parameters[2])); + try + { + lCommand.ExecuteNonQuery(); + ea.Connection.WriteLine("501 Password changed."); + } + catch + { + ea.Connection.WriteLine("501 Unable to add user."); + } + } + finally + { + SpbServUserManager.ReleaseConnection(lConnection); + } + } + + public static void Cmd_Site_Change(Object o, CommandEventArgs ea) + { + if (!((VirtualFtpSession)ea.Session).IsSuperUser) + { + ea.Connection.WriteLine("500 Unknown command"); + return; + } + if (ea.Parameters.Length < 4) + { + ea.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + String lField = ea.Parameters[2].ToUpper(); + String lValue = ea.Parameters[3]; + + MySqlConnection lConnection = SpbServUserManager.GetConnection(); + try + { + try + { + MySqlCommand lCommand = lConnection.CreateCommand(); + if (lField.Equals("ADMIN")) + { + if (lValue.Equals("1")) + { + lCommand.CommandText = String.Format("update login set admin=2 where username='{0}'", ea.Parameters[1].Replace("'", "''")); + } + else + { + lCommand.CommandText = String.Format("update login set admin=0 where username='{0}'", ea.Parameters[1].Replace("'", "''")); + } + lCommand.ExecuteNonQuery(); + } + else if (lField.Equals("FILEADMIN")) + { + if (lValue.Equals("1")) + { + lCommand.CommandText = String.Format("update login set admin=1 where username='{0}'", ea.Parameters[1].Replace("'", "''")); + } + else + { + lCommand.CommandText = String.Format("update login set admin=0 where username='{0}'", ea.Parameters[1].Replace("'", "''")); + } + lCommand.ExecuteNonQuery(); + } + ea.Connection.WriteLine("501 User changed."); + } + catch + { + ea.Connection.WriteLine("501 Unable to add user."); + } + } + finally + { + SpbServUserManager.ReleaseConnection(lConnection); + } + } + + public static void Cmd_Site_Help(Object o, CommandEventArgs ea) + { + if (!((VirtualFtpSession)ea.Session).IsSuperUser) + { + ea.Connection.WriteLine("500 Unknown command"); + return; + } + ea.Connection.WriteLine(@"501-|---------------------------------------------------------------------|"); + ea.Connection.WriteLine(@"| Virtual FTP server |"); + ea.Connection.WriteLine(@"|---------------------------------------------------------------------|"); + ea.Connection.WriteLine(@"| ADDUSER username Add a new user to the user list |"); + ea.Connection.WriteLine(@"| DELUSER username Remove a user from the user list |"); + ea.Connection.WriteLine(@"| PASSWD username password Change the password of a user |"); + ea.Connection.WriteLine(@"| CHANGE username admin 1/0 Make a user a site admin |"); + ea.Connection.WriteLine(@"| CHANGE username fileadmin 1/0 Make a user a file admin |"); + ea.Connection.WriteLine(@"501 |---------------------------------------------------------------------|"); + } + + public void Cmd_SITE(Object o, CommandEventArgs ea) + { + if (((FtpSession)ea.Session).State != FtpState.LoggedIn) + { + ea.Connection.WriteLine("503 Bad sequence of commands."); + return; + } + if (ea.Parameters.Length < 1) + { + ea.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + OnCommandHandler lCmd = (OnCommandHandler)fSubCommands[ea.Parameters[0].ToUpper()]; + if (lCmd == null) + { + ea.Connection.WriteLine("500 Unknown command"); + return; + } + lCmd(o, ea); + } + + } +} diff --git a/Samples/C#/Virtual FTP/SpbServUserManager.cs b/Samples/C#/Virtual FTP/SpbServUserManager.cs new file mode 100644 index 0000000..39be4e9 --- /dev/null +++ b/Samples/C#/Virtual FTP/SpbServUserManager.cs @@ -0,0 +1,106 @@ +using System; +using System.Net; +using System.Text; +using System.Security.Cryptography; +using RemObjects.InternetPack.Ftp.VirtualFtp; +using ByteFX.Data.MySqlClient; + +namespace spbServ.VirtualFtp +{ + /// + /// Summary description for SpbServUserManager. + /// + public class SpbServUserManager : IFtpUserManager + { + public SpbServUserManager() + { + } + + //private MySqlConnection fConnection; + + public static string GenerateHash(string aValue) + { + MD5 lMD5 = new MD5CryptoServiceProvider(); + byte[] lHash = lMD5.ComputeHash(Encoding.ASCII.GetBytes(aValue)); + StringBuilder sb = new StringBuilder(lHash.Length*1); + for (int i = 0; i < lHash.Length; i++) + { + if (lHash[i] <= 0x0f) sb.Append('0'); + sb.Append(lHash[i].ToString("x")); + } + return sb.ToString(); + } + + const string sConnectionString = "user id=iptable;database=iptable;data source=ws9.elitedev.com;password=ipiv73737"; + + public static MySqlConnection GetConnection() + { + MySqlConnection lConnection = new MySqlConnection(); + lConnection.ConnectionString = sConnectionString; + lConnection.Open(); + return lConnection; + } + + public static void ReleaseConnection(MySqlConnection e) + { + e.Close(); + } + + public virtual bool CheckIP(EndPoint aRemote, EndPoint aLocal) + { + using (MySqlConnection lConnection = new MySqlConnection()) + { + lConnection.ConnectionString = sConnectionString; + lConnection.Open(); + + MySqlCommand lCmd = new MySqlCommand(); + lCmd.Connection = lConnection; + + lCmd.CommandText = String.Format("SELECT COUNT(*) FROM ips WHERE ip='{0}'",((IPEndPoint)aRemote).Address.ToString()); + object lValue = lCmd.ExecuteScalar(); + return ((int)lValue >= 1); + } + } + + public virtual bool CheckLogin(string aUsername, string aPassword, VirtualFtpSession aSession) + { + VirtualFtpSession lSession = (VirtualFtpSession)aSession; + using (MySqlConnection lConnection = new MySqlConnection()) + { + lConnection.ConnectionString = sConnectionString; + lConnection.Open(); + + MySqlCommand lCmd = new MySqlCommand(String.Format("select l.admin from login l inner join ips z on l.username=z.username where l.username='{0}' and z.ip='{1}' and l.passwordhash='{2}'", + aUsername.Replace("'", "''"), ((IPEndPoint)aSession.RemoteEndPoint).Address.ToString(), GenerateHash(aPassword)), lConnection); + using (MySqlDataReader lReader = lCmd.ExecuteReader()) + { + if (lReader.Read()) + { + if (lReader.GetInt32(0) == 1) + { + lSession.IsSuperUser = false; + lSession.IsFileAdmin = true; + } + else + if (lReader.GetInt32(0) == 2) + { + lSession.IsSuperUser = true; + lSession.IsFileAdmin = true; + } + else + { + lSession.IsSuperUser = false; + lSession.IsFileAdmin = false; + } + return true; + } + else + { + return false; + } + } + } + } + + } +} diff --git a/Samples/C#/Virtual FTP/SpbServUserManager.resx b/Samples/C#/Virtual FTP/SpbServUserManager.resx new file mode 100644 index 0000000..ae00aff --- /dev/null +++ b/Samples/C#/Virtual FTP/SpbServUserManager.resx @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Private + + + Private + + + 17, 17 + + + SpbServUserManager + + + False + + + Private + + \ No newline at end of file diff --git a/Samples/C#/Virtual FTP/VirtualFTP.2008.csproj b/Samples/C#/Virtual FTP/VirtualFTP.2008.csproj new file mode 100644 index 0000000..43a680b --- /dev/null +++ b/Samples/C#/Virtual FTP/VirtualFTP.2008.csproj @@ -0,0 +1,117 @@ + + + Local + 8.0.50727 + 2.0 + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD} + Debug + AnyCPU + App.ico + + + VirtualFTP + + + JScript + Grid + IE50 + false + WinExe + VirtualFtpServer + OnBuildSuccess + VirtualFtp.MainForm + + + + + 2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE;DEBUGSERVER + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + + + + False + + + True + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Form + + + MainForm.cs + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/Virtual FTP/VirtualFTP.2008.sln b/Samples/C#/Virtual FTP/VirtualFTP.2008.sln new file mode 100644 index 0000000..e403f49 --- /dev/null +++ b/Samples/C#/Virtual FTP/VirtualFTP.2008.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualFTP", "VirtualFTP.2008.csproj", "{CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + IDE|Any CPU = IDE|Any CPU + Release|Any CPU = Release|Any CPU + RO|Any CPU = RO|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.IDE|Any CPU.ActiveCfg = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.IDE|Any CPU.Build.0 = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.Release|Any CPU.Build.0 = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.RO|Any CPU.ActiveCfg = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.RO|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/Virtual FTP/VirtualFTP.2010.csproj b/Samples/C#/Virtual FTP/VirtualFTP.2010.csproj new file mode 100644 index 0000000..366d44a --- /dev/null +++ b/Samples/C#/Virtual FTP/VirtualFTP.2010.csproj @@ -0,0 +1,120 @@ + + + Local + 10.0.20506 + 2.0 + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD} + Debug + AnyCPU + App.ico + + + VirtualFTP + + + JScript + Grid + IE50 + false + WinExe + VirtualFtpServer + OnBuildSuccess + VirtualFtp.MainForm + + + + + 3.5 + v2.0 + + + bin\Debug\ + false + 285212672 + false + + + DEBUG;TRACE;DEBUGSERVER + + + true + 4096 + false + + + false + false + false + false + 4 + full + prompt + AllRules.ruleset + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + none + prompt + AllRules.ruleset + + + + False + + + True + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + Code + + + Form + + + MainForm.cs + + + + + + + + + + \ No newline at end of file diff --git a/Samples/C#/Virtual FTP/VirtualFTP.2010.sln b/Samples/C#/Virtual FTP/VirtualFTP.2010.sln new file mode 100644 index 0000000..e720b4b --- /dev/null +++ b/Samples/C#/Virtual FTP/VirtualFTP.2010.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualFTP", "VirtualFTP.2010.csproj", "{CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + IDE|Any CPU = IDE|Any CPU + Release|Any CPU = Release|Any CPU + RO|Any CPU = RO|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.IDE|Any CPU.ActiveCfg = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.IDE|Any CPU.Build.0 = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.Release|Any CPU.Build.0 = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.RO|Any CPU.ActiveCfg = Release|Any CPU + {CD3244AF-1C50-47E8-A21F-6C8C3DC9CCBD}.RO|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/C#/Virtual FTP/VirtualFTP.Sample.html b/Samples/C#/Virtual FTP/VirtualFTP.Sample.html new file mode 100644 index 0000000..17a3adf --- /dev/null +++ b/Samples/C#/Virtual FTP/VirtualFTP.Sample.html @@ -0,0 +1,46 @@ + + + + + + + + + +
+

+ Virtual FTP Sample +

+ +
+

Purpose

+

+This sample shows how to create and configure virtual FTP. This supports building + a virtual folder structure, similar to IIS, based on real and non-existing folders.

+

+Note: +
+To connect to Virtual FTP, please use port 4444. (ftp://localhost:4444/) +
UserName=test +
Password=test +

+ +

Examine the Code

+
    +
  • Look at the StartServer function. There you can find how you can configure your ftp. +You can add a VirtualFolder or a DiskFolder. +
  • +
  • Also there you can see how to add the users allowed to  use this ftp.
  • +
+ +

Getting started

+
    +
  • Build the application.
  • +
  • Run the application and start the virtual FTP
  • +
  • With the help of the link on the form, open virtual FTP in your WebBrowser
  • +
  • Test virtual ftp.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/C#/Virtual FTP/cVirtualFTP.csproj b/Samples/C#/Virtual FTP/cVirtualFTP.csproj new file mode 100644 index 0000000..729fc70 --- /dev/null +++ b/Samples/C#/Virtual FTP/cVirtualFTP.csproj @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/Delphi for .NET/Sample Server/Main.TWinForm2.resources b/Samples/Delphi for .NET/Sample Server/Main.TWinForm2.resources new file mode 100644 index 0000000..793206f Binary files /dev/null and b/Samples/Delphi for .NET/Sample Server/Main.TWinForm2.resources differ diff --git a/Samples/Delphi for .NET/Sample Server/Main.pas b/Samples/Delphi for .NET/Sample Server/Main.pas new file mode 100644 index 0000000..2d0518d --- /dev/null +++ b/Samples/Delphi for .NET/Sample Server/Main.pas @@ -0,0 +1,187 @@ +unit Main; + +interface + +uses + System.Drawing, System.Collections, System.ComponentModel, + System.Windows.Forms, System.Data, System.Resources, + System.IO, System.Diagnostics, + RemObjects.InternetPack.StandardServers, + RemObjects.InternetPack.Http; + +type + TWinForm2 = class(System.Windows.Forms.Form) + {$REGION 'Designer Managed Code'} + strict private + /// + /// Required designer variable. + /// + Components: System.ComponentModel.Container; + pictureBox1: System.Windows.Forms.PictureBox; + btn_Deactivate: System.Windows.Forms.Button; + btn_Activate: System.Windows.Forms.Button; + lbl_Link: System.Windows.Forms.LinkLabel; + SimpleHttpServer1: RemObjects.InternetPack.Http.SimpleHttpServer; + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + procedure InitializeComponent; + procedure btn_Activate_Click(sender: System.Object; e: System.EventArgs); + procedure btn_Deactivate_Click(sender: System.Object; e: System.EventArgs); + procedure TWinForm2_Closed(sender: System.Object; e: System.EventArgs); + procedure LinkLabel1_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); + {$ENDREGION} + strict protected + /// + /// Clean up any resources being used. + /// + procedure Dispose(Disposing: Boolean); override; + private + fEchoServer:EchoServer; + fHttpServer:SimpleHttpServer; + public + constructor Create; + end; + + [assembly: RuntimeRequiredAttribute(TypeOf(TWinForm2))] + +implementation + +{$REGION 'Windows Form Designer generated code'} +/// +/// Required method for Designer support -- do not modify +/// the contents of this method with the code editor. +/// +procedure TWinForm2.InitializeComponent; +var + resources: System.Resources.ResourceManager; +begin + resources := System.Resources.ResourceManager.Create(TypeOf(TWinForm2)); + Self.pictureBox1 := System.Windows.Forms.PictureBox.Create; + Self.btn_Deactivate := System.Windows.Forms.Button.Create; + Self.btn_Activate := System.Windows.Forms.Button.Create; + Self.lbl_Link := System.Windows.Forms.LinkLabel.Create; + Self.SimpleHttpServer1 := RemObjects.InternetPack.Http.SimpleHttpServer.Create; + Self.SuspendLayout; + // + // pictureBox1 + // + Self.pictureBox1.Anchor := (System.Windows.Forms.AnchorStyles((System.Windows.Forms.AnchorStyles.Bottom + or System.Windows.Forms.AnchorStyles.Left))); + Self.pictureBox1.Image := (System.Drawing.Image(resources.GetObject('pictureBox1.Image'))); + Self.pictureBox1.Location := System.Drawing.Point.Create(8, 63); + Self.pictureBox1.Name := 'pictureBox1'; + Self.pictureBox1.Size := System.Drawing.Size.Create(120, 30); + Self.pictureBox1.TabIndex := 6; + Self.pictureBox1.TabStop := False; + // + // btn_Deactivate + // + Self.btn_Deactivate.Enabled := False; + Self.btn_Deactivate.Location := System.Drawing.Point.Create(128, 8); + Self.btn_Deactivate.Name := 'btn_Deactivate'; + Self.btn_Deactivate.Size := System.Drawing.Size.Create(112, 23); + Self.btn_Deactivate.TabIndex := 5; + Self.btn_Deactivate.Text := 'Deactivate Servers'; + Include(Self.btn_Deactivate.Click, Self.btn_Deactivate_Click); + // + // btn_Activate + // + Self.btn_Activate.Location := System.Drawing.Point.Create(8, 8); + Self.btn_Activate.Name := 'btn_Activate'; + Self.btn_Activate.Size := System.Drawing.Size.Create(112, 23); + Self.btn_Activate.TabIndex := 4; + Self.btn_Activate.Text := 'Activate Servers'; + Include(Self.btn_Activate.Click, Self.btn_Activate_Click); + // + // lbl_Link + // + Self.lbl_Link.Location := System.Drawing.Point.Create(8, 40); + Self.lbl_Link.Name := 'lbl_Link'; + Self.lbl_Link.Size := System.Drawing.Size.Create(160, 16); + Self.lbl_Link.TabIndex := 7; + Self.lbl_Link.TabStop := True; + Self.lbl_Link.Text := 'http://localhost:81/index.html'; + Include(Self.lbl_Link.LinkClicked, Self.LinkLabel1_LinkClicked); + // + // SimpleHttpServer1 + // + Self.SimpleHttpServer1.Active := False; + Self.SimpleHttpServer1.ConnectionClass := nil; + Self.SimpleHttpServer1.RootPath := ''; + Self.SimpleHttpServer1.ValidateRequests := False; + // + // TWinForm2 + // + Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13); + Self.ClientSize := System.Drawing.Size.Create(304, 101); + Self.Controls.Add(Self.lbl_Link); + Self.Controls.Add(Self.pictureBox1); + Self.Controls.Add(Self.btn_Deactivate); + Self.Controls.Add(Self.btn_Activate); + Self.Icon := (System.Drawing.Icon(resources.GetObject('$this.Icon'))); + Self.Name := 'TWinForm2'; + Self.Text := 'Internet Pack Sample Server (Delphi)'; + Include(Self.Load, Self.btn_Activate_Click); + Include(Self.Closed, Self.TWinForm2_Closed); + Self.ResumeLayout(False); +end; +{$ENDREGION} + +procedure TWinForm2.Dispose(Disposing: Boolean); +begin + if Disposing then + begin + if Components <> nil then + Components.Dispose(); + end; + inherited Dispose(Disposing); +end; + +constructor TWinForm2.Create; +begin + inherited Create; + // + // Required for Windows Form Designer support + // + InitializeComponent; + // + // TODO: Add any constructor code after InitializeComponent call + // +end; + +procedure TWinForm2.LinkLabel1_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); +begin + Process.Start(lbl_Link.Text); +end; + +procedure TWinForm2.TWinForm2_Closed(sender: System.Object; e: System.EventArgs); +begin + btn_Deactivate_Click(self, e); +end; + +procedure TWinForm2.btn_Activate_Click(sender: System.Object; e: System.EventArgs); +begin + fEchoServer := EchoServer.Create(); + fEchoServer.Open(); + + fHttpServer := SimpleHttpServer.Create(); + fHttpServer.Port := 81; // avoid conflict if IIS is installed, too + fHttpServer.RootPath := Path.GetDirectoryName(GetType().Assembly.Location)+'\HttpRoot'; + fHttpServer.ServerName := 'Internet Pack HTTP Server'; + fHttpServer.Open(); + + btn_Activate.Enabled := false; + btn_Deactivate.Enabled := true; +end; + +procedure TWinForm2.btn_Deactivate_Click(sender: System.Object; e: System.EventArgs); +begin + if (fEchoServer <> nil) then fEchoServer.Close(); + if (fHttpServer <> nil) then fHttpServer.Close(); + btn_Activate.Enabled := true; + btn_Deactivate.Enabled := false; +end; + +end. diff --git a/Samples/Delphi for .NET/Sample Server/Main.resx b/Samples/Delphi for .NET/Sample Server/Main.resx new file mode 100644 index 0000000..c8f401f --- /dev/null +++ b/Samples/Delphi for .NET/Sample Server/Main.resx @@ -0,0 +1,615 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + False + + + Private + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + + False + + + Private + + + False + + + Private + + + Private + + + False + + + 17, 17 + + + Private + + + False + + + (Default) + + + False + + + False + + + 8, 8 + + + True + + + 80 + + + True + + + + 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 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + YP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xMTP9LS0v/SUlJ/0dH + R/9GRkafRUVFDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpqah9paWmfaGho/2Zm + Zv9kZGT/Y2Nj/2FhYf9gYGD/Xl5e/1xcXP9bW1v/WVlZ/1hYWP9WVlb/VFRU/1NTU/9RUVH/T09P/05O + Tv9MTEz/S0tLn0lJSR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAaGhoD2dnZ19mZmavZGRk72NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZWf9YWFj/VlZW/1RU + VP9TU1PvUVFRr09PT19PT08PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjYy9iYmJPYWFhf19fX39eXl5/XFxcf1pa + Wn9ZWVl/V1dXT1ZWVi8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD//wAA//8AAP/8AAA//wAA//AAAA// + AAD/wAAAA/8AAP+AAAAB/wAA/wAAAAD/AAD+AAAAAH8AAPwAAAAAPwAA+AAAAAAfAADwAAAAAA8AAPAA + AAAADwAA4AAAAAAHAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAABwAA4AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAP4A + AAAAfwAA/4AAAAH/AAD/wAAAA/8AAP/gAAAH/wAA//gAAB//AAD//gAAf/8AAP//4Af//wAAKAAAACAA + AABAAAAAAQAgAAAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISEPHx8fXx0dHZ8aGhrPGBgY/xUVFf8TExP/ERER/w4ODu8MDAy/CgoKfwgI + CC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApKSkfJiYmnyQkJO8hISH/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/w4O + Dv8MDAz/CQkJ/wcHB88GBgZfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAALS0tfysrK/8oKCj/JiYm/yQkJP8hISH/Hx8f/x0dHf8aGhr/GBgY/xUV + Ff8TExP/ERER/w4ODv8MDAz/CQkJ/wcHB/8FBQW/BAQELwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAANTU1DzIyMs8vLy//LS0t/ysrK/8oKCj/NjYf/1JSD/9dXQj/bW0A/2lp + AP9kZAD/YGAA/1NTAv9CQgX/KioK/xISDf8MDAz/CQkJ/wcHB/8FBQXvAwMDXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5OQ83NzfPNDQ0/zIyMv8vLy//Q0Mi/2xsCv9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/T08A/y8vBv8MDAz/CQkJ/wcHB/8FBQX/AwMDXwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + 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 + + + \ No newline at end of file diff --git a/Samples/Delphi for .NET/Sample Server/SampleServer.bdsproj b/Samples/Delphi for .NET/Sample Server/SampleServer.bdsproj new file mode 100644 index 0000000..2bcb691 --- /dev/null +++ b/Samples/Delphi for .NET/Sample Server/SampleServer.bdsproj @@ -0,0 +1,193 @@ + + + + + + + + + + + + SampleServer.dpr + + + 7.0 + + + 0 + 0 + 1 + 1 + 0 + 0 + 1 + 1 + 1 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + 0 + 0 + 0 + 0 + 0 + 1 + 0 + 1 + 1 + 1 + True + True + WinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; + + False + + False + False + False + False + False + False + False + False + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + + 0 + 0 + 1 + True + False + False + 4096 + 1048576 + 4194304 + + + + ..\..\Bin\Samples + + + + c:\windows\microsoft.net\framework\v1.1.4322;..\..\Bin;r:\internet pack\bin;R:\Internet Pack\bin + r:\internet pack\bin\RemObjects.InternetPack.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.XML.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Windows.Forms.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Drawing.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Data.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.dll + + + True + + + + + + False + + + + + + False + + + + $00000000 + + + + False + False + 1 + 0 + 0 + 0 + False + False + False + False + False + 1033 + 1252 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/Delphi for .NET/Sample Server/SampleServer.cfg b/Samples/Delphi for .NET/Sample Server/SampleServer.cfg new file mode 100644 index 0000000..63b34c0 --- /dev/null +++ b/Samples/Delphi for .NET/Sample Server/SampleServer.cfg @@ -0,0 +1,48 @@ +-$A- +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-vn +-AWinTypes=Borland.Vcl.Windows;WinProcs=Borland.Vcl.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE; +-H+ +-W+ +-M +-$M4096,1048576 +-K$00400000 +-E"..\..\Bin\Samples" +-U"c:\windows\microsoft.net\framework\v1.1.4322;..\..\Bin;r:\internet pack\bin" +-O"c:\windows\microsoft.net\framework\v1.1.4322;..\..\Bin" +-I"c:\windows\microsoft.net\framework\v1.1.4322;..\..\Bin" +-R"c:\windows\microsoft.net\framework\v1.1.4322;..\..\Bin" +-LU"r:\internet pack\bin\RemObjects.InternetPack.Core.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.XML.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Windows.Forms.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Drawing.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.Data.dll;c:\windows\microsoft.net\framework\v1.1.4322\System.dll" +-w-SYMBOL_DEPRECATED +-w-SYMBOL_LIBRARY +-w-SYMBOL_PLATFORM +-w-SYMBOL_EXPERIMENTAL +-w-UNIT_LIBRARY +-w-UNIT_PLATFORM +-w-UNIT_DEPRECATED +-w-UNIT_EXPERIMENTAL diff --git a/Samples/Delphi for .NET/Sample Server/SampleServer.dpr b/Samples/Delphi for .NET/Sample Server/SampleServer.dpr new file mode 100644 index 0000000..cf740da --- /dev/null +++ b/Samples/Delphi for .NET/Sample Server/SampleServer.dpr @@ -0,0 +1,86 @@ +program SampleServer; + +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Data.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Drawing.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.Windows.Forms.dll'} +{%DelphiDotNetAssemblyCompiler '$(SystemRoot)\microsoft.net\framework\v1.1.4322\System.XML.dll'} +{$R 'Main.TWinForm2.resources' 'Main.resx'} +{%DelphiDotNetAssemblyCompiler '..\..\bin\RemObjects.InternetPack.Core.dll'} + +uses + System.Reflection, + System.Runtime.CompilerServices, + System.Windows.Forms, + Main in 'Main.pas' {Main.TWinForm2: System.Windows.Forms.Form}; + +{$R *.res} + +{$REGION 'Program/Assembly Information'} +// +// 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: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] + +// The Delphi compiler controls the AssemblyTitleAttribute via the ExeDescription. +// You can set this in the IDE via the Project Options. +// Manually setting the AssemblyTitle attribute below will override the IDE +// setting. +// [assembly: AssemblyTitle('')] + + +// +// 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 Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion('1.0.*')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')], provided your output +// directory is the project directory (the default). +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] +{$ENDREGION} + +[STAThread] +begin + Application.Run(TWinForm2.Create); +end. diff --git a/Samples/Delphi for .NET/Sample Server/SampleServer.res b/Samples/Delphi for .NET/Sample Server/SampleServer.res new file mode 100644 index 0000000..56b23d8 Binary files /dev/null and b/Samples/Delphi for .NET/Sample Server/SampleServer.res differ diff --git a/Samples/Oxygene/FTP Sync/App.ico b/Samples/Oxygene/FTP Sync/App.ico new file mode 100644 index 0000000..f8971dc Binary files /dev/null and b/Samples/Oxygene/FTP Sync/App.ico differ diff --git a/Samples/Oxygene/FTP Sync/AssemblyInfo.pas b/Samples/Oxygene/FTP Sync/AssemblyInfo.pas new file mode 100644 index 0000000..c5feb4a --- /dev/null +++ b/Samples/Oxygene/FTP Sync/AssemblyInfo.pas @@ -0,0 +1,49 @@ +namespace FtpSync; + +interface + +uses + System.Reflection; + +[assembly: AssemblyTitle('')] +[assembly: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] +[assembly: AssemblyVersion('1.0.0.1')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory, which in Chrome by default is the +// same as the project directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] + +implementation + +end. \ No newline at end of file diff --git a/Samples/Oxygene/FTP Sync/FtpSync.2008.sln b/Samples/Oxygene/FTP Sync/FtpSync.2008.sln new file mode 100644 index 0000000..181b11f --- /dev/null +++ b/Samples/Oxygene/FTP Sync/FtpSync.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "FtpSync", "FtpSync.oxygene", "{23407C77-DAB9-4100-A34D-3746A890944E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {23407C77-DAB9-4100-A34D-3746A890944E}.Debug|Default.ActiveCfg = Debug + {23407C77-DAB9-4100-A34D-3746A890944E}.Debug|Default.Build.0 = Debug + {23407C77-DAB9-4100-A34D-3746A890944E}.Release|Default.ActiveCfg = Release + {23407C77-DAB9-4100-A34D-3746A890944E}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/FTP Sync/FtpSync.2010.sln b/Samples/Oxygene/FTP Sync/FtpSync.2010.sln new file mode 100644 index 0000000..a8cdcf0 --- /dev/null +++ b/Samples/Oxygene/FTP Sync/FtpSync.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "FtpSync", "FtpSync.oxygene", "{23407C77-DAB9-4100-A34D-3746A890944E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {23407C77-DAB9-4100-A34D-3746A890944E}.Debug|Default.ActiveCfg = Debug + {23407C77-DAB9-4100-A34D-3746A890944E}.Debug|Default.Build.0 = Debug + {23407C77-DAB9-4100-A34D-3746A890944E}.Release|Default.ActiveCfg = Release + {23407C77-DAB9-4100-A34D-3746A890944E}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/FTP Sync/FtpSync.Sample.html b/Samples/Oxygene/FTP Sync/FtpSync.Sample.html new file mode 100644 index 0000000..2822611 --- /dev/null +++ b/Samples/Oxygene/FTP Sync/FtpSync.Sample.html @@ -0,0 +1,54 @@ + + + + + + + + +
+

+ FtpSync Sample +

+ +
+

Purpose

+

+ This sample shows how to use the FtpClient class to gain access to an ftp + site and download files from there.
+The +FtpSync application compares files on the local computer against those on the ftp folder and downloads files that are new or have changed.
+ +FtpSync is a console application with following uasge:

+

+
+ +FtpSync local user:pass@server/remote [-delete] [-passive] [-l]

+

+ +
+ +where:
+ + + + + + + + + + +
local:local directory - long file names with spaces should be quoted
user/pass:ftp username & password
server:ftp server (i.e., ftp.whatever.com)
remote:directory on the remote server
-delete:delete local files that don't exist on the server
-passive:turn on 'Passive' mode
-l:log - show FTP commands
+
+

+ + +

Getting started

+
    +
  • Build the application.
  • +
  • Run the application with your parameters. Try to synchronize your local folder with an ftp folder
  • +
+ + + \ No newline at end of file diff --git a/Samples/Oxygene/FTP Sync/FtpSync.oxygene b/Samples/Oxygene/FTP Sync/FtpSync.oxygene new file mode 100644 index 0000000..82b8f22 --- /dev/null +++ b/Samples/Oxygene/FTP Sync/FtpSync.oxygene @@ -0,0 +1,46 @@ + + + FtpSync + Exe + FtpSync + False + False + App.ico + Release + {23407C77-DAB9-4100-A34D-3746A890944E} + v2.0 + + + DEBUG;TRACE; + .\bin\Debug + True + True + + + .\bin\Release + False + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/Oxygene/FTP Sync/FtpSyncWorker.pas b/Samples/Oxygene/FTP Sync/FtpSyncWorker.pas new file mode 100644 index 0000000..820c421 --- /dev/null +++ b/Samples/Oxygene/FTP Sync/FtpSyncWorker.pas @@ -0,0 +1,418 @@ +namespace FtpSync; + +interface + +uses + System.IO, + System.Collections.Generic, + System.Collections.Specialized, + System.Text.RegularExpressions, + RemObjects.InternetPack.Ftp; + +type + SyncMode = public enum(Local, Remote); + + FtpSyncWorker = public class + private + fLocalDirectory : string; + fServer: String; + fServerDirectory: String; + fUsername: string; + fPassword: string; + fDoNotDeleteMissingItems: boolean; + fRecursiveSync: boolean; + fPassiveMode: boolean; + fDoShowFtpClientLog: boolean; + fSyncMode: SyncMode := SyncMode.Local; + fFtpClient: FtpClient; + method fFtpClient_OnTransferProgress(aSender: object; ea: RemObjects.InternetPack.Events.TransferProgressEventArgs); + method SetLocalDirectory(value: String); + method SetServer(value: String); + method SetServerDirectory(value: String); + method SetDoNotDeleteMissingItems(value: boolean); + method SetRecursiveSync(value: Boolean); + method SetPassiveMode(value: Boolean); + + method ServerLog(Sender: object; ea: RemObjects.InternetPack.CommandBased.ClientLogArgs); + method ClientLog(aMessage: String); + protected + public + property LocalDirectory: String read fLocalDirectory write SetLocalDirectory; + property Server: String read fServer write SetServer; + property ServerDirectory: String read fServerDirectory write SetServerDirectory; + property DeleteMissing: boolean read fDoNotDeleteMissingItems write SetDoNotDeleteMissingItems; + property Subdirectories: Boolean read fRecursiveSync write SetRecursiveSync; + property Passive: Boolean read fPassiveMode write SetPassiveMode; + + method Log(Sender: object; ea: RemObjects.InternetPack.CommandBased.ClientLogArgs); + method CheckArgs(args: array of string): Boolean; + method SyncDirectory(aLocalDirectory: string; aRemoteDirectory: string); + method Sync(); + end; + +implementation + +method FtpSyncWorker.ServerLog(Sender: object; ea: RemObjects.InternetPack.CommandBased.ClientLogArgs); +begin + if (fDoShowFtpClientLog) then begin + var lIcon: String := ' '; + case ea.Direction of + RemObjects.InternetPack.CommandBased.LogDirection.Receive: lIcon := '<'; + RemObjects.InternetPack.CommandBased.LogDirection.Send: lIcon := '>'; + RemObjects.InternetPack.CommandBased.LogDirection.Status: lIcon := '!'; + end; + + Console.WriteLine('[{0}] {1}', lIcon, ea.Text); + end; +end; + +method FtpSyncWorker.ClientLog(aMessage: String); +begin + Console.WriteLine('[i] {0}', aMessage); +end; + +method FtpSyncWorker.fFtpClient_OnTransferProgress(aSender: object; ea: RemObjects.InternetPack.Events.TransferProgressEventArgs); +begin + Console.Write('.'); +end; + + +method FtpSyncWorker.SetLocalDirectory(value: String); +begin + if (fLocalDirectory = value) then Exit; + fLocalDirectory := value; +end; + +method FtpSyncWorker.SetServer(value: String); +begin + if (fServer = value) then Exit; + fServer := value; +end; + +method FtpSyncWorker.SetServerDirectory(value: String); +begin + if (fServerDirectory = value) then Exit; + fServerDirectory := value; +end; + +method FtpSyncWorker.SetDoNotDeleteMissingItems(value: Boolean); +begin + if (fDoNotDeleteMissingItems = value) then Exit; + fDoNotDeleteMissingItems := value; +end; + +method FtpSyncWorker.SetRecursiveSync(value: Boolean); +begin + if (fRecursiveSync = value) then Exit; + fRecursiveSync := value; +end; + +method FtpSyncWorker.SetPassiveMode(value: Boolean); +begin + if (fPassiveMode = value) then Exit; + fPassiveMode := value; +end; + +method FtpSyncWorker.Log(Sender: object; ea: RemObjects.InternetPack.CommandBased.ClientLogArgs); +begin + if (fDoShowFtpClientLog) then Console.WriteLine(ea.Text); +end; + +method FtpSyncWorker.CheckArgs(args: array of string): Boolean; +begin + var lCount: Integer := args.Length; + var lBadParam: Boolean := false; + if (lCount > 0) then + fLocalDirectory := args[0] + else + lBadParam := true; + + if (lCount > 1) then begin + var lMatch: Match := Regex.Match(args[1], '(?\S+):(?\S+)@(?[^/\s]+)/(?\S*)'); + if (lMatch.Success) then begin + fUsername := lMatch.Groups['user'].Value; + fPassword := lMatch.Groups['pass'].Value; + fServer := lMatch.Groups['server'].Value; + fServerDirectory := lMatch.Groups['dir'].Value; + end else begin + lBadParam := true; + Console.WriteLine('Invalid server parameters'); + Console.WriteLine(''); + end; + end else + lBadParam := true; + + for i: Integer := 2 to lCount - 1 do begin + case args[i].ToLower() of + '-local': fSyncMode := SyncMode.Local; + '-remote': fSyncMode := SyncMode.Remote; + '-nodelete': fDoNotDeleteMissingItems := true; + '-passive': fPassiveMode := true; + '-r': fRecursiveSync := true; + '-l': fDoShowFtpClientLog := true; + '-help': lBadParam := true; + '/help': lBadParam := true; + else begin + Console.WriteLine('Invalid command line paramter ''+args[i]+'''); + Console.WriteLine(''); + lBadParam := true; + end; + end; + end; + + if (lBadParam) then begin + Console.WriteLine('RemObjects Internet Pack - FtpSync Sample'); + Console.WriteLine(''); + Console.WriteLine(' Usage: FtpSync local user:pass@server/remote [-local] [-remote] [-nodelete] [-passive] [-l] [-r]'); + Console.WriteLine(); + Console.WriteLine(' Compares files on local computer against those on the remote server, '); + Console.WriteLine(' downloading those that are new or have changed.'); + Console.WriteLine(); + Console.WriteLine(' local : local directory - long file names with spaces should be quoted'); + Console.WriteLine(' user/pass : ftp username & password'); + Console.WriteLine(' server : ftp server (i.e., ftp.whatever.com'); + Console.WriteLine(' remote : directory on remote server'); + Console.WriteLine(); + Console.WriteLine(' -nodelete : forbid to delete missing files'); + Console.WriteLine(' -passive : turn on ''Passive mode'''); + Console.WriteLine(' -r : recursive synchronization (through subdirectories)'); + Console.WriteLine(' -l : log - show FTP commands'); + Console.WriteLine(' -local : Local mode of sync (Master = ftp; slave = localhost). By default'); + Console.WriteLine(' -remote : Remote mode of sync (Master = localhost; slave = ftp)'); + Console.WriteLine(); + Console.WriteLine('Press enter to exit.'); + Console.ReadLine(); + end; + exit (not lBadParam); + end; + +method FtpSyncWorker.Sync(); +begin + try + fFtpClient := new FtpClient(); + fFtpClient.HostName := fServer; + fFtpClient.UserName := fUsername; + fFtpClient.Password := fPassword; + fFtpClient.Passive := fPassiveMode; + fFtpClient.Port := 21; + + fFtpClient.OnLog += new RemObjects.InternetPack.CommandBased.ClientLogEvent(ServerLog); + fFtpClient.OnTransferProgress +=new RemObjects.InternetPack.Events.TransferProgressEventHandler(fFtpClient_OnTransferProgress); + ClientLog('Connecting to ' + fServer); + fFtpClient.Open(); + try + fFtpClient.Login(); + SyncDirectory(fLocalDirectory, '/' + fServerDirectory); + finally + ClientLog('Disconnecting'); + fFtpClient.Quit(); + fFtpClient.Close(); + end + except + on ex: Exception do begin + ClientLog(String.Format('Error syncing directory ({0})', ex.Message)); + if (Assigned(ex.StackTrace) and fDoShowFtpClientLog) then ClientLog(ex.StackTrace); + + end; + end; + ClientLog('Press enter to continue...'); + Console.ReadLine(); +end; + + +method FtpSyncWorker.SyncDirectory(aLocalDirectory: string; aRemoteDirectory: string); +begin + var lOriginalLocalDirectory: String := Directory.GetCurrentDirectory(); + var lOriginalRemoteDirectory: String := fFtpClient.GetCurrentDirectory(); + + try + {$REGION Step into synchronized directories} + ClientLog(String.Format('Local change directory to {0}', aLocalDirectory)); + Directory.SetCurrentDirectory(aLocalDirectory); + + ClientLog(String.Format('Remote change directory to {0}', aRemoteDirectory)); + fFtpClient.ChangeDirectory(aRemoteDirectory); + {$ENDREGION} + + {$REGION Retrieve content} + ClientLog('Retrieving directory contents'); + fFtpClient.List(); + {$ENDREGION} + + {$REGION Get Local Folders List} + var lLocalFolders: Dictionary := new Dictionary(); + if (fRecursiveSync) then begin + for each lName: String in Directory.GetDirectories(aLocalDirectory) do begin + var d: DirectoryInfo := new DirectoryInfo(lName); + lLocalFolders.Add(d.Name, d); + end; + end; + {$ENDREGION} + + {$REGION Get Local Files List} + var lLocalFiles: Dictionary := new Dictionary(); + for each lName: String in Directory.GetFiles(aLocalDirectory) do begin + var f: FileInfo := new FileInfo(lName); + lLocalFiles.Add(f.Name, f); + end; + {$ENDREGION} + + {$REGION Get Remote Files and Directories List} + var lRemoteFolders: Dictionary := new Dictionary(); + var lRemoteFiles: Dictionary := new Dictionary(); + for each lRemoteItem: FtpListingItem in fFtpClient.CurrentDirectoryContents do begin + if lRemoteItem.Directory then begin + if fRecursiveSync then begin + if (lRemoteItem.FileName <> '..') then + lRemoteFolders.Add(lRemoteItem.FileName, lRemoteItem); + end; + end + else + lRemoteFiles.Add(lRemoteItem.FileName, lRemoteItem); + end; + {$ENDREGION} + + {$REGION Synchronization...} + case fSyncMode of + {$REGION Local Synchronization. Master: ftp; slave: local;} + SyncMode.Local: begin + {$REGION Folders synchronization} + for each lName: String in lRemoteFolders.Keys do begin + ClientLog(String.Format('Synchronizing folder "{0}"...', lName)); + var lLocalItemName: String := Path.Combine(aLocalDirectory, lName); + + if (not Directory.Exists(lLocalItemName)) then + Directory.CreateDirectory(lLocalItemName); + + SyncDirectory(lLocalItemName, lName); + + ClientLog(String.Format('Folder "{0}" has been synchronized', lName)); + lLocalFolders.Remove(lName); + end; + + {$REGION Delete local folders that doesn't exists on FTP} + if (not fDoNotDeleteMissingItems) then begin + for each toDelete: DirectoryInfo in lLocalFolders.Values do + toDelete.Delete(true);//delete recursive + + lLocalFolders.Clear(); + end; + {$ENDREGION} + + {$ENDREGION} + + {$REGION Files synchronization} + for each lName: String in lRemoteFiles.Keys do begin + var lRemoteItem: FtpListingItem := lRemoteFiles[lName]; + var lNeedSync: Boolean := true; + if (lLocalFiles.ContainsKey(lName)) then begin + var lLocalItem: FileInfo := lLocalFiles[lName]; + lNeedSync := ( + (lLocalItem.Length <> lRemoteItem.Size) OR + (lLocalItem.LastWriteTime <> lRemoteItem.FileDate)); + end; + ClientLog( + String.Format( + 'File {0} {1}', + lRemoteItem.FileName, + iif(lNeedSync, 'requires synchronization.', 'doesn''t require synchronization.'))); + if lNeedSync then begin + ClientLog(String.Format('Downloading {0}...', lRemoteItem.FileName)); + with lStream: Stream := File.Open(Path.Combine(aLocalDirectory, lName), FileMode.Create) do begin + fFtpClient.Retrieve(lRemoteItem, lStream); + lStream.Close(); + File.SetLastWriteTime(lRemoteItem.FileName, lRemoteItem.FileDate); + end; + ClientLog(String.Format('File {0} has been downloaded.', lRemoteItem.FileName)); + end; + lLocalFiles.Remove(lName); + end; + + {$REGION Delete local files that doesn't exists on FTP} + if (not fDoNotDeleteMissingItems) then begin + for each toDelete: FileInfo in lLocalFiles.Values do + toDelete.Delete(); + lLocalFiles.Clear(); + end; + {$ENDREGION} + + {$ENDREGION} + end; + {$ENDREGION} + + {$REGION Remote Synchronization. Master: local; slave: ftp;} + SyncMode.Remote: begin + + {$REGION Folders Synchronization.} + for each lName: String in lLocalFolders.Keys do begin + ClientLog(String.Format("Synchronizing folder '{0}'...", lName)); + var lLocalItemName: String := Path.Combine(aLocalDirectory, lName); + + if not lRemoteFolders.ContainsKey(lName) then fFtpClient.MakeDirectory(lName); + SyncDirectory(lLocalItemName, lName); + ClientLog(String.Format("Folder '{0}' has been synchronized", lName)); + lRemoteFolders.Remove(lName); + end; + {$REGION Delete FTP folders that doesn't exists locally} + if not fDoNotDeleteMissingItems then begin + for each toDelete: FtpListingItem in lRemoteFolders.Values do + fFtpClient.RemoveDirectory(toDelete.FileName); + lRemoteFolders.Clear(); + end; + {$ENDREGION} + {$ENDREGION} + + {$REGION Files Synchronization.} + for each lName: String in lLocalFiles.Keys do begin + var lLocalItemName: String := Path.Combine(aLocalDirectory, lName); + + var lNeedSync: Boolean := true; + if lRemoteFiles.ContainsKey(lName) then begin + var lLocalItem: FileInfo := new FileInfo(lLocalItemName); + var lRemoteItem: FtpListingItem := lRemoteFiles[lName]; + lNeedSync := ( + (lLocalItem.Length <> lRemoteItem.Size) OR + (lLocalItem.LastWriteTime <> lRemoteItem.FileDate)); + end; + ClientLog( + String.Format( + 'File {0} {1}', + lName, + iif(lNeedSync, 'requires synchronization.', 'doesn''t require synchronization.'))); + if lNeedSync then begin + ClientLog(String.Format('Uploading {0}...', lName)); + if (File.Exists(lLocalItemName)) then + with fs: FileStream := new FileStream(lLocalItemName, FileMode.Open, FileAccess.Read) do + fFtpClient.Store(lName, fs); + + ClientLog(String.Format('File {0} has been uploaded.', lName)); + end; + lRemoteFiles.Remove(lName); + end; + + {$REGION Delete FTP files that doesn't exist locally} + if not fDoNotDeleteMissingItems then begin + for each toDelete: FtpListingItem in lRemoteFiles.Values do + fFtpClient.Delete(toDelete.FileName); + lRemoteFiles.Clear(); + end; + {$ENDREGION} + + {$ENDREGION} + end; + {$ENDREGION} + end;//case + {$ENDREGION} + finally + {$REGION Step out of synchronized directories} + Directory.SetCurrentDirectory(lOriginalLocalDirectory); + ClientLog(String.Format('Local change directory to {0}', lOriginalLocalDirectory)); + fFtpClient.ChangeDirectory(lOriginalRemoteDirectory); + ClientLog(String.Format('Remote change directory to {0}', lOriginalRemoteDirectory)); + {$ENDREGION} + end; +end; + + + +end. \ No newline at end of file diff --git a/Samples/Oxygene/FTP Sync/FtpSyncWorker.resx b/Samples/Oxygene/FTP Sync/FtpSyncWorker.resx new file mode 100644 index 0000000..65e523c --- /dev/null +++ b/Samples/Oxygene/FTP Sync/FtpSyncWorker.resx @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + AAABAAQAICAQAAEABADoAgAARgAAABAQEAABAAQAKAEAAC4DAAAgIAAAAQAgAKgQAABWBAAAEBAAAAEA + IABoBAAA/hQAACgAAAAgAAAAQAAAAAEABAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAB4iIiIiIgAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAH//////+AAAAAAAAAAAB/ + t///////gAAAAAAAAAAAe/f0RERET4AAAAAAAAAAAH+3//////+AiIiIiIiIAAB79/RERERPgI+Pj4+P + iAAAf7f//////4D4+Pj4+PgAAHv39ERERE+Aj4+Pj4+IAAB/t///////gPj////4+AAAe/f0RERET4CP + cAAA/4gAAH+3//////+A9wB4AA/4AAB79///////j/APj4cH+AAAf7d3d3d3d3+AiPj4APgAAHv7+/v7 + 94+PcI+PjwD4AAB/v7+/v7f4/4B4+PgA+AAAd3d3d3d3j4/wD4+PcPgAAAB////////49wf48AD4AAAA + f///////j/AAgHAA+AAAAH/////////wAHAAAPgAAAB/////////cAAAAHD4AAAAf////////wBwAACH + fwAAAH////////8HgAAAj/gAAAB/////////D3AHAA/4AAAAZmZmaGhmj38ADwAI+AAAAGZmZoZmhm// + APj3B/gAAABmZmZmhmaGjwD4jwf4AAAAd3d3d3d3d39w93949wAAAAAAAAAAAAAAgAAAcAAAAAAAAAAA + AAAAAAdwAAAAAAAAAAAAAAAAAAAAAAAAAAD4AD//+AA///gAP//AAD//wAA//8AAAAHAAAABwAAAAcAA + AAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHAAAABwAAAAcAAAAHwAAAB8AAAAfAAAAHwAAAB8AAAAfAA + AAHwAAAB8AAAAfAAAAHwAAAB8AAAAf//89////n//////ygAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD/ + /wD/AAAA/wD/AP//AAD///8AAAAAAAAAAAAAf///8AAAAAB/RETwAAAAe3////AAAAB/f0RE8HAAAHt/ + ///////wf39ERP8HD/B7f///8I+A8H93d3fw/4Dwe/v78Pd/d/B3d3dw9wcH8AB////wAAfwAA////cA + D/AAZmZm/wgH8ABmZmZvD4fwAAAAAABwAADAPwAAwD8AAAA/AAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAMAAAADAAAAAwAAAAMAAAAD/3wAAKAAAACAAAABAAAAAAQAgAAAAAACAEAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtmIj/qZOE/6SPfv+giXj/m4Rz/5Z/ + bf+Remj/jHVj/4dwXf+Da1j/f2ZS/3phTv92XUn/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMCom//56uP/8dPF//HR + wf/xz8D/8M29/+/Muv/vyrn/78i2/+/HtP/uxrP/7cWx/3lgTP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwqyf//nt + 5//56+T/+Oni//jo4P/45d3/9+Pa//bh1//239X/9d3S//Tc0P/uxrP/fWRR/wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtmIj/qZOE/6SP + fv/Fr6P/+u/q//rt6P/57OX/+Onj//jo4P/45t3/9uPb//bi2P/24Nb/9d7T/+7Htf+AZ1X/cVhE/3BW + Qv9vVED/blM//21SPf9rUT3/ak87/2lOOf9oTTj/Z0w4/2ZLNv9lSjX/ZEk0/2NJM/9jSDP/AAAAAMCo + m/973PP/Y87u/8mzqf/78u7/+/Dr/9hqMv/IYSz/tVcn/6JOJP+RRiL/hkEg/4ZBIP/24Nf/78q4/4Nq + Wf/GsKT/xK2i/8OsoP/Bqp3/wKic/7+mmf+9pJf/u6KV/7mhk/+4npD/tpyO/7SbjP+ymYr/sZeH/2NI + NP8AAAAAwqyf/4Hf9P983fP/zLiu//z08P/78u7/+vDs//vv6v/67ef/+ezl//nq4v/45+D/9+Xd//fj + 2//vzLv/hm5d//bh1//239T/9d7S//Xc0P/02s7/9NnM//PXyv/z1cj/8tTG//HTxP/y0cL/8dDA//DO + vv+zmYv/ZUk1/wAAAADFr6P/huH0/4Lf9f/PvLP//Pbz//z08f/YajL/x2Es/7VXJ/+hTiX/kUYj/4ZB + IP+GQSD/+Ofe//DOvv+Lc2D/9uLY//Xg1//239X/9d7S//Tc0P/02s7/89nM//PXyf/y1cf/8tTF//LT + w//y0cL/8dDA/7Wcjf9mSzb/AAAAAMmzqf+M4/X/h+H0/9PCuf/9+fb//ff0//z28v/89PD/+/Lu//vx + 6//67+n/+e3m//nq5P/56eH/8tHB/453Zf/35Nr/9+LY//bh1v/239T/9d7S//Xc0P/02s7/89nL//PW + yv/y1cj/8tPF//LTxP/x0cL/uJ6Q/2dNOP8AAAAAzLiu/5Ll9f+M4/X/18e+//36+f/++Pf/2Goy/8hg + LP+1Vyj/ok4k/5FGIv+GQSD/hkEg//nr5f/y1Mb/kXpp//jm3P/349r/9uLY//////////////////// + //////////////LXyf/z1sf/8tPG//LSw/+6oZP/aE46/wAAAADPvLP/l+j1/5Ll9f/bzMT//vz7//37 + +v/9+vj//fj1//z39P/89fL//PPv//vx7f/68Or/+u7o//PXyf+Vfm3/+Obf//fl3P//////gICA/wAA + AP8AAAD/AAAA/wAAAP8AAAD///////PWyf/z1cf/8tTG/7ykl/9qTzv/AAAAANPCuf+c6vb/l+j2/97R + y//+/f3//vz8/9hqMv/IYSz/tVYn/6JOJP+RRiL/hkEg/4ZBIP/68ez/9NnN/5mCcf/46OD//////4CA + gP8AAAD/AAAA/4CAgP/AwMD/AAAA/wAAAP8AAAD///////PXyf/z1cf/vqea/2tSPf8AAAAA18e+/6Hs + 9v+d6vb/4tbQ/////v/+/f3//v38//78+v/++vn//fn3//339v/99vP//PXx//vz7//13dH/nIZ1//// + ////////AAAA/wAAAP/249n/9+HX//bg1f/13tT/gICA/wAAAP+AgID///////LWyP/AqZ3/bVQ//wAA + AADbzMT/pe73/6Hs9v/m3Nb////////////+/v7///39//78+//++/r//fn3//359v/89/T//Pby//Xf + 1f+ginr//////8DAwP8AAAD/wMDA//fk2//249n/9uHX//Xg1v/13tP/AAAA/wAAAP//////89jL/8Or + n/9vVUH/AAAAAN7Ry/+o7/f/pO33/+ng3P///////////////////v////39///8+//9/Pr//fv4//34 + 9v/9+PX//Pbz/7Silf//////gICA/wAAAP/45+D/9+bd//fk2//349n/9uDX//bf1f8AAAD/AAAA//// + ///02c3/xK6i/3FXQ/8AAAAA4tbQ/6vw9/+o7/f/7eXi/+vi3v/p39z/593Y/+Ta1P/j19L/4dXO/9/R + y//cz8j/2svF/9jJwf/YysH/+u/q///////AwMD/AAAA/4CAgP/459//9+bd//fk2//24tn/9uHX/wAA + AP8AAAD///////Tazv/HsaX/c1lF/wAAAADm3Nb/q/D3/6vw9/+o7/f/pu72/6Ls9/+f6/b/mun2/5Xn + 9f+Q5fX/jOP1/4Da9f+ginr/+/Pv//vy7v/68Oz///////////8AAAD/AAAA//jp4f/459//9+bd//fj + 2v/34tj/gICA/wAAAP//////9NzQ/8mzqP91W0j/AAAAAOng3P+r8Pf/q/D3/6vw9/+p7/f/pu32/6Ls + 9v+f6/b/m+n2/5bn9f+S5fX/jOP0/6SOfv/79PH/+/Lv//vx7f/78Ov//////4CAgP8AAAD/gICA//jp + 4f/AwMD/9+Xc/wAAAP8AAAD/AAAA///////13tL/y7as/3deSv8AAAAA7eXi/+vi3v/p39z/593Y/+Ta + 1P/j19L/4dXO/9/Ry//cz8j/2svF/9jJwf/Wx77/p5KC//z18v/89PH/+/Lu//vy7f//////AAAA/wAA + AP8AAAD/+erj/wAAAP+AgID/AAAA/wAAAP8AAAD///////Xe1P/Nuq//eWBN/wAAAAAAAAAAAAAAAAAA + AAC3opP//////////v/+/v3//vz8//78+//++/n//fr4//359//9+PX//Pb0//z18v/79PH/+/Lu//// + //8AAAD/AAAA/wAAAP+AgID/AAAA/wAAAP8AAAD/AAAA/wAAAP//////9eDV/8+8s/97Yk//AAAAAAAA + AAAAAAAAAAAAALeik//////////////+/v/+/f3//v38//78+//++vn//fr4//359//99/X//ff0//z1 + 8f//////gICA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+AgID/AAAA///////24tj/0cC3/35k + Uv8AAAAAAAAAAAAAAAAAAAAAt6KT///////////////////+/v///f3//vz8//78+v/9+/r//fn4//35 + 9v/9+PT//Pfz//////8AAAD/AAAA/4CAgP8AAAD/AAAA/wAAAP8AAAD/AAAA/8DAwP+AgID/gICA//// + ///Uw7r/gGdU/wAAAAAAAAAAAAAAAAAAAAC3opP//////////////////////////v/+/f3//vz8//77 + +//9+/r//vr4//349v/99/X//////wAAAP+AgID/wMDA/wAAAP8AAAD/AAAA/wAAAP8AAAD/wMDA//// + ////////9+Tc/9fHvv+CaVf/AAAAAAAAAAAAAAAAAAAAALeik//////////////////////////////+ + /v/+/f3//v38//77+//++vn//fn4//359v//////AAAA//////+AgID/AAAA/wAAAP+AgID/AAAA/wAA + AP8AAAD///////jn3//35t3/9+Tb/4RsWv8AAAAAAAAAAAAAAAAAAAAA0ZFy/9GRcv/RkXL/0Ixr/9CG + Yf/OflX/zXVJ/8xtPf/KZDH/yl0n/8lZIP/JWSD/yVkg//////+AgID//////wAAAP8AAAD/AAAA//// + //8AAAD/AAAA/wAAAP/AwMD//////8lZIP/JWSD/yVkg/wAAAAAAAAAAAAAAAAAAAADRkXL//sCf//6+ + nf/9vJv//bqY//25lv/8t5P/+7WQ//uzjf/7sIr/+q6G//mrg//4qYD/////////////////AAAA/wAA + AP//////9Jpt//////+AgID/AAAA/4CAgP//////85Vl//OVZf/JWSD/AAAAAAAAAAAAAAAAAAAAANGR + cv/+wJ///r6d//29m//9u5j//bmV//y2k//7tZD/+7KN//uwiv/5rob/+auE//ipgP/4p37/96V6//// + //8AAAD/AAAA///////0mm3/9Jhq//////8AAAD/gICA///////zlWX/85Vl/8lZIP8AAAAAAAAAAAAA + AAAAAAAA0ZFy//7Anv/+v53//rya//27mP/9uZX//LaT//y1kP/7s43/+rGK//muh//5q4P/+aqB//en + ff/3pXr//////4CAgP8AAAD///////SabP/0mGr//////4CAgP/AwMD///////OVZf/zlWX/yVkg/wAA + AAAAAAAAAAAAAAAAAADRkXL/0ZFy/9GRcv/QjGv/0IZh/85+Vf/NdUn/zG09/8pkMf/KXSf/yVkg/8lZ + IP/JWSD/yVkg/8lZIP//////wMDA/wAAAP//////yVkg/8lZIP//////gICA///////JWSD/yVkg/8lZ + IP/JWSD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgICA/4CAgP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////+AA///gAP//4AD//wAAAAEAAAABAAAAAQAA + AAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAeAAAAHgAAAB4AAAAeAA + AAHgAAAB4AAAAeAAAAHgAAAB4AAAAeAAAAH///P//////ygAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC3opP/Y0k1/2NJNf9jSTX/Y0k1/2NJNf9jSTX/Y0k1/wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt6KT//Xe0//d0Mn/2szE/9fHv//Uw7r/0b+1/2NJ + Nf9jSTX/Y0k1/2NJNf9jSTX/Y0k1/2NJNf+3opP/Y0k1/7eik//35Nv/6ptz/+qVa//qkGL/6opa/9TD + u/9jSTX/27+t/9u7p//bu6f/27un/8+0o/9jSTX/t6KT/wGn6P+7ppf/+eni//jl3f/24tj/9d/T//Tb + zv/ZycH/Y0k1//fk2//24df/9d3T//Tbzv/PtKP/Y0k1/7eik/8Tsez/wauc//rv6v/qm3P/6pVr/+qS + ZP/rj2D/9NvO/2ZNOf/3597/9+Pb//bg1v/13tL/z7Sj/2NJNf+7ppf/NcHw/8eyo//89PD//ff1/+Lt + 4f/56eL/+OXd//bi2P9kSjb/+eri//fn3v/249n/9uDW/8+0o/9jSTX/wauc/1/R8//Puan//fn2/+qb + c//qlWv/6pJk/+uPYP/l29b/Zk05//rs5//46eL/9+bd//bi2f/Quav/Y0k1/8eyo/+H4fX/2MKy//78 + +////f3//vz5//359v/89fP/+/Lv/2RKNv/67un/+erk//jo4f/35dz/0cG2/2NJNf/Puan/pe73/9jC + sv/YwrL/1L+u/9jCsv/YwrL/2MKy/9jCsv9kSjb/+/Lu//rv6f/56+X/+Ojh/9HBtv9jSTX/2MKy/6vw + 9/+r8Pf/q/D3/6vw9/+r8Pf/q/D3/2RKNv/9+/n//fj1//z18f/78u3/+u7p//nq5P/Rwbb/Y0k1/9jC + sv/YwrL/1L+u/9jCsv/YwrL/2MKy/9jCsv9kSjb//vz8//76+f/99/X/+/Tx//vx7f/67uj/+erj/2NJ + Nf8AAAAAAAAAAOqqi//qqov/6qqL/+mlhP/pn3r/55du/+aOYv/lhlb/431K/+N2QP/icjn/4nI5/+Jy + Of/IYi//AAAAAAAAAADqqov//8Ki//7An//9vZr//LmW//u1kP/6sIv/+auE//inff/2onf/9Z1x//WZ + av/zlWX/zWUx/wAAAAAAAAAA6qqL/+qqi//qqov/6qqL/+qmhv/poX//6Jt2/+eUbP/mjmL/5YdY/+SB + Tv/ke0b/43Y+/+JyOf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA//8AAMA/AADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAwAAAAMAAAADAAAAA//8AAA== + + + \ No newline at end of file diff --git a/Samples/Oxygene/FTP Sync/Main.pas b/Samples/Oxygene/FTP Sync/Main.pas new file mode 100644 index 0000000..35cd448 --- /dev/null +++ b/Samples/Oxygene/FTP Sync/Main.pas @@ -0,0 +1,19 @@ +namespace FtpSync; + +interface + +type + ConsoleApp = class + public + class method Main(args: array of string); + end; + +implementation + +class method ConsoleApp.Main(args: array of string); +begin + with lWorker := new FtpSyncWorker() do + if (lWorker.CheckArgs(args)) then lWorker.Sync(); +end; + +end. \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Responses/App.ico b/Samples/Oxygene/HTTP Responses/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/Oxygene/HTTP Responses/App.ico differ diff --git a/Samples/Oxygene/HTTP Responses/AssemblyInfo.pas b/Samples/Oxygene/HTTP Responses/AssemblyInfo.pas new file mode 100644 index 0000000..71812a7 --- /dev/null +++ b/Samples/Oxygene/HTTP Responses/AssemblyInfo.pas @@ -0,0 +1,49 @@ +namespace HTTPResponses; + +interface + +uses + System.Reflection; + +[assembly: AssemblyTitle('')] +[assembly: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] +[assembly: AssemblyVersion('1.0.0.1')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory, which in Chrome by default is the +// same as the project directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] + +implementation + +end. \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Responses/HTTPResponses.2008.sln b/Samples/Oxygene/HTTP Responses/HTTPResponses.2008.sln new file mode 100644 index 0000000..1ceac09 --- /dev/null +++ b/Samples/Oxygene/HTTP Responses/HTTPResponses.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "HTTPResponses", "HTTPResponses.oxygene", "{7F2AEC27-045A-4023-BF01-488C214D4A3D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7F2AEC27-045A-4023-BF01-488C214D4A3D}.Debug|Default.ActiveCfg = Debug + {7F2AEC27-045A-4023-BF01-488C214D4A3D}.Debug|Default.Build.0 = Debug + {7F2AEC27-045A-4023-BF01-488C214D4A3D}.Release|Default.ActiveCfg = Release + {7F2AEC27-045A-4023-BF01-488C214D4A3D}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/HTTP Responses/HTTPResponses.2010.sln b/Samples/Oxygene/HTTP Responses/HTTPResponses.2010.sln new file mode 100644 index 0000000..987a9f5 --- /dev/null +++ b/Samples/Oxygene/HTTP Responses/HTTPResponses.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "HTTPResponses", "HTTPResponses.oxygene", "{7F2AEC27-045A-4023-BF01-488C214D4A3D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7F2AEC27-045A-4023-BF01-488C214D4A3D}.Debug|Default.ActiveCfg = Debug + {7F2AEC27-045A-4023-BF01-488C214D4A3D}.Debug|Default.Build.0 = Debug + {7F2AEC27-045A-4023-BF01-488C214D4A3D}.Release|Default.ActiveCfg = Release + {7F2AEC27-045A-4023-BF01-488C214D4A3D}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/HTTP Responses/HTTPResponses.oxygene b/Samples/Oxygene/HTTP Responses/HTTPResponses.oxygene new file mode 100644 index 0000000..8f83312 --- /dev/null +++ b/Samples/Oxygene/HTTP Responses/HTTPResponses.oxygene @@ -0,0 +1,47 @@ + + + HTTPResponses + WinExe + HTTPResponses + False + App.ico + Release + {7F2AEC27-045A-4023-BF01-488C214D4A3D} + v2.0 + + + DEBUG;TRACE; + .\bin\Debug + True + + + .\bin\Release + False + + + + + + + + + + + + + + + + + + + + + + Form + HTTPResponses.MainForm + + + + + \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Responses/HttpResponses.Sample.html b/Samples/Oxygene/HTTP Responses/HttpResponses.Sample.html new file mode 100644 index 0000000..3fa0428 --- /dev/null +++ b/Samples/Oxygene/HTTP Responses/HttpResponses.Sample.html @@ -0,0 +1,47 @@ + + + + + + + + + +
+

+ HTTP Responses Sample +

+ +
+

Purpose

+

+This sample shows how to create and configure a simple HTTP server. +

+ +

Examine the Code

+
    +
  • +See the httpServer_OnHttpRequest handler, where all possible RequestPaths + are processed. +The options possible are as follows: +
      +
    • / or /home - load home page with all available links.
    • +
    • /file - download the HttpResponses application.
    • +
    • /bytes - download random.bin file that contains random generated data.
    • +
    • /error - shows a custom error with code 555.
    • +
    +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run the application.
  • +
  • Click the link on the top of the application window to open the home page of our server in your web browser.
  • +
  • Try all the links on the home page.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Responses/Main.pas b/Samples/Oxygene/HTTP Responses/Main.pas new file mode 100644 index 0000000..7ccb791 --- /dev/null +++ b/Samples/Oxygene/HTTP Responses/Main.pas @@ -0,0 +1,222 @@ +namespace HTTPResponses; + +interface + +uses + System.Windows.Forms, + System.Drawing, + System.Diagnostics, + System.IO; + +type + /// + /// Summary description for MainForm. + /// + MainForm = class(System.Windows.Forms.Form) + {$REGION Windows Form Designer generated fields} + private + components: System.ComponentModel.IContainer; + lb_Log: System.Windows.Forms.ListBox; + httpServer: RemObjects.InternetPack.Http.HttpServer; + llblinkLabel1: System.Windows.Forms.LinkLabel; + method InitializeComponent; + {$ENDREGION} + private + method httpServer_OnHttpRequest(aSender: System.Object; ea: RemObjects.InternetPack.Http.OnHttpRequestArgs); + method MainForm_Closed(sender: System.Object; e: System.EventArgs); + method MainForm_Load(sender: System.Object; e: System.EventArgs); + method llblinkLabel1_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); + protected + method Dispose(aDisposing: boolean); override; + public + constructor; + class method Main; + end; + +implementation + +{$REGION Construction and Disposition} +constructor MainForm; +begin + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // +end; + +method MainForm.Dispose(aDisposing: boolean); +begin + if aDisposing then begin + if assigned(components) then + components.Dispose(); + + // + // TODO: Add custom disposition code here + // + end; + inherited Dispose(aDisposing); +end; +{$ENDREGION} + +{$REGION Windows Form Designer generated code} +method MainForm.InitializeComponent; +begin + self.components := new System.ComponentModel.Container(); + var resources: System.ComponentModel.ComponentResourceManager := new System.ComponentModel.ComponentResourceManager(typeOf(MainForm)); + self.llblinkLabel1 := new System.Windows.Forms.LinkLabel(); + self.httpServer := new RemObjects.InternetPack.Http.HttpServer(self.components); + self.lb_Log := new System.Windows.Forms.ListBox(); + self.SuspendLayout(); + // + // llblinkLabel1 + // + self.llblinkLabel1.Location := new System.Drawing.Point(8, 10); + self.llblinkLabel1.Name := 'llblinkLabel1'; + self.llblinkLabel1.Size := new System.Drawing.Size(100, 23); + self.llblinkLabel1.TabIndex := 2; + self.llblinkLabel1.TabStop := true; + self.llblinkLabel1.Text := 'http://localhost:82'; + self.llblinkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(@self.llblinkLabel1_LinkClicked); + // + // httpServer + // + self.httpServer.Port := 82; + self.httpServer.ValidateRequests := false; + self.httpServer.OnHttpRequest += new RemObjects.InternetPack.Http.OnHttpRequestHandler(@self.httpServer_OnHttpRequest); + // + // lb_Log + // + self.lb_Log.Anchor := ((((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Bottom) + or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.lb_Log.IntegralHeight := false; + self.lb_Log.Location := new System.Drawing.Point(11, 32); + self.lb_Log.Name := 'lb_Log'; + self.lb_Log.Size := new System.Drawing.Size(371, 228); + self.lb_Log.TabIndex := 3; + // + // MainForm + // + self.AutoScaleBaseSize := new System.Drawing.Size(5, 13); + self.ClientSize := new System.Drawing.Size(394, 272); + self.Controls.Add(self.lb_Log); + self.Controls.Add(self.llblinkLabel1); + self.Icon := (resources.GetObject('$this.Icon') as System.Drawing.Icon); + self.MinimumSize := new System.Drawing.Size(410, 310); + self.Name := 'MainForm'; + self.Text := 'Internet Pack HTTP Response Sample'; + self.Load += new System.EventHandler(@self.MainForm_Load); + self.Closed += new System.EventHandler(@self.MainForm_Closed); + self.ResumeLayout(false); +end; +{$ENDREGION} + +{$REGION Application Entry Point} +[STAThread] +class method MainForm.Main; +begin + Application.EnableVisualStyles(); + + try + with lForm := new MainForm() do + Application.Run(lForm); + except + on E: Exception do begin + MessageBox.Show(E.Message); + end; + end; +end; +{$ENDREGION} + +const sWelcome: String = + 'Internet Pack HTTP Responses Test App'+ + '

'+ + 'Valid links:'+ + '
'+ + '/home show this page'+ + '
'+ + '/file send back a file (this .exe)'+ + '
'+ + '/bytes send back a buffer of random bytes'+ + '
'+ + '/error Display a custom error'; + +method MainForm.llblinkLabel1_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); +begin + Process.Start(llblinkLabel1.Text); +end; + +method MainForm.MainForm_Load(sender: System.Object; e: System.EventArgs); +begin + httpServer.Active := true; +end; + +method MainForm.MainForm_Closed(sender: System.Object; e: System.EventArgs); +begin + httpServer.Active := false; +end; + +method MainForm.httpServer_OnHttpRequest(aSender: System.Object; ea: RemObjects.InternetPack.Http.OnHttpRequestArgs); +var + lBuffer: array of byte; + lExeName: String; + lRandom: Random; +begin + lb_Log.Invoke(method begin + lb_Log.Items.Add(ea.Request.Header.RequestPath); + end); + + case (ea.Request.Header.RequestPath) of + //--------------------------------------------------- + '/', '/home': begin + ea.Response.ContentString := sWelcome; + ea.Response.Header.SetHeaderValue('Content-Type', 'text/html'); + end; + //--------------------------------------------------- + '/bytes': begin + lBuffer := new byte[256]; + lRandom := new Random(); + lRandom.NextBytes(lBuffer); + ea.Response.ContentBytes := lBuffer; + ea.Response.Header.SetHeaderValue('Content-Disposition', 'filename=random.bin'); + ea.Response.Header.SetHeaderValue('Content-Type', 'application/binary'); + end; + //--------------------------------------------------- + '/error': begin + ea.Response.SendError(555, 'Custom Error', 'A custom error message'); + end; + //--------------------------------------------------- + '/file': begin + lExeName := Self.GetType().Assembly.Location; + try + ea.Response.ContentStream := + new FileStream( + lExeName, + FileMode.Open, + FileAccess.Read, + FileShare.Read + ); + ea.Response.Header.SetHeaderValue( + 'Content-Disposition', + String.Format('filename="{0}"', + Path.GetFileName(lExeName)) + ); + ea.Response.Header.SetHeaderValue('Content-Type', 'application/binary'); + ea.Response.CloseStream := true; + except + on e: Exception do begin + ea.Response.SendError(404, String.Format('File {0} not found', lExeName), e); + end; + end; + end + //--------------------------------------------------- + else ea.Response.SendError(404, 'Requested path not found'); + //--------------------------------------------------- + end; +end; + +end. \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Responses/Main.resx b/Samples/Oxygene/HTTP Responses/Main.resx new file mode 100644 index 0000000..cf05be8 --- /dev/null +++ b/Samples/Oxygene/HTTP Responses/Main.resx @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + 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 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + YP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xMTP9LS0v/SUlJ/0dH + R/9GRkafRUVFDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpqah9paWmfaGho/2Zm + Zv9kZGT/Y2Nj/2FhYf9gYGD/Xl5e/1xcXP9bW1v/WVlZ/1hYWP9WVlb/VFRU/1NTU/9RUVH/T09P/05O + Tv9MTEz/S0tLn0lJSR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAaGhoD2dnZ19mZmavZGRk72NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZWf9YWFj/VlZW/1RU + VP9TU1PvUVFRr09PT19PT08PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjYy9iYmJPYWFhf19fX39eXl5/XFxcf1pa + Wn9ZWVl/V1dXT1ZWVi8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD//wAA//8AAP/8AAA//wAA//AAAA// + AAD/wAAAA/8AAP+AAAAB/wAA/wAAAAD/AAD+AAAAAH8AAPwAAAAAPwAA+AAAAAAfAADwAAAAAA8AAPAA + AAAADwAA4AAAAAAHAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAABwAA4AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAP4A + AAAAfwAA/4AAAAH/AAD/wAAAA/8AAP/gAAAH/wAA//gAAB//AAD//gAAf/8AAP//4Af//wAAKAAAACAA + AABAAAAAAQAgAAAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISEPHx8fXx0dHZ8aGhrPGBgY/xUVFf8TExP/ERER/w4ODu8MDAy/CgoKfwgI + CC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApKSkfJiYmnyQkJO8hISH/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/w4O + Dv8MDAz/CQkJ/wcHB88GBgZfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAALS0tfysrK/8oKCj/JiYm/yQkJP8hISH/Hx8f/x0dHf8aGhr/GBgY/xUV + Ff8TExP/ERER/w4ODv8MDAz/CQkJ/wcHB/8FBQW/BAQELwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAANTU1DzIyMs8vLy//LS0t/ysrK/8oKCj/NjYf/1JSD/9dXQj/bW0A/2lp + AP9kZAD/YGAA/1NTAv9CQgX/KioK/xISDf8MDAz/CQkJ/wcHB/8FBQXvAwMDXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5OQ83NzfPNDQ0/zIyMv8vLy//Q0Mi/2xsCv9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/T08A/y8vBv8MDAz/CQkJ/wcHB/8FBQX/AwMDXwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + 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 + + + \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Spy/App.ico b/Samples/Oxygene/HTTP Spy/App.ico new file mode 100644 index 0000000..2103830 Binary files /dev/null and b/Samples/Oxygene/HTTP Spy/App.ico differ diff --git a/Samples/Oxygene/HTTP Spy/AssemblyInfo.pas b/Samples/Oxygene/HTTP Spy/AssemblyInfo.pas new file mode 100644 index 0000000..c03946c --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/AssemblyInfo.pas @@ -0,0 +1,49 @@ +namespace HTTPSpy; + +interface + +uses + System.Reflection; + +[assembly: AssemblyTitle('')] +[assembly: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] +[assembly: AssemblyVersion('1.0.0.1')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory, which in Chrome by default is the +// same as the project directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] + +implementation + +end. \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Spy/HTTPSpy.2008.sln b/Samples/Oxygene/HTTP Spy/HTTPSpy.2008.sln new file mode 100644 index 0000000..78db54c --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/HTTPSpy.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "HTTPSpy", "HTTPSpy.oxygene", "{68032377-D8D8-4FEB-88F9-92658A6A56A2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {68032377-D8D8-4FEB-88F9-92658A6A56A2}.Debug|Default.ActiveCfg = Debug + {68032377-D8D8-4FEB-88F9-92658A6A56A2}.Debug|Default.Build.0 = Debug + {68032377-D8D8-4FEB-88F9-92658A6A56A2}.Release|Default.ActiveCfg = Release + {68032377-D8D8-4FEB-88F9-92658A6A56A2}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/HTTP Spy/HTTPSpy.2010.sln b/Samples/Oxygene/HTTP Spy/HTTPSpy.2010.sln new file mode 100644 index 0000000..22f1453 --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/HTTPSpy.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "HTTPSpy", "HTTPSpy.oxygene", "{68032377-D8D8-4FEB-88F9-92658A6A56A2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {68032377-D8D8-4FEB-88F9-92658A6A56A2}.Debug|Default.ActiveCfg = Debug + {68032377-D8D8-4FEB-88F9-92658A6A56A2}.Debug|Default.Build.0 = Debug + {68032377-D8D8-4FEB-88F9-92658A6A56A2}.Release|Default.ActiveCfg = Release + {68032377-D8D8-4FEB-88F9-92658A6A56A2}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/HTTP Spy/HTTPSpy.Sample.html b/Samples/Oxygene/HTTP Spy/HTTPSpy.Sample.html new file mode 100644 index 0000000..781cf29 --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/HTTPSpy.Sample.html @@ -0,0 +1,41 @@ + + + + + + + + +
+

+ HTTPSpy Sample +

+ +
+

Purpose

+

+This example demonstrates how HTTP works. +With help of the HttpClient components, we can send a  HttpClientRequest and retrieve back a HttpClientResponse. The application shows us the request and response header parameters and the context of the response in text and hex view. + Also, you have the ability to add your custom request header parameter. +

+ +

Examine the Code

+
    +
  • +See the btnSubmit_Click handler. This is the main function where we create and configure the HttpClientRequest and send it to the server with the help of the + HttpClient component. +In order to get response from the server we call the Dispatch function with our HttpClientRequest instance as a parameter. +
  • +
+ + +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run the  application.
  • +
  • Using an existing URL, click on the "Submit" button. See the response of the request on the Result page.
  • +
+ + + diff --git a/Samples/Oxygene/HTTP Spy/HTTPSpy.oxygene b/Samples/Oxygene/HTTP Spy/HTTPSpy.oxygene new file mode 100644 index 0000000..d08c969 --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/HTTPSpy.oxygene @@ -0,0 +1,52 @@ + + + HTTPSpy + WinExe + HTTPSpy + False + False + App.ico + Release + {68032377-D8D8-4FEB-88F9-92658A6A56A2} + v2.0 + + + DEBUG;TRACE; + .\bin\Debug + True + + + .\bin\Release + False + + + + + + + + + + + + + + + + + + + + + + Form + HTTPSpy.MainForm + + + + + + + + + \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Spy/Main.pas b/Samples/Oxygene/HTTP Spy/Main.pas new file mode 100644 index 0000000..58f70a7 --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/Main.pas @@ -0,0 +1,626 @@ +namespace HTTPSpy; + +interface + +uses + System.Windows.Forms, + System.Data, + System.Drawing, + System.Text, + RemObjects.InternetPack.Http; + +type + /// + /// Summary description for MainForm. + /// + MainForm = class(System.Windows.Forms.Form) + {$REGION Windows Form Designer generated field} + private + tabControl1: System.Windows.Forms.TabControl; + dvHeaders: System.Data.DataView; + dataGrid1: System.Windows.Forms.DataGrid; + tabPage2: System.Windows.Forms.TabPage; + splitter2: System.Windows.Forms.Splitter; + dataGrid2: System.Windows.Forms.DataGrid; + dvParams: System.Data.DataView; + httpClient1: RemObjects.InternetPack.Http.HttpClient; + cbKeepAlive: System.Windows.Forms.CheckBox; + btnSubmit: System.Windows.Forms.Button; + rbPost: System.Windows.Forms.RadioButton; + label1: System.Windows.Forms.Label; + rbGet: System.Windows.Forms.RadioButton; + edUrl: System.Windows.Forms.TextBox; + pictureBox1: System.Windows.Forms.PictureBox; + pnlpanel2: System.Windows.Forms.Panel; + dataColumn4: System.Data.DataColumn; + dataColumn3: System.Data.DataColumn; + tblParams: System.Data.DataTable; + dataColumn2: System.Data.DataColumn; + dataColumn1: System.Data.DataColumn; + tblHeaders: System.Data.DataTable; + dataSet1: System.Data.DataSet; + dataGrid3: System.Windows.Forms.DataGrid; + splitter1: System.Windows.Forms.Splitter; + rbHex: System.Windows.Forms.RadioButton; + pnlpanel1: System.Windows.Forms.Panel; + edResult: System.Windows.Forms.TextBox; + tabPage1: System.Windows.Forms.TabPage; + rbText: System.Windows.Forms.RadioButton; + dataColumn6: System.Data.DataColumn; + dataColumn5: System.Data.DataColumn; + tblResponseHeaders: System.Data.DataTable; + components: System.ComponentModel.Container := nil; + method InitializeComponent; + {$ENDREGION} + private + _LastResultString : String; + _LastResultBytes: Array of byte; + _LastLength: Integer := 0; + hexWidth: Integer := 16; + + method MainForm_Load(sender: System.Object; e: System.EventArgs); + method rbText_CheckedChanged(sender: System.Object; e: System.EventArgs); + method btnSubmit_Click(sender: System.Object; e: System.EventArgs); + method AddHeader(Name: string; Value: string); + method AddResponseHeader(Name: string; Value: string); + method ShowResponse(aResponse: HttpClientResponse); + method SetResultText(); + protected + method Dispose(aDisposing: boolean); override; + public + constructor; + end; + +implementation + +{$REGION Construction and Disposition} +constructor MainForm; +begin + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // +end; + +method MainForm.Dispose(aDisposing: boolean); +begin + if aDisposing then begin + if assigned(components) then + components.Dispose(); + + // + // TODO: Add custom disposition code here + // + end; + inherited Dispose(aDisposing); +end; +{$ENDREGION} + +{$REGION Windows Form Designer generated code} +method MainForm.InitializeComponent; +begin + var resources: System.ComponentModel.ComponentResourceManager := new System.ComponentModel.ComponentResourceManager(typeOf(MainForm)); + self.tblResponseHeaders := new System.Data.DataTable(); + self.dataColumn5 := new System.Data.DataColumn(); + self.dataColumn6 := new System.Data.DataColumn(); + self.rbText := new System.Windows.Forms.RadioButton(); + self.tabPage1 := new System.Windows.Forms.TabPage(); + self.edResult := new System.Windows.Forms.TextBox(); + self.pnlpanel1 := new System.Windows.Forms.Panel(); + self.rbHex := new System.Windows.Forms.RadioButton(); + self.splitter1 := new System.Windows.Forms.Splitter(); + self.dataGrid3 := new System.Windows.Forms.DataGrid(); + self.dataSet1 := new System.Data.DataSet(); + self.tblHeaders := new System.Data.DataTable(); + self.dataColumn1 := new System.Data.DataColumn(); + self.dataColumn2 := new System.Data.DataColumn(); + self.tblParams := new System.Data.DataTable(); + self.dataColumn3 := new System.Data.DataColumn(); + self.dataColumn4 := new System.Data.DataColumn(); + self.pnlpanel2 := new System.Windows.Forms.Panel(); + self.pictureBox1 := new System.Windows.Forms.PictureBox(); + self.edUrl := new System.Windows.Forms.TextBox(); + self.rbGet := new System.Windows.Forms.RadioButton(); + self.label1 := new System.Windows.Forms.Label(); + self.rbPost := new System.Windows.Forms.RadioButton(); + self.btnSubmit := new System.Windows.Forms.Button(); + self.cbKeepAlive := new System.Windows.Forms.CheckBox(); + self.httpClient1 := new RemObjects.InternetPack.Http.HttpClient(); + self.dvParams := new System.Data.DataView(); + self.dataGrid2 := new System.Windows.Forms.DataGrid(); + self.splitter2 := new System.Windows.Forms.Splitter(); + self.tabPage2 := new System.Windows.Forms.TabPage(); + self.dataGrid1 := new System.Windows.Forms.DataGrid(); + self.dvHeaders := new System.Data.DataView(); + self.tabControl1 := new System.Windows.Forms.TabControl(); + (self.tblResponseHeaders as System.ComponentModel.ISupportInitialize).BeginInit(); + self.tabPage1.SuspendLayout(); + self.pnlpanel1.SuspendLayout(); + (self.dataGrid3 as System.ComponentModel.ISupportInitialize).BeginInit(); + (self.dataSet1 as System.ComponentModel.ISupportInitialize).BeginInit(); + (self.tblHeaders as System.ComponentModel.ISupportInitialize).BeginInit(); + (self.tblParams as System.ComponentModel.ISupportInitialize).BeginInit(); + self.pnlpanel2.SuspendLayout(); + (self.pictureBox1 as System.ComponentModel.ISupportInitialize).BeginInit(); + (self.dvParams as System.ComponentModel.ISupportInitialize).BeginInit(); + (self.dataGrid2 as System.ComponentModel.ISupportInitialize).BeginInit(); + self.tabPage2.SuspendLayout(); + (self.dataGrid1 as System.ComponentModel.ISupportInitialize).BeginInit(); + (self.dvHeaders as System.ComponentModel.ISupportInitialize).BeginInit(); + self.tabControl1.SuspendLayout(); + self.SuspendLayout(); + // + // tblResponseHeaders + // + self.tblResponseHeaders.Columns.AddRange(array of System.Data.DataColumn([self.dataColumn5, + self.dataColumn6])); + self.tblResponseHeaders.TableName := 'ResponseHeaders'; + // + // dataColumn5 + // + self.dataColumn5.ColumnName := 'Name'; + // + // dataColumn6 + // + self.dataColumn6.ColumnName := 'Value'; + // + // rbText + // + self.rbText.Anchor := ((System.Windows.Forms.AnchorStyles.Bottom or System.Windows.Forms.AnchorStyles.Left) as System.Windows.Forms.AnchorStyles); + self.rbText.Checked := true; + self.rbText.Location := new System.Drawing.Point(8, 4); + self.rbText.Name := 'rbText'; + self.rbText.Size := new System.Drawing.Size(60, 20); + self.rbText.TabIndex := 5; + self.rbText.TabStop := true; + self.rbText.Text := 'Text'; + self.rbText.CheckedChanged += new System.EventHandler(@self.rbText_CheckedChanged); + // + // tabPage1 + // + self.tabPage1.Controls.Add(self.edResult); + self.tabPage1.Controls.Add(self.pnlpanel1); + self.tabPage1.Controls.Add(self.splitter1); + self.tabPage1.Controls.Add(self.dataGrid3); + self.tabPage1.Location := new System.Drawing.Point(4, 22); + self.tabPage1.Name := 'tabPage1'; + self.tabPage1.Padding := new System.Windows.Forms.Padding(5); + self.tabPage1.Size := new System.Drawing.Size(660, 480); + self.tabPage1.TabIndex := 0; + self.tabPage1.Text := 'Result'; + // + // edResult + // + self.edResult.Dock := System.Windows.Forms.DockStyle.Fill; + self.edResult.Location := new System.Drawing.Point(5, 213); + self.edResult.Multiline := true; + self.edResult.Name := 'edResult'; + self.edResult.ReadOnly := true; + self.edResult.ScrollBars := System.Windows.Forms.ScrollBars.Vertical; + self.edResult.Size := new System.Drawing.Size(650, 235); + self.edResult.TabIndex := 4; + // + // pnlpanel1 + // + self.pnlpanel1.Controls.Add(self.rbHex); + self.pnlpanel1.Controls.Add(self.rbText); + self.pnlpanel1.Dock := System.Windows.Forms.DockStyle.Bottom; + self.pnlpanel1.Location := new System.Drawing.Point(5, 448); + self.pnlpanel1.Name := 'pnlpanel1'; + self.pnlpanel1.Size := new System.Drawing.Size(650, 27); + self.pnlpanel1.TabIndex := 10; + // + // rbHex + // + self.rbHex.Anchor := ((System.Windows.Forms.AnchorStyles.Bottom or System.Windows.Forms.AnchorStyles.Left) as System.Windows.Forms.AnchorStyles); + self.rbHex.Location := new System.Drawing.Point(68, 4); + self.rbHex.Name := 'rbHex'; + self.rbHex.Size := new System.Drawing.Size(60, 20); + self.rbHex.TabIndex := 6; + self.rbHex.Text := 'Hex'; + // + // splitter1 + // + self.splitter1.Dock := System.Windows.Forms.DockStyle.Top; + self.splitter1.Location := new System.Drawing.Point(5, 208); + self.splitter1.Name := 'splitter1'; + self.splitter1.Size := new System.Drawing.Size(650, 5); + self.splitter1.TabIndex := 8; + self.splitter1.TabStop := false; + // + // dataGrid3 + // + self.dataGrid3.CaptionText := 'Result Headers'; + self.dataGrid3.DataMember := ''; + self.dataGrid3.DataSource := self.tblResponseHeaders; + self.dataGrid3.Dock := System.Windows.Forms.DockStyle.Top; + self.dataGrid3.HeaderForeColor := System.Drawing.SystemColors.ControlText; + self.dataGrid3.Location := new System.Drawing.Point(5, 5); + self.dataGrid3.Name := 'dataGrid3'; + self.dataGrid3.PreferredColumnWidth := 300; + self.dataGrid3.ReadOnly := true; + self.dataGrid3.Size := new System.Drawing.Size(650, 203); + self.dataGrid3.TabIndex := 7; + // + // dataSet1 + // + self.dataSet1.DataSetName := 'NewDataSet'; + self.dataSet1.Locale := new System.Globalization.CultureInfo('en-US'); + self.dataSet1.Tables.AddRange(array of System.Data.DataTable([self.tblHeaders, + self.tblParams, + self.tblResponseHeaders])); + // + // tblHeaders + // + self.tblHeaders.Columns.AddRange(array of System.Data.DataColumn([self.dataColumn1, + self.dataColumn2])); + self.tblHeaders.TableName := 'Headers'; + // + // dataColumn1 + // + self.dataColumn1.ColumnName := 'Name'; + // + // dataColumn2 + // + self.dataColumn2.ColumnName := 'Value'; + // + // tblParams + // + self.tblParams.Columns.AddRange(array of System.Data.DataColumn([self.dataColumn3, + self.dataColumn4])); + self.tblParams.TableName := 'Params'; + // + // dataColumn3 + // + self.dataColumn3.ColumnName := 'Name'; + // + // dataColumn4 + // + self.dataColumn4.ColumnName := 'Value'; + // + // pnlpanel2 + // + self.pnlpanel2.Controls.Add(self.pictureBox1); + self.pnlpanel2.Controls.Add(self.edUrl); + self.pnlpanel2.Controls.Add(self.rbGet); + self.pnlpanel2.Controls.Add(self.label1); + self.pnlpanel2.Controls.Add(self.rbPost); + self.pnlpanel2.Controls.Add(self.btnSubmit); + self.pnlpanel2.Controls.Add(self.cbKeepAlive); + self.pnlpanel2.Dock := System.Windows.Forms.DockStyle.Top; + self.pnlpanel2.Location := new System.Drawing.Point(0, 0); + self.pnlpanel2.Name := 'pnlpanel2'; + self.pnlpanel2.Size := new System.Drawing.Size(669, 71); + self.pnlpanel2.TabIndex := 12; + // + // pictureBox1 + // + self.pictureBox1.Anchor := ((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.pictureBox1.Image := (resources.GetObject('pictureBox1.Image') as System.Drawing.Image); + self.pictureBox1.Location := new System.Drawing.Point(548, 40); + self.pictureBox1.Name := 'pictureBox1'; + self.pictureBox1.Size := new System.Drawing.Size(120, 30); + self.pictureBox1.TabIndex := 10; + self.pictureBox1.TabStop := false; + // + // edUrl + // + self.edUrl.Anchor := (((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.edUrl.Location := new System.Drawing.Point(52, 12); + self.edUrl.Name := 'edUrl'; + self.edUrl.Size := new System.Drawing.Size(523, 20); + self.edUrl.TabIndex := 0; + self.edUrl.Text := 'http://www.remobjects.com'; + // + // rbGet + // + self.rbGet.Checked := true; + self.rbGet.Location := new System.Drawing.Point(52, 36); + self.rbGet.Name := 'rbGet'; + self.rbGet.Size := new System.Drawing.Size(42, 20); + self.rbGet.TabIndex := 7; + self.rbGet.TabStop := true; + self.rbGet.Text := 'Get'; + // + // label1 + // + self.label1.Location := new System.Drawing.Point(12, 16); + self.label1.Name := 'label1'; + self.label1.Size := new System.Drawing.Size(32, 23); + self.label1.TabIndex := 4; + self.label1.Text := 'URL:'; + // + // rbPost + // + self.rbPost.Location := new System.Drawing.Point(100, 36); + self.rbPost.Name := 'rbPost'; + self.rbPost.Size := new System.Drawing.Size(48, 20); + self.rbPost.TabIndex := 8; + self.rbPost.Text := 'Post'; + // + // btnSubmit + // + self.btnSubmit.Anchor := ((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.btnSubmit.Location := new System.Drawing.Point(596, 11); + self.btnSubmit.Name := 'btnSubmit'; + self.btnSubmit.Size := new System.Drawing.Size(72, 24); + self.btnSubmit.TabIndex := 2; + self.btnSubmit.Text := 'Submit'; + self.btnSubmit.Click += new System.EventHandler(@self.btnSubmit_Click); + // + // cbKeepAlive + // + self.cbKeepAlive.Checked := true; + self.cbKeepAlive.CheckState := System.Windows.Forms.CheckState.Checked; + self.cbKeepAlive.Location := new System.Drawing.Point(160, 36); + self.cbKeepAlive.Name := 'cbKeepAlive'; + self.cbKeepAlive.Size := new System.Drawing.Size(104, 20); + self.cbKeepAlive.TabIndex := 9; + self.cbKeepAlive.Text := 'Keep Alive'; + // + // httpClient1 + // + self.httpClient1.ConnectionClass := nil; + self.httpClient1.ConnectionFactory := nil; + self.httpClient1.CustomConnectionPool := nil; + self.httpClient1.HostAddress := nil; + self.httpClient1.HostName := nil; + self.httpClient1.Password := ''; + self.httpClient1.Port := 0; + self.httpClient1.Url := nil; + self.httpClient1.UserName := ''; + // + // dvParams + // + self.dvParams.Table := self.tblParams; + // + // dataGrid2 + // + self.dataGrid2.CaptionText := 'Request Content'; + self.dataGrid2.DataMember := ''; + self.dataGrid2.DataSource := self.dvParams; + self.dataGrid2.Dock := System.Windows.Forms.DockStyle.Bottom; + self.dataGrid2.HeaderForeColor := System.Drawing.SystemColors.ControlText; + self.dataGrid2.Location := new System.Drawing.Point(5, 294); + self.dataGrid2.Name := 'dataGrid2'; + self.dataGrid2.PreferredColumnWidth := 300; + self.dataGrid2.Size := new System.Drawing.Size(651, 181); + self.dataGrid2.TabIndex := 1; + // + // splitter2 + // + self.splitter2.Dock := System.Windows.Forms.DockStyle.Bottom; + self.splitter2.Location := new System.Drawing.Point(5, 289); + self.splitter2.Name := 'splitter2'; + self.splitter2.Size := new System.Drawing.Size(651, 5); + self.splitter2.TabIndex := 2; + self.splitter2.TabStop := false; + // + // tabPage2 + // + self.tabPage2.Controls.Add(self.splitter2); + self.tabPage2.Controls.Add(self.dataGrid2); + self.tabPage2.Controls.Add(self.dataGrid1); + self.tabPage2.Location := new System.Drawing.Point(4, 22); + self.tabPage2.Name := 'tabPage2'; + self.tabPage2.Padding := new System.Windows.Forms.Padding(5); + self.tabPage2.Size := new System.Drawing.Size(661, 480); + self.tabPage2.TabIndex := 1; + self.tabPage2.Text := 'Parameters'; + // + // dataGrid1 + // + self.dataGrid1.CaptionText := 'Request Headers'; + self.dataGrid1.DataMember := ''; + self.dataGrid1.DataSource := self.dvHeaders; + self.dataGrid1.Dock := System.Windows.Forms.DockStyle.Fill; + self.dataGrid1.HeaderForeColor := System.Drawing.SystemColors.ControlText; + self.dataGrid1.Location := new System.Drawing.Point(5, 5); + self.dataGrid1.Name := 'dataGrid1'; + self.dataGrid1.PreferredColumnWidth := 300; + self.dataGrid1.Size := new System.Drawing.Size(651, 470); + self.dataGrid1.TabIndex := 0; + // + // dvHeaders + // + self.dvHeaders.Table := self.tblHeaders; + // + // tabControl1 + // + self.tabControl1.Controls.Add(self.tabPage2); + self.tabControl1.Controls.Add(self.tabPage1); + self.tabControl1.Dock := System.Windows.Forms.DockStyle.Fill; + self.tabControl1.Location := new System.Drawing.Point(0, 71); + self.tabControl1.Name := 'tabControl1'; + self.tabControl1.SelectedIndex := 0; + self.tabControl1.Size := new System.Drawing.Size(669, 506); + self.tabControl1.TabIndex := 11; + // + // MainForm + // + self.ClientSize := new System.Drawing.Size(669, 577); + self.Controls.Add(self.tabControl1); + self.Controls.Add(self.pnlpanel2); + self.Icon := (resources.GetObject('$this.Icon') as System.Drawing.Icon); + self.MinimumSize := new System.Drawing.Size(685, 615); + self.Name := 'MainForm'; + self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen; + self.Text := 'RemObjects Internet Pack for .NET - HTTP Spy'; + self.Load += new System.EventHandler(@self.MainForm_Load); + (self.tblResponseHeaders as System.ComponentModel.ISupportInitialize).EndInit(); + self.tabPage1.ResumeLayout(false); + self.tabPage1.PerformLayout(); + self.pnlpanel1.ResumeLayout(false); + (self.dataGrid3 as System.ComponentModel.ISupportInitialize).EndInit(); + (self.dataSet1 as System.ComponentModel.ISupportInitialize).EndInit(); + (self.tblHeaders as System.ComponentModel.ISupportInitialize).EndInit(); + (self.tblParams as System.ComponentModel.ISupportInitialize).EndInit(); + self.pnlpanel2.ResumeLayout(false); + self.pnlpanel2.PerformLayout(); + (self.pictureBox1 as System.ComponentModel.ISupportInitialize).EndInit(); + (self.dvParams as System.ComponentModel.ISupportInitialize).EndInit(); + (self.dataGrid2 as System.ComponentModel.ISupportInitialize).EndInit(); + self.tabPage2.ResumeLayout(false); + (self.dataGrid1 as System.ComponentModel.ISupportInitialize).EndInit(); + (self.dvHeaders as System.ComponentModel.ISupportInitialize).EndInit(); + self.tabControl1.ResumeLayout(false); + self.ResumeLayout(false); +end; +{$ENDREGION} + +method MainForm.AddHeader(Name: string; Value: string); +begin + var aRow: DataRow := tblHeaders.NewRow(); + aRow['Name'] := Name; + aRow['Value'] := Value; + tblHeaders.Rows.Add(aRow); +end; + +method MainForm.AddResponseHeader(Name: string; Value: string); +begin + var aRow: DataRow := tblResponseHeaders.NewRow(); + aRow['Name'] := Name; + aRow['Value'] := Value; + tblResponseHeaders.Rows.Add(aRow); +end; + +method MainForm.btnSubmit_Click(sender: System.Object; e: System.EventArgs); +var + i: Integer; + aRow: DataRow; +begin + var lRequest: HttpClientRequest := new HttpClientRequest(); + if (rbPost.Checked) then begin + lRequest.RequestType := RequestType.Post; + + var lParams: string := ''; + + for i := 0 to tblParams.Rows.Count - 1 do begin + aRow := tblParams.Rows[i]; + lParams := lParams + string.Format('{0}={1}#10#13', aRow['Name'].ToString(),aRow['Value'].ToString()); + end; + lRequest.ContentString := lParams; + end; + + lRequest.Url.Parse(edUrl.Text); + + // set headers + for i := 0 to tblHeaders.Rows.Count - 1 do begin + aRow := tblHeaders.Rows[i]; + lRequest.Header.SetHeaderValue(aRow['Name'].ToString(),aRow['Value'].ToString()); + end; + + httpClient1.KeepAlive := cbKeepAlive.Checked; + lRequest.KeepAlive := httpClient1.KeepAlive; + + tblResponseHeaders.Clear(); + edResult.Text := ''; + + tabControl1.SelectedIndex := 1; + Application.DoEvents(); + + try + + var lResponse: HttpClientResponse := httpClient1.Dispatch(lRequest); + ShowResponse(lResponse); + except + On ex: HttpException do begin + ShowResponse(ex.Response); + end; + On ex: Exception do begin + _LastResultString := 'Error retrieving response: ' + ex.Message; + _LastResultBytes := new UnicodeEncoding().GetBytes(_LastResultString); + _LastLength := _LastResultBytes.Length; + SetResultText(); + end; + end; +end; + +method MainForm.ShowResponse(aResponse: HttpClientResponse); +var aRow: DataRow; +begin + _LastResultString := aResponse.ContentString; + _LastResultBytes := aResponse.ContentBytes; + try + _LastLength := aResponse.ContentLength; + except + _LastLength := _LastResultBytes.Length; + end; + + AddResponseHeader(aResponse.Header.FirstHeader,''); + for each aHeader: HttpHeader in aResponse.Header do begin + AddResponseHeader(aHeader.Name,aHeader.Value); + if aHeader.Name = 'Set-Cookie' then begin + if (MessageBox.Show('Keep Cookie for future requests?', 'Internet Pack', MessageBoxButtons.YesNo) = DialogResult.Yes) then begin + aRow := tblHeaders.NewRow(); + aRow['Name'] := 'Cookie'; + aRow['Value'] := aHeader.Value; + tblHeaders.Rows.Add(aRow); + end; + end; + end; + SetResultText(); +end; + +method MainForm.SetResultText(); +var i: Integer; +begin + Cursor := Cursors.WaitCursor; + try + if _LastLength <> 0 then begin + if (rbText.Checked) then begin + edResult.Text := _LastResultString; + edResult.Font := new Font('Courier New', 8.25); + end else begin + var lHex: string := ''; + var lChars: string := ''; + + for i := 0 to _LastLength - 1 do begin + if i mod hexWidth = 0 then begin + if i > 0 then begin + lHex := lHex + '| ' + lChars + System.Environment.NewLine; + lChars := ''; + end; + lHex := lHex + i.ToString('X8') + ': '; + end; + + lHex := lHex + _LastResultBytes[i].ToString('X2') + ' '; + if _LastResultBytes[i] < 32 then + lChars := lChars + '.' + else + lChars := lChars + char(_LastResultBytes[i]); + end; + + if (_LastLength mod hexWidth > 0) then begin + for i := _LastLength mod hexWidth to hexWidth - 1 do begin + lHex := lHex + ' '; + end; + lHex := lHex + '| ' + lChars; + end; + + edResult.Text := lHex; + edResult.Font := new Font('Courier New',8.25); + end; + end; + finally + Cursor := Cursors.Default; + end; +end; + +method MainForm.rbText_CheckedChanged(sender: System.Object; e: System.EventArgs); +begin + SetResultText(); +end; + +method MainForm.MainForm_Load(sender: System.Object; e: System.EventArgs); +begin + AddHeader('Accept',httpClient1.Accept); + AddHeader('User-Agent',httpClient1.UserAgent); +end; + +end. \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Spy/Main.resx b/Samples/Oxygene/HTTP Spy/Main.resx new file mode 100644 index 0000000..46977b2 --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/Main.resx @@ -0,0 +1,594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 9, 14 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + + 104, 14 + + + 210, 14 + + + 310, 14 + + + + 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 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + YP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xMTP9LS0v/SUlJ/0dH + R/9GRkafRUVFDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpqah9paWmfaGho/2Zm + Zv9kZGT/Y2Nj/2FhYf9gYGD/Xl5e/1xcXP9bW1v/WVlZ/1hYWP9WVlb/VFRU/1NTU/9RUVH/T09P/05O + Tv9MTEz/S0tLn0lJSR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAaGhoD2dnZ19mZmavZGRk72NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZWf9YWFj/VlZW/1RU + VP9TU1PvUVFRr09PT19PT08PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjYy9iYmJPYWFhf19fX39eXl5/XFxcf1pa + Wn9ZWVl/V1dXT1ZWVi8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD//wAA//8AAP/8AAA//wAA//AAAA// + AAD/wAAAA/8AAP+AAAAB/wAA/wAAAAD/AAD+AAAAAH8AAPwAAAAAPwAA+AAAAAAfAADwAAAAAA8AAPAA + AAAADwAA4AAAAAAHAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAABwAA4AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAP4A + AAAAfwAA/4AAAAH/AAD/wAAAA/8AAP/gAAAH/wAA//gAAB//AAD//gAAf/8AAP//4Af//wAAKAAAACAA + AABAAAAAAQAgAAAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISEPHx8fXx0dHZ8aGhrPGBgY/xUVFf8TExP/ERER/w4ODu8MDAy/CgoKfwgI + CC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApKSkfJiYmnyQkJO8hISH/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/w4O + Dv8MDAz/CQkJ/wcHB88GBgZfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAALS0tfysrK/8oKCj/JiYm/yQkJP8hISH/Hx8f/x0dHf8aGhr/GBgY/xUV + Ff8TExP/ERER/w4ODv8MDAz/CQkJ/wcHB/8FBQW/BAQELwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAANTU1DzIyMs8vLy//LS0t/ysrK/8oKCj/NjYf/1JSD/9dXQj/bW0A/2lp + AP9kZAD/YGAA/1NTAv9CQgX/KioK/xISDf8MDAz/CQkJ/wcHB/8FBQXvAwMDXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5OQ83NzfPNDQ0/zIyMv8vLy//Q0Mi/2xsCv9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/T08A/y8vBv8MDAz/CQkJ/wcHB/8FBQX/AwMDXwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + 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 + + + \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Spy/Program.pas b/Samples/Oxygene/HTTP Spy/Program.pas new file mode 100644 index 0000000..8074543 --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/Program.pas @@ -0,0 +1,32 @@ +namespace HTTPSpy; + +interface + +type + Program = public static class + public + class method Main; + end; + +implementation + +uses + System.Windows.Forms; + +/// +/// The main entry point for the application. +/// +[STAThread] +class method Program.Main; +begin + try + Application.EnableVisualStyles(); + Application.Run(new MainForm()); + except + on E: Exception do begin + MessageBox.Show(E.Message); + end; + end; +end; + +end. \ No newline at end of file diff --git a/Samples/Oxygene/HTTP Spy/Resources.resx b/Samples/Oxygene/HTTP Spy/Resources.resx new file mode 100644 index 0000000..c5770db --- /dev/null +++ b/Samples/Oxygene/HTTP Spy/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Samples/Oxygene/SMTP Client/App.ico b/Samples/Oxygene/SMTP Client/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/Oxygene/SMTP Client/App.ico differ diff --git a/Samples/Oxygene/SMTP Client/AssemblyInfo.pas b/Samples/Oxygene/SMTP Client/AssemblyInfo.pas new file mode 100644 index 0000000..7479159 --- /dev/null +++ b/Samples/Oxygene/SMTP Client/AssemblyInfo.pas @@ -0,0 +1,49 @@ +namespace SMTP_Client_Sample; + +interface + +uses + System.Reflection; + +[assembly: AssemblyTitle('')] +[assembly: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] +[assembly: AssemblyVersion('1.0.0.1')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory, which in Chrome by default is the +// same as the project directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] + +implementation + +end. \ No newline at end of file diff --git a/Samples/Oxygene/SMTP Client/MainForm.pas b/Samples/Oxygene/SMTP Client/MainForm.pas new file mode 100644 index 0000000..27baf72 --- /dev/null +++ b/Samples/Oxygene/SMTP Client/MainForm.pas @@ -0,0 +1,525 @@ +namespace SMTP_Client_Sample; + +interface + +uses + System.Windows.Forms, + System.Drawing; + +type + /// + /// Summary description for MainForm. + /// + MainForm = class(System.Windows.Forms.Form) + {$REGION Windows Form Designer generated fields} + private + smtpClient: RemObjects.InternetPack.Email.SmtpClient; + btnSendEMail: System.Windows.Forms.Button; + label8: System.Windows.Forms.Label; + txtBCC: System.Windows.Forms.TextBox; + label7: System.Windows.Forms.Label; + txtSubject: System.Windows.Forms.TextBox; + txtCC: System.Windows.Forms.TextBox; + label3: System.Windows.Forms.Label; + label6: System.Windows.Forms.Label; + txtTo: System.Windows.Forms.TextBox; + txtSenderName: System.Windows.Forms.TextBox; + label5: System.Windows.Forms.Label; + txtSenderAddress: System.Windows.Forms.TextBox; + txtMessage: System.Windows.Forms.TextBox; + lblFromEmail: System.Windows.Forms.Label; + lblSenderName: System.Windows.Forms.Label; + groupBox3: System.Windows.Forms.GroupBox; + chkUseAuth: System.Windows.Forms.CheckBox; + lblUserName: System.Windows.Forms.Label; + lblPassword: System.Windows.Forms.Label; + txtUserName: System.Windows.Forms.TextBox; + txtPassword: System.Windows.Forms.TextBox; + groupBox2: System.Windows.Forms.GroupBox; + txtSMTPServer: System.Windows.Forms.TextBox; + lblSMTPServer: System.Windows.Forms.Label; + groupBox1: System.Windows.Forms.GroupBox; + pictureBox1: System.Windows.Forms.PictureBox; + lblRO: System.Windows.Forms.Label; + lblIP: System.Windows.Forms.Label; + msg: RemObjects.InternetPack.Messages.MailMessage; + components: System.ComponentModel.Container := nil; + method InitializeComponent; + {$ENDREGION} + private + method btnSendEMail_Click(sender: System.Object; e: System.EventArgs); + method getRequiredValue(ctrl: TextBox; errorMessage: String): String; + method chkUseAuth_CheckedChanged(sender: System.Object; e: System.EventArgs); + protected + method Dispose(aDisposing: boolean); override; + public + constructor; + class method Main; + end; + +implementation + +{$REGION Construction and Disposition} +constructor MainForm; +begin + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // + msg := new RemObjects.InternetPack.Messages.MailMessage(); +end; + +method MainForm.Dispose(aDisposing: boolean); +begin + if aDisposing then begin + if assigned(components) then + components.Dispose(); + + // + // TODO: Add custom disposition code here + // + end; + inherited Dispose(aDisposing); +end; +{$ENDREGION} + +{$REGION Windows Form Designer generated code} +method MainForm.InitializeComponent; +begin + var resources: System.ComponentModel.ComponentResourceManager := new System.ComponentModel.ComponentResourceManager(typeOf(MainForm)); + self.lblIP := new System.Windows.Forms.Label(); + self.lblRO := new System.Windows.Forms.Label(); + self.pictureBox1 := new System.Windows.Forms.PictureBox(); + self.groupBox1 := new System.Windows.Forms.GroupBox(); + self.lblSMTPServer := new System.Windows.Forms.Label(); + self.txtSMTPServer := new System.Windows.Forms.TextBox(); + self.groupBox2 := new System.Windows.Forms.GroupBox(); + self.txtPassword := new System.Windows.Forms.TextBox(); + self.txtUserName := new System.Windows.Forms.TextBox(); + self.lblPassword := new System.Windows.Forms.Label(); + self.lblUserName := new System.Windows.Forms.Label(); + self.chkUseAuth := new System.Windows.Forms.CheckBox(); + self.groupBox3 := new System.Windows.Forms.GroupBox(); + self.lblSenderName := new System.Windows.Forms.Label(); + self.lblFromEmail := new System.Windows.Forms.Label(); + self.txtMessage := new System.Windows.Forms.TextBox(); + self.txtSenderAddress := new System.Windows.Forms.TextBox(); + self.label5 := new System.Windows.Forms.Label(); + self.txtSenderName := new System.Windows.Forms.TextBox(); + self.txtTo := new System.Windows.Forms.TextBox(); + self.label6 := new System.Windows.Forms.Label(); + self.label3 := new System.Windows.Forms.Label(); + self.txtCC := new System.Windows.Forms.TextBox(); + self.txtSubject := new System.Windows.Forms.TextBox(); + self.label7 := new System.Windows.Forms.Label(); + self.txtBCC := new System.Windows.Forms.TextBox(); + self.label8 := new System.Windows.Forms.Label(); + self.btnSendEMail := new System.Windows.Forms.Button(); + self.smtpClient := new RemObjects.InternetPack.Email.SmtpClient(); + (self.pictureBox1 as System.ComponentModel.ISupportInitialize).BeginInit(); + self.groupBox1.SuspendLayout(); + self.groupBox2.SuspendLayout(); + self.groupBox3.SuspendLayout(); + self.SuspendLayout(); + // + // lblIP + // + self.lblIP.AutoSize := true; + self.lblIP.Font := new System.Drawing.Font('Verdana', 15.75, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.lblIP.Location := new System.Drawing.Point(415, 134); + self.lblIP.Name := 'lblIP'; + self.lblIP.Size := new System.Drawing.Size(173, 25); + self.lblIP.TabIndex := 17; + self.lblIP.Text := 'Internet Pack'; + // + // lblRO + // + self.lblRO.AutoSize := true; + self.lblRO.Font := new System.Drawing.Font('Verdana', 14.25, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.lblRO.Location := new System.Drawing.Point(464, 102); + self.lblRO.Name := 'lblRO'; + self.lblRO.Size := new System.Drawing.Size(124, 23); + self.lblRO.TabIndex := 18; + self.lblRO.Text := 'RemObjects'; + // + // pictureBox1 + // + self.pictureBox1.BackColor := System.Drawing.SystemColors.ActiveBorder; + self.pictureBox1.Image := (resources.GetObject('pictureBox1.Image') as System.Drawing.Image); + self.pictureBox1.Location := new System.Drawing.Point(501, 14); + self.pictureBox1.Name := 'pictureBox1'; + self.pictureBox1.Size := new System.Drawing.Size(88, 80); + self.pictureBox1.SizeMode := System.Windows.Forms.PictureBoxSizeMode.StretchImage; + self.pictureBox1.TabIndex := 19; + self.pictureBox1.TabStop := false; + // + // groupBox1 + // + self.groupBox1.Controls.Add(self.lblSMTPServer); + self.groupBox1.Controls.Add(self.txtSMTPServer); + self.groupBox1.Controls.Add(self.groupBox2); + self.groupBox1.Controls.Add(self.chkUseAuth); + self.groupBox1.Location := new System.Drawing.Point(7, 6); + self.groupBox1.Name := 'groupBox1'; + self.groupBox1.Size := new System.Drawing.Size(360, 160); + self.groupBox1.TabIndex := 15; + self.groupBox1.TabStop := false; + self.groupBox1.Text := 'Server Information'; + // + // lblSMTPServer + // + self.lblSMTPServer.Location := new System.Drawing.Point(6, 19); + self.lblSMTPServer.Name := 'lblSMTPServer'; + self.lblSMTPServer.Size := new System.Drawing.Size(160, 15); + self.lblSMTPServer.TabIndex := 0; + self.lblSMTPServer.Text := 'Outgoing Mail Server (SMTP):'; + // + // txtSMTPServer + // + self.txtSMTPServer.Location := new System.Drawing.Point(168, 16); + self.txtSMTPServer.Name := 'txtSMTPServer'; + self.txtSMTPServer.Size := new System.Drawing.Size(168, 20); + self.txtSMTPServer.TabIndex := 1; + self.txtSMTPServer.Text := 'ws7.elitedev.com'; + // + // groupBox2 + // + self.groupBox2.Controls.Add(self.txtPassword); + self.groupBox2.Controls.Add(self.txtUserName); + self.groupBox2.Controls.Add(self.lblPassword); + self.groupBox2.Controls.Add(self.lblUserName); + self.groupBox2.Location := new System.Drawing.Point(16, 72); + self.groupBox2.Name := 'groupBox2'; + self.groupBox2.Size := new System.Drawing.Size(336, 80); + self.groupBox2.TabIndex := 3; + self.groupBox2.TabStop := false; + self.groupBox2.Text := 'Login Information'; + // + // txtPassword + // + self.txtPassword.Enabled := false; + self.txtPassword.Location := new System.Drawing.Point(88, 48); + self.txtPassword.Name := 'txtPassword'; + self.txtPassword.PasswordChar := '*'; + self.txtPassword.Size := new System.Drawing.Size(232, 20); + self.txtPassword.TabIndex := 3; + // + // txtUserName + // + self.txtUserName.Enabled := false; + self.txtUserName.Location := new System.Drawing.Point(88, 24); + self.txtUserName.Name := 'txtUserName'; + self.txtUserName.Size := new System.Drawing.Size(232, 20); + self.txtUserName.TabIndex := 1; + // + // lblPassword + // + self.lblPassword.AutoSize := true; + self.lblPassword.Enabled := false; + self.lblPassword.Location := new System.Drawing.Point(25, 51); + self.lblPassword.Name := 'lblPassword'; + self.lblPassword.Size := new System.Drawing.Size(56, 13); + self.lblPassword.TabIndex := 2; + self.lblPassword.Text := 'Password:'; + // + // lblUserName + // + self.lblUserName.AutoSize := true; + self.lblUserName.Enabled := false; + self.lblUserName.Location := new System.Drawing.Point(18, 27); + self.lblUserName.Name := 'lblUserName'; + self.lblUserName.Size := new System.Drawing.Size(63, 13); + self.lblUserName.TabIndex := 0; + self.lblUserName.Text := 'User Name:'; + // + // chkUseAuth + // + self.chkUseAuth.Location := new System.Drawing.Point(16, 48); + self.chkUseAuth.Name := 'chkUseAuth'; + self.chkUseAuth.Size := new System.Drawing.Size(279, 24); + self.chkUseAuth.TabIndex := 2; + self.chkUseAuth.Text := 'My outgoing server (SMTP) requires authentication'; + self.chkUseAuth.CheckedChanged += new System.EventHandler(@self.chkUseAuth_CheckedChanged); + // + // groupBox3 + // + self.groupBox3.Controls.Add(self.lblSenderName); + self.groupBox3.Controls.Add(self.lblFromEmail); + self.groupBox3.Controls.Add(self.txtMessage); + self.groupBox3.Controls.Add(self.txtSenderAddress); + self.groupBox3.Controls.Add(self.label5); + self.groupBox3.Controls.Add(self.txtSenderName); + self.groupBox3.Controls.Add(self.txtTo); + self.groupBox3.Controls.Add(self.label6); + self.groupBox3.Controls.Add(self.label3); + self.groupBox3.Controls.Add(self.txtCC); + self.groupBox3.Controls.Add(self.txtSubject); + self.groupBox3.Controls.Add(self.label7); + self.groupBox3.Controls.Add(self.txtBCC); + self.groupBox3.Controls.Add(self.label8); + self.groupBox3.Controls.Add(self.btnSendEMail); + self.groupBox3.Location := new System.Drawing.Point(7, 174); + self.groupBox3.Name := 'groupBox3'; + self.groupBox3.Size := new System.Drawing.Size(584, 296); + self.groupBox3.TabIndex := 16; + self.groupBox3.TabStop := false; + self.groupBox3.Text := 'EMail'; + // + // lblSenderName + // + self.lblSenderName.AutoSize := true; + self.lblSenderName.Font := new System.Drawing.Font('Microsoft Sans Serif', 8.25, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.lblSenderName.Location := new System.Drawing.Point(29, 19); + self.lblSenderName.Name := 'lblSenderName'; + self.lblSenderName.Size := new System.Drawing.Size(64, 13); + self.lblSenderName.TabIndex := 0; + self.lblSenderName.Text := 'From Name:'; + // + // lblFromEmail + // + self.lblFromEmail.AutoSize := true; + self.lblFromEmail.Font := new System.Drawing.Font('Microsoft Sans Serif', 8.25, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.lblFromEmail.Location := new System.Drawing.Point(60, 43); + self.lblFromEmail.Name := 'lblFromEmail'; + self.lblFromEmail.Size := new System.Drawing.Size(33, 13); + self.lblFromEmail.TabIndex := 2; + self.lblFromEmail.Text := 'From:'; + // + // txtMessage + // + self.txtMessage.Location := new System.Drawing.Point(104, 120); + self.txtMessage.Multiline := true; + self.txtMessage.Name := 'txtMessage'; + self.txtMessage.Size := new System.Drawing.Size(464, 136); + self.txtMessage.TabIndex := 13; + self.txtMessage.Text := ''; + // + // txtSenderAddress + // + self.txtSenderAddress.Location := new System.Drawing.Point(104, 40); + self.txtSenderAddress.Name := 'txtSenderAddress'; + self.txtSenderAddress.Size := new System.Drawing.Size(232, 20); + self.txtSenderAddress.TabIndex := 3; + self.txtSenderAddress.Text := 'alexanderk@remobjects.com'; + // + // label5 + // + self.label5.AutoSize := true; + self.label5.Font := new System.Drawing.Font('Microsoft Sans Serif', 8.25, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.label5.Location := new System.Drawing.Point(44, 123); + self.label5.Name := 'label5'; + self.label5.Size := new System.Drawing.Size(53, 13); + self.label5.TabIndex := 12; + self.label5.Text := 'Message:'; + // + // txtSenderName + // + self.txtSenderName.Location := new System.Drawing.Point(104, 16); + self.txtSenderName.Name := 'txtSenderName'; + self.txtSenderName.Size := new System.Drawing.Size(232, 20); + self.txtSenderName.TabIndex := 1; + self.txtSenderName.Text := 'John Doe'; + // + // txtTo + // + self.txtTo.Location := new System.Drawing.Point(392, 16); + self.txtTo.Name := 'txtTo'; + self.txtTo.Size := new System.Drawing.Size(176, 20); + self.txtTo.TabIndex := 5; + // + // label6 + // + self.label6.AutoSize := true; + self.label6.Font := new System.Drawing.Font('Microsoft Sans Serif', 8.25, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.label6.Location := new System.Drawing.Point(366, 19); + self.label6.Name := 'label6'; + self.label6.Size := new System.Drawing.Size(23, 13); + self.label6.TabIndex := 4; + self.label6.Text := 'To:'; + // + // label3 + // + self.label3.AutoSize := true; + self.label3.Font := new System.Drawing.Font('Microsoft Sans Serif', 8.25, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.label3.Location := new System.Drawing.Point(51, 91); + self.label3.Name := 'label3'; + self.label3.Size := new System.Drawing.Size(46, 13); + self.label3.TabIndex := 10; + self.label3.Text := 'Subject:'; + // + // txtCC + // + self.txtCC.Location := new System.Drawing.Point(392, 40); + self.txtCC.Name := 'txtCC'; + self.txtCC.Size := new System.Drawing.Size(176, 20); + self.txtCC.TabIndex := 7; + // + // txtSubject + // + self.txtSubject.Location := new System.Drawing.Point(104, 88); + self.txtSubject.Name := 'txtSubject'; + self.txtSubject.Size := new System.Drawing.Size(464, 20); + self.txtSubject.TabIndex := 11; + self.txtSubject.Text := ''; + // + // label7 + // + self.label7.AutoSize := true; + self.label7.Font := new System.Drawing.Font('Microsoft Sans Serif', 8.25, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.label7.Location := new System.Drawing.Point(365, 43); + self.label7.Name := 'label7'; + self.label7.Size := new System.Drawing.Size(24, 13); + self.label7.TabIndex := 6; + self.label7.Text := 'CC:'; + // + // txtBCC + // + self.txtBCC.Location := new System.Drawing.Point(392, 64); + self.txtBCC.Name := 'txtBCC'; + self.txtBCC.Size := new System.Drawing.Size(176, 20); + self.txtBCC.TabIndex := 9; + // + // label8 + // + self.label8.AutoSize := true; + self.label8.Font := new System.Drawing.Font('Microsoft Sans Serif', 8.25, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, (204 as System.Byte)); + self.label8.Location := new System.Drawing.Point(358, 67); + self.label8.Name := 'label8'; + self.label8.Size := new System.Drawing.Size(31, 13); + self.label8.TabIndex := 8; + self.label8.Text := 'BCC:'; + // + // btnSendEMail + // + self.btnSendEMail.Location := new System.Drawing.Point(456, 264); + self.btnSendEMail.Name := 'btnSendEMail'; + self.btnSendEMail.Size := new System.Drawing.Size(112, 23); + self.btnSendEMail.TabIndex := 14; + self.btnSendEMail.Text := 'Send Email'; + self.btnSendEMail.Click += new System.EventHandler(@self.btnSendEMail_Click); + // + // smtpClient + // + self.smtpClient.AuthPassword := nil; + self.smtpClient.AuthUser := nil; + self.smtpClient.ConnectionClass := nil; + self.smtpClient.ConnectionFactory := nil; + self.smtpClient.HeloDomain := 'remobjects.com'; + self.smtpClient.HostAddress := nil; + self.smtpClient.HostName := ''; + self.smtpClient.Port := 25; + self.smtpClient.UseAuth := false; + // + // MainForm + // + self.AutoScaleBaseSize := new System.Drawing.Size(5, 13); + self.ClientSize := new System.Drawing.Size(598, 476); + self.Controls.Add(self.lblIP); + self.Controls.Add(self.lblRO); + self.Controls.Add(self.pictureBox1); + self.Controls.Add(self.groupBox1); + self.Controls.Add(self.groupBox3); + self.FormBorderStyle := System.Windows.Forms.FormBorderStyle.FixedDialog; + self.Icon := (resources.GetObject('$this.Icon') as System.Drawing.Icon); + self.MaximizeBox := false; + self.Name := 'MainForm'; + self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen; + self.Text := 'SMTP Client Sample'; + (self.pictureBox1 as System.ComponentModel.ISupportInitialize).EndInit(); + self.groupBox1.ResumeLayout(false); + self.groupBox1.PerformLayout(); + self.groupBox2.ResumeLayout(false); + self.groupBox2.PerformLayout(); + self.groupBox3.ResumeLayout(false); + self.groupBox3.PerformLayout(); + self.ResumeLayout(false); + self.PerformLayout(); +end; +{$ENDREGION} + +{$REGION Application Entry Point} +[STAThread] +class method MainForm.Main; +begin + Application.EnableVisualStyles(); + + try + with lForm := new MainForm() do + Application.Run(lForm); + except + on E: Exception do begin + MessageBox.Show(E.Message); + end; + end; +end; +{$ENDREGION} + +method MainForm.chkUseAuth_CheckedChanged(sender: System.Object; e: System.EventArgs); +begin + lblUserName.Enabled := chkUseAuth.Checked; + txtUserName.Enabled := chkUseAuth.Checked; + lblPassword.Enabled := chkUseAuth.Checked; + txtPassword.Enabled := chkUseAuth.Checked; +end; + +method MainForm.btnSendEMail_Click(sender: System.Object; e: System.EventArgs); +var + cc: String; + bcc: String; +begin + // Clear lists before sending + msg.To.Clear(); + msg.Cc.Clear(); + msg.Bcc.Clear(); + try + msg.To.Add(getRequiredValue(txtTo, 'Value of field "To" is required')); + + cc := txtCC.Text.Trim(); + if (cc.Length > 0) then msg.Cc.Add(cc); + + bcc := txtBCC.Text.Trim(); + if (bcc.Length > 0) then msg.Bcc.Add(bcc); + + smtpClient.HostName := getRequiredValue(txtSMTPServer, 'Host Name is required'); + smtpClient.HeloDomain := 'remobjects.com'; + + smtpClient.UseAuth := chkUseAuth.Checked; + smtpClient.AuthUser := txtUserName.Text; + smtpClient.AuthPassword := txtPassword.Text; + + msg.From.Name := getRequiredValue(txtSenderName, 'From field value is required'); + msg.From.Address := getRequiredValue(txtSenderAddress, 'EMail field value is required'); + msg.Subject := getRequiredValue(txtSubject, 'Subject of letter is required'); + msg.Contents := getRequiredValue(txtMessage, 'Content of letter is required'); + + + smtpClient.Open(); + smtpClient.SendMessage(msg); + smtpClient.Close(); + MessageBox.Show('Email has been sent successfully!', 'SMTP Client Sample'); +except + on ex: Exception do begin + MessageBox.Show(ex.Message, 'Error during sending letter.', MessageBoxButtons.OK, MessageBoxIcon.Error); + end; +end; + +end; + +method MainForm.getRequiredValue(ctrl: TextBox; errorMessage: String): String; +begin + if (not Assigned(ctrl)) then Exit(nil); + + result := ctrl.Text.Trim(); + if (result.Length = 0) then begin + ctrl.Focus(); + raise new Exception(errorMessage); + end; +end; + + + +end. \ No newline at end of file diff --git a/Samples/Oxygene/SMTP Client/MainForm.resx b/Samples/Oxygene/SMTP Client/MainForm.resx new file mode 100644 index 0000000..5b171cf --- /dev/null +++ b/Samples/Oxygene/SMTP Client/MainForm.resx @@ -0,0 +1,736 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + 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 + + + + 17, 17 + + + + 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 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + YP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xMTP9LS0v/SUlJ/0dH + R/9GRkafRUVFDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpqah9paWmfaGho/2Zm + Zv9kZGT/Y2Nj/2FhYf9gYGD/Xl5e/1xcXP9bW1v/WVlZ/1hYWP9WVlb/VFRU/1NTU/9RUVH/T09P/05O + Tv9MTEz/S0tLn0lJSR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAaGhoD2dnZ19mZmavZGRk72NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZWf9YWFj/VlZW/1RU + VP9TU1PvUVFRr09PT19PT08PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjYy9iYmJPYWFhf19fX39eXl5/XFxcf1pa + Wn9ZWVl/V1dXT1ZWVi8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD//wAA//8AAP/8AAA//wAA//AAAA// + AAD/wAAAA/8AAP+AAAAB/wAA/wAAAAD/AAD+AAAAAH8AAPwAAAAAPwAA+AAAAAAfAADwAAAAAA8AAPAA + AAAADwAA4AAAAAAHAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAABwAA4AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAP4A + AAAAfwAA/4AAAAH/AAD/wAAAA/8AAP/gAAAH/wAA//gAAB//AAD//gAAf/8AAP//4Af//wAAKAAAACAA + AABAAAAAAQAgAAAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISEPHx8fXx0dHZ8aGhrPGBgY/xUVFf8TExP/ERER/w4ODu8MDAy/CgoKfwgI + CC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApKSkfJiYmnyQkJO8hISH/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/w4O + Dv8MDAz/CQkJ/wcHB88GBgZfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAALS0tfysrK/8oKCj/JiYm/yQkJP8hISH/Hx8f/x0dHf8aGhr/GBgY/xUV + Ff8TExP/ERER/w4ODv8MDAz/CQkJ/wcHB/8FBQW/BAQELwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAANTU1DzIyMs8vLy//LS0t/ysrK/8oKCj/NjYf/1JSD/9dXQj/bW0A/2lp + AP9kZAD/YGAA/1NTAv9CQgX/KioK/xISDf8MDAz/CQkJ/wcHB/8FBQXvAwMDXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5OQ83NzfPNDQ0/zIyMv8vLy//Q0Mi/2xsCv9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/T08A/y8vBv8MDAz/CQkJ/wcHB/8FBQX/AwMDXwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + 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 + + + \ No newline at end of file diff --git a/Samples/Oxygene/SMTP Client/SMTP Client.2008.sln b/Samples/Oxygene/SMTP Client/SMTP Client.2008.sln new file mode 100644 index 0000000..8b03ac9 --- /dev/null +++ b/Samples/Oxygene/SMTP Client/SMTP Client.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "SMTP Client", "SMTP Client.oxygene", "{CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}.Debug|Default.ActiveCfg = Debug + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}.Debug|Default.Build.0 = Debug + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}.Release|Default.ActiveCfg = Release + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/SMTP Client/SMTP Client.2010.sln b/Samples/Oxygene/SMTP Client/SMTP Client.2010.sln new file mode 100644 index 0000000..a7812da --- /dev/null +++ b/Samples/Oxygene/SMTP Client/SMTP Client.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "SMTP Client", "SMTP Client.oxygene", "{CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}.Debug|Default.ActiveCfg = Debug + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}.Debug|Default.Build.0 = Debug + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}.Release|Default.ActiveCfg = Release + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/SMTP Client/SMTP Client.Sample.html b/Samples/Oxygene/SMTP Client/SMTP Client.Sample.html new file mode 100644 index 0000000..4e6a04b --- /dev/null +++ b/Samples/Oxygene/SMTP Client/SMTP Client.Sample.html @@ -0,0 +1,38 @@ + + + + + + + + + +

SMTP Client Sample

+ +

Purpose

+ +

+ The sample shows how we can use the SMTP client component for sending emails. +

+ + +

Examine the code

+
    +
  • Sending emails is pretty simple (see MainForm's btnSendEMail_Click handler). + We create and configure an instance of MailMessage.
  • +
  • Next we put the SmtpClient component onto the form and configure it.
  • +
  • After configuring these components we can send the message (smtpClient.SendMessage(msg);). +
+ +

Getting started

+
    +
  • Compile the solution.
  • +
  • Run the Smtp client application.
  • +
  • Specify the outgoing mail server (if your mail server requires authentication you + will need to specify login and password).
  • +
  • Create an email and send it
  • +
+ + + + diff --git a/Samples/Oxygene/SMTP Client/SMTP Client.oxygene b/Samples/Oxygene/SMTP Client/SMTP Client.oxygene new file mode 100644 index 0000000..0d38206 --- /dev/null +++ b/Samples/Oxygene/SMTP Client/SMTP Client.oxygene @@ -0,0 +1,48 @@ + + + SMTP Client Sample + WinExe + SMTP Client Sample + False + False + App.ico + Release + {CF645DB2-9A6A-45D1-ADBA-59FEF96B5103} + v2.0 + + + DEBUG;TRACE; + .\bin\Debug + True + + + .\bin\Release + False + + + + + + + + + + + + + + + + + + + + + + Form + SMTP_Client_Sample.MainForm + + + + + \ No newline at end of file diff --git a/Samples/Oxygene/Sample Server/App.ico b/Samples/Oxygene/Sample Server/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/Oxygene/Sample Server/App.ico differ diff --git a/Samples/Oxygene/Sample Server/AssemblyInfo.pas b/Samples/Oxygene/Sample Server/AssemblyInfo.pas new file mode 100644 index 0000000..66f4faf --- /dev/null +++ b/Samples/Oxygene/Sample Server/AssemblyInfo.pas @@ -0,0 +1,49 @@ +namespace SampleServer; + +interface + +uses + System.Reflection; + +[assembly: AssemblyTitle('')] +[assembly: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] +[assembly: AssemblyVersion('1.0.0.1')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory, which in Chrome by default is the +// same as the project directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] + +implementation + +end. \ No newline at end of file diff --git a/Samples/Oxygene/Sample Server/HttpRoot/Styles.css b/Samples/Oxygene/Sample Server/HttpRoot/Styles.css new file mode 100644 index 0000000..c8e0628 --- /dev/null +++ b/Samples/Oxygene/Sample Server/HttpRoot/Styles.css @@ -0,0 +1,103 @@ +body +{ + background-color: #f7f7f7; + margin-top: 15px; + margin-bottom: 15px; + margin-left: 15px; + margin-right: 15px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + font-family: tahoma, verdana, sans-serif; + font-size: 10pt; + width: 700px; + color: #000000; +} +p +{ + padding-top: 0; + padding-bottom: 0; + padding-left: 0; + padding-right: 0.5em; +} +ul +{ + padding-top: 0; + padding-bottom: 0; + list-style-type: disc; +} +li +{ + padding-top: 0; + padding-bottom: 0; +} +img +{ + margin: 5px; + border-width: 0; +} +table +{ + background-color: #f7f7f7; + margin: 15px; + padding: 0px; + font-size: 10pt; +} +tr +{ + background-color: #f7f7f7; + margin: 15px; + padding: 0px; + font-size: 10pt; +} +td, th +{ + background-color: #f7f7f7; + margin: 0; + padding: 5px; + font-size: 10pt; +} +td ul +{ + padding-left: 2em; +} + +img:left { margin-left: 0; } +img:right { margin-right: 0; } +p.h1 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:13pt; + font-weight:bold; +} +p.h2 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:11pt; + font-weight:bold; +} +p.h3 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:10pt; + font-weight:bold; +} +pre +{ + margin-top:0px; + margin-bottom:0px; + margin-left:0px; + margin-right:0px; +} +.spaced +{ + letter-spacing:1px; + color:#000060; +} diff --git a/Samples/Oxygene/Sample Server/HttpRoot/index.html b/Samples/Oxygene/Sample Server/HttpRoot/index.html new file mode 100644 index 0000000..ffaee1e --- /dev/null +++ b/Samples/Oxygene/Sample Server/HttpRoot/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + +

+ Sample Server +

+ + +

Purpose

+

+This example shows how easy it is to create a simple custom Http server. The + +SimpleHttpServer class is a file-based HTTP Server that will make all files in (and below) the specified RootPath folder available via HTTP. + + +

Examine the Code

+
    +
  • look at the ActivateServers procedure to see how to create and configure the HTTP server. The +RootPath property should reference the folder in our file system which we want to expose. +
  • +
  • +See the OnHttpRequest handler. There we can catch and process any incoming requests that come to the server. +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run application.
  • +
  • Configure server properties and activate the servers.
  • +
  • Click link on the main window to open home page of our server in web browser.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/Oxygene/Sample Server/HttpRoot/ip.png b/Samples/Oxygene/Sample Server/HttpRoot/ip.png new file mode 100644 index 0000000..ea014ca Binary files /dev/null and b/Samples/Oxygene/Sample Server/HttpRoot/ip.png differ diff --git a/Samples/Oxygene/Sample Server/Main.pas b/Samples/Oxygene/Sample Server/Main.pas new file mode 100644 index 0000000..e55328c --- /dev/null +++ b/Samples/Oxygene/Sample Server/Main.pas @@ -0,0 +1,419 @@ +namespace SampleServer; + +interface + +uses + System.Windows.Forms, + System.Drawing, + RemObjects.InternetPack.StandardServers, + RemObjects.InternetPack.Http, + System.IO; +type + LogRequestDelegate = delegate(request: String); + /// + /// Summary description for MainForm. + /// + MainForm = class(System.Windows.Forms.Form) + {$REGION Windows Form Designer generated fields} + private + components: System.ComponentModel.Container := nil; + method InitializeComponent; + assembly + pictureBox1: System.Windows.Forms.PictureBox; + Label2: System.Windows.Forms.Label; + txtLog: System.Windows.Forms.TextBox; + btnAction: System.Windows.Forms.Button; + lblLink: System.Windows.Forms.LinkLabel; + lblPort: System.Windows.Forms.Label; + nudPort: System.Windows.Forms.NumericUpDown; + lblRoot: System.Windows.Forms.Label; + txtRoot: System.Windows.Forms.TextBox; + txtServerName: System.Windows.Forms.TextBox; + lblServerName: System.Windows.Forms.Label; + lblCount: System.Windows.Forms.Label; + nudCount: System.Windows.Forms.NumericUpDown; + lblUrl: System.Windows.Forms.Label; + GroupBox1: System.Windows.Forms.GroupBox; + {$ENDREGION} + private + fEchoServer: EchoServer; + fHttpServer: SimpleHttpServer; + method nudPort_ValueChanged(sender: System.Object; e: System.EventArgs); + method MainForm_Closed(sender: System.Object; e: System.EventArgs); + method MainForm_Load(sender: System.Object; e: System.EventArgs); + method lblLink_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); + method ActivateServers(); + method DeactivateServers(); + method SetEnable(mode: Boolean); + method AddLog(line: String); + method LogRequest(aSender: object; ea: OnHttpRequestArgs); + method btnAction_Click(sender: System.Object; e: System.EventArgs); + protected + method Dispose(aDisposing: boolean); override; + public + constructor; + class method Main; + end; + +implementation + +{$REGION Construction and Disposition} +constructor MainForm; +begin + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // +end; + +method MainForm.Dispose(aDisposing: boolean); +begin + if aDisposing then begin + if assigned(components) then + components.Dispose(); + + // + // TODO: Add custom disposition code here + // + end; + inherited Dispose(aDisposing); +end; +{$ENDREGION} + +{$REGION Windows Form Designer generated code} +method MainForm.InitializeComponent; +begin + var resources: System.ComponentModel.ComponentResourceManager := new System.ComponentModel.ComponentResourceManager(typeOf(MainForm)); + self.GroupBox1 := new System.Windows.Forms.GroupBox(); + self.lblUrl := new System.Windows.Forms.Label(); + self.nudCount := new System.Windows.Forms.NumericUpDown(); + self.lblCount := new System.Windows.Forms.Label(); + self.lblServerName := new System.Windows.Forms.Label(); + self.txtServerName := new System.Windows.Forms.TextBox(); + self.txtRoot := new System.Windows.Forms.TextBox(); + self.lblRoot := new System.Windows.Forms.Label(); + self.nudPort := new System.Windows.Forms.NumericUpDown(); + self.lblPort := new System.Windows.Forms.Label(); + self.lblLink := new System.Windows.Forms.LinkLabel(); + self.btnAction := new System.Windows.Forms.Button(); + self.txtLog := new System.Windows.Forms.TextBox(); + self.Label2 := new System.Windows.Forms.Label(); + self.pictureBox1 := new System.Windows.Forms.PictureBox(); + self.GroupBox1.SuspendLayout(); + (self.nudCount as System.ComponentModel.ISupportInitialize).BeginInit(); + (self.nudPort as System.ComponentModel.ISupportInitialize).BeginInit(); + (self.pictureBox1 as System.ComponentModel.ISupportInitialize).BeginInit(); + self.SuspendLayout(); + // + // GroupBox1 + // + self.GroupBox1.Anchor := (((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.GroupBox1.Controls.Add(self.lblUrl); + self.GroupBox1.Controls.Add(self.nudCount); + self.GroupBox1.Controls.Add(self.lblCount); + self.GroupBox1.Controls.Add(self.lblServerName); + self.GroupBox1.Controls.Add(self.txtServerName); + self.GroupBox1.Controls.Add(self.txtRoot); + self.GroupBox1.Controls.Add(self.lblRoot); + self.GroupBox1.Controls.Add(self.nudPort); + self.GroupBox1.Controls.Add(self.lblPort); + self.GroupBox1.Controls.Add(self.lblLink); + self.GroupBox1.Controls.Add(self.btnAction); + self.GroupBox1.Location := new System.Drawing.Point(8, 47); + self.GroupBox1.Name := 'GroupBox1'; + self.GroupBox1.Size := new System.Drawing.Size(528, 144); + self.GroupBox1.TabIndex := 0; + self.GroupBox1.TabStop := false; + self.GroupBox1.Text := 'HttpServer'; + // + // lblUrl + // + self.lblUrl.AutoSize := true; + self.lblUrl.Enabled := false; + self.lblUrl.Location := new System.Drawing.Point(98, 120); + self.lblUrl.Name := 'lblUrl'; + self.lblUrl.Size := new System.Drawing.Size(32, 13); + self.lblUrl.TabIndex := 8; + self.lblUrl.Text := 'URL:'; + // + // nudCount + // + self.nudCount.Location := new System.Drawing.Point(139, 88); + self.nudCount.Name := 'nudCount'; + self.nudCount.Size := new System.Drawing.Size(48, 20); + self.nudCount.TabIndex := 7; + self.nudCount.Value := new System.Decimal(array of System.Int32([5, + 0, + 0, + 0])); + // + // lblCount + // + self.lblCount.AutoSize := true; + self.lblCount.Location := new System.Drawing.Point(15, 90); + self.lblCount.Name := 'lblCount'; + self.lblCount.Size := new System.Drawing.Size(115, 13); + self.lblCount.TabIndex := 6; + self.lblCount.Text := 'Listener Thread Count:'; + // + // lblServerName + // + self.lblServerName.AutoSize := true; + self.lblServerName.Location := new System.Drawing.Point(58, 67); + self.lblServerName.Name := 'lblServerName'; + self.lblServerName.Size := new System.Drawing.Size(72, 13); + self.lblServerName.TabIndex := 4; + self.lblServerName.Text := '&Server Name:'; + // + // txtServerName + // + self.txtServerName.Anchor := (((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.txtServerName.Location := new System.Drawing.Point(139, 64); + self.txtServerName.Name := 'txtServerName'; + self.txtServerName.Size := new System.Drawing.Size(376, 20); + self.txtServerName.TabIndex := 5; + self.txtServerName.Text := 'TextBox3'; + // + // txtRoot + // + self.txtRoot.Anchor := (((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.txtRoot.Location := new System.Drawing.Point(139, 40); + self.txtRoot.Name := 'txtRoot'; + self.txtRoot.Size := new System.Drawing.Size(376, 20); + self.txtRoot.TabIndex := 3; + self.txtRoot.Text := 'TextBox2'; + // + // lblRoot + // + self.lblRoot.AutoSize := true; + self.lblRoot.Location := new System.Drawing.Point(75, 43); + self.lblRoot.Name := 'lblRoot'; + self.lblRoot.Size := new System.Drawing.Size(55, 13); + self.lblRoot.TabIndex := 2; + self.lblRoot.Text := '&RootPath:'; + // + // nudPort + // + self.nudPort.Location := new System.Drawing.Point(139, 16); + self.nudPort.Name := 'nudPort'; + self.nudPort.Size := new System.Drawing.Size(48, 20); + self.nudPort.TabIndex := 1; + self.nudPort.Value := new System.Decimal(array of System.Int32([83, + 0, + 0, + 0])); + self.nudPort.ValueChanged += new System.EventHandler(@self.nudPort_ValueChanged); + // + // lblPort + // + self.lblPort.AutoSize := true; + self.lblPort.Location := new System.Drawing.Point(101, 18); + self.lblPort.Name := 'lblPort'; + self.lblPort.Size := new System.Drawing.Size(29, 13); + self.lblPort.TabIndex := 0; + self.lblPort.Text := '&Port:'; + // + // lblLink + // + self.lblLink.Anchor := (((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.lblLink.Enabled := false; + self.lblLink.Location := new System.Drawing.Point(139, 120); + self.lblLink.Name := 'lblLink'; + self.lblLink.Size := new System.Drawing.Size(168, 16); + self.lblLink.TabIndex := 9; + self.lblLink.TabStop := true; + self.lblLink.Text := 'http://localhost:82/index.html'; + self.lblLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(@self.lblLink_LinkClicked); + // + // btnAction + // + self.btnAction.Anchor := ((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.btnAction.Location := new System.Drawing.Point(403, 115); + self.btnAction.Name := 'btnAction'; + self.btnAction.Size := new System.Drawing.Size(112, 23); + self.btnAction.TabIndex := 10; + self.btnAction.Text := 'Activate Servers'; + self.btnAction.Click += new System.EventHandler(@self.btnAction_Click); + // + // txtLog + // + self.txtLog.Anchor := ((((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Bottom) + or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.txtLog.Location := new System.Drawing.Point(8, 223); + self.txtLog.Multiline := true; + self.txtLog.Name := 'txtLog'; + self.txtLog.ScrollBars := System.Windows.Forms.ScrollBars.Both; + self.txtLog.Size := new System.Drawing.Size(528, 222); + self.txtLog.TabIndex := 2; + self.txtLog.WordWrap := false; + // + // Label2 + // + self.Label2.Location := new System.Drawing.Point(8, 199); + self.Label2.Name := 'Label2'; + self.Label2.Size := new System.Drawing.Size(32, 16); + self.Label2.TabIndex := 1; + self.Label2.Text := 'Log'; + // + // pictureBox1 + // + self.pictureBox1.Image := (resources.GetObject('pictureBox1.Image') as System.Drawing.Image); + self.pictureBox1.Location := new System.Drawing.Point(8, 7); + self.pictureBox1.Name := 'pictureBox1'; + self.pictureBox1.Size := new System.Drawing.Size(120, 30); + self.pictureBox1.TabIndex := 17; + self.pictureBox1.TabStop := false; + // + // MainForm + // + self.AutoScaleBaseSize := new System.Drawing.Size(5, 13); + self.ClientSize := new System.Drawing.Size(534, 442); + self.Controls.Add(self.GroupBox1); + self.Controls.Add(self.txtLog); + self.Controls.Add(self.Label2); + self.Controls.Add(self.pictureBox1); + self.FormBorderStyle := System.Windows.Forms.FormBorderStyle.FixedDialog; + self.Icon := (resources.GetObject('$this.Icon') as System.Drawing.Icon); + self.MinimumSize := new System.Drawing.Size(550, 480); + self.Name := 'MainForm'; + self.StartPosition := System.Windows.Forms.FormStartPosition.CenterScreen; + self.Text := 'Internet Pack Sample Server'; + self.Load += new System.EventHandler(@self.MainForm_Load); + self.Closed += new System.EventHandler(@self.MainForm_Closed); + self.GroupBox1.ResumeLayout(false); + self.GroupBox1.PerformLayout(); + (self.nudCount as System.ComponentModel.ISupportInitialize).EndInit(); + (self.nudPort as System.ComponentModel.ISupportInitialize).EndInit(); + (self.pictureBox1 as System.ComponentModel.ISupportInitialize).EndInit(); + self.ResumeLayout(false); + self.PerformLayout(); +end; +{$ENDREGION} + +{$REGION Application Entry Point} +[STAThread] +class method MainForm.Main; +begin + Application.EnableVisualStyles(); + + try + with lForm := new MainForm() do + Application.Run(lForm); + except + on E: Exception do begin + MessageBox.Show(E.Message); + end; + end; +end; +{$ENDREGION} + +method MainForm.btnAction_Click(sender: System.Object; e: System.EventArgs); +begin + If btnAction.Text = 'Activate Servers' Then + ActivateServers() + Else + DeactivateServers(); +end; + + method MainForm.ActivateServers(); + begin + AddLog('Trying to activate servers...'); + fEchoServer := New EchoServer(); + try + fEchoServer.Open(); + AddLog('EchoServer is active.'); + except on ex: Exception do + AddLog('Can''t activate EchoServer. An exception occured: ' + ex.Message); + end; + + + fHttpServer := New SimpleHttpServer(); + fHttpServer.Port := Convert.ToInt32(nudPort.Value); + fHttpServer.RootPath := txtRoot.Text; + fHttpServer.ServerName := txtServerName.Text; + fHttpServer.Binding.ListenerThreadCount := Convert.ToInt32(nudCount.Value); + //fHttpServer.OnHttpRequest += new LogRequestDelegate(@Self.LogRequest); + fHttpServer.OnHttpRequest += new OnHttpRequestHandler(LogRequest); + fHttpServer.Open(); + AddLog(String.Format('SimpleHttpServer is active on {0} port.', fHttpServer.Port)); + SetEnable(False); + AddLog('Servers activated.'); + btnAction.Text := 'Deactivate Servers'; + end; + + method MainForm.LogRequest(aSender: object; ea: OnHttpRequestArgs); + begin + AddLog(String.Format('Request to {0}', ea.Request.Header.RequestPath)) + end; + + + method MainForm.SetEnable(mode: Boolean); + begin + lblPort.Enabled := mode; + nudPort.Enabled := mode; + lblServerName.Enabled := mode; + txtServerName.Enabled := mode; + lblRoot.Enabled := mode; + txtRoot.Enabled := mode; + lblUrl.Enabled := Not mode; + lblLink.Enabled := Not mode; + end; + + method MainForm.DeactivateServers(); + begin + AddLog('Trying to deactivate servers...'); + If Assigned(fEchoServer) Then + fEchoServer.Close(); + AddLog('EchoServer is closed.'); + If Assigned(fHttpServer) Then + fHttpServer.Close(); + AddLog('HttpServer is closed.'); + SetEnable(True); + AddLog('Servers is deactivated'); + btnAction.Text := 'Activate Servers'; + end; + +method MainForm.lblLink_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); +begin + if (File.Exists(fHttpServer.RootPath + '\index.html')) then + System.Diagnostics.Process.Start(lblLink.Text) + else + MessageBox.Show(fHttpServer.RootPath + '\index.html can not be opened, because it does not exists.', 'Warning', + MessageBoxButtons.OK, MessageBoxIcon.Warning); +end; + +method MainForm.MainForm_Load(sender: System.Object; e: System.EventArgs); +begin + txtRoot.Text := Path.Combine(Path.GetDirectoryName(Self.GetType().Assembly.Location), 'HttpRoot'); + txtServerName.Text := 'Internet Pack HTTP Server'; + nudPort.Value := 82; + nudCount.Value := 5; +end; + +method MainForm.AddLog(line: String); +begin + txtLog.Invoke(method begin + txtLog.AppendText(Environment.NewLine + String.Format('{0}: {1}', DateTime.Now.ToLongTimeString(), line)); + end); +end; + +method MainForm.MainForm_Closed(sender: System.Object; e: System.EventArgs); +begin + DeactivateServers(); +end; + +method MainForm.nudPort_ValueChanged(sender: System.Object; e: System.EventArgs); +begin + lblLink.Text := String.Format('http://localhost:{0}/index.html', nudPort.Value); +end; + +end. \ No newline at end of file diff --git a/Samples/Oxygene/Sample Server/Main.resx b/Samples/Oxygene/Sample Server/Main.resx new file mode 100644 index 0000000..a726801 --- /dev/null +++ b/Samples/Oxygene/Sample Server/Main.resx @@ -0,0 +1,582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 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 + CQAPDw8AGxsLABQUFAAYGBgAHR0dAC4uDAA8PAQANjYJADQ0DgA9PQsAKioTACIiGwArKysAMTEiADQ0 + NAA7OzEAODg4AD4+PgBAQAMAREQCAEREDgBNTQoASUkMAFNTBABZWQAARUUSAEFBFABWVhAAYWEAAGlp + AABtbQIAZmYJAG1tCgBycgAAe3sAAHV1CwB9fQ0AZGQUAHh4GwBYWCEAX18nAFlZKgBFRTwAc3MqAHt7 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + 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 + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + OS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trf2pqav9oaGj/ZmZm/2Rk + ZP9hYWH/e3tn/6amcP+9vXH/wsFs/728Zf+4uF7/m5pV/35+T/9UVEz/SUlJ/0dHR/9FRUX/QkJC/0BA + QM8+Pj4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trL2pq + ar9oaGj/ZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dH + R+9ERER/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGpqak9oaGivZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05O + Tt9LS0t/SkpKDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZ2cPZWVlT2NjY39hYWG/Xl5ev1xcXL9aWlq/V1dXn1VV + VW9SUlIvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAP//wAA//4A + AD/4AAAf8AAAD+AAAAfgAAADwAAAA8AAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAcAAAAHAAAAD4AAAA/AAAAf4AAAP/AAAH/4AAH//gAD//+AH/ygA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJycnPyIi + Iq8dHR3vGBgY/xQUFP8PDw//CwsLvwcHB18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUPMDAwrysr + K/8xMSL/RUUS/0REDv89PQv/NjYJ/xsbC/8KCgr/BgYGzwMDAy8AAAAAAAAAAAAAAAA/Pz8POTk5zzU1 + Nf9kZBT/g4MA/3t7AP9ycgD/aWkA/2FhAP9ZWQD/PDwE/w4OCf8GBgbvAwMDLwAAAAAAAAAAQ0NDjz4+ + Pv94eBv/lJQA/4uLAP+DgwD/e3sA/3JyAP9paQD/YWEA/1lZAP9ERAL/Dg4J/wYGBs8AAAAATExMH0hI + SP97ezb/qakY/56eCP+UlAD/i4sA/4ODAP97ewD/cnIA/2lpAP9hYQD/WVkA/0BAA/8KCgr/BwcHX1BQ + UI9MTEz/uLg7/7S0Kv+pqRj/np4I/5SUAP+LiwD/g4MA/3t7AP9ycgD/aWkA/1NTBP9ZWQD/GxsL/woK + Cs9VVVW/cnJT/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uLAP+DgwD/e3sA/3JyAP9JSQz/KioT/y4u + DP8PDw//W1tb75KSX//OzVz/e3tM/8C/O/+0tCr/e3sr/19fJ/99fQ3/dXUL/21tCv9mZgn/QUEU/yIi + G/80NA7/FBQU/19fX99+fmL/X19X/2ppUv+Likr/g4NA/3t7Nf9zcyr/WVkq/1hYIf+LiwD/g4MA/1ZW + EP9tbQL/TU0K/xgYGP9kZGS/hYVq/66tcf+BgVz/1tVc/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uL + AP+DgwD/e3sA/0VFEv8dHR3/Z2dnb2RkZP/k44j/29p7/9nYbP/W1Vz/y8pL/8C/O/+0tCr/qakY/56e + CP+UlAD/i4sA/4ODAP8xMSL/IiIir2trax9paWn/lZR1//j3j//t7H//4eFu/9bVXP/Lykv/wL87/7S0 + Kv+pqRj/np4I/5SUAP9kZBT/Kysr/ygoKF8AAAAAa2trf2lpaf/FxIb/+PeP/+3sf//h4W7/1tVc/8vK + S//Avzv/tLQq/6mpGP+LixH/Ozsx/zAwML8AAAAAAAAAAAAAAABra2ufaWlp/6iofP/u7Yz/7ex//+Hh + bv/W1Vz/y8pL/8C/O/97ezb/RUU8/zk5Oc81NTUPAAAAAAAAAAAAAAAAAAAAAGtra39paWn/ZGRk/4WF + av+jomv/m5tg/3p6VP9UVEz/SEhI/0NDQ68/Pz8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trH2ho + aI9kZGS/X19f/1tbW/9WVlbPUVFRn0xMTD8AAAAAAAAAAAAAAAAAAAAA8A8AAMADAACAAQAAgAEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAMABAADgAwAA8A8AAA== + + + \ No newline at end of file diff --git a/Samples/Oxygene/Sample Server/SampleServer.2008.sln b/Samples/Oxygene/Sample Server/SampleServer.2008.sln new file mode 100644 index 0000000..f568fa9 --- /dev/null +++ b/Samples/Oxygene/Sample Server/SampleServer.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "SampleServer", "SampleServer.oxygene", "{90E5FBCE-0445-40DB-BEC8-933B393487FE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {90E5FBCE-0445-40DB-BEC8-933B393487FE}.Debug|Default.ActiveCfg = Debug + {90E5FBCE-0445-40DB-BEC8-933B393487FE}.Debug|Default.Build.0 = Debug + {90E5FBCE-0445-40DB-BEC8-933B393487FE}.Release|Default.ActiveCfg = Release + {90E5FBCE-0445-40DB-BEC8-933B393487FE}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/Sample Server/SampleServer.2010.sln b/Samples/Oxygene/Sample Server/SampleServer.2010.sln new file mode 100644 index 0000000..2c7235a --- /dev/null +++ b/Samples/Oxygene/Sample Server/SampleServer.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "SampleServer", "SampleServer.oxygene", "{90E5FBCE-0445-40DB-BEC8-933B393487FE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {90E5FBCE-0445-40DB-BEC8-933B393487FE}.Debug|Default.ActiveCfg = Debug + {90E5FBCE-0445-40DB-BEC8-933B393487FE}.Debug|Default.Build.0 = Debug + {90E5FBCE-0445-40DB-BEC8-933B393487FE}.Release|Default.ActiveCfg = Release + {90E5FBCE-0445-40DB-BEC8-933B393487FE}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/Sample Server/SampleServer.Sample.html b/Samples/Oxygene/Sample Server/SampleServer.Sample.html new file mode 100644 index 0000000..2323841 --- /dev/null +++ b/Samples/Oxygene/Sample Server/SampleServer.Sample.html @@ -0,0 +1,44 @@ + + + + + + + + + + +

+ Sample Server +

+ + +

Purpose

+

+This example shows how easy it is to create a simple custom Http server. The + +SimpleHttpServer class is a file-based HTTP Server that will make all files in (and below) the specified RootPath folder available via HTTP. + + +

Examine the Code

+
    +
  • look at the ActivateServers procedure to see how to create and configure the HTTP server. The +RootPath property should reference the folder in our file system which we want to expose. +
  • +
  • +See the OnHttpRequest handler. There we can catch and process any incoming requests that come to the server. +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run application.
  • +
  • Configure server properties and activate the servers.
  • +
  • Click link on the main window to open home page of our server in web browser.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/Oxygene/Sample Server/SampleServer.oxygene b/Samples/Oxygene/Sample Server/SampleServer.oxygene new file mode 100644 index 0000000..b088e21 --- /dev/null +++ b/Samples/Oxygene/Sample Server/SampleServer.oxygene @@ -0,0 +1,57 @@ + + + + SampleServer + WinExe + SampleServer + False + False + App.ico + Release + {90E5FBCE-0445-40DB-BEC8-933B393487FE} + v2.0 + + + DEBUG;TRACE; + .\bin\Debug + True + + + .\bin\Release + False + + + + + $(ProgramFiles)\RemObjects Software\Internet Pack for .NET\Bin\RemObjects.InternetPack.dll + + + + + + + + + + + + Form + SampleServer.MainForm + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + \ No newline at end of file diff --git a/Samples/Oxygene/Virtual FTP/App.ico b/Samples/Oxygene/Virtual FTP/App.ico new file mode 100644 index 0000000..2103830 Binary files /dev/null and b/Samples/Oxygene/Virtual FTP/App.ico differ diff --git a/Samples/Oxygene/Virtual FTP/AssemblyInfo.pas b/Samples/Oxygene/Virtual FTP/AssemblyInfo.pas new file mode 100644 index 0000000..448c14e --- /dev/null +++ b/Samples/Oxygene/Virtual FTP/AssemblyInfo.pas @@ -0,0 +1,49 @@ +namespace VirtualFTP; + +interface + +uses + System.Reflection; + +[assembly: AssemblyTitle('')] +[assembly: AssemblyDescription('')] +[assembly: AssemblyConfiguration('')] +[assembly: AssemblyCompany('')] +[assembly: AssemblyProduct('')] +[assembly: AssemblyCopyright('')] +[assembly: AssemblyTrademark('')] +[assembly: AssemblyCulture('')] +[assembly: AssemblyVersion('1.0.0.1')] + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory, which in Chrome by default is the +// same as the project directory. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile('mykey.snk')] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile('')] +[assembly: AssemblyKeyName('')] + +implementation + +end. \ No newline at end of file diff --git a/Samples/Oxygene/Virtual FTP/Main.pas b/Samples/Oxygene/Virtual FTP/Main.pas new file mode 100644 index 0000000..d422698 --- /dev/null +++ b/Samples/Oxygene/Virtual FTP/Main.pas @@ -0,0 +1,271 @@ +namespace VirtualFTP; + +interface + +uses + System.Windows.Forms, + System.Drawing, + System.IO, + RemObjects.InternetPack.Ftp.VirtualFtp; + +type + /// + /// Summary description for MainForm. + /// + MainForm = class(System.Windows.Forms.Form) + {$REGION Windows Form Designer generated fields} + private + components: System.ComponentModel.Container := nil; + method InitializeComponent; + assembly + pictureBox1: System.Windows.Forms.PictureBox; + llShortcut: System.Windows.Forms.LinkLabel; + label1: System.Windows.Forms.Label; + txtLog: System.Windows.Forms.TextBox; + GroupBox1: System.Windows.Forms.GroupBox; + btnStartStop: System.Windows.Forms.Button; + {$ENDREGION} + private + fRootFolder: VirtualFolder; + fUserManager: IFtpUserManager; + fFtpServer: VirtualFtpServer; + port: Integer := 4444; + method MainForm_Closed(sender: System.Object; e: System.EventArgs); + method llShortcut_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); + method addToLog(line: String); + method btnStartStop_Click(sender: System.Object; e: System.EventArgs); + protected + method Dispose(aDisposing: boolean); override; + public + constructor; + class method Main; + method StartServer(aPort: Integer); + method StopServer(); + end; + +implementation + +{$REGION Construction and Disposition} +constructor MainForm; +begin + // + // Required for Windows Form Designer support + // + InitializeComponent(); + + // + // TODO: Add any constructor code after InitializeComponent call + // +end; + +method MainForm.Dispose(aDisposing: boolean); +begin + if aDisposing then begin + if assigned(components) then + components.Dispose(); + + // + // TODO: Add custom disposition code here + // + end; + inherited Dispose(aDisposing); +end; +{$ENDREGION} + +{$REGION Windows Form Designer generated code} +method MainForm.InitializeComponent; +begin + var resources: System.ComponentModel.ComponentResourceManager := new System.ComponentModel.ComponentResourceManager(typeOf(MainForm)); + self.btnStartStop := new System.Windows.Forms.Button(); + self.GroupBox1 := new System.Windows.Forms.GroupBox(); + self.txtLog := new System.Windows.Forms.TextBox(); + self.label1 := new System.Windows.Forms.Label(); + self.llShortcut := new System.Windows.Forms.LinkLabel(); + self.pictureBox1 := new System.Windows.Forms.PictureBox(); + self.GroupBox1.SuspendLayout(); + (self.pictureBox1 as System.ComponentModel.ISupportInitialize).BeginInit(); + self.SuspendLayout(); + // + // btnStartStop + // + self.btnStartStop.Anchor := ((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.btnStartStop.Location := new System.Drawing.Point(231, 8); + self.btnStartStop.Name := 'btnStartStop'; + self.btnStartStop.Size := new System.Drawing.Size(75, 23); + self.btnStartStop.TabIndex := 13; + self.btnStartStop.Text := 'Start'; + self.btnStartStop.Click += new System.EventHandler(@self.btnStartStop_Click); + // + // GroupBox1 + // + self.GroupBox1.Anchor := ((((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Bottom) + or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.GroupBox1.Controls.Add(self.txtLog); + self.GroupBox1.Location := new System.Drawing.Point(8, 103); + self.GroupBox1.Name := 'GroupBox1'; + self.GroupBox1.Size := new System.Drawing.Size(298, 132); + self.GroupBox1.TabIndex := 12; + self.GroupBox1.TabStop := false; + self.GroupBox1.Text := 'Log'; + // + // txtLog + // + self.txtLog.Anchor := ((((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Bottom) + or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.txtLog.Location := new System.Drawing.Point(8, 16); + self.txtLog.Multiline := true; + self.txtLog.Name := 'txtLog'; + self.txtLog.ScrollBars := System.Windows.Forms.ScrollBars.Both; + self.txtLog.Size := new System.Drawing.Size(282, 108); + self.txtLog.TabIndex := 6; + self.txtLog.WordWrap := false; + // + // label1 + // + self.label1.Anchor := (((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.label1.Location := new System.Drawing.Point(3, 38); + self.label1.Name := 'label1'; + self.label1.Size := new System.Drawing.Size(309, 24); + self.label1.TabIndex := 11; + self.label1.Text := 'In order to login on ftp please use login: test; password: test.'; + self.label1.TextAlign := System.Drawing.ContentAlignment.MiddleCenter; + // + // llShortcut + // + self.llShortcut.Anchor := (((System.Windows.Forms.AnchorStyles.Top or System.Windows.Forms.AnchorStyles.Left) + or System.Windows.Forms.AnchorStyles.Right) as System.Windows.Forms.AnchorStyles); + self.llShortcut.Location := new System.Drawing.Point(4, 62); + self.llShortcut.Name := 'llShortcut'; + self.llShortcut.Size := new System.Drawing.Size(300, 33); + self.llShortcut.TabIndex := 10; + self.llShortcut.TextAlign := System.Drawing.ContentAlignment.MiddleCenter; + self.llShortcut.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(@self.llShortcut_LinkClicked); + // + // pictureBox1 + // + self.pictureBox1.Image := (resources.GetObject('pictureBox1.Image') as System.Drawing.Image); + self.pictureBox1.Location := new System.Drawing.Point(8, 7); + self.pictureBox1.Name := 'pictureBox1'; + self.pictureBox1.Size := new System.Drawing.Size(120, 30); + self.pictureBox1.SizeMode := System.Windows.Forms.PictureBoxSizeMode.AutoSize; + self.pictureBox1.TabIndex := 9; + self.pictureBox1.TabStop := false; + // + // MainForm + // + self.AutoScaleBaseSize := new System.Drawing.Size(5, 13); + self.ClientSize := new System.Drawing.Size(314, 242); + self.Controls.Add(self.btnStartStop); + self.Controls.Add(self.GroupBox1); + self.Controls.Add(self.label1); + self.Controls.Add(self.llShortcut); + self.Controls.Add(self.pictureBox1); + self.Icon := (resources.GetObject('$this.Icon') as System.Drawing.Icon); + self.MinimumSize := new System.Drawing.Size(330, 280); + self.Name := 'MainForm'; + self.Text := 'Virtual FTP'; + self.Closed += new System.EventHandler(@self.MainForm_Closed); + self.GroupBox1.ResumeLayout(false); + self.GroupBox1.PerformLayout(); + (self.pictureBox1 as System.ComponentModel.ISupportInitialize).EndInit(); + self.ResumeLayout(false); + self.PerformLayout(); +end; +{$ENDREGION} + +{$REGION Application Entry Point} +[STAThread] +class method MainForm.Main; +begin + Application.EnableVisualStyles(); + + try + with lForm := new MainForm() do + Application.Run(lForm); + except + on E: Exception do begin + MessageBox.Show(E.Message); + end; + end; +end; +{$ENDREGION} + +method MainForm.btnStartStop_Click(sender: System.Object; e: System.EventArgs); +begin + If (btnStartStop.Text = 'Start') Then Begin + addToLog('Starting Virtual FTP on ' + port.ToString() + ' port...'); + StartServer(port); + llShortcut.Text := String.Format('ftp://localhost:{0}/', port); + addToLog('Virtual FTP is running under ' + Environment.OSVersion.ToString()); + btnStartStop.Text := 'Stop'; + End Else Begin + addToLog('Shutting down Virtual FTP ...'); + StopServer(); + llShortcut.Text := ''; + addToLog('Virtual FTP is stopped.'); + btnStartStop.Text := 'Start'; + End +end; + +method MainForm.addToLog(line: String); +begin + txtLog.Invoke(method begin + txtLog.Text := txtLog.Text + + System.DateTime.Now.ToLongTimeString() + + ': ' + + line + + Environment.NewLine; + end); +end; + +method MainForm.StartServer(aPort: Integer); +var lDiskFolder: String; +begin + lDiskFolder := Path.GetDirectoryName(typeof(Self).Assembly.Location) + '..\..\..\'; + + fRootFolder := New VirtualFolder(nil, '[ROOT]'); + fRootFolder.Add(New VirtualFolder(nil, 'virtual')); + fRootFolder.Add(New DiscFolder(nil, 'drive-c', 'c:\')); + fRootFolder.Add(New DiscFolder(nil, 'disc', lDiskFolder)); + fRootFolder.Add(New EmptyFile(nil, '=== Welcome to the FTP ===')); + + fUserManager := New UserManager(); + UserManager(fUserManager).AddUser('test', 'test'); + + fFtpServer := New VirtualFtpServer(); + fFtpServer.Port := aPort; + fFtpServer.Timeout := 60 * 1000; {* 1 minute *} + if fFtpServer.BindingV4<>nil then + fFtpServer.BindingV4.ListenerThreadCount := 10 + else + fFtpServer.BindingV6.ListenerThreadCount := 10; + fFtpServer.RootFolder := fRootFolder; + fFtpServer.UserManager := fUserManager; + fFtpServer.ServerName := 'VirtualFTP Sample - powered by RemObjects Internet Pack for .NET'; + + fFtpServer.Open(); + + addToLog('VirtualFTP 0.3 BETA - started up'); + end; + +method MainForm.StopServer(); +begin + If (fFTPServer<>nil) Then + fFtpServer.Close() +end; + +method MainForm.llShortcut_LinkClicked(sender: System.Object; e: System.Windows.Forms.LinkLabelLinkClickedEventArgs); +begin + If llShortcut.Text <> '' Then + System.Diagnostics.Process.Start(llShortcut.Text); +end; + +method MainForm.MainForm_Closed(sender: System.Object; e: System.EventArgs); +begin + StopServer(); +end; + +end. \ No newline at end of file diff --git a/Samples/Oxygene/Virtual FTP/Main.resx b/Samples/Oxygene/Virtual FTP/Main.resx new file mode 100644 index 0000000..a726801 --- /dev/null +++ b/Samples/Oxygene/Virtual FTP/Main.resx @@ -0,0 +1,582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + + + AAABAAYAMDAAAAEACACoDgAAZgAAACAgAAABAAgAqAgAAA4PAAAQEAAAAQAIAGgFAAC2FwAAMDAAAAEA + IACoJQAAHh0AACAgAAABACAAqBAAAMZCAAAQEAAAAQAgAGgEAABuUwAAKAAAADAAAABgAAAAAQAIAAAA + AAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAAFBQUACQkJAA0NDQAUFAsAEhINABwcCQAaGg8AHh4OABIS + EgAVFRUAGhoUABwcFgAZGRkAHR0dACQkBwAqKgcAJiYIACYmDwAoKAkALCwJAD4+AwAxMQoAPDwIACcn + EAAkJBcALi4QAC0tFAAhIRoAIyMcACQkHQAtLR4AMDARAD8/FAAzMxgANjYZADk5GwA8PBgAOzscAD09 + HgAhISEAJSUlAC0tJQApKSkALS0tADQ0JAA0NCsAPT0rADExMQA1NTUAOjoxAD8/NgA5OTkAPDw8AEJC + AgBDQwQARkYHAElJAABNTQAASkoGAE5OBQBHRwwASUkJAElJDQBMTA0AUVEAAFVVAABRUQYAVVUHAFlZ + AABdXQAAWVkHAF9fCABDQxYASUkQAElJGQBOThsAVlYQAFRUFABbWxwAYWEAAGVlAABnZwQAaWkAAG5u + AABlZQkAcXEAAHV1AABycgUAdXUFAHp6AAB9fQAAdHQLAH5+DQBkZBAAb28UAGRkGgBiYh8AdXUVAEFB + IABERCIARkYkAE5OIgBJSSUATEwnAE5OKABcXCMAUFAqAFJSLABUVC8ARUUyAEtLOQBPTz0AVlYxAF9f + MgBrayAAZGQtAHR0IAB0dDcAcnI/AH5+OQBBQUEARUVFAE1NRQBJSUkATU1NAF5eRQBSUkoAUlJSAFVV + VQBYV1AAWVlZAF1dXQBjY0EAZGRMAGpqSQB8e0EAe3tMAGhoUQBiYlsAaWlbAGpqXABtbV8AdnZZAHp6 + XQBhYWEAZGRkAGpqZABoaGgAdXVoAHFxcQB+fn4AgoIAAIWFAACCggYAiYkAAI6OAACKigYAgYEMAJGR + AACWlgAAmZkBAJ2dBgCEhBMAh4cXAIeHGACKihwAnp4UAKGhDAClpRIAqakXAKalHwCtrB0Ah4cjAI6O + IQCRkSUAmJgjAJiYJACUlCoAl5cvAImJMgCYmDAAm5s0AJKSPwCenjgArKwrALCwIwC0tCkAuLgvAKGh + PQCpqDwAsLAwALy7NAC/vzoAhoZPAIuLTACbm0wAiopfAJ6eUACZmV4ApKRCAKenRwCsrEIAqKhHAKur + SwCrq0wAtLNBAK6uUACpqVQAp6dcALKxVQC6uVUAtbVaAL29WgCGhmQAg4NpAIiIbgCWlmQAlpZuAJOT + dAC7u2MApqZwALy7dACzsnwAv75+AMPDQADHx0YAy8pMAM/OUQDT0lcA1tZcAMLCbQDa2WIA3t1oANnY + bADGxXIA0dF4AOLhbgDm5XQA4uF6AOvqfACBgYEAhoaGAIyMjACRkZEAlpaWAJ+fnwC6uoEAoaGhAKys + rADCwoMA6+qIAPHwhQD19IsA8/KSAPn4kQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACSMQ0KCgoKCgMDK4MAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACDKA0oDQ0NDQoKCgMKAwMDA3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAeigoKCgoKA0NDQoKCgoDAwMDAwADKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJIrKysrKCgoKCgN + DQ0KCgoKCgMDAwMDAAN6AAAAAAAAAAAAAAAAAAAAAAAAAAAANSsrKysrKygtIUBHR0REPDwXEwcDAwMD + AwAACgAAAAAAAAAAAAAAAAAAAAAAAAAxMTErMSsrS1VXV1dUVFJSUFBQQkI4EwMDAwMDAAAAAAAAAAAA + AAAAAAAAAAAA8TExMSsxK0taWlpaV1dXVFRSUlBQUEJQQjgPAwIDAwADgwAAAAAAAAAAAAAAAAD0NTEx + MTEvXJ2ZmZlaWldXVFRUUlJQUEJCQkJCFQQCAgMAA5YAAAAAAAAAAAAAAAA1NTUxMWidnZ2dmZlaWlpX + V1RUUlJSUFBQUEJCPDwGAwADAAAAAAAAAAAAAAAAAHw1NTU1aqCgoJ2dnZmZWlpaWldUVFRSUlBQQkJC + Qjw8DwMDAwAKAAAAAAAAAAAAlnp6NTVsoqKgoJ2gnZmZmZlaWldXV1RSUlJQUFBQQkI8PAYDAAMAegAA + AAAAAAAAeno1em+nqKKgoKCdnZ2dmZlaWlpXV1RUVFJSUFBCUEJCQjgEAwMDAAAAAAAAAACDenp6NbCq + qaiioqCgoJ2dnZmZWlpaV1dXVFJSUlBQQlBCQjw4AgMAAygAAAAAAAB8fHp6drqsqqmooqKgoJ2gnZmZ + mZlaWldXVFRUUlJQUEJCQkI8EAMDAwMAAAAAAJJ8fHp7v7u6rKqpqKKgoKCdnZ2dmZlaWlpXV1dUUlJS + UFBQQkJCPAQDAwArAAAAAHx8fHyIwLy7uqyqqaiioqCgoJ2dnZmZWlpaV1dUVFRSUBs4UFBCQhADAwMD + AAAAAIN8g3zOwcC8u7qsqqmooqCgoJ2gnZmZmZlaWldXV1RUUjgNG0JQQjwDAwMDAAAAl3x8fIbi4sDA + vLu6rKqpqKKgoKCdnZ2dmZlaWlpXV1RUVFIZDQ04UEIHBAMDegAAg4ODfMLj4uLBwLy7uqyqqaiioqCg + oJ2dnZmZWlpaV1dUVFQ9DQ0KGUITCgMDAwAAg3yDg9Dk4+LhwcB4ubqsqqmooqKgoJ2gnZmZmZlaWldX + VFRSHA0NCg0SCgoDAwAAg4N8g9Pl5OPiyn53vLu6rK1xbGtraGhoZGRkZCcnJycnIyMiHA0NDQ0KCgQJ + AwAAg4ODg+bm5eTDfHy+wLy7urFvNTU1a2hoaGhkZGRkJycnIyMjKCgNDQ0KCgoKCQAAg4ODg+jo1Yp8 + fIfiwcC8u7qsrnQ1M2GdoJ2dnZ2ZmVpaWldHKCgoDSJQCgoKCgAAg4ODg+nHg3yDfMbi4cHAvLu6rKqn + dW40aF+gnZ2ZmZlaWlpLKCgcSlJSCgoKCgAAg4ODg4yDg4N8g8/PyMi9uLazs7CupqRzbjEzT5mdmZmZ + mVcoKCdSVFRSDQ0KCgAAkpKDg4ODfIODfIB8fHx8e3p6ejV5NTU1NTExMTGdnZ2ZmU4rT1dXV1RQDSgK + DQAAkoOSg4yDg4N9g9LS0s/JyL24trOzr66mpKSeXZmgnZ2dmUtXWlpXV1RHKA0NDQAAkpKDkt3ei4OD + g9Hm5ePj4uLBwLu7uqyqqaiioqCdoJ2dmZmZWlpaV1dKKCgNDQAA8ZKSktf879eDgI/o5uXk4+LhwcDA + u7qsqqmooqKgoKCdnZmZmZlaWlcnDSgofAAA9pKSg5L7/PDrioPc6Obl5OPi4sDAvLu6rKqpqKKgoKCd + nZ2dmZlaWlcoKCgolgAAAJKSkpLf/fzw79nF6Ojm5eTj4uLBwLy7uqyqqaiioKCgoJ2dnZmZWk8pKSgo + AAAAAJaSkpKV//388PDr6eno5uXk4+LhwcC8u7qsqqmooqKgoKCdnZmZmS0pKSgxAAAAAACUkpKS+v/9 + /PDu7+3p6Obl5OPi4sDAvLu6rKqpqKKgoKCdoJ2dVSsrKykAAAAAAACUlJKSk/7//fz78O7t6Ojm5eTj + 4uLBwLy7uqyqqaiioKCgnZ2ZLisrKysAAAAAAAAAlJSSktv///388PDu7eno5uXk4+LhwcC8u7qsqqmo + oqKgoKBoLjErLvEAAAAAAAAAlpSUk5Lf///9/PDw7u3p6Obl5OPi4sDAvLu6rKqpqKKgoGEuMDArNQAA + AAAAAAAAAJSUlJSS+v///fzw8O7t6ejm5eTj4uLBwLy7uqyqqaiiXzMwMTAxAAAAAAAAAAAAAPmUlJSU + kvf///388PDu7eno5uXk4+LhwcC8u7qsqql1NTQxMTDyAAAAAAAAAAAAAADylJSUlJLb/v/9/PDw7u3p + 6Obl5OPi4sDAvLu6q3I1NTQ0MYMAAAAAAAAAAAAAAAAA8pSUlJSSlfr//fzw8O7t6ejm5eTj4uLBwLy0 + b3o1NTU0gwAAAAAAAAAAAAAAAAAAAPKUlJSUkpLY4P388PDu7eno5uXk4+Lht4V6ejV6NTWEAAAAAAAA + AAAAAAAAAAAAAAD4lJSUlJKSkpPa3uzw7u3p6ObTxomAe3p6eno1efEAAAAAAAAAAAAAAAAAAAAAAAAA + AJaUlJSSkpGRg4ODg5CQg4N8g3x8fHx8enp8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4lJSSkpKSkpGD + g4ODg3yDfHyDfHx8evIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiUkpKSg5KRg4ODg4ODg3x9fIDy + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSWkpKRkYODg4N9g4PyAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + wAP//wAA//4AAH//AAD/+AAAH/8AAP/gAAAH/wAA/8AAAAP/AAD/gAAAAf8AAP4AAAAAfwAA/AAAAAA/ + AAD8AAAAAD8AAPgAAAAAHwAA8AAAAAAPAADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADAAAAAAAMAAMAA + AAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAA + AAAAAwAAwAAAAAADAADgAAAAAAcAAOAAAAAABwAA8AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA+AAAAAAf + AAD8AAAAAD8AAP4AAAAAfwAA/wAAAAD/AAD/gAAAAf8AAP/gAAAH/wAA//AAAA//AAD//AAAP/8AAP// + gAH//wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAEAAAAAAAAAAAAAAABAAAAAQAAAAAAAAYG + BgAJCQkADAwMABAQCwASEg0AGBgJABISEgAUFBQAHBwWABkZGQAeHh4ALy8GACoqCgAuLgkAKysPADc3 + DQAqKhEALS0TACgoFQAmJhkANTUYADY2HwA4OBoAOzscAD4+HgAhISEAJCQkACkpKQAuLi4AODghADEx + MQA1NTUAOjoxAD8/NgA5OTkAPj4+AENDAgBHRwEAQkIFAEZGBQBLSwAAT08AAE1NBgBTUwAAV1cBAFJS + BgBYWAAAXFwAAFFRCABSUg8AV1cNAF1dCABERBIAQEAaAE9PHABQUBMAXV0dAGBgAABkZAAAZ2cEAGho + AABtbQAAa2sEAGVlCQBsbAoAcXEAAHV1AAB3dwUAenoAAH19AABycg8AYGAYAHNzFAB5eREAfHsfAEJC + IQBFRSIASUklAExMJwBPTykASUktAFhYKABcXC4AREQ7AEtLOQBrayAAfX0tAHBwMQBBQUEARkZGAExM + QwBJSUkATU1NAFpaQQBUVEwAWlpJAFJSUgBVVVUAWFhYAF1dXQBubk4Afn5PAGFhWgBmZlgAYWFhAGVl + ZQBtbWcAaGhoAG5ubgB7e2cAcnJyAHZ2dgB7e3sAgYEAAIaGAACDgwYAi4sAAI+PAACFhQoAk5MAAJeX + AACcnAUAj48WAJaVFACiog4AqKcWAK2tHwCHhyMAgoI/AJSUNgCYmDwArKwpALOzJwC5uDAAtrY5AL6+ + OACKilUAlpZQAJKSVQCbmlUAlpZaAJ+eWwCqqkoAvLxBALi4XgCEhGEAiIdlAIGBbgCLimkAnp5lAIuK + cQCbmnQAnp54AKyrYgC9vGUApqZwAK6uegC9vXIAxMNBAMnJSQDPzlIA1dRaAMLBbADLym8A0tFiANra + YwDg32sA5uV0AOLhewDr6nwAg4ODAIWFhQCIiIgAj4+PAJaWlgChoaEA4+KHAOnojwDx8IUA9vaNAPz7 + lQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAG9aCggICApcAAAAAAAAAAAAAAAAAAAAAAAAAAAAbyAaChoKCggICAMDAyAAAAAAAAAA + AAAAAAAAAAAAAB0dGhoaGgoKCggICAMDAwBaAAAAAAAAAAAAAAAAAGMdHR0aHjI0Qj87OyonDQQDAwAI + AAAAAAAAAAAAAABjICAdTEdyckJCPz87OjotKgwDAwMAAAAAAAAAAAAAaSMgIEh1c3JyckJCPz87Ojot + LScGAwADAAAAAAAAAHEjIyNJeHV1c3JyQkJCPz87OjotLSoEAwACAAAAAAAAWlojS3p6eHV1c3JyckJC + Pzs7OjotLSoEAwIgAAAAAGNaWlh+fXp6eHV1c3JyckJCPz87Oi0tLScGAAMAAAAAXFpbhH9+fXp6eHV1 + c3JyQnI/Pzs7OzotLQwDAxoAAG1cXIGGhX9+fXp6eHV1c3JyQkJCQj8tKi0tKgQDAwAAYVxhkIiGhX9+ + fXp6eHV1c3NyckJCPz8REC0tDQQDbwBhYWWgn4iGhX9+fXp6eHV1c3JyckJCPy0KCicqBwcjAGFhi6Gg + n4eChX9+fXp6eHV1c3JyQkJCPxQKChEIBwOwY2GOoqGPYIOGhX9VIyNQT09MTB5MGBgVGgoKCggICKxj + Y6OliVxgn4aGhX+AU1NKeHV1dXNycjsaGhQxEQgIrGNjlmhhYYqgn4iGhX9+fFZROXN1c3JyOBo1Pz8T + CAgAaWNjY2NhYWFcXFtaWiMjIyMjIHR1c3IeQEJCPwoKCgBpaZWVY2GNoqGgn4aGhYV+fXp4eHV1c3Jy + ckI/GgoKAGlpbrOeZ2imoqGgn4iGhX9+fXp4eHV1c3JyQjQaGmEAbWlpsbOpkpqmoqGgn4iGhX9+fXp6 + eHV1c3JyGBoaAACwaWmZtLOqpKemoqGgn4iGhX9+fXp4eHV1c0IdGxsAAABtaWmytLOqqKemoqGgn4iG + hX9+fXp6eHV1Nx0bZAAAAK5tbW61tLOqqKemoqGgn4iGhX9+fXp4eEkdHR0AAAAAAG1pbZm1tLOqqKem + oqGgn4iGhX9+fXp0Ix8dbQAAAAAAAG1paZm1tLOqqKemoqGgn4iGhX9+fCMjH1wAAAAAAAAAsG1taZey + tLOqqKemoqGgn4iGhVdUIyNbAAAAAAAAAAAAsG1pbWudsbOqqKemoqGgn4NeWiNaWgAAAAAAAAAAAAAA + AG1paWlpbpyeo5uRimZfXFpaWmcAAAAAAAAAAAAAAAAAAK5tbWlpY2NjY2FhYVxcXGEAAAAAAAAAAAAA + AAAAAAAAAACubWlpaWNjY2NhYW0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACurK6srgAAAAAAAAAAAAAA + AAD/8A///4AB//8AAH/8AAA/+AAAH/AAAA/gAAAH4AAAA8AAAAPAAAABgAAAAYAAAACAAAAAgAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAAGAAAABwAAAAcAAAAPgAAAD8AAAB/AAAA/4AAAf/gAAP/8A + AP//wAP///wf/ygAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAABAAAAAAAACgoKAA4O + CQAPDw8AGxsLABQUFAAYGBgAHR0dAC4uDAA8PAQANjYJADQ0DgA9PQsAKioTACIiGwArKysAMTEiADQ0 + NAA7OzEAODg4AD4+PgBAQAMAREQCAEREDgBNTQoASUkMAFNTBABZWQAARUUSAEFBFABWVhAAYWEAAGlp + AABtbQIAZmYJAG1tCgBycgAAe3sAAHV1CwB9fQ0AZGQUAHh4GwBYWCEAX18nAFlZKgBFRTwAc3MqAHt7 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + 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 + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + OS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trf2pqav9oaGj/ZmZm/2Rk + ZP9hYWH/e3tn/6amcP+9vXH/wsFs/728Zf+4uF7/m5pV/35+T/9UVEz/SUlJ/0dHR/9FRUX/QkJC/0BA + QM8+Pj4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trL2pq + ar9oaGj/ZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dH + R+9ERER/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGpqak9oaGivZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05O + Tt9LS0t/SkpKDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZ2cPZWVlT2NjY39hYWG/Xl5ev1xcXL9aWlq/V1dXn1VV + VW9SUlIvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAP//wAA//4A + AD/4AAAf8AAAD+AAAAfgAAADwAAAA8AAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAcAAAAHAAAAD4AAAA/AAAAf4AAAP/AAAH/4AAH//gAD//+AH/ygA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJycnPyIi + Iq8dHR3vGBgY/xQUFP8PDw//CwsLvwcHB18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUPMDAwrysr + K/8xMSL/RUUS/0REDv89PQv/NjYJ/xsbC/8KCgr/BgYGzwMDAy8AAAAAAAAAAAAAAAA/Pz8POTk5zzU1 + Nf9kZBT/g4MA/3t7AP9ycgD/aWkA/2FhAP9ZWQD/PDwE/w4OCf8GBgbvAwMDLwAAAAAAAAAAQ0NDjz4+ + Pv94eBv/lJQA/4uLAP+DgwD/e3sA/3JyAP9paQD/YWEA/1lZAP9ERAL/Dg4J/wYGBs8AAAAATExMH0hI + SP97ezb/qakY/56eCP+UlAD/i4sA/4ODAP97ewD/cnIA/2lpAP9hYQD/WVkA/0BAA/8KCgr/BwcHX1BQ + UI9MTEz/uLg7/7S0Kv+pqRj/np4I/5SUAP+LiwD/g4MA/3t7AP9ycgD/aWkA/1NTBP9ZWQD/GxsL/woK + Cs9VVVW/cnJT/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uLAP+DgwD/e3sA/3JyAP9JSQz/KioT/y4u + DP8PDw//W1tb75KSX//OzVz/e3tM/8C/O/+0tCr/e3sr/19fJ/99fQ3/dXUL/21tCv9mZgn/QUEU/yIi + G/80NA7/FBQU/19fX99+fmL/X19X/2ppUv+Likr/g4NA/3t7Nf9zcyr/WVkq/1hYIf+LiwD/g4MA/1ZW + EP9tbQL/TU0K/xgYGP9kZGS/hYVq/66tcf+BgVz/1tVc/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uL + AP+DgwD/e3sA/0VFEv8dHR3/Z2dnb2RkZP/k44j/29p7/9nYbP/W1Vz/y8pL/8C/O/+0tCr/qakY/56e + CP+UlAD/i4sA/4ODAP8xMSL/IiIir2trax9paWn/lZR1//j3j//t7H//4eFu/9bVXP/Lykv/wL87/7S0 + Kv+pqRj/np4I/5SUAP9kZBT/Kysr/ygoKF8AAAAAa2trf2lpaf/FxIb/+PeP/+3sf//h4W7/1tVc/8vK + S//Avzv/tLQq/6mpGP+LixH/Ozsx/zAwML8AAAAAAAAAAAAAAABra2ufaWlp/6iofP/u7Yz/7ex//+Hh + bv/W1Vz/y8pL/8C/O/97ezb/RUU8/zk5Oc81NTUPAAAAAAAAAAAAAAAAAAAAAGtra39paWn/ZGRk/4WF + av+jomv/m5tg/3p6VP9UVEz/SEhI/0NDQ68/Pz8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trH2ho + aI9kZGS/X19f/1tbW/9WVlbPUVFRn0xMTD8AAAAAAAAAAAAAAAAAAAAA8A8AAMADAACAAQAAgAEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAMABAADgAwAA8A8AAA== + + + \ No newline at end of file diff --git a/Samples/Oxygene/Virtual FTP/VirtualFTP.2008.sln b/Samples/Oxygene/Virtual FTP/VirtualFTP.2008.sln new file mode 100644 index 0000000..40fef59 --- /dev/null +++ b/Samples/Oxygene/Virtual FTP/VirtualFTP.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "VirtualFTP", "VirtualFTP.oxygene", "{71130CD9-002B-49BC-811A-1F172BFBAA28}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {71130CD9-002B-49BC-811A-1F172BFBAA28}.Debug|Default.ActiveCfg = Debug + {71130CD9-002B-49BC-811A-1F172BFBAA28}.Debug|Default.Build.0 = Debug + {71130CD9-002B-49BC-811A-1F172BFBAA28}.Release|Default.ActiveCfg = Release + {71130CD9-002B-49BC-811A-1F172BFBAA28}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/Virtual FTP/VirtualFTP.2010.sln b/Samples/Oxygene/Virtual FTP/VirtualFTP.2010.sln new file mode 100644 index 0000000..353a1c8 --- /dev/null +++ b/Samples/Oxygene/Virtual FTP/VirtualFTP.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{656346D9-4656-40DA-A068-22D5425D4639}") = "VirtualFTP", "VirtualFTP.oxygene", "{71130CD9-002B-49BC-811A-1F172BFBAA28}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Default = Debug|Default + Release|Default = Release|Default + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {71130CD9-002B-49BC-811A-1F172BFBAA28}.Debug|Default.ActiveCfg = Debug + {71130CD9-002B-49BC-811A-1F172BFBAA28}.Debug|Default.Build.0 = Debug + {71130CD9-002B-49BC-811A-1F172BFBAA28}.Release|Default.ActiveCfg = Release + {71130CD9-002B-49BC-811A-1F172BFBAA28}.Release|Default.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/Oxygene/Virtual FTP/VirtualFTP.Sample.html b/Samples/Oxygene/Virtual FTP/VirtualFTP.Sample.html new file mode 100644 index 0000000..17a3adf --- /dev/null +++ b/Samples/Oxygene/Virtual FTP/VirtualFTP.Sample.html @@ -0,0 +1,46 @@ + + + + + + + + + +
+

+ Virtual FTP Sample +

+ +
+

Purpose

+

+This sample shows how to create and configure virtual FTP. This supports building + a virtual folder structure, similar to IIS, based on real and non-existing folders.

+

+Note: +
+To connect to Virtual FTP, please use port 4444. (ftp://localhost:4444/) +
UserName=test +
Password=test +

+ +

Examine the Code

+
    +
  • Look at the StartServer function. There you can find how you can configure your ftp. +You can add a VirtualFolder or a DiskFolder. +
  • +
  • Also there you can see how to add the users allowed to  use this ftp.
  • +
+ +

Getting started

+
    +
  • Build the application.
  • +
  • Run the application and start the virtual FTP
  • +
  • With the help of the link on the form, open virtual FTP in your WebBrowser
  • +
  • Test virtual ftp.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/Oxygene/Virtual FTP/VirtualFTP.oxygene b/Samples/Oxygene/Virtual FTP/VirtualFTP.oxygene new file mode 100644 index 0000000..3bde947 --- /dev/null +++ b/Samples/Oxygene/Virtual FTP/VirtualFTP.oxygene @@ -0,0 +1,54 @@ + + + VirtualFTP + WinExe + VirtualFTP + False + False + App.ico + Release + {71130CD9-002B-49BC-811A-1F172BFBAA28} + v2.0 + + + DEBUG;TRACE; + .\bin\Debug + True + + + .\bin\Release + False + + + + + + + + True + + + + + + + + + + + + + + + + + Form + VirtualFTP.MainForm + + + + + + + + \ No newline at end of file diff --git a/Samples/Samples.2003.sln b/Samples/Samples.2003.sln new file mode 100644 index 0000000..2edf6e0 --- /dev/null +++ b/Samples/Samples.2003.sln @@ -0,0 +1,22 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HTTPSpy.2003", "C#\HTTP Spy\HTTPSpy.2003.csproj", "{E2A1390A-0AFA-4ACF-9DA3-917E832901F5}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + RO = RO + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Debug.ActiveCfg = Debug|.NET + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Debug.Build.0 = Debug|.NET + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Release.ActiveCfg = Release|.NET + {E2A1390A-0AFA-4ACF-9DA3-917E832901F5}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Samples/Samples.html b/Samples/Samples.html new file mode 100644 index 0000000..e8dd3ba --- /dev/null +++ b/Samples/Samples.html @@ -0,0 +1,204 @@ + + + + + + + + +

+ Internet Pack Samples for .NET +

+ +

+ Please keep the following things in mind when working with the samples:

+
    +
  • + To help you find the sample you need and also to provide a suggested start order, + the samples are listed in one or more categories followed by the actual sample descriptions + in alphabetical order.
  • +
  • There is an html file provided in the top level folder of each sample. These files + contain useful notes and we recommend that you read the appropriate file before + running a sample.
  • +
  • There may be samples shipped that are not listed below. Samples are + not added to the list until they have passed our final quality inspection. This + doesn't mean they are faulty, only that they are still pending final inspection.
  • +
+

+ + Sample Categories

+ + + + + + + + + + + + + + + + + + + + + + + + +
+

+ Category +

+
+

+ Samples +

+
+ Introduction + HTTP Responses
+ HTTP Spy
+ Intermediate + Sample Server
+ SMTP Client
+ Advanced + FTP Sync
+ Virtual FTP
+

+ + Sample Descriptions

+
    +
  • Samples are provided for more than one language.
  • +
  • The versions below relate to + the folders under the ..\Samples folder. 
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Name + Versions + Category + Description
+ FtpSync + C#, Oxygene, VB + Advanced + This sample compares files on the local computer against those on an ftp folder + and downloads files that are new or have changed.
+ HTTP Responses + C#, Oxygene, VB + Introduction + This sample shows how to create and configure a simple HTTP server.
+ HTTP Spy + C#, Oxygene, VB + Introduction + This example demonstrates how HTTP works. With help of the HttpClient components, + we can send a  HttpClientRequest and retrieve back a HttpClientResponse.
+ Sample Server + C#, Oxygene, Delphi, VB + Intermediate + This example shows how easy it is to create a simple custom Http server.
+ SMTP Client + C#, Oxygene, VB + Intermediate + The sample shows how we can use the SMTP client component for sending emails.
+ Virtual FTP + C#, Oxygene, VB + Advanced + This sample shows how to create and configure virtual FTP.
+
+ +

+ Support

+

+ If you encounter any problems or have questions regarding the samples, please feel + free to ask on our newsgroup at + news://news.remobjects.com/remobjects.public.internetpack.net. +

+

+ Thank you very much,
+ Your RemObjects Team
+ http://www.remobjects.com +

+

+  

+ +

+  

+ +

 

+ + \ No newline at end of file diff --git a/Samples/Styles.css b/Samples/Styles.css new file mode 100644 index 0000000..c8e0628 --- /dev/null +++ b/Samples/Styles.css @@ -0,0 +1,103 @@ +body +{ + background-color: #f7f7f7; + margin-top: 15px; + margin-bottom: 15px; + margin-left: 15px; + margin-right: 15px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + font-family: tahoma, verdana, sans-serif; + font-size: 10pt; + width: 700px; + color: #000000; +} +p +{ + padding-top: 0; + padding-bottom: 0; + padding-left: 0; + padding-right: 0.5em; +} +ul +{ + padding-top: 0; + padding-bottom: 0; + list-style-type: disc; +} +li +{ + padding-top: 0; + padding-bottom: 0; +} +img +{ + margin: 5px; + border-width: 0; +} +table +{ + background-color: #f7f7f7; + margin: 15px; + padding: 0px; + font-size: 10pt; +} +tr +{ + background-color: #f7f7f7; + margin: 15px; + padding: 0px; + font-size: 10pt; +} +td, th +{ + background-color: #f7f7f7; + margin: 0; + padding: 5px; + font-size: 10pt; +} +td ul +{ + padding-left: 2em; +} + +img:left { margin-left: 0; } +img:right { margin-right: 0; } +p.h1 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:13pt; + font-weight:bold; +} +p.h2 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:11pt; + font-weight:bold; +} +p.h3 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:10pt; + font-weight:bold; +} +pre +{ + margin-top:0px; + margin-bottom:0px; + margin-left:0px; + margin-right:0px; +} +.spaced +{ + letter-spacing:1px; + color:#000060; +} diff --git a/Samples/VB/FTP Sync/AssemblyInfo.vb b/Samples/VB/FTP Sync/AssemblyInfo.vb new file mode 100644 index 0000000..705ac1e --- /dev/null +++ b/Samples/VB/FTP Sync/AssemblyInfo.vb @@ -0,0 +1,32 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' 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: + + diff --git a/Samples/VB/FTP Sync/FtpSync.2008.sln b/Samples/VB/FTP Sync/FtpSync.2008.sln new file mode 100644 index 0000000..64aa84c --- /dev/null +++ b/Samples/VB/FTP Sync/FtpSync.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "FtpSync", "FtpSync.2008.vbproj", "{3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/FTP Sync/FtpSync.2008.vbproj b/Samples/VB/FTP Sync/FtpSync.2008.vbproj new file mode 100644 index 0000000..a391775 --- /dev/null +++ b/Samples/VB/FTP Sync/FtpSync.2008.vbproj @@ -0,0 +1,110 @@ + + + Local + 8.0.50727 + 2.0 + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8} + Debug + AnyCPU + + + + + FtpSync + + + None + JScript + Grid + IE50 + false + Exe + Binary + On + Off + FtpSync + FtpSync.FtpSyncMain + + + Console + + + 2.0 + + + bin\ + FtpSync.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + + + bin\ + FtpSync.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + + + + + + System + + + System.Data + + + System.XML + + + + + + + + + + + + Code + + + Code + + + Code + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/FTP Sync/FtpSync.2010.sln b/Samples/VB/FTP Sync/FtpSync.2010.sln new file mode 100644 index 0000000..00bf712 --- /dev/null +++ b/Samples/VB/FTP Sync/FtpSync.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "FtpSync", "FtpSync.2010.vbproj", "{3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/FTP Sync/FtpSync.2010.vbproj b/Samples/VB/FTP Sync/FtpSync.2010.vbproj new file mode 100644 index 0000000..0b9454b --- /dev/null +++ b/Samples/VB/FTP Sync/FtpSync.2010.vbproj @@ -0,0 +1,116 @@ + + + Local + 10.0.20506 + 2.0 + {3AD70679-57DE-4CF2-B7AF-BB9F320E9DB8} + Debug + AnyCPU + + + + + FtpSync + + + None + JScript + Grid + IE50 + false + Exe + Binary + On + Off + FtpSync + FtpSync.FtpSyncMain + + + Console + + + 3.5 + v2.0 + + + bin\ + FtpSync.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + AllRules.ruleset + + + bin\ + FtpSync.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + AllRules.ruleset + + + + + + System + + + System.Data + + + System.XML + + + + + + + + + + + + Code + + + Code + + + Code + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/FTP Sync/FtpSync.Sample.html b/Samples/VB/FTP Sync/FtpSync.Sample.html new file mode 100644 index 0000000..2822611 --- /dev/null +++ b/Samples/VB/FTP Sync/FtpSync.Sample.html @@ -0,0 +1,54 @@ + + + + + + + + +
+

+ FtpSync Sample +

+ +
+

Purpose

+

+ This sample shows how to use the FtpClient class to gain access to an ftp + site and download files from there.
+The +FtpSync application compares files on the local computer against those on the ftp folder and downloads files that are new or have changed.
+ +FtpSync is a console application with following uasge:

+

+
+ +FtpSync local user:pass@server/remote [-delete] [-passive] [-l]

+

+ +
+ +where:
+ + + + + + + + + + +
local:local directory - long file names with spaces should be quoted
user/pass:ftp username & password
server:ftp server (i.e., ftp.whatever.com)
remote:directory on the remote server
-delete:delete local files that don't exist on the server
-passive:turn on 'Passive' mode
-l:log - show FTP commands
+
+

+ + +

Getting started

+
    +
  • Build the application.
  • +
  • Run the application with your parameters. Try to synchronize your local folder with an ftp folder
  • +
+ + + \ No newline at end of file diff --git a/Samples/VB/FTP Sync/FtpSyncMain.vb b/Samples/VB/FTP Sync/FtpSyncMain.vb new file mode 100644 index 0000000..3ce7921 --- /dev/null +++ b/Samples/VB/FTP Sync/FtpSyncMain.vb @@ -0,0 +1,10 @@ +Module FtpSyncMain + + Sub Main(ByVal args As String()) + Dim lWorker As FtpSyncWorker + lWorker = New FtpSyncWorker + If lWorker.CheckArgs(args) Then + lWorker.Sync() + End If + End Sub +End Module diff --git a/Samples/VB/FTP Sync/FtpSyncWorker.vb b/Samples/VB/FTP Sync/FtpSyncWorker.vb new file mode 100644 index 0000000..251a149 --- /dev/null +++ b/Samples/VB/FTP Sync/FtpSyncWorker.vb @@ -0,0 +1,284 @@ +Imports System +Imports System.IO +Imports System.Collections.Specialized +Imports System.Text.RegularExpressions +Imports RemObjects.InternetPack.Ftp +Public Class FtpSyncWorker + + Public Sub New() + + End Sub + +#Region " private fields... " + Private _LocalDirectory As String + Private _Server As String + Private _ServerDirectory As String + Private _Username As String + Private _Password As String + Private _DeleteMissing As Boolean + Private _Upload As Boolean + Private _Subdirectories As Boolean + Private _Passive As Boolean + Private _ShowLog As Boolean + Private WithEvents _Ftp As FtpClient +#End Region + + + Public Sub Log(ByVal Sender As Object, ByVal ea As RemObjects.InternetPack.CommandBased.ClientLogArgs) Handles _Ftp.OnLog + If _ShowLog Then + Console.WriteLine(ea.Text) + End If + End Sub + + Private Sub _Ftp_OnTransferProgress(ByVal aSender As Object, ByVal ea As RemObjects.InternetPack.Events.TransferProgressEventArgs) Handles _Ftp.OnTransferProgress + Console.Write(".") + End Sub + + Public Sub Sync() + + Try + _Ftp = New FtpClient + _Ftp.HostName = _Server + _Ftp.UserName = _Username + _Ftp.Password = _Password + _Ftp.Passive = _Passive + _Ftp.Port = 21 + + '_Ftp.OnLog += new RemObjects.InternetPack.CommandBased.ClientLogEvent(Log); + ' _Ftp.OnTransferProgress +=new RemObjects.InternetPack.Events.TransferProgressEventHandler(_Ftp_OnTransferProgress); + Console.WriteLine("Connecting to " & _Server) + _Ftp.Open() + Try + _Ftp.Login() + SyncDirectory(_LocalDirectory, "/" & _ServerDirectory) + Finally + Console.WriteLine("Disconnecting") + _Ftp.Quit() + _Ftp.Close() + End Try + + Catch ex As Exception + Console.WriteLine("Error syncing directory ({0})", ex.Message) + If ex.StackTrace <> Nothing And _ShowLog Then + Console.WriteLine(ex.StackTrace) + End If + Console.WriteLine("Press enter to continue...") + Console.ReadLine() + End Try + End Sub + + Public Sub SyncDirectory(ByVal aLocalDirectory As String, ByVal aRemoteDirectory As String) + Dim lOriginalLocalDirectory As String + lOriginalLocalDirectory = Directory.GetCurrentDirectory() + Dim lOriginalRemoteDirectory As String + lOriginalRemoteDirectory = _Ftp.GetCurrentDirectory() + Try + Console.WriteLine("Local change directory to " + aLocalDirectory) + Directory.SetCurrentDirectory(aLocalDirectory) + Console.WriteLine("Remote change directory to " + aRemoteDirectory) + _Ftp.ChangeDirectory(aRemoteDirectory) + Console.WriteLine("") + + Console.WriteLine("Retrieving directory contents") + _Ftp.List() + Console.WriteLine("") + + Dim localFiles As StringCollection + localFiles = New StringCollection + localFiles.AddRange(Directory.GetFiles(aLocalDirectory)) + + If _Upload Then + Console.WriteLine("Upload not yet implemented") + End If + + Dim i As Integer + For i = 0 To _Ftp.CurrentDirectoryContents.Count - 1 + Dim lItem As FtpListingItem + lItem = _Ftp.CurrentDirectoryContents(i) + + If Not lItem.Directory Then + Dim lDownload As Boolean + lDownload = False + If File.Exists(lItem.FileName) Then + Dim lInfo As FileInfo + lInfo = New FileInfo(lItem.FileName) + + If lInfo.LastWriteTime <> lItem.FileDate Then + lDownload = True + End If + + If lInfo.Length <> lItem.Size Then + lDownload = True + End If + + localFiles.Remove(Path.Combine(aLocalDirectory, lItem.FileName)) + Else + lDownload = True + End If + + If lDownload Then + Console.WriteLine("Downloading " & lItem.FileName) + Dim lStream As Stream + lStream = File.Open(lItem.FileName, FileMode.Create) + + With lStream + _Ftp.Retrieve(lItem, lStream) + lStream.Close() + File.SetLastWriteTime(lItem.FileName, lItem.FileDate) + End With + Console.WriteLine("") + End If + End If + Next + + If _DeleteMissing Then + Dim ii As Integer + For ii = 0 To localFiles.Count - 1 + File.Delete(localFiles(ii)) + Next + End If + + Finally + Directory.SetCurrentDirectory(lOriginalLocalDirectory) + _Ftp.ChangeDirectory(lOriginalRemoteDirectory) + Console.WriteLine("") + End Try + End Sub + + Public Function CheckArgs(ByVal args As String()) As Boolean + Dim lCount As Integer + lCount = args.Length + Dim lBadParam As Boolean + lBadParam = False + + If (lCount > 0) Then + _LocalDirectory = args(0) + Else + lBadParam = True + End If + + If (lCount > 1) Then + Dim lMatch As Match + lMatch = Regex.Match(args(1), "(?\S+):(?\S+)@(?[^/\s]+)/(?\S*)") + If lMatch.Success Then + _Username = lMatch.Groups("user").Value + _Password = lMatch.Groups("pass").Value + _Server = lMatch.Groups("server").Value + _ServerDirectory = lMatch.Groups("dir").Value + Else + lBadParam = True + Console.WriteLine("Invalid server parameters") + Console.WriteLine("") + End If + Else + lBadParam = True + End If + Dim i As Integer + For i = 2 To lCount - 1 + Select Case args(i).ToLower() + Case "-delete" + _DeleteMissing = True + Case "-upload" + _Upload = True + Case "-passive" + _Passive = True + Case "-s" + _Subdirectories = True + Case "-l" + _ShowLog = True + Case "-help" + lBadParam = True + Case "/help" + lBadParam = True + Case Else + Console.WriteLine("Invalid command line paramter '" & args(i) & "'") + Console.WriteLine("") + lBadParam = True + End Select + Next + + If lBadParam Then + Console.WriteLine("RemObjects Internet Pack - FtpSync Sample") + Console.WriteLine("") + Console.WriteLine(" Usage: FtpSync local user:pass@server/remote [-delete] [-passive] [-l]") + Console.WriteLine() + Console.WriteLine(" Compares files on local computer against those on the remote server, ") + Console.WriteLine(" downloading those that are new or have changed.") + Console.WriteLine() + Console.WriteLine(" local : local directory - long file names with spaces should be quoted") + Console.WriteLine(" user/pass : ftp username & password") + Console.WriteLine(" server : ftp server (i.e., ftp.whatever.com") + Console.WriteLine(" remote : directory on remote server") + Console.WriteLine() + Console.WriteLine(" -delete : delete local files that don't exist on server") + Console.WriteLine(" -passive : turn on 'Passive mode'") + 'Console.WriteLine(" -upload : reverse comparision & transfer direction") + 'Console.WriteLine(" -s : recurse through subdirectories") + Console.WriteLine(" -l : log - show FTP commands") + Console.WriteLine() + Console.WriteLine("Press enter to exit.") + Console.ReadLine() + End If + Return Not lBadParam + End Function + +#Region "public properties..." + + Property LocalDirectory() As String + Get + Return _LocalDirectory + End Get + Set(ByVal Value As String) + _LocalDirectory = Value + End Set + End Property + Property Server() As String + Get + Return _Server + End Get + Set(ByVal Value As String) + _Server = Value + End Set + End Property + Property ServerDirectory() As String + Get + Return _ServerDirectory + End Get + Set(ByVal Value As String) + _ServerDirectory = Value + End Set + End Property + Property DeleteMissing() As Boolean + Get + Return _DeleteMissing + End Get + Set(ByVal Value As Boolean) + _DeleteMissing = Value + End Set + End Property + Property Upload() As Boolean + Get + Return _Upload + End Get + Set(ByVal Value As Boolean) + _Upload = Value + End Set + End Property + Property Subdirectories() As Boolean + Get + Return _Subdirectories + End Get + Set(ByVal Value As Boolean) + _Subdirectories = Value + End Set + End Property + Property Passive() As Boolean + Get + Return _Passive + End Get + Set(ByVal Value As Boolean) + _Passive = Value + End Set + End Property +#End Region +End Class diff --git a/Samples/VB/HTTP Responses/App.ico b/Samples/VB/HTTP Responses/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/VB/HTTP Responses/App.ico differ diff --git a/Samples/VB/HTTP Responses/AssemblyInfo.vb b/Samples/VB/HTTP Responses/AssemblyInfo.vb new file mode 100644 index 0000000..490d2bc --- /dev/null +++ b/Samples/VB/HTTP Responses/AssemblyInfo.vb @@ -0,0 +1,32 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' 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: + + diff --git a/Samples/VB/HTTP Responses/HTTPResponses.2008.sln b/Samples/VB/HTTP Responses/HTTPResponses.2008.sln new file mode 100644 index 0000000..ec955aa --- /dev/null +++ b/Samples/VB/HTTP Responses/HTTPResponses.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "HTTPResponses", "HTTPResponses.2008.vbproj", "{0665746D-617A-40CB-9A20-BC7A1B297CB6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0665746D-617A-40CB-9A20-BC7A1B297CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0665746D-617A-40CB-9A20-BC7A1B297CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0665746D-617A-40CB-9A20-BC7A1B297CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0665746D-617A-40CB-9A20-BC7A1B297CB6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/HTTP Responses/HTTPResponses.2008.vbproj b/Samples/VB/HTTP Responses/HTTPResponses.2008.vbproj new file mode 100644 index 0000000..7256b87 --- /dev/null +++ b/Samples/VB/HTTP Responses/HTTPResponses.2008.vbproj @@ -0,0 +1,118 @@ + + + Local + 8.0.50727 + 2.0 + {0665746D-617A-40CB-9A20-BC7A1B297CB6} + Debug + AnyCPU + App.ico + + + HTTPResponses + + + None + JScript + Grid + IE50 + false + WinExe + Binary + On + Off + HTTPResponses + HTTPResponses.MainForm + + + WindowsFormsWithCustomSubMain + + + 2.0 + + + bin\ + HTTPResponses.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + + + bin\ + HTTPResponses.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + + + + + + + + + + Code + + + Form + + + MainForm.vb + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/HTTP Responses/HTTPResponses.2010.sln b/Samples/VB/HTTP Responses/HTTPResponses.2010.sln new file mode 100644 index 0000000..ac8e542 --- /dev/null +++ b/Samples/VB/HTTP Responses/HTTPResponses.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "HTTPResponses", "HTTPResponses.2010.vbproj", "{0665746D-617A-40CB-9A20-BC7A1B297CB6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0665746D-617A-40CB-9A20-BC7A1B297CB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0665746D-617A-40CB-9A20-BC7A1B297CB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0665746D-617A-40CB-9A20-BC7A1B297CB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0665746D-617A-40CB-9A20-BC7A1B297CB6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/HTTP Responses/HTTPResponses.2010.vbproj b/Samples/VB/HTTP Responses/HTTPResponses.2010.vbproj new file mode 100644 index 0000000..e56c263 --- /dev/null +++ b/Samples/VB/HTTP Responses/HTTPResponses.2010.vbproj @@ -0,0 +1,124 @@ + + + Local + 10.0.20506 + 2.0 + {0665746D-617A-40CB-9A20-BC7A1B297CB6} + Debug + AnyCPU + App.ico + + + HTTPResponses + + + None + JScript + Grid + IE50 + false + WinExe + Binary + On + Off + HTTPResponses + HTTPResponses.MainForm + + + WindowsFormsWithCustomSubMain + + + 3.5 + v2.0 + + + bin\ + HTTPResponses.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + AllRules.ruleset + + + bin\ + HTTPResponses.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + AllRules.ruleset + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + + + + + + + + + + Code + + + Form + + + MainForm.vb + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/HTTP Responses/HttpResponses.Sample.html b/Samples/VB/HTTP Responses/HttpResponses.Sample.html new file mode 100644 index 0000000..3fa0428 --- /dev/null +++ b/Samples/VB/HTTP Responses/HttpResponses.Sample.html @@ -0,0 +1,47 @@ + + + + + + + + + +
+

+ HTTP Responses Sample +

+ +
+

Purpose

+

+This sample shows how to create and configure a simple HTTP server. +

+ +

Examine the Code

+
    +
  • +See the httpServer_OnHttpRequest handler, where all possible RequestPaths + are processed. +The options possible are as follows: +
      +
    • / or /home - load home page with all available links.
    • +
    • /file - download the HttpResponses application.
    • +
    • /bytes - download random.bin file that contains random generated data.
    • +
    • /error - shows a custom error with code 555.
    • +
    +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run the application.
  • +
  • Click the link on the top of the application window to open the home page of our server in your web browser.
  • +
  • Try all the links on the home page.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/VB/HTTP Responses/MainForm.resx b/Samples/VB/HTTP Responses/MainForm.resx new file mode 100644 index 0000000..ec8ea24 --- /dev/null +++ b/Samples/VB/HTTP Responses/MainForm.resx @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + 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 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + YP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xMTP9LS0v/SUlJ/0dH + R/9GRkafRUVFDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpqah9paWmfaGho/2Zm + Zv9kZGT/Y2Nj/2FhYf9gYGD/Xl5e/1xcXP9bW1v/WVlZ/1hYWP9WVlb/VFRU/1NTU/9RUVH/T09P/05O + Tv9MTEz/S0tLn0lJSR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAaGhoD2dnZ19mZmavZGRk72NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZWf9YWFj/VlZW/1RU + VP9TU1PvUVFRr09PT19PT08PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjYy9iYmJPYWFhf19fX39eXl5/XFxcf1pa + Wn9ZWVl/V1dXT1ZWVi8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD//wAA//8AAP/8AAA//wAA//AAAA// + AAD/wAAAA/8AAP+AAAAB/wAA/wAAAAD/AAD+AAAAAH8AAPwAAAAAPwAA+AAAAAAfAADwAAAAAA8AAPAA + AAAADwAA4AAAAAAHAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAABwAA4AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAP4A + AAAAfwAA/4AAAAH/AAD/wAAAA/8AAP/gAAAH/wAA//gAAB//AAD//gAAf/8AAP//4Af//wAAKAAAACAA + AABAAAAAAQAgAAAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISEPHx8fXx0dHZ8aGhrPGBgY/xUVFf8TExP/ERER/w4ODu8MDAy/CgoKfwgI + CC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApKSkfJiYmnyQkJO8hISH/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/w4O + Dv8MDAz/CQkJ/wcHB88GBgZfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAALS0tfysrK/8oKCj/JiYm/yQkJP8hISH/Hx8f/x0dHf8aGhr/GBgY/xUV + Ff8TExP/ERER/w4ODv8MDAz/CQkJ/wcHB/8FBQW/BAQELwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAANTU1DzIyMs8vLy//LS0t/ysrK/8oKCj/NjYf/1JSD/9dXQj/bW0A/2lp + AP9kZAD/YGAA/1NTAv9CQgX/KioK/xISDf8MDAz/CQkJ/wcHB/8FBQXvAwMDXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5OQ83NzfPNDQ0/zIyMv8vLy//Q0Mi/2xsCv9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/T08A/y8vBv8MDAz/CQkJ/wcHB/8FBQX/AwMDXwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + 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 + + + \ No newline at end of file diff --git a/Samples/VB/HTTP Responses/MainForm.vb b/Samples/VB/HTTP Responses/MainForm.vb new file mode 100644 index 0000000..77b9267 --- /dev/null +++ b/Samples/VB/HTTP Responses/MainForm.vb @@ -0,0 +1,155 @@ + +Public Class MainForm + Inherits System.Windows.Forms.Form + + Const sWelcome As String = _ + "Internet Pack HTTP Responses Test App" & _ + "

" & _ + "Valid links:" & _ + "
" & _ + "/home show this page" & _ + "
" & _ + "/file send back a file (this .exe)" & _ + "
" & _ + "/bytes send back a buffer of random bytes" & _ + "
" & _ + "/error Display a custom error" + +#Region " Windows Form Designer generated code " + + Public Sub New() + MyBase.New() + + Application.EnableVisualStyles() + + 'This call is required by the Windows Form Designer. + InitializeComponent() + + 'Add any initialization after the InitializeComponent() call + End Sub + + 'Form overrides dispose to clean up the component list. + Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing Then + If Not (components Is Nothing) Then + components.Dispose() + End If + End If + MyBase.Dispose(disposing) + End Sub + + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + Friend WithEvents lb_Log As System.Windows.Forms.ListBox + Friend WithEvents llblinkLabel1 As System.Windows.Forms.LinkLabel + Friend WithEvents HttpServer As RemObjects.InternetPack.Http.HttpServer + Private Sub InitializeComponent() + Me.components = New System.ComponentModel.Container + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainForm)) + Me.lb_Log = New System.Windows.Forms.ListBox + Me.llblinkLabel1 = New System.Windows.Forms.LinkLabel + Me.HttpServer = New RemObjects.InternetPack.Http.HttpServer(Me.components) + Me.SuspendLayout() + ' + 'lb_Log + ' + Me.lb_Log.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _ + Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.lb_Log.IntegralHeight = False + Me.lb_Log.Location = New System.Drawing.Point(11, 34) + Me.lb_Log.Name = "lb_Log" + Me.lb_Log.Size = New System.Drawing.Size(371, 226) + Me.lb_Log.TabIndex = 3 + ' + 'llblinkLabel1 + ' + Me.llblinkLabel1.Location = New System.Drawing.Point(8, 10) + Me.llblinkLabel1.Name = "llblinkLabel1" + Me.llblinkLabel1.Size = New System.Drawing.Size(100, 23) + Me.llblinkLabel1.TabIndex = 2 + Me.llblinkLabel1.TabStop = True + Me.llblinkLabel1.Text = "http://localhost:82" + ' + 'HttpServer + ' + Me.HttpServer.Port = 82 + Me.HttpServer.ValidateRequests = False + ' + 'MainForm + ' + Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) + Me.ClientSize = New System.Drawing.Size(394, 272) + Me.Controls.Add(Me.lb_Log) + Me.Controls.Add(Me.llblinkLabel1) + Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon) + Me.MinimumSize = New System.Drawing.Size(410, 310) + Me.Name = "MainForm" + Me.Text = "Internet Pack HTTP Response Sample" + Me.ResumeLayout(False) + + End Sub + +#End Region + + Private Sub llblinkLabel1_LinkClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles llblinkLabel1.LinkClicked + Process.Start(llblinkLabel1.Text) + End Sub + + Public Sub Invoke_LogMessage(ByVal aString As String) + lb_Log.Items.Add(aString) + End Sub + + Private Delegate Sub InvokeDelegate(ByVal aString As String) + + Private Sub HttpServer_OnHttpRequest(ByVal aSender As Object, ByVal ea As RemObjects.InternetPack.Http.OnHttpRequestArgs) Handles HttpServer.OnHttpRequest + Invoke(New InvokeDelegate(AddressOf Invoke_LogMessage), ea.Request.Header.RequestPath) + Select Case ea.Request.Header.RequestPath + Case "/", "/home" + ea.Response.ContentString = sWelcome + ea.Response.Header.SetHeaderValue("Content-Type", "text/html") + + + Case "/bytes" + Dim lBuffer(256) As Byte + Dim lRandom As Random + lRandom = New Random + + lRandom.NextBytes(lBuffer) + ea.Response.ContentBytes = lBuffer + ea.Response.Header.SetHeaderValue("Content-Disposition", "filename=random.bin") + ea.Response.Header.SetHeaderValue("Content-Type", "application/binary") + + Case "/error" + ea.Response.SendError(555, "Custom Error", "A custom error message") + + Case "/file" + Dim lExeName As String = Me.GetType().Assembly.Location + Try + ea.Response.ContentStream = New System.IO.FileStream(lExeName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read) + ea.Response.Header.SetHeaderValue("Content-Disposition", String.Format("filename={0}", System.IO.Path.GetFileName(lExeName))) + ea.Response.Header.SetHeaderValue("Content-Type", "application/binary") + ea.Response.CloseStream = True ' default, anyway + Catch e As Exception + ea.Response.SendError(404, String.Format("File {0} not found", lExeName), e) + End Try + + Case Else + ea.Response.SendError(404, "requested path not found") + End Select + + End Sub + + Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load + HttpServer.Active = True + End Sub + + Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed + HttpServer.Active = False + End Sub + +End Class diff --git a/Samples/VB/HTTP Spy/App.ico b/Samples/VB/HTTP Spy/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/VB/HTTP Spy/App.ico differ diff --git a/Samples/VB/HTTP Spy/AssemblyInfo.vb b/Samples/VB/HTTP Spy/AssemblyInfo.vb new file mode 100644 index 0000000..40cb8a5 --- /dev/null +++ b/Samples/VB/HTTP Spy/AssemblyInfo.vb @@ -0,0 +1,32 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' 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: + + diff --git a/Samples/VB/HTTP Spy/HTTPSpy.Sample.html b/Samples/VB/HTTP Spy/HTTPSpy.Sample.html new file mode 100644 index 0000000..781cf29 --- /dev/null +++ b/Samples/VB/HTTP Spy/HTTPSpy.Sample.html @@ -0,0 +1,41 @@ + + + + + + + + +
+

+ HTTPSpy Sample +

+ +
+

Purpose

+

+This example demonstrates how HTTP works. +With help of the HttpClient components, we can send a  HttpClientRequest and retrieve back a HttpClientResponse. The application shows us the request and response header parameters and the context of the response in text and hex view. + Also, you have the ability to add your custom request header parameter. +

+ +

Examine the Code

+
    +
  • +See the btnSubmit_Click handler. This is the main function where we create and configure the HttpClientRequest and send it to the server with the help of the + HttpClient component. +In order to get response from the server we call the Dispatch function with our HttpClientRequest instance as a parameter. +
  • +
+ + +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run the  application.
  • +
  • Using an existing URL, click on the "Submit" button. See the response of the request on the Result page.
  • +
+ + + diff --git a/Samples/VB/HTTP Spy/HttpSpy.2008.sln b/Samples/VB/HTTP Spy/HttpSpy.2008.sln new file mode 100644 index 0000000..9ce0627 --- /dev/null +++ b/Samples/VB/HTTP Spy/HttpSpy.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "HttpSpy", "HttpSpy.2008.vbproj", "{DBBF660A-6931-4C8B-9048-0FFCA1CB8308}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/HTTP Spy/HttpSpy.2008.vbproj b/Samples/VB/HTTP Spy/HttpSpy.2008.vbproj new file mode 100644 index 0000000..de7724d --- /dev/null +++ b/Samples/VB/HTTP Spy/HttpSpy.2008.vbproj @@ -0,0 +1,83 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308} + WinExe + HTTPSpy.MainForm + HTTPSpy + HttpSpy + WindowsForms + App.ico + + + + + 2.0 + + + true + full + true + true + bin\Debug\ + HttpSpy._2005.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + HttpSpy._2005.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + + + + + + + + + + + + + + + + + + + + + + Form + + + + + MainForm.vb + Designer + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/HTTP Spy/HttpSpy.2010.sln b/Samples/VB/HTTP Spy/HttpSpy.2010.sln new file mode 100644 index 0000000..97329ce --- /dev/null +++ b/Samples/VB/HTTP Spy/HttpSpy.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "HttpSpy", "HttpSpy.2010.vbproj", "{DBBF660A-6931-4C8B-9048-0FFCA1CB8308}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/HTTP Spy/HttpSpy.2010.vbproj b/Samples/VB/HTTP Spy/HttpSpy.2010.vbproj new file mode 100644 index 0000000..246a021 --- /dev/null +++ b/Samples/VB/HTTP Spy/HttpSpy.2010.vbproj @@ -0,0 +1,87 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {DBBF660A-6931-4C8B-9048-0FFCA1CB8308} + WinExe + HTTPSpy.MainForm + HTTPSpy + HttpSpy + WindowsForms + App.ico + + + + + 3.5 + v2.0 + + + true + full + true + true + bin\Debug\ + HttpSpy._2005.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + AllRules.ruleset + + + pdbonly + false + true + true + bin\Release\ + HttpSpy._2005.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + AllRules.ruleset + + + + + + + + + + + + + + + + + + + + + + + + Form + + + + + MainForm.vb + Designer + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/HTTP Spy/MainForm.resx b/Samples/VB/HTTP Spy/MainForm.resx new file mode 100644 index 0000000..ba9f492 --- /dev/null +++ b/Samples/VB/HTTP Spy/MainForm.resx @@ -0,0 +1,594 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 22, 11 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== + + + + 117, 11 + + + 223, 11 + + + 323, 11 + + + + AAABAAYAMDAAAAEACACoDgAAZgAAACAgAAABAAgAqAgAAA4PAAAQEAAAAQAIAGgFAAC2FwAAMDAAAAEA + IACoJQAAHh0AACAgAAABACAAqBAAAMZCAAAQEAAAAQAgAGgEAABuUwAAKAAAADAAAABgAAAAAQAIAAAA + AAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAAFBQUACQkJAA0NDQAUFAsAEhINABwcCQAaGg8AHh4OABIS + EgAVFRUAGhoUABwcFgAZGRkAHR0dACQkBwAqKgcAJiYIACYmDwAoKAkALCwJAD4+AwAxMQoAPDwIACcn + EAAkJBcALi4QAC0tFAAhIRoAIyMcACQkHQAtLR4AMDARAD8/FAAzMxgANjYZADk5GwA8PBgAOzscAD09 + HgAhISEAJSUlAC0tJQApKSkALS0tADQ0JAA0NCsAPT0rADExMQA1NTUAOjoxAD8/NgA5OTkAPDw8AEJC + AgBDQwQARkYHAElJAABNTQAASkoGAE5OBQBHRwwASUkJAElJDQBMTA0AUVEAAFVVAABRUQYAVVUHAFlZ + AABdXQAAWVkHAF9fCABDQxYASUkQAElJGQBOThsAVlYQAFRUFABbWxwAYWEAAGVlAABnZwQAaWkAAG5u + AABlZQkAcXEAAHV1AABycgUAdXUFAHp6AAB9fQAAdHQLAH5+DQBkZBAAb28UAGRkGgBiYh8AdXUVAEFB + IABERCIARkYkAE5OIgBJSSUATEwnAE5OKABcXCMAUFAqAFJSLABUVC8ARUUyAEtLOQBPTz0AVlYxAF9f + MgBrayAAZGQtAHR0IAB0dDcAcnI/AH5+OQBBQUEARUVFAE1NRQBJSUkATU1NAF5eRQBSUkoAUlJSAFVV + VQBYV1AAWVlZAF1dXQBjY0EAZGRMAGpqSQB8e0EAe3tMAGhoUQBiYlsAaWlbAGpqXABtbV8AdnZZAHp6 + XQBhYWEAZGRkAGpqZABoaGgAdXVoAHFxcQB+fn4AgoIAAIWFAACCggYAiYkAAI6OAACKigYAgYEMAJGR + AACWlgAAmZkBAJ2dBgCEhBMAh4cXAIeHGACKihwAnp4UAKGhDAClpRIAqakXAKalHwCtrB0Ah4cjAI6O + IQCRkSUAmJgjAJiYJACUlCoAl5cvAImJMgCYmDAAm5s0AJKSPwCenjgArKwrALCwIwC0tCkAuLgvAKGh + PQCpqDwAsLAwALy7NAC/vzoAhoZPAIuLTACbm0wAiopfAJ6eUACZmV4ApKRCAKenRwCsrEIAqKhHAKur + SwCrq0wAtLNBAK6uUACpqVQAp6dcALKxVQC6uVUAtbVaAL29WgCGhmQAg4NpAIiIbgCWlmQAlpZuAJOT + dAC7u2MApqZwALy7dACzsnwAv75+AMPDQADHx0YAy8pMAM/OUQDT0lcA1tZcAMLCbQDa2WIA3t1oANnY + bADGxXIA0dF4AOLhbgDm5XQA4uF6AOvqfACBgYEAhoaGAIyMjACRkZEAlpaWAJ+fnwC6uoEAoaGhAKys + rADCwoMA6+qIAPHwhQD19IsA8/KSAPn4kQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACSMQ0KCgoKCgMDK4MAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACDKA0oDQ0NDQoKCgMKAwMDA3oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAeigoKCgoKA0NDQoKCgoDAwMDAwADKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJIrKysrKCgoKCgN + DQ0KCgoKCgMDAwMDAAN6AAAAAAAAAAAAAAAAAAAAAAAAAAAANSsrKysrKygtIUBHR0REPDwXEwcDAwMD + AwAACgAAAAAAAAAAAAAAAAAAAAAAAAAxMTErMSsrS1VXV1dUVFJSUFBQQkI4EwMDAwMDAAAAAAAAAAAA + AAAAAAAAAAAA8TExMSsxK0taWlpaV1dXVFRSUlBQUEJQQjgPAwIDAwADgwAAAAAAAAAAAAAAAAD0NTEx + MTEvXJ2ZmZlaWldXVFRUUlJQUEJCQkJCFQQCAgMAA5YAAAAAAAAAAAAAAAA1NTUxMWidnZ2dmZlaWlpX + V1RUUlJSUFBQUEJCPDwGAwADAAAAAAAAAAAAAAAAAHw1NTU1aqCgoJ2dnZmZWlpaWldUVFRSUlBQQkJC + Qjw8DwMDAwAKAAAAAAAAAAAAlnp6NTVsoqKgoJ2gnZmZmZlaWldXV1RSUlJQUFBQQkI8PAYDAAMAegAA + AAAAAAAAeno1em+nqKKgoKCdnZ2dmZlaWlpXV1RUVFJSUFBCUEJCQjgEAwMDAAAAAAAAAACDenp6NbCq + qaiioqCgoJ2dnZmZWlpaV1dXVFJSUlBQQlBCQjw4AgMAAygAAAAAAAB8fHp6drqsqqmooqKgoJ2gnZmZ + mZlaWldXVFRUUlJQUEJCQkI8EAMDAwMAAAAAAJJ8fHp7v7u6rKqpqKKgoKCdnZ2dmZlaWlpXV1dUUlJS + UFBQQkJCPAQDAwArAAAAAHx8fHyIwLy7uqyqqaiioqCgoJ2dnZmZWlpaV1dUVFRSUBs4UFBCQhADAwMD + AAAAAIN8g3zOwcC8u7qsqqmooqCgoJ2gnZmZmZlaWldXV1RUUjgNG0JQQjwDAwMDAAAAl3x8fIbi4sDA + vLu6rKqpqKKgoKCdnZ2dmZlaWlpXV1RUVFIZDQ04UEIHBAMDegAAg4ODfMLj4uLBwLy7uqyqqaiioqCg + oJ2dnZmZWlpaV1dUVFQ9DQ0KGUITCgMDAwAAg3yDg9Dk4+LhwcB4ubqsqqmooqKgoJ2gnZmZmZlaWldX + VFRSHA0NCg0SCgoDAwAAg4N8g9Pl5OPiyn53vLu6rK1xbGtraGhoZGRkZCcnJycnIyMiHA0NDQ0KCgQJ + AwAAg4ODg+bm5eTDfHy+wLy7urFvNTU1a2hoaGhkZGRkJycnIyMjKCgNDQ0KCgoKCQAAg4ODg+jo1Yp8 + fIfiwcC8u7qsrnQ1M2GdoJ2dnZ2ZmVpaWldHKCgoDSJQCgoKCgAAg4ODg+nHg3yDfMbi4cHAvLu6rKqn + dW40aF+gnZ2ZmZlaWlpLKCgcSlJSCgoKCgAAg4ODg4yDg4N8g8/PyMi9uLazs7CupqRzbjEzT5mdmZmZ + mVcoKCdSVFRSDQ0KCgAAkpKDg4ODfIODfIB8fHx8e3p6ejV5NTU1NTExMTGdnZ2ZmU4rT1dXV1RQDSgK + DQAAkoOSg4yDg4N9g9LS0s/JyL24trOzr66mpKSeXZmgnZ2dmUtXWlpXV1RHKA0NDQAAkpKDkt3ei4OD + g9Hm5ePj4uLBwLu7uqyqqaiioqCdoJ2dmZmZWlpaV1dKKCgNDQAA8ZKSktf879eDgI/o5uXk4+LhwcDA + u7qsqqmooqKgoKCdnZmZmZlaWlcnDSgofAAA9pKSg5L7/PDrioPc6Obl5OPi4sDAvLu6rKqpqKKgoKCd + nZ2dmZlaWlcoKCgolgAAAJKSkpLf/fzw79nF6Ojm5eTj4uLBwLy7uqyqqaiioKCgoJ2dnZmZWk8pKSgo + AAAAAJaSkpKV//388PDr6eno5uXk4+LhwcC8u7qsqqmooqKgoKCdnZmZmS0pKSgxAAAAAACUkpKS+v/9 + /PDu7+3p6Obl5OPi4sDAvLu6rKqpqKKgoKCdoJ2dVSsrKykAAAAAAACUlJKSk/7//fz78O7t6Ojm5eTj + 4uLBwLy7uqyqqaiioKCgnZ2ZLisrKysAAAAAAAAAlJSSktv///388PDu7eno5uXk4+LhwcC8u7qsqqmo + oqKgoKBoLjErLvEAAAAAAAAAlpSUk5Lf///9/PDw7u3p6Obl5OPi4sDAvLu6rKqpqKKgoGEuMDArNQAA + AAAAAAAAAJSUlJSS+v///fzw8O7t6ejm5eTj4uLBwLy7uqyqqaiiXzMwMTAxAAAAAAAAAAAAAPmUlJSU + kvf///388PDu7eno5uXk4+LhwcC8u7qsqql1NTQxMTDyAAAAAAAAAAAAAADylJSUlJLb/v/9/PDw7u3p + 6Obl5OPi4sDAvLu6q3I1NTQ0MYMAAAAAAAAAAAAAAAAA8pSUlJSSlfr//fzw8O7t6ejm5eTj4uLBwLy0 + b3o1NTU0gwAAAAAAAAAAAAAAAAAAAPKUlJSUkpLY4P388PDu7eno5uXk4+Lht4V6ejV6NTWEAAAAAAAA + AAAAAAAAAAAAAAD4lJSUlJKSkpPa3uzw7u3p6ObTxomAe3p6eno1efEAAAAAAAAAAAAAAAAAAAAAAAAA + AJaUlJSSkpGRg4ODg5CQg4N8g3x8fHx8enp8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD4lJSSkpKSkpGD + g4ODg3yDfHyDfHx8evIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPiUkpKSg5KRg4ODg4ODg3x9fIDy + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPSWkpKRkYODg4N9g4PyAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + wAP//wAA//4AAH//AAD/+AAAH/8AAP/gAAAH/wAA/8AAAAP/AAD/gAAAAf8AAP4AAAAAfwAA/AAAAAA/ + AAD8AAAAAD8AAPgAAAAAHwAA8AAAAAAPAADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADAAAAAAAMAAMAA + AAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAA + AAAAAwAAwAAAAAADAADgAAAAAAcAAOAAAAAABwAA8AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA+AAAAAAf + AAD8AAAAAD8AAP4AAAAAfwAA/wAAAAD/AAD/gAAAAf8AAP/gAAAH/wAA//AAAA//AAD//AAAP/8AAP// + gAH//wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAEAAAAAAAAAAAAAAABAAAAAQAAAAAAAAYG + BgAJCQkADAwMABAQCwASEg0AGBgJABISEgAUFBQAHBwWABkZGQAeHh4ALy8GACoqCgAuLgkAKysPADc3 + DQAqKhEALS0TACgoFQAmJhkANTUYADY2HwA4OBoAOzscAD4+HgAhISEAJCQkACkpKQAuLi4AODghADEx + MQA1NTUAOjoxAD8/NgA5OTkAPj4+AENDAgBHRwEAQkIFAEZGBQBLSwAAT08AAE1NBgBTUwAAV1cBAFJS + BgBYWAAAXFwAAFFRCABSUg8AV1cNAF1dCABERBIAQEAaAE9PHABQUBMAXV0dAGBgAABkZAAAZ2cEAGho + AABtbQAAa2sEAGVlCQBsbAoAcXEAAHV1AAB3dwUAenoAAH19AABycg8AYGAYAHNzFAB5eREAfHsfAEJC + IQBFRSIASUklAExMJwBPTykASUktAFhYKABcXC4AREQ7AEtLOQBrayAAfX0tAHBwMQBBQUEARkZGAExM + QwBJSUkATU1NAFpaQQBUVEwAWlpJAFJSUgBVVVUAWFhYAF1dXQBubk4Afn5PAGFhWgBmZlgAYWFhAGVl + ZQBtbWcAaGhoAG5ubgB7e2cAcnJyAHZ2dgB7e3sAgYEAAIaGAACDgwYAi4sAAI+PAACFhQoAk5MAAJeX + AACcnAUAj48WAJaVFACiog4AqKcWAK2tHwCHhyMAgoI/AJSUNgCYmDwArKwpALOzJwC5uDAAtrY5AL6+ + OACKilUAlpZQAJKSVQCbmlUAlpZaAJ+eWwCqqkoAvLxBALi4XgCEhGEAiIdlAIGBbgCLimkAnp5lAIuK + cQCbmnQAnp54AKyrYgC9vGUApqZwAK6uegC9vXIAxMNBAMnJSQDPzlIA1dRaAMLBbADLym8A0tFiANra + YwDg32sA5uV0AOLhewDr6nwAg4ODAIWFhQCIiIgAj4+PAJaWlgChoaEA4+KHAOnojwDx8IUA9vaNAPz7 + lQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAG9aCggICApcAAAAAAAAAAAAAAAAAAAAAAAAAAAAbyAaChoKCggICAMDAyAAAAAAAAAA + AAAAAAAAAAAAAB0dGhoaGgoKCggICAMDAwBaAAAAAAAAAAAAAAAAAGMdHR0aHjI0Qj87OyonDQQDAwAI + AAAAAAAAAAAAAABjICAdTEdyckJCPz87OjotKgwDAwMAAAAAAAAAAAAAaSMgIEh1c3JyckJCPz87Ojot + LScGAwADAAAAAAAAAHEjIyNJeHV1c3JyQkJCPz87OjotLSoEAwACAAAAAAAAWlojS3p6eHV1c3JyckJC + Pzs7OjotLSoEAwIgAAAAAGNaWlh+fXp6eHV1c3JyckJCPz87Oi0tLScGAAMAAAAAXFpbhH9+fXp6eHV1 + c3JyQnI/Pzs7OzotLQwDAxoAAG1cXIGGhX9+fXp6eHV1c3JyQkJCQj8tKi0tKgQDAwAAYVxhkIiGhX9+ + fXp6eHV1c3NyckJCPz8REC0tDQQDbwBhYWWgn4iGhX9+fXp6eHV1c3JyckJCPy0KCicqBwcjAGFhi6Gg + n4eChX9+fXp6eHV1c3JyQkJCPxQKChEIBwOwY2GOoqGPYIOGhX9VIyNQT09MTB5MGBgVGgoKCggICKxj + Y6OliVxgn4aGhX+AU1NKeHV1dXNycjsaGhQxEQgIrGNjlmhhYYqgn4iGhX9+fFZROXN1c3JyOBo1Pz8T + CAgAaWNjY2NhYWFcXFtaWiMjIyMjIHR1c3IeQEJCPwoKCgBpaZWVY2GNoqGgn4aGhYV+fXp4eHV1c3Jy + ckI/GgoKAGlpbrOeZ2imoqGgn4iGhX9+fXp4eHV1c3JyQjQaGmEAbWlpsbOpkpqmoqGgn4iGhX9+fXp6 + eHV1c3JyGBoaAACwaWmZtLOqpKemoqGgn4iGhX9+fXp4eHV1c0IdGxsAAABtaWmytLOqqKemoqGgn4iG + hX9+fXp6eHV1Nx0bZAAAAK5tbW61tLOqqKemoqGgn4iGhX9+fXp4eEkdHR0AAAAAAG1pbZm1tLOqqKem + oqGgn4iGhX9+fXp0Ix8dbQAAAAAAAG1paZm1tLOqqKemoqGgn4iGhX9+fCMjH1wAAAAAAAAAsG1taZey + tLOqqKemoqGgn4iGhVdUIyNbAAAAAAAAAAAAsG1pbWudsbOqqKemoqGgn4NeWiNaWgAAAAAAAAAAAAAA + AG1paWlpbpyeo5uRimZfXFpaWmcAAAAAAAAAAAAAAAAAAK5tbWlpY2NjY2FhYVxcXGEAAAAAAAAAAAAA + AAAAAAAAAACubWlpaWNjY2NhYW0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACurK6srgAAAAAAAAAAAAAA + AAD/8A///4AB//8AAH/8AAA/+AAAH/AAAA/gAAAH4AAAA8AAAAPAAAABgAAAAYAAAACAAAAAgAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAIAAAAGAAAABwAAAAcAAAAPgAAAD8AAAB/AAAA/4AAAf/gAAP/8A + AP//wAP///wf/ygAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAABAAAAAAAACgoKAA4O + CQAPDw8AGxsLABQUFAAYGBgAHR0dAC4uDAA8PAQANjYJADQ0DgA9PQsAKioTACIiGwArKysAMTEiADQ0 + NAA7OzEAODg4AD4+PgBAQAMAREQCAEREDgBNTQoASUkMAFNTBABZWQAARUUSAEFBFABWVhAAYWEAAGlp + AABtbQIAZmYJAG1tCgBycgAAe3sAAHV1CwB9fQ0AZGQUAHh4GwBYWCEAX18nAFlZKgBFRTwAc3MqAHt7 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9 + DwYFAzEAAAAAAAAAAD8PEBwXDAoEAxEAAAAAADYRKEIlJCAfGwkCBQAAAF4UKURDQiUkIB8bFgMRAAAx + MEdFRENCJSQgHxsWAwBfMUlIR0VEQ0IlJCAaGgQTQTlVSkhHRURDQiUkGQ0IAz1OVjdKSC8rJyYjIhwO + CgU/PjQ4TEswLiwrQ0IeIBkGXE9TTVdVSkhHRURDQiUcBgA9Y1lYV1VKSEdFRENCED0APVFlW1pXVUpI + R0VEKA8AAAA9YmVbWldVSkhHRhM9AAAAYD1UZFtaV1VKMC02AAAAAAAAPT1PUlA6MzFBAAAAAAAAAABh + XDY2P14AAAAAAPgfAADgBwAAwAMAAIABAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAgAEAAMAB + AADAAwAA8AcAAPgfAAAoAAAAMAAAAGAAAAABACAAAAAAAIAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACAgIB8eHh5vHR0drxsbG98ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwM + DN8LCwuvCgoKbwkJCR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlJSVfIyMjvyEhIf8fHx//Hh4e/xwcHP8bGxv/GRkZ/xcXF/8WFhb/FBQU/xMT + E/8RERH/Dw8P/w4ODv8MDAz/CwsL/wkJCf8ICAi/BwcHXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKSkpTygoKN8mJib/JCQk/yMjI/8hISH/Hx8f/x4eHv8cHBz/Gxsb/xkZ + Gf8XFxf/FhYW/xQUFP8TExP/ERER/w8PD/8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBt8FBQVPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4uLh8sLCy/Kysr/ykpKf8oKCj/JiYm/yQkJP8jIyP/ISEh/x8f + H/8eHh7/HBwc/xsbG/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8PDw//Dg4O/wwMDP8LCwv/CQkJ/wgI + CP8GBgb/BAQEvwQEBB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMTExTzAwMO8uLi7/LCws/ysrK/8pKSn/KCgo/yYm + Jv8kJCT/LS0e/z8/FP9MTA3/WFgH/1VVB/9TUwb/UFAG/05OBf9LSwX/PDwI/ywsC/8aGg//Dw8P/w4O + Dv8MDAz/CwsL/wkJCf8ICAj/BgYG/wQEBO8DAwNPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTV/MzMz/zExMf8wMDD/Li4u/yws + LP8rKyv/KSkp/0hIGf9lZQn/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1pa + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + YP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xMTP9LS0v/SUlJ/0dH + R/9GRkafRUVFDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGpqah9paWmfaGho/2Zm + Zv9kZGT/Y2Nj/2FhYf9gYGD/Xl5e/1xcXP9bW1v/WVlZ/1hYWP9WVlb/VFRU/1NTU/9RUVH/T09P/05O + Tv9MTEz/S0tLn0lJSR8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAaGhoD2dnZ19mZmavZGRk72NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZWf9YWFj/VlZW/1RU + VP9TU1PvUVFRr09PT19PT08PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNjYy9iYmJPYWFhf19fX39eXl5/XFxcf1pa + Wn9ZWVl/V1dXT1ZWVi8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD//wAA//8AAP/8AAA//wAA//AAAA// + AAD/wAAAA/8AAP+AAAAB/wAA/wAAAAD/AAD+AAAAAH8AAPwAAAAAPwAA+AAAAAAfAADwAAAAAA8AAPAA + AAAADwAA4AAAAAAHAADgAAAAAAcAAMAAAAAAAwAAwAAAAAADAACAAAAAAAEAAIAAAAAAAQAAgAAAAAAB + AACAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAAwAAAAAAD + AADAAAAAAAMAAOAAAAAABwAA4AAAAAAHAADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAP4A + AAAAfwAA/4AAAAH/AAD/wAAAA/8AAP/gAAAH/wAA//gAAB//AAD//gAAf/8AAP//4Af//wAAKAAAACAA + AABAAAAAAQAgAAAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhISEPHx8fXx0dHZ8aGhrPGBgY/xUVFf8TExP/ERER/w4ODu8MDAy/CgoKfwgI + CC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAApKSkfJiYmnyQkJO8hISH/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/w4O + Dv8MDAz/CQkJ/wcHB88GBgZfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAALS0tfysrK/8oKCj/JiYm/yQkJP8hISH/Hx8f/x0dHf8aGhr/GBgY/xUV + Ff8TExP/ERER/w4ODv8MDAz/CQkJ/wcHB/8FBQW/BAQELwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAANTU1DzIyMs8vLy//LS0t/ysrK/8oKCj/NjYf/1JSD/9dXQj/bW0A/2lp + AP9kZAD/YGAA/1NTAv9CQgX/KioK/xISDf8MDAz/CQkJ/wcHB/8FBQXvAwMDXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5OQ83NzfPNDQ0/zIyMv8vLy//Q0Mi/2xsCv9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/T08A/y8vBv8MDAz/CQkJ/wcHB/8FBQX/AwMDXwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + 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 + + + \ No newline at end of file diff --git a/Samples/VB/HTTP Spy/MainForm.vb b/Samples/VB/HTTP Spy/MainForm.vb new file mode 100644 index 0000000..d7e3a10 --- /dev/null +++ b/Samples/VB/HTTP Spy/MainForm.vb @@ -0,0 +1,579 @@ +Imports RemObjects.InternetPack.Http +Imports System.Text + +Public Class MainForm + Inherits System.Windows.Forms.Form + + Private WithEvents tabControl1 As System.Windows.Forms.TabControl + Private WithEvents dvHeaders As System.Data.DataView + Private WithEvents dataGrid1 As System.Windows.Forms.DataGrid + Private WithEvents tabPage2 As System.Windows.Forms.TabPage + Private WithEvents splitter2 As System.Windows.Forms.Splitter + Private WithEvents dataGrid2 As System.Windows.Forms.DataGrid + Private WithEvents dvParams As System.Data.DataView + Private WithEvents httpClient1 As RemObjects.InternetPack.Http.HttpClient + Private WithEvents cbKeepAlive As System.Windows.Forms.CheckBox + Private WithEvents btnSubmit As System.Windows.Forms.Button + Private WithEvents rbPost As System.Windows.Forms.RadioButton + Private WithEvents label1 As System.Windows.Forms.Label + Private WithEvents rbGet As System.Windows.Forms.RadioButton + Private WithEvents edUrl As System.Windows.Forms.TextBox + Private WithEvents pictureBox1 As System.Windows.Forms.PictureBox + Private WithEvents pnlpanel2 As System.Windows.Forms.Panel + Private WithEvents dataColumn4 As System.Data.DataColumn + Private WithEvents dataColumn3 As System.Data.DataColumn + Private WithEvents tblParams As System.Data.DataTable + Private WithEvents dataColumn2 As System.Data.DataColumn + Private WithEvents dataColumn1 As System.Data.DataColumn + Private WithEvents tblHeaders As System.Data.DataTable + Private WithEvents dataSet1 As System.Data.DataSet + Private WithEvents dataGrid3 As System.Windows.Forms.DataGrid + Private WithEvents splitter1 As System.Windows.Forms.Splitter + Private WithEvents rbHex As System.Windows.Forms.RadioButton + Private WithEvents pnlpanel1 As System.Windows.Forms.Panel + Private WithEvents edResult As System.Windows.Forms.TextBox + Private WithEvents tabPage1 As System.Windows.Forms.TabPage + Private WithEvents rbText As System.Windows.Forms.RadioButton + Private WithEvents dataColumn6 As System.Data.DataColumn + Private WithEvents dataColumn5 As System.Data.DataColumn + Private WithEvents tblResponseHeaders As System.Data.DataTable + Private components As System.ComponentModel.IContainer + + Private _LastResultString As String + Private _LastResultBytes As Byte() + Private _LastLength As Integer = 0 + Private hexWidth As Integer = 16 + + Public Sub New() + MyBase.New() + + Application.EnableVisualStyles() + InitializeComponent() + End Sub + + Protected Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing AndAlso components IsNot Nothing Then + components.Dispose() + End If + MyBase.Dispose(disposing) + End Sub + + Private Sub InitializeComponent() + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainForm)) + Me.tblResponseHeaders = New System.Data.DataTable + Me.dataColumn5 = New System.Data.DataColumn + Me.dataColumn6 = New System.Data.DataColumn + Me.rbText = New System.Windows.Forms.RadioButton + Me.tabPage1 = New System.Windows.Forms.TabPage + Me.edResult = New System.Windows.Forms.TextBox + Me.pnlpanel1 = New System.Windows.Forms.Panel + Me.rbHex = New System.Windows.Forms.RadioButton + Me.splitter1 = New System.Windows.Forms.Splitter + Me.dataGrid3 = New System.Windows.Forms.DataGrid + Me.dataSet1 = New System.Data.DataSet + Me.tblHeaders = New System.Data.DataTable + Me.dataColumn1 = New System.Data.DataColumn + Me.dataColumn2 = New System.Data.DataColumn + Me.tblParams = New System.Data.DataTable + Me.dataColumn3 = New System.Data.DataColumn + Me.dataColumn4 = New System.Data.DataColumn + Me.pnlpanel2 = New System.Windows.Forms.Panel + Me.pictureBox1 = New System.Windows.Forms.PictureBox + Me.edUrl = New System.Windows.Forms.TextBox + Me.rbGet = New System.Windows.Forms.RadioButton + Me.label1 = New System.Windows.Forms.Label + Me.rbPost = New System.Windows.Forms.RadioButton + Me.btnSubmit = New System.Windows.Forms.Button + Me.cbKeepAlive = New System.Windows.Forms.CheckBox + Me.httpClient1 = New RemObjects.InternetPack.Http.HttpClient + Me.dvParams = New System.Data.DataView + Me.dataGrid2 = New System.Windows.Forms.DataGrid + Me.splitter2 = New System.Windows.Forms.Splitter + Me.tabPage2 = New System.Windows.Forms.TabPage + Me.dataGrid1 = New System.Windows.Forms.DataGrid + Me.dvHeaders = New System.Data.DataView + Me.tabControl1 = New System.Windows.Forms.TabControl + CType(Me.tblResponseHeaders, System.ComponentModel.ISupportInitialize).BeginInit() + Me.tabPage1.SuspendLayout() + Me.pnlpanel1.SuspendLayout() + CType(Me.dataGrid3, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.dataSet1, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.tblHeaders, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.tblParams, System.ComponentModel.ISupportInitialize).BeginInit() + Me.pnlpanel2.SuspendLayout() + CType(Me.pictureBox1, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.dvParams, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.dataGrid2, System.ComponentModel.ISupportInitialize).BeginInit() + Me.tabPage2.SuspendLayout() + CType(Me.dataGrid1, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.dvHeaders, System.ComponentModel.ISupportInitialize).BeginInit() + Me.tabControl1.SuspendLayout() + Me.SuspendLayout() + ' + 'tblResponseHeaders + ' + Me.tblResponseHeaders.Columns.AddRange(New System.Data.DataColumn() {Me.dataColumn5, Me.dataColumn6}) + Me.tblResponseHeaders.TableName = "ResponseHeaders" + ' + 'dataColumn5 + ' + Me.dataColumn5.ColumnName = "Name" + ' + 'dataColumn6 + ' + Me.dataColumn6.ColumnName = "Value" + ' + 'rbText + ' + Me.rbText.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles) + Me.rbText.Checked = True + Me.rbText.Location = New System.Drawing.Point(8, 4) + Me.rbText.Name = "rbText" + Me.rbText.Size = New System.Drawing.Size(60, 20) + Me.rbText.TabIndex = 5 + Me.rbText.TabStop = True + Me.rbText.Text = "Text" + ' + 'tabPage1 + ' + Me.tabPage1.Controls.Add(Me.edResult) + Me.tabPage1.Controls.Add(Me.pnlpanel1) + Me.tabPage1.Controls.Add(Me.splitter1) + Me.tabPage1.Controls.Add(Me.dataGrid3) + Me.tabPage1.Location = New System.Drawing.Point(4, 22) + Me.tabPage1.Name = "tabPage1" + Me.tabPage1.Padding = New System.Windows.Forms.Padding(5) + Me.tabPage1.Size = New System.Drawing.Size(660, 480) + Me.tabPage1.TabIndex = 0 + Me.tabPage1.Text = "Result" + ' + 'edResult + ' + Me.edResult.Dock = System.Windows.Forms.DockStyle.Fill + Me.edResult.Location = New System.Drawing.Point(5, 213) + Me.edResult.Multiline = True + Me.edResult.Name = "edResult" + Me.edResult.ReadOnly = True + Me.edResult.ScrollBars = System.Windows.Forms.ScrollBars.Vertical + Me.edResult.Size = New System.Drawing.Size(650, 235) + Me.edResult.TabIndex = 4 + ' + 'pnlpanel1 + ' + Me.pnlpanel1.Controls.Add(Me.rbHex) + Me.pnlpanel1.Controls.Add(Me.rbText) + Me.pnlpanel1.Dock = System.Windows.Forms.DockStyle.Bottom + Me.pnlpanel1.Location = New System.Drawing.Point(5, 448) + Me.pnlpanel1.Name = "pnlpanel1" + Me.pnlpanel1.Size = New System.Drawing.Size(650, 27) + Me.pnlpanel1.TabIndex = 10 + ' + 'rbHex + ' + Me.rbHex.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles) + Me.rbHex.Location = New System.Drawing.Point(68, 4) + Me.rbHex.Name = "rbHex" + Me.rbHex.Size = New System.Drawing.Size(60, 20) + Me.rbHex.TabIndex = 6 + Me.rbHex.Text = "Hex" + ' + 'splitter1 + ' + Me.splitter1.Dock = System.Windows.Forms.DockStyle.Top + Me.splitter1.Location = New System.Drawing.Point(5, 208) + Me.splitter1.Name = "splitter1" + Me.splitter1.Size = New System.Drawing.Size(650, 5) + Me.splitter1.TabIndex = 8 + Me.splitter1.TabStop = False + ' + 'dataGrid3 + ' + Me.dataGrid3.CaptionText = "Result Headers" + Me.dataGrid3.DataMember = "" + Me.dataGrid3.DataSource = Me.tblResponseHeaders + Me.dataGrid3.Dock = System.Windows.Forms.DockStyle.Top + Me.dataGrid3.HeaderForeColor = System.Drawing.SystemColors.ControlText + Me.dataGrid3.Location = New System.Drawing.Point(5, 5) + Me.dataGrid3.Name = "dataGrid3" + Me.dataGrid3.PreferredColumnWidth = 300 + Me.dataGrid3.ReadOnly = True + Me.dataGrid3.Size = New System.Drawing.Size(650, 203) + Me.dataGrid3.TabIndex = 7 + ' + 'dataSet1 + ' + Me.dataSet1.DataSetName = "NewDataSet" + Me.dataSet1.Locale = New System.Globalization.CultureInfo("en-US") + Me.dataSet1.Tables.AddRange(New System.Data.DataTable() {Me.tblHeaders, Me.tblParams, Me.tblResponseHeaders}) + ' + 'tblHeaders + ' + Me.tblHeaders.Columns.AddRange(New System.Data.DataColumn() {Me.dataColumn1, Me.dataColumn2}) + Me.tblHeaders.TableName = "Headers" + ' + 'dataColumn1 + ' + Me.dataColumn1.ColumnName = "Name" + ' + 'dataColumn2 + ' + Me.dataColumn2.ColumnName = "Value" + ' + 'tblParams + ' + Me.tblParams.Columns.AddRange(New System.Data.DataColumn() {Me.dataColumn3, Me.dataColumn4}) + Me.tblParams.TableName = "Params" + ' + 'dataColumn3 + ' + Me.dataColumn3.ColumnName = "Name" + ' + 'dataColumn4 + ' + Me.dataColumn4.ColumnName = "Value" + ' + 'pnlpanel2 + ' + Me.pnlpanel2.Controls.Add(Me.pictureBox1) + Me.pnlpanel2.Controls.Add(Me.edUrl) + Me.pnlpanel2.Controls.Add(Me.rbGet) + Me.pnlpanel2.Controls.Add(Me.label1) + Me.pnlpanel2.Controls.Add(Me.rbPost) + Me.pnlpanel2.Controls.Add(Me.btnSubmit) + Me.pnlpanel2.Controls.Add(Me.cbKeepAlive) + Me.pnlpanel2.Dock = System.Windows.Forms.DockStyle.Top + Me.pnlpanel2.Location = New System.Drawing.Point(0, 0) + Me.pnlpanel2.Name = "pnlpanel2" + Me.pnlpanel2.Size = New System.Drawing.Size(669, 71) + Me.pnlpanel2.TabIndex = 12 + ' + 'pictureBox1 + ' + Me.pictureBox1.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.pictureBox1.Image = CType(resources.GetObject("pictureBox1.Image"), System.Drawing.Image) + Me.pictureBox1.Location = New System.Drawing.Point(548, 40) + Me.pictureBox1.Name = "pictureBox1" + Me.pictureBox1.Size = New System.Drawing.Size(120, 30) + Me.pictureBox1.TabIndex = 10 + Me.pictureBox1.TabStop = False + ' + 'edUrl + ' + Me.edUrl.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.edUrl.Location = New System.Drawing.Point(52, 12) + Me.edUrl.Name = "edUrl" + Me.edUrl.Size = New System.Drawing.Size(523, 20) + Me.edUrl.TabIndex = 0 + Me.edUrl.Text = "http://www.remobjects.com" + ' + 'rbGet + ' + Me.rbGet.Checked = True + Me.rbGet.Location = New System.Drawing.Point(52, 36) + Me.rbGet.Name = "rbGet" + Me.rbGet.Size = New System.Drawing.Size(54, 20) + Me.rbGet.TabIndex = 7 + Me.rbGet.TabStop = True + Me.rbGet.Text = "Get" + ' + 'label1 + ' + Me.label1.Location = New System.Drawing.Point(12, 16) + Me.label1.Name = "label1" + Me.label1.Size = New System.Drawing.Size(32, 23) + Me.label1.TabIndex = 4 + Me.label1.Text = "URL:" + ' + 'rbPost + ' + Me.rbPost.Location = New System.Drawing.Point(112, 36) + Me.rbPost.Name = "rbPost" + Me.rbPost.Size = New System.Drawing.Size(48, 20) + Me.rbPost.TabIndex = 8 + Me.rbPost.Text = "Post" + ' + 'btnSubmit + ' + Me.btnSubmit.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.btnSubmit.Location = New System.Drawing.Point(596, 11) + Me.btnSubmit.Name = "btnSubmit" + Me.btnSubmit.Size = New System.Drawing.Size(72, 24) + Me.btnSubmit.TabIndex = 2 + Me.btnSubmit.Text = "Submit" + ' + 'cbKeepAlive + ' + Me.cbKeepAlive.Checked = True + Me.cbKeepAlive.CheckState = System.Windows.Forms.CheckState.Checked + Me.cbKeepAlive.Location = New System.Drawing.Point(172, 36) + Me.cbKeepAlive.Name = "cbKeepAlive" + Me.cbKeepAlive.Size = New System.Drawing.Size(104, 20) + Me.cbKeepAlive.TabIndex = 9 + Me.cbKeepAlive.Text = "Keep Alive" + ' + 'httpClient1 + ' + Me.httpClient1.ConnectionClass = Nothing + Me.httpClient1.ConnectionFactory = Nothing + Me.httpClient1.CustomConnectionPool = Nothing + Me.httpClient1.HostAddress = Nothing + Me.httpClient1.HostName = Nothing + Me.httpClient1.Password = "" + Me.httpClient1.Port = 0 + Me.httpClient1.Url = Nothing + Me.httpClient1.UserName = "" + ' + 'dvParams + ' + Me.dvParams.Table = Me.tblParams + ' + 'dataGrid2 + ' + Me.dataGrid2.CaptionText = "Request Content" + Me.dataGrid2.DataMember = "" + Me.dataGrid2.DataSource = Me.dvParams + Me.dataGrid2.Dock = System.Windows.Forms.DockStyle.Bottom + Me.dataGrid2.HeaderForeColor = System.Drawing.SystemColors.ControlText + Me.dataGrid2.Location = New System.Drawing.Point(5, 294) + Me.dataGrid2.Name = "dataGrid2" + Me.dataGrid2.PreferredColumnWidth = 300 + Me.dataGrid2.Size = New System.Drawing.Size(651, 181) + Me.dataGrid2.TabIndex = 1 + ' + 'splitter2 + ' + Me.splitter2.Dock = System.Windows.Forms.DockStyle.Bottom + Me.splitter2.Location = New System.Drawing.Point(5, 289) + Me.splitter2.Name = "splitter2" + Me.splitter2.Size = New System.Drawing.Size(651, 5) + Me.splitter2.TabIndex = 2 + Me.splitter2.TabStop = False + ' + 'tabPage2 + ' + Me.tabPage2.Controls.Add(Me.splitter2) + Me.tabPage2.Controls.Add(Me.dataGrid2) + Me.tabPage2.Controls.Add(Me.dataGrid1) + Me.tabPage2.Location = New System.Drawing.Point(4, 22) + Me.tabPage2.Name = "tabPage2" + Me.tabPage2.Padding = New System.Windows.Forms.Padding(5) + Me.tabPage2.Size = New System.Drawing.Size(661, 480) + Me.tabPage2.TabIndex = 1 + Me.tabPage2.Text = "Parameters" + ' + 'dataGrid1 + ' + Me.dataGrid1.CaptionText = "Request Headers" + Me.dataGrid1.DataMember = "" + Me.dataGrid1.DataSource = Me.dvHeaders + Me.dataGrid1.Dock = System.Windows.Forms.DockStyle.Fill + Me.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText + Me.dataGrid1.Location = New System.Drawing.Point(5, 5) + Me.dataGrid1.Name = "dataGrid1" + Me.dataGrid1.PreferredColumnWidth = 300 + Me.dataGrid1.Size = New System.Drawing.Size(651, 470) + Me.dataGrid1.TabIndex = 0 + ' + 'dvHeaders + ' + Me.dvHeaders.Table = Me.tblHeaders + ' + 'tabControl1 + ' + Me.tabControl1.Controls.Add(Me.tabPage2) + Me.tabControl1.Controls.Add(Me.tabPage1) + Me.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill + Me.tabControl1.Location = New System.Drawing.Point(0, 71) + Me.tabControl1.Name = "tabControl1" + Me.tabControl1.SelectedIndex = 0 + Me.tabControl1.Size = New System.Drawing.Size(669, 506) + Me.tabControl1.TabIndex = 11 + ' + 'MainForm + ' + Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) + Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font + Me.ClientSize = New System.Drawing.Size(669, 577) + Me.Controls.Add(Me.tabControl1) + Me.Controls.Add(Me.pnlpanel2) + Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon) + Me.MinimumSize = New System.Drawing.Size(685, 615) + Me.Name = "MainForm" + Me.Text = "RemObjects Internet Pack for .NET - HTTP Spy" + CType(Me.tblResponseHeaders, System.ComponentModel.ISupportInitialize).EndInit() + Me.tabPage1.ResumeLayout(False) + Me.tabPage1.PerformLayout() + Me.pnlpanel1.ResumeLayout(False) + CType(Me.dataGrid3, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.dataSet1, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.tblHeaders, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.tblParams, System.ComponentModel.ISupportInitialize).EndInit() + Me.pnlpanel2.ResumeLayout(False) + Me.pnlpanel2.PerformLayout() + CType(Me.pictureBox1, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.dvParams, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.dataGrid2, System.ComponentModel.ISupportInitialize).EndInit() + Me.tabPage2.ResumeLayout(False) + CType(Me.dataGrid1, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.dvHeaders, System.ComponentModel.ISupportInitialize).EndInit() + Me.tabControl1.ResumeLayout(False) + Me.ResumeLayout(False) + + End Sub + + Private Sub AddHeader(ByVal Name As String, ByVal Value As String) + Dim aRow As DataRow + aRow = tblHeaders.NewRow() + aRow("Name") = Name + aRow("Value") = Value + tblHeaders.Rows.Add(aRow) + End Sub + + Private Sub AddResponseHeader(ByVal Name As String, ByVal Value As String) + Dim aRow As DataRow + aRow = tblResponseHeaders.NewRow() + + aRow("Name") = Name + aRow("Value") = Value + tblResponseHeaders.Rows.Add(aRow) + End Sub + + Private Sub btnSubmit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSubmit.Click + Dim i As Integer + Dim lRequest As HttpClientRequest + lRequest = New HttpClientRequest() + If (rbPost.Checked) Then + lRequest.RequestType = RequestType.Post + + Dim lParams As String + lParams = "" + + For i = 0 To tblParams.Rows.Count - 1 + Dim aRow As DataRow + aRow = tblParams.Rows(i) + lParams = lParams & String.Format("{0}={1}\r\n", aRow("Name").ToString(), aRow("Value").ToString()) + Next + lRequest.ContentString = lParams + End If + + + lRequest.Url.Parse(edUrl.Text) + + ' set headers + For i = 0 To tblHeaders.Rows.Count - 1 + Dim aRow As DataRow + aRow = tblHeaders.Rows(i) + lRequest.Header.SetHeaderValue(aRow("Name").ToString(), aRow("Value").ToString()) + Next + + httpClient1.KeepAlive = cbKeepAlive.Checked + lRequest.KeepAlive = httpClient1.KeepAlive + + tblResponseHeaders.Clear() + edResult.Text = "" + + tabControl1.SelectedIndex = 1 + Application.DoEvents() + + Try + Dim lResponse As HttpClientResponse + lResponse = httpClient1.Dispatch(lRequest) + ShowResponse(lResponse) + + Catch ex As HttpException + + ShowResponse(ex.Response) + + + Catch ex1 As Exception + + _LastResultString = "Error retrieving response: " + ex1.Message + _LastResultBytes = New UnicodeEncoding().GetBytes(_LastResultString) + _LastLength = _LastResultBytes.Length + SetResultText() + + End Try + End Sub + + Public Sub ShowResponse(ByVal aResponse As HttpClientResponse) + + _LastResultString = aResponse.ContentString + _LastResultBytes = aResponse.ContentBytes + Try + _LastLength = aResponse.ContentLength + Catch + _LastLength = _LastResultBytes.Length + End Try + + AddResponseHeader(aResponse.Header.FirstHeader, "") + For Each aHeader As HttpHeader In aResponse.Header + AddResponseHeader(aHeader.Name, aHeader.Value) + If aHeader.Name = "Set-Cookie" Then + If MessageBox.Show("Keep Cookie for future requests?", "Internet Pack", MessageBoxButtons.YesNo) = Windows.Forms.DialogResult.Yes Then + Dim aRow As DataRow + aRow = tblHeaders.NewRow() + aRow("Name") = "Cookie" + aRow("Value") = aHeader.Value + tblHeaders.Rows.Add(aRow) + End If + End If + Next + SetResultText() + End Sub + + Private Sub SetResultText() + Cursor = Cursors.WaitCursor + Try + + + If _LastLength <> 0 Then + If rbText.Checked Then + edResult.Text = _LastResultString + edResult.Font = New Font("Courier New", 8.25F) + Else + Dim lHex As String = "" + Dim lChars As String = "" + Dim i As Integer + For i = 0 To _LastLength - 1 + If i Mod hexWidth = 0 Then + If i > 0 Then + lHex += "| " + lChars + System.Environment.NewLine + lChars = "" + End If + lHex += i.ToString("X8") + ": " + End If + + lHex = lHex & _LastResultBytes(i).ToString("X2") & " " + If _LastResultBytes(i) < 32 Then + lChars += "." + Else + lChars = lChars + Microsoft.VisualBasic.ChrW(_LastResultBytes(i)) + End If + Next + + If (_LastLength Mod hexWidth > 0) Then + For i = _LastLength Mod hexWidth To hexWidth - 1 + lHex = lHex & " " + Next + lHex += "| " + lChars + End If + + edResult.Text = lHex + edResult.Font = New Font("Courier New", 8.25F) + End If + End If + Finally + Cursor = Cursors.Default + End Try + End Sub + + Private Sub rbText_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rbText.CheckedChanged + SetResultText() + End Sub + + Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load + AddHeader("Accept", httpClient1.Accept) + AddHeader("User-Agent", httpClient1.UserAgent) + End Sub +End Class diff --git a/Samples/VB/SMTP Client/App.ico b/Samples/VB/SMTP Client/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/VB/SMTP Client/App.ico differ diff --git a/Samples/VB/SMTP Client/AssemblyInfo.vb b/Samples/VB/SMTP Client/AssemblyInfo.vb new file mode 100644 index 0000000..c267fa1 --- /dev/null +++ b/Samples/VB/SMTP Client/AssemblyInfo.vb @@ -0,0 +1,32 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' 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: + + diff --git a/Samples/VB/SMTP Client/MainForm.resx b/Samples/VB/SMTP Client/MainForm.resx new file mode 100644 index 0000000..9e27d07 --- /dev/null +++ b/Samples/VB/SMTP Client/MainForm.resx @@ -0,0 +1,736 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + 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 + AP9XVwD/Q0ME/ygoCf8ODg7/DAwM/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDfwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+Pj4POzs7zzk5Of83Nzf/NDQ0/2BgGP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + OS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trf2pqav9oaGj/ZmZm/2Rk + ZP9hYWH/e3tn/6amcP+9vXH/wsFs/728Zf+4uF7/m5pV/35+T/9UVEz/SUlJ/0dHR/9FRUX/QkJC/0BA + QM8+Pj4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trL2pq + ar9oaGj/ZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dH + R+9ERER/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGpqak9oaGivZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05O + Tt9LS0t/SkpKDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZ2cPZWVlT2NjY39hYWG/Xl5ev1xcXL9aWlq/V1dXn1VV + VW9SUlIvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAP//wAA//4A + AD/4AAAf8AAAD+AAAAfgAAADwAAAA8AAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAcAAAAHAAAAD4AAAA/AAAAf4AAAP/AAAH/4AAH//gAD//+AH/ygA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJycnPyIi + Iq8dHR3vGBgY/xQUFP8PDw//CwsLvwcHB18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUPMDAwrysr + K/8xMSL/RUUS/0REDv89PQv/NjYJ/xsbC/8KCgr/BgYGzwMDAy8AAAAAAAAAAAAAAAA/Pz8POTk5zzU1 + Nf9kZBT/g4MA/3t7AP9ycgD/aWkA/2FhAP9ZWQD/PDwE/w4OCf8GBgbvAwMDLwAAAAAAAAAAQ0NDjz4+ + Pv94eBv/lJQA/4uLAP+DgwD/e3sA/3JyAP9paQD/YWEA/1lZAP9ERAL/Dg4J/wYGBs8AAAAATExMH0hI + SP97ezb/qakY/56eCP+UlAD/i4sA/4ODAP97ewD/cnIA/2lpAP9hYQD/WVkA/0BAA/8KCgr/BwcHX1BQ + UI9MTEz/uLg7/7S0Kv+pqRj/np4I/5SUAP+LiwD/g4MA/3t7AP9ycgD/aWkA/1NTBP9ZWQD/GxsL/woK + Cs9VVVW/cnJT/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uLAP+DgwD/e3sA/3JyAP9JSQz/KioT/y4u + DP8PDw//W1tb75KSX//OzVz/e3tM/8C/O/+0tCr/e3sr/19fJ/99fQ3/dXUL/21tCv9mZgn/QUEU/yIi + G/80NA7/FBQU/19fX99+fmL/X19X/2ppUv+Likr/g4NA/3t7Nf9zcyr/WVkq/1hYIf+LiwD/g4MA/1ZW + EP9tbQL/TU0K/xgYGP9kZGS/hYVq/66tcf+BgVz/1tVc/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uL + AP+DgwD/e3sA/0VFEv8dHR3/Z2dnb2RkZP/k44j/29p7/9nYbP/W1Vz/y8pL/8C/O/+0tCr/qakY/56e + CP+UlAD/i4sA/4ODAP8xMSL/IiIir2trax9paWn/lZR1//j3j//t7H//4eFu/9bVXP/Lykv/wL87/7S0 + Kv+pqRj/np4I/5SUAP9kZBT/Kysr/ygoKF8AAAAAa2trf2lpaf/FxIb/+PeP/+3sf//h4W7/1tVc/8vK + S//Avzv/tLQq/6mpGP+LixH/Ozsx/zAwML8AAAAAAAAAAAAAAABra2ufaWlp/6iofP/u7Yz/7ex//+Hh + bv/W1Vz/y8pL/8C/O/97ezb/RUU8/zk5Oc81NTUPAAAAAAAAAAAAAAAAAAAAAGtra39paWn/ZGRk/4WF + av+jomv/m5tg/3p6VP9UVEz/SEhI/0NDQ68/Pz8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trH2ho + aI9kZGS/X19f/1tbW/9WVlbPUVFRn0xMTD8AAAAAAAAAAAAAAAAAAAAA8A8AAMADAACAAQAAgAEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAMABAADgAwAA8A8AAA== + + + \ No newline at end of file diff --git a/Samples/VB/SMTP Client/MainForm.vb b/Samples/VB/SMTP Client/MainForm.vb new file mode 100644 index 0000000..554b2aa --- /dev/null +++ b/Samples/VB/SMTP Client/MainForm.vb @@ -0,0 +1,487 @@ +Public Class MainForm + Inherits System.Windows.Forms.Form + +#Region " Windows Form Designer generated code " + + Public Sub New() + MyBase.New() + + Application.EnableVisualStyles() + + 'This call is required by the Windows Form Designer. + InitializeComponent() + + 'Add any initialization after the InitializeComponent() call + msg = New RemObjects.InternetPack.Messages.MailMessage + + End Sub + + 'Form overrides dispose to clean up the component list. + Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing Then + If Not (components Is Nothing) Then + components.Dispose() + End If + End If + MyBase.Dispose(disposing) + End Sub + + Friend msg As RemObjects.InternetPack.Messages.MailMessage + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + Friend WithEvents groupBox1 As System.Windows.Forms.GroupBox + Friend WithEvents lblSMTPServer As System.Windows.Forms.Label + Friend WithEvents txtSMTPServer As System.Windows.Forms.TextBox + Friend WithEvents groupBox2 As System.Windows.Forms.GroupBox + Friend WithEvents txtPassword As System.Windows.Forms.TextBox + Friend WithEvents txtUserName As System.Windows.Forms.TextBox + Friend WithEvents lblPassword As System.Windows.Forms.Label + Friend WithEvents lblUserName As System.Windows.Forms.Label + Friend WithEvents chkUseAuth As System.Windows.Forms.CheckBox + Friend WithEvents smtpClient As RemObjects.InternetPack.Email.SmtpClient + Friend WithEvents lblIP As System.Windows.Forms.Label + Friend WithEvents lblRO As System.Windows.Forms.Label + Friend WithEvents pictureBox1 As System.Windows.Forms.PictureBox + Friend WithEvents groupBox3 As System.Windows.Forms.GroupBox + Friend WithEvents lblSenderName As System.Windows.Forms.Label + Friend WithEvents lblFromEmail As System.Windows.Forms.Label + Friend WithEvents txtMessage As System.Windows.Forms.TextBox + Friend WithEvents txtSenderAddress As System.Windows.Forms.TextBox + Friend WithEvents label5 As System.Windows.Forms.Label + Friend WithEvents txtSenderName As System.Windows.Forms.TextBox + Friend WithEvents txtTo As System.Windows.Forms.TextBox + Friend WithEvents label6 As System.Windows.Forms.Label + Friend WithEvents label3 As System.Windows.Forms.Label + Friend WithEvents txtCC As System.Windows.Forms.TextBox + Friend WithEvents txtSubject As System.Windows.Forms.TextBox + Friend WithEvents label7 As System.Windows.Forms.Label + Friend WithEvents txtBCC As System.Windows.Forms.TextBox + Friend WithEvents label8 As System.Windows.Forms.Label + Friend WithEvents btnSendEMail As System.Windows.Forms.Button + Private Sub InitializeComponent() + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainForm)) + Me.groupBox1 = New System.Windows.Forms.GroupBox + Me.lblSMTPServer = New System.Windows.Forms.Label + Me.txtSMTPServer = New System.Windows.Forms.TextBox + Me.groupBox2 = New System.Windows.Forms.GroupBox + Me.txtPassword = New System.Windows.Forms.TextBox + Me.txtUserName = New System.Windows.Forms.TextBox + Me.lblPassword = New System.Windows.Forms.Label + Me.lblUserName = New System.Windows.Forms.Label + Me.chkUseAuth = New System.Windows.Forms.CheckBox + Me.smtpClient = New RemObjects.InternetPack.Email.SmtpClient + Me.lblIP = New System.Windows.Forms.Label + Me.lblRO = New System.Windows.Forms.Label + Me.pictureBox1 = New System.Windows.Forms.PictureBox + Me.groupBox3 = New System.Windows.Forms.GroupBox + Me.lblSenderName = New System.Windows.Forms.Label + Me.lblFromEmail = New System.Windows.Forms.Label + Me.txtMessage = New System.Windows.Forms.TextBox + Me.txtSenderAddress = New System.Windows.Forms.TextBox + Me.label5 = New System.Windows.Forms.Label + Me.txtSenderName = New System.Windows.Forms.TextBox + Me.txtTo = New System.Windows.Forms.TextBox + Me.label6 = New System.Windows.Forms.Label + Me.label3 = New System.Windows.Forms.Label + Me.txtCC = New System.Windows.Forms.TextBox + Me.txtSubject = New System.Windows.Forms.TextBox + Me.label7 = New System.Windows.Forms.Label + Me.txtBCC = New System.Windows.Forms.TextBox + Me.label8 = New System.Windows.Forms.Label + Me.btnSendEMail = New System.Windows.Forms.Button + Me.groupBox1.SuspendLayout() + Me.groupBox2.SuspendLayout() + CType(Me.pictureBox1, System.ComponentModel.ISupportInitialize).BeginInit() + Me.groupBox3.SuspendLayout() + Me.SuspendLayout() + ' + 'groupBox1 + ' + Me.groupBox1.Controls.Add(Me.lblSMTPServer) + Me.groupBox1.Controls.Add(Me.txtSMTPServer) + Me.groupBox1.Controls.Add(Me.groupBox2) + Me.groupBox1.Controls.Add(Me.chkUseAuth) + Me.groupBox1.Location = New System.Drawing.Point(7, 6) + Me.groupBox1.Name = "groupBox1" + Me.groupBox1.Size = New System.Drawing.Size(360, 160) + Me.groupBox1.TabIndex = 15 + Me.groupBox1.TabStop = False + Me.groupBox1.Text = "Server Information" + ' + 'lblSMTPServer + ' + Me.lblSMTPServer.Location = New System.Drawing.Point(6, 19) + Me.lblSMTPServer.Name = "lblSMTPServer" + Me.lblSMTPServer.Size = New System.Drawing.Size(160, 15) + Me.lblSMTPServer.TabIndex = 0 + Me.lblSMTPServer.Text = "Outgoing Mail Server (SMTP):" + ' + 'txtSMTPServer + ' + Me.txtSMTPServer.Location = New System.Drawing.Point(168, 16) + Me.txtSMTPServer.Name = "txtSMTPServer" + Me.txtSMTPServer.Size = New System.Drawing.Size(168, 20) + Me.txtSMTPServer.TabIndex = 1 + Me.txtSMTPServer.Text = "ws7.elitedev.com" + ' + 'groupBox2 + ' + Me.groupBox2.Controls.Add(Me.txtPassword) + Me.groupBox2.Controls.Add(Me.txtUserName) + Me.groupBox2.Controls.Add(Me.lblPassword) + Me.groupBox2.Controls.Add(Me.lblUserName) + Me.groupBox2.Location = New System.Drawing.Point(16, 72) + Me.groupBox2.Name = "groupBox2" + Me.groupBox2.Size = New System.Drawing.Size(336, 80) + Me.groupBox2.TabIndex = 3 + Me.groupBox2.TabStop = False + Me.groupBox2.Text = "Login Information" + ' + 'txtPassword + ' + Me.txtPassword.Enabled = False + Me.txtPassword.Location = New System.Drawing.Point(88, 48) + Me.txtPassword.Name = "txtPassword" + Me.txtPassword.PasswordChar = Global.Microsoft.VisualBasic.ChrW(42) + Me.txtPassword.Size = New System.Drawing.Size(232, 20) + Me.txtPassword.TabIndex = 3 + ' + 'txtUserName + ' + Me.txtUserName.Enabled = False + Me.txtUserName.Location = New System.Drawing.Point(88, 24) + Me.txtUserName.Name = "txtUserName" + Me.txtUserName.Size = New System.Drawing.Size(232, 20) + Me.txtUserName.TabIndex = 1 + ' + 'lblPassword + ' + Me.lblPassword.AutoSize = True + Me.lblPassword.Enabled = False + Me.lblPassword.Location = New System.Drawing.Point(21, 51) + Me.lblPassword.Name = "lblPassword" + Me.lblPassword.Size = New System.Drawing.Size(56, 13) + Me.lblPassword.TabIndex = 2 + Me.lblPassword.Text = "Password:" + ' + 'lblUserName + ' + Me.lblUserName.AutoSize = True + Me.lblUserName.Enabled = False + Me.lblUserName.Location = New System.Drawing.Point(14, 27) + Me.lblUserName.Name = "lblUserName" + Me.lblUserName.Size = New System.Drawing.Size(63, 13) + Me.lblUserName.TabIndex = 0 + Me.lblUserName.Text = "User Name:" + ' + 'chkUseAuth + ' + Me.chkUseAuth.Location = New System.Drawing.Point(16, 48) + Me.chkUseAuth.Name = "chkUseAuth" + Me.chkUseAuth.Size = New System.Drawing.Size(279, 24) + Me.chkUseAuth.TabIndex = 2 + Me.chkUseAuth.Text = "My outgoing server (SMTP) requires authentication" + ' + 'smtpClient + ' + Me.smtpClient.AuthPassword = Nothing + Me.smtpClient.AuthUser = Nothing + Me.smtpClient.ConnectionClass = Nothing + Me.smtpClient.ConnectionFactory = Nothing + Me.smtpClient.HeloDomain = "remobjects.com" + Me.smtpClient.HostAddress = Nothing + Me.smtpClient.HostName = "" + Me.smtpClient.Port = 25 + Me.smtpClient.UseAuth = False + ' + 'lblIP + ' + Me.lblIP.AutoSize = True + Me.lblIP.Font = New System.Drawing.Font("Verdana", 15.75!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.lblIP.Location = New System.Drawing.Point(415, 134) + Me.lblIP.Name = "lblIP" + Me.lblIP.Size = New System.Drawing.Size(173, 25) + Me.lblIP.TabIndex = 17 + Me.lblIP.Text = "Internet Pack" + ' + 'lblRO + ' + Me.lblRO.AutoSize = True + Me.lblRO.Font = New System.Drawing.Font("Verdana", 14.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.lblRO.Location = New System.Drawing.Point(464, 102) + Me.lblRO.Name = "lblRO" + Me.lblRO.Size = New System.Drawing.Size(124, 23) + Me.lblRO.TabIndex = 18 + Me.lblRO.Text = "RemObjects" + ' + 'pictureBox1 + ' + Me.pictureBox1.BackColor = System.Drawing.SystemColors.ActiveBorder + Me.pictureBox1.Image = CType(resources.GetObject("pictureBox1.Image"), System.Drawing.Image) + Me.pictureBox1.Location = New System.Drawing.Point(501, 14) + Me.pictureBox1.Name = "pictureBox1" + Me.pictureBox1.Size = New System.Drawing.Size(88, 80) + Me.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage + Me.pictureBox1.TabIndex = 19 + Me.pictureBox1.TabStop = False + ' + 'groupBox3 + ' + Me.groupBox3.Controls.Add(Me.lblSenderName) + Me.groupBox3.Controls.Add(Me.lblFromEmail) + Me.groupBox3.Controls.Add(Me.txtMessage) + Me.groupBox3.Controls.Add(Me.txtSenderAddress) + Me.groupBox3.Controls.Add(Me.label5) + Me.groupBox3.Controls.Add(Me.txtSenderName) + Me.groupBox3.Controls.Add(Me.txtTo) + Me.groupBox3.Controls.Add(Me.label6) + Me.groupBox3.Controls.Add(Me.label3) + Me.groupBox3.Controls.Add(Me.txtCC) + Me.groupBox3.Controls.Add(Me.txtSubject) + Me.groupBox3.Controls.Add(Me.label7) + Me.groupBox3.Controls.Add(Me.txtBCC) + Me.groupBox3.Controls.Add(Me.label8) + Me.groupBox3.Controls.Add(Me.btnSendEMail) + Me.groupBox3.Location = New System.Drawing.Point(7, 174) + Me.groupBox3.Name = "groupBox3" + Me.groupBox3.Size = New System.Drawing.Size(584, 296) + Me.groupBox3.TabIndex = 16 + Me.groupBox3.TabStop = False + Me.groupBox3.Text = "EMail" + ' + 'lblSenderName + ' + Me.lblSenderName.AutoSize = True + Me.lblSenderName.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.lblSenderName.Location = New System.Drawing.Point(29, 19) + Me.lblSenderName.Name = "lblSenderName" + Me.lblSenderName.Size = New System.Drawing.Size(64, 13) + Me.lblSenderName.TabIndex = 0 + Me.lblSenderName.Text = "From Name:" + ' + 'lblFromEmail + ' + Me.lblFromEmail.AutoSize = True + Me.lblFromEmail.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.lblFromEmail.Location = New System.Drawing.Point(60, 43) + Me.lblFromEmail.Name = "lblFromEmail" + Me.lblFromEmail.Size = New System.Drawing.Size(33, 13) + Me.lblFromEmail.TabIndex = 2 + Me.lblFromEmail.Text = "From:" + ' + 'txtMessage + ' + Me.txtMessage.Location = New System.Drawing.Point(104, 120) + Me.txtMessage.Multiline = True + Me.txtMessage.Name = "txtMessage" + Me.txtMessage.Size = New System.Drawing.Size(464, 136) + Me.txtMessage.TabIndex = 13 + Me.txtMessage.Text = "" + ' + 'txtSenderAddress + ' + Me.txtSenderAddress.Location = New System.Drawing.Point(104, 40) + Me.txtSenderAddress.Name = "txtSenderAddress" + Me.txtSenderAddress.Size = New System.Drawing.Size(232, 20) + Me.txtSenderAddress.TabIndex = 3 + Me.txtSenderAddress.Text = "alexanderk@remobjects.com" + ' + 'label5 + ' + Me.label5.AutoSize = True + Me.label5.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.label5.Location = New System.Drawing.Point(40, 123) + Me.label5.Name = "label5" + Me.label5.Size = New System.Drawing.Size(53, 13) + Me.label5.TabIndex = 12 + Me.label5.Text = "Message:" + ' + 'txtSenderName + ' + Me.txtSenderName.Location = New System.Drawing.Point(104, 16) + Me.txtSenderName.Name = "txtSenderName" + Me.txtSenderName.Size = New System.Drawing.Size(232, 20) + Me.txtSenderName.TabIndex = 1 + Me.txtSenderName.Text = "John Doe" + ' + 'txtTo + ' + Me.txtTo.Location = New System.Drawing.Point(392, 16) + Me.txtTo.Name = "txtTo" + Me.txtTo.Size = New System.Drawing.Size(176, 20) + Me.txtTo.TabIndex = 5 + ' + 'label6 + ' + Me.label6.AutoSize = True + Me.label6.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.label6.Location = New System.Drawing.Point(366, 19) + Me.label6.Name = "label6" + Me.label6.Size = New System.Drawing.Size(23, 13) + Me.label6.TabIndex = 4 + Me.label6.Text = "To:" + ' + 'label3 + ' + Me.label3.AutoSize = True + Me.label3.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.label3.Location = New System.Drawing.Point(47, 91) + Me.label3.Name = "label3" + Me.label3.Size = New System.Drawing.Size(46, 13) + Me.label3.TabIndex = 10 + Me.label3.Text = "Subject:" + ' + 'txtCC + ' + Me.txtCC.Location = New System.Drawing.Point(392, 40) + Me.txtCC.Name = "txtCC" + Me.txtCC.Size = New System.Drawing.Size(176, 20) + Me.txtCC.TabIndex = 7 + ' + 'txtSubject + ' + Me.txtSubject.Location = New System.Drawing.Point(104, 88) + Me.txtSubject.Name = "txtSubject" + Me.txtSubject.Size = New System.Drawing.Size(464, 20) + Me.txtSubject.TabIndex = 11 + Me.txtSubject.Text = "" + ' + 'label7 + ' + Me.label7.AutoSize = True + Me.label7.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.label7.Location = New System.Drawing.Point(365, 43) + Me.label7.Name = "label7" + Me.label7.Size = New System.Drawing.Size(24, 13) + Me.label7.TabIndex = 6 + Me.label7.Text = "CC:" + ' + 'txtBCC + ' + Me.txtBCC.Location = New System.Drawing.Point(392, 64) + Me.txtBCC.Name = "txtBCC" + Me.txtBCC.Size = New System.Drawing.Size(176, 20) + Me.txtBCC.TabIndex = 9 + ' + 'label8 + ' + Me.label8.AutoSize = True + Me.label8.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) + Me.label8.Location = New System.Drawing.Point(358, 67) + Me.label8.Name = "label8" + Me.label8.Size = New System.Drawing.Size(31, 13) + Me.label8.TabIndex = 8 + Me.label8.Text = "BCC:" + ' + 'btnSendEMail + ' + Me.btnSendEMail.Location = New System.Drawing.Point(456, 264) + Me.btnSendEMail.Name = "btnSendEMail" + Me.btnSendEMail.Size = New System.Drawing.Size(112, 23) + Me.btnSendEMail.TabIndex = 14 + Me.btnSendEMail.Text = "Send Email" + ' + 'MainForm + ' + Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) + Me.ClientSize = New System.Drawing.Size(598, 476) + Me.Controls.Add(Me.lblIP) + Me.Controls.Add(Me.lblRO) + Me.Controls.Add(Me.pictureBox1) + Me.Controls.Add(Me.groupBox3) + Me.Controls.Add(Me.groupBox1) + Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog + Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon) + Me.MaximizeBox = False + Me.Name = "MainForm" + Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen + Me.Text = "SMTP Client Sample" + Me.groupBox1.ResumeLayout(False) + Me.groupBox1.PerformLayout() + Me.groupBox2.ResumeLayout(False) + Me.groupBox2.PerformLayout() + CType(Me.pictureBox1, System.ComponentModel.ISupportInitialize).EndInit() + Me.groupBox3.ResumeLayout(False) + Me.groupBox3.PerformLayout() + Me.ResumeLayout(False) + Me.PerformLayout() + + End Sub + +#End Region + + Private Sub chkUseAuth_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkUseAuth.CheckedChanged + lblUserName.Enabled = chkUseAuth.Checked + txtUserName.Enabled = chkUseAuth.Checked + lblPassword.Enabled = chkUseAuth.Checked + txtPassword.Enabled = chkUseAuth.Checked + End Sub + + Private Sub btnSendEMail_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSendEMail.Click + ' Clear lists before sending + msg.To.Clear() + msg.Cc.Clear() + msg.Bcc.Clear() + Try + + msg.To.Add(getRequiredValue(txtTo, "Value of field 'To' is required")) + + Dim cc As String + cc = txtCC.Text.Trim() + If cc.Length > 0 Then + msg.Cc.Add(cc) + End If + + + Dim bcc As String + bcc = txtBCC.Text.Trim() + If (bcc.Length > 0) Then + msg.Bcc.Add(bcc) + End If + + smtpClient.HostName = getRequiredValue(txtSMTPServer, "Host Name is required") + smtpClient.HeloDomain = "remobjects.com" + + smtpClient.UseAuth = chkUseAuth.Checked + smtpClient.AuthUser = txtUserName.Text + smtpClient.AuthPassword = txtPassword.Text + + msg.From.Name = getRequiredValue(txtSenderName, "From field value is required") + msg.From.Address = getRequiredValue(txtSenderAddress, "EMail field value is required") + msg.Subject = getRequiredValue(txtSubject, "Subject of letter is required") + msg.Contents = getRequiredValue(txtMessage, "Content of letter is required") + + + smtpClient.Open() + smtpClient.SendMessage(msg) + smtpClient.Close() + MessageBox.Show("Email has been sent successfully!", "SMTP Client Sample") + + Catch ex As Exception + MessageBox.Show(ex.Message, "Error during sending letter.", MessageBoxButtons.OK, MessageBoxIcon.Error) + End Try + + + End Sub + + Private Function getRequiredValue(ByVal ctrl As TextBox, ByVal errorMessage As String) As String + + If ctrl Is Nothing Then + Return Nothing + End If + + Dim result As String + result = ctrl.Text.Trim() + If result.Length = 0 Then + ctrl.Focus() + Err.Raise(errorMessage) + End If + + Return result + + End Function +End Class diff --git a/Samples/VB/SMTP Client/SMTP Client Sample.2008.sln b/Samples/VB/SMTP Client/SMTP Client Sample.2008.sln new file mode 100644 index 0000000..eb77a1e --- /dev/null +++ b/Samples/VB/SMTP Client/SMTP Client Sample.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SMTP Client Sample", "SMTP Client Sample.2008.vbproj", "{0E5F8C7C-F610-4293-8B6C-331D0BA77E46}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/SMTP Client/SMTP Client Sample.2008.vbproj b/Samples/VB/SMTP Client/SMTP Client Sample.2008.vbproj new file mode 100644 index 0000000..b50d2c0 --- /dev/null +++ b/Samples/VB/SMTP Client/SMTP Client Sample.2008.vbproj @@ -0,0 +1,118 @@ + + + Local + 8.0.50727 + 2.0 + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46} + Debug + AnyCPU + + + + + SMTP Client Sample + + + None + JScript + Grid + IE50 + false + WinExe + Binary + On + Off + SMTP_Client_Sample + SMTP_Client_Sample.MainForm + + + WindowsFormsWithCustomSubMain + + + 2.0 + + + bin\ + SMTP Client Sample.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + + + bin\ + SMTP Client Sample.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + + + + + + + + + Code + + + Form + + + MainForm.vb + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/SMTP Client/SMTP Client Sample.2010.sln b/Samples/VB/SMTP Client/SMTP Client Sample.2010.sln new file mode 100644 index 0000000..281524e --- /dev/null +++ b/Samples/VB/SMTP Client/SMTP Client Sample.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SMTP Client Sample", "SMTP Client Sample.2010.vbproj", "{0E5F8C7C-F610-4293-8B6C-331D0BA77E46}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/SMTP Client/SMTP Client Sample.2010.vbproj b/Samples/VB/SMTP Client/SMTP Client Sample.2010.vbproj new file mode 100644 index 0000000..48ea1f9 --- /dev/null +++ b/Samples/VB/SMTP Client/SMTP Client Sample.2010.vbproj @@ -0,0 +1,124 @@ + + + Local + 10.0.20506 + 2.0 + {0E5F8C7C-F610-4293-8B6C-331D0BA77E46} + Debug + AnyCPU + + + + + SMTP Client Sample + + + None + JScript + Grid + IE50 + false + WinExe + Binary + On + Off + SMTP_Client_Sample + SMTP_Client_Sample.MainForm + + + WindowsFormsWithCustomSubMain + + + 3.5 + v2.0 + + + bin\ + SMTP Client Sample.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + AllRules.ruleset + + + bin\ + SMTP Client Sample.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + AllRules.ruleset + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + + + + + + + + + Code + + + Form + + + MainForm.vb + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/SMTP Client/SMTP Client Sample.Sample.html b/Samples/VB/SMTP Client/SMTP Client Sample.Sample.html new file mode 100644 index 0000000..4715f20 --- /dev/null +++ b/Samples/VB/SMTP Client/SMTP Client Sample.Sample.html @@ -0,0 +1,38 @@ + + + + + + + + +

SMTP Client Sample

+ +

Purpose

+ +

+ The sample shows how we can use the SMTP client component for sending emails. +

+ + +

Examine the code

+
    +
  • Sending emails is pretty simple (see MainForm's btnSendEMail_Click handler). + We create and configure an instance of MailMessage.
  • +
  • Next we put the SmtpClient component onto the form and configure it.
  • +
  • After configuring these components we can send the message (smtpClient.SendMessage(msg);). +
+ +

Getting started

+
    +
  • Compile the solution.
  • +
  • Run the Smtp client application.
  • +
  • Specify the outgoing mail server (if your mail server requires authentication you + will need to specify login and password).
  • +
  • Create an email and send it
  • +
+ + + + + diff --git a/Samples/VB/Sample Server/App.ico b/Samples/VB/Sample Server/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/VB/Sample Server/App.ico differ diff --git a/Samples/VB/Sample Server/AssemblyInfo.vb b/Samples/VB/Sample Server/AssemblyInfo.vb new file mode 100644 index 0000000..4ec06b5 --- /dev/null +++ b/Samples/VB/Sample Server/AssemblyInfo.vb @@ -0,0 +1,32 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' 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: + + diff --git a/Samples/VB/Sample Server/HttpRoot/Styles.css b/Samples/VB/Sample Server/HttpRoot/Styles.css new file mode 100644 index 0000000..c8e0628 --- /dev/null +++ b/Samples/VB/Sample Server/HttpRoot/Styles.css @@ -0,0 +1,103 @@ +body +{ + background-color: #f7f7f7; + margin-top: 15px; + margin-bottom: 15px; + margin-left: 15px; + margin-right: 15px; + padding-top: 10px; + padding-bottom: 10px; + padding-left: 10px; + padding-right: 10px; + font-family: tahoma, verdana, sans-serif; + font-size: 10pt; + width: 700px; + color: #000000; +} +p +{ + padding-top: 0; + padding-bottom: 0; + padding-left: 0; + padding-right: 0.5em; +} +ul +{ + padding-top: 0; + padding-bottom: 0; + list-style-type: disc; +} +li +{ + padding-top: 0; + padding-bottom: 0; +} +img +{ + margin: 5px; + border-width: 0; +} +table +{ + background-color: #f7f7f7; + margin: 15px; + padding: 0px; + font-size: 10pt; +} +tr +{ + background-color: #f7f7f7; + margin: 15px; + padding: 0px; + font-size: 10pt; +} +td, th +{ + background-color: #f7f7f7; + margin: 0; + padding: 5px; + font-size: 10pt; +} +td ul +{ + padding-left: 2em; +} + +img:left { margin-left: 0; } +img:right { margin-right: 0; } +p.h1 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:13pt; + font-weight:bold; +} +p.h2 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:11pt; + font-weight:bold; +} +p.h3 +{ + margin-top: 1em; + margin-bottom: 0.5px; + padding-bottom:0px; + font-size:10pt; + font-weight:bold; +} +pre +{ + margin-top:0px; + margin-bottom:0px; + margin-left:0px; + margin-right:0px; +} +.spaced +{ + letter-spacing:1px; + color:#000060; +} diff --git a/Samples/VB/Sample Server/HttpRoot/index.html b/Samples/VB/Sample Server/HttpRoot/index.html new file mode 100644 index 0000000..ffaee1e --- /dev/null +++ b/Samples/VB/Sample Server/HttpRoot/index.html @@ -0,0 +1,44 @@ + + + + + + + + + + +

+ Sample Server +

+ + +

Purpose

+

+This example shows how easy it is to create a simple custom Http server. The + +SimpleHttpServer class is a file-based HTTP Server that will make all files in (and below) the specified RootPath folder available via HTTP. + + +

Examine the Code

+
    +
  • look at the ActivateServers procedure to see how to create and configure the HTTP server. The +RootPath property should reference the folder in our file system which we want to expose. +
  • +
  • +See the OnHttpRequest handler. There we can catch and process any incoming requests that come to the server. +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run application.
  • +
  • Configure server properties and activate the servers.
  • +
  • Click link on the main window to open home page of our server in web browser.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/VB/Sample Server/HttpRoot/ip.png b/Samples/VB/Sample Server/HttpRoot/ip.png new file mode 100644 index 0000000..ea014ca Binary files /dev/null and b/Samples/VB/Sample Server/HttpRoot/ip.png differ diff --git a/Samples/VB/Sample Server/MainForm.resx b/Samples/VB/Sample Server/MainForm.resx new file mode 100644 index 0000000..a726801 --- /dev/null +++ b/Samples/VB/Sample Server/MainForm.resx @@ -0,0 +1,582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 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 + CQAPDw8AGxsLABQUFAAYGBgAHR0dAC4uDAA8PAQANjYJADQ0DgA9PQsAKioTACIiGwArKysAMTEiADQ0 + NAA7OzEAODg4AD4+PgBAQAMAREQCAEREDgBNTQoASUkMAFNTBABZWQAARUUSAEFBFABWVhAAYWEAAGlp + AABtbQIAZmYJAG1tCgBycgAAe3sAAHV1CwB9fQ0AZGQUAHh4GwBYWCEAX18nAFlZKgBFRTwAc3MqAHt7 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + 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 + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + OS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trf2pqav9oaGj/ZmZm/2Rk + ZP9hYWH/e3tn/6amcP+9vXH/wsFs/728Zf+4uF7/m5pV/35+T/9UVEz/SUlJ/0dHR/9FRUX/QkJC/0BA + QM8+Pj4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trL2pq + ar9oaGj/ZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dH + R+9ERER/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGpqak9oaGivZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05O + Tt9LS0t/SkpKDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZ2cPZWVlT2NjY39hYWG/Xl5ev1xcXL9aWlq/V1dXn1VV + VW9SUlIvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAP//wAA//4A + AD/4AAAf8AAAD+AAAAfgAAADwAAAA8AAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAcAAAAHAAAAD4AAAA/AAAAf4AAAP/AAAH/4AAH//gAD//+AH/ygA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJycnPyIi + Iq8dHR3vGBgY/xQUFP8PDw//CwsLvwcHB18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUPMDAwrysr + K/8xMSL/RUUS/0REDv89PQv/NjYJ/xsbC/8KCgr/BgYGzwMDAy8AAAAAAAAAAAAAAAA/Pz8POTk5zzU1 + Nf9kZBT/g4MA/3t7AP9ycgD/aWkA/2FhAP9ZWQD/PDwE/w4OCf8GBgbvAwMDLwAAAAAAAAAAQ0NDjz4+ + Pv94eBv/lJQA/4uLAP+DgwD/e3sA/3JyAP9paQD/YWEA/1lZAP9ERAL/Dg4J/wYGBs8AAAAATExMH0hI + SP97ezb/qakY/56eCP+UlAD/i4sA/4ODAP97ewD/cnIA/2lpAP9hYQD/WVkA/0BAA/8KCgr/BwcHX1BQ + UI9MTEz/uLg7/7S0Kv+pqRj/np4I/5SUAP+LiwD/g4MA/3t7AP9ycgD/aWkA/1NTBP9ZWQD/GxsL/woK + Cs9VVVW/cnJT/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uLAP+DgwD/e3sA/3JyAP9JSQz/KioT/y4u + DP8PDw//W1tb75KSX//OzVz/e3tM/8C/O/+0tCr/e3sr/19fJ/99fQ3/dXUL/21tCv9mZgn/QUEU/yIi + G/80NA7/FBQU/19fX99+fmL/X19X/2ppUv+Likr/g4NA/3t7Nf9zcyr/WVkq/1hYIf+LiwD/g4MA/1ZW + EP9tbQL/TU0K/xgYGP9kZGS/hYVq/66tcf+BgVz/1tVc/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uL + AP+DgwD/e3sA/0VFEv8dHR3/Z2dnb2RkZP/k44j/29p7/9nYbP/W1Vz/y8pL/8C/O/+0tCr/qakY/56e + CP+UlAD/i4sA/4ODAP8xMSL/IiIir2trax9paWn/lZR1//j3j//t7H//4eFu/9bVXP/Lykv/wL87/7S0 + Kv+pqRj/np4I/5SUAP9kZBT/Kysr/ygoKF8AAAAAa2trf2lpaf/FxIb/+PeP/+3sf//h4W7/1tVc/8vK + S//Avzv/tLQq/6mpGP+LixH/Ozsx/zAwML8AAAAAAAAAAAAAAABra2ufaWlp/6iofP/u7Yz/7ex//+Hh + bv/W1Vz/y8pL/8C/O/97ezb/RUU8/zk5Oc81NTUPAAAAAAAAAAAAAAAAAAAAAGtra39paWn/ZGRk/4WF + av+jomv/m5tg/3p6VP9UVEz/SEhI/0NDQ68/Pz8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trH2ho + aI9kZGS/X19f/1tbW/9WVlbPUVFRn0xMTD8AAAAAAAAAAAAAAAAAAAAA8A8AAMADAACAAQAAgAEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAMABAADgAwAA8A8AAA== + + + \ No newline at end of file diff --git a/Samples/VB/Sample Server/MainForm.vb b/Samples/VB/Sample Server/MainForm.vb new file mode 100644 index 0000000..ef30b7c --- /dev/null +++ b/Samples/VB/Sample Server/MainForm.vb @@ -0,0 +1,357 @@ +Imports RemObjects.InternetPack.StandardServers +Imports RemObjects.InternetPack.Http +Imports System.IO + +Public Class MainForm + Inherits System.Windows.Forms.Form + +#Region " Windows Form Designer generated code " + + Public Sub New() + MyBase.New() + + Application.EnableVisualStyles() + + 'This call is required by the Windows Form Designer. + InitializeComponent() + + 'Add any initialization after the InitializeComponent() call + + End Sub + + 'Form overrides dispose to clean up the component list. + Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing Then + If Not (components Is Nothing) Then + components.Dispose() + End If + End If + MyBase.Dispose(disposing) + End Sub + + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + Friend WithEvents pictureBox1 As System.Windows.Forms.PictureBox + Friend WithEvents Label2 As System.Windows.Forms.Label + Friend WithEvents btnAction As System.Windows.Forms.Button + Friend WithEvents nudPort As System.Windows.Forms.NumericUpDown + Friend WithEvents lblLink As System.Windows.Forms.LinkLabel + Friend WithEvents lblPort As System.Windows.Forms.Label + Friend WithEvents lblRoot As System.Windows.Forms.Label + Friend WithEvents txtRoot As System.Windows.Forms.TextBox + Friend WithEvents txtServerName As System.Windows.Forms.TextBox + Friend WithEvents lblServerName As System.Windows.Forms.Label + Friend WithEvents nudCount As System.Windows.Forms.NumericUpDown + Friend WithEvents lblCount As System.Windows.Forms.Label + Friend WithEvents lblUrl As System.Windows.Forms.Label + Friend WithEvents txtLog As System.Windows.Forms.TextBox + Friend WithEvents gbHttpServer As System.Windows.Forms.GroupBox + + Private Sub InitializeComponent() + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainForm)) + Me.lblLink = New System.Windows.Forms.LinkLabel + Me.pictureBox1 = New System.Windows.Forms.PictureBox + Me.btnAction = New System.Windows.Forms.Button + Me.nudPort = New System.Windows.Forms.NumericUpDown + Me.lblPort = New System.Windows.Forms.Label + Me.Label2 = New System.Windows.Forms.Label + Me.txtLog = New System.Windows.Forms.TextBox + Me.gbHttpServer = New System.Windows.Forms.GroupBox + Me.lblUrl = New System.Windows.Forms.Label + Me.nudCount = New System.Windows.Forms.NumericUpDown + Me.lblCount = New System.Windows.Forms.Label + Me.lblServerName = New System.Windows.Forms.Label + Me.txtServerName = New System.Windows.Forms.TextBox + Me.txtRoot = New System.Windows.Forms.TextBox + Me.lblRoot = New System.Windows.Forms.Label + CType(Me.pictureBox1, System.ComponentModel.ISupportInitialize).BeginInit() + CType(Me.nudPort, System.ComponentModel.ISupportInitialize).BeginInit() + Me.gbHttpServer.SuspendLayout() + CType(Me.nudCount, System.ComponentModel.ISupportInitialize).BeginInit() + Me.SuspendLayout() + ' + 'lblLink + ' + Me.lblLink.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.lblLink.Enabled = False + Me.lblLink.Location = New System.Drawing.Point(139, 120) + Me.lblLink.Name = "lblLink" + Me.lblLink.Size = New System.Drawing.Size(168, 16) + Me.lblLink.TabIndex = 9 + Me.lblLink.TabStop = True + Me.lblLink.Text = "http://localhost:82/index.html" + ' + 'pictureBox1 + ' + Me.pictureBox1.Image = CType(resources.GetObject("pictureBox1.Image"), System.Drawing.Image) + Me.pictureBox1.Location = New System.Drawing.Point(8, 8) + Me.pictureBox1.Name = "pictureBox1" + Me.pictureBox1.Size = New System.Drawing.Size(120, 30) + Me.pictureBox1.TabIndex = 11 + Me.pictureBox1.TabStop = False + ' + 'btnAction + ' + Me.btnAction.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.btnAction.Location = New System.Drawing.Point(402, 115) + Me.btnAction.Name = "btnAction" + Me.btnAction.Size = New System.Drawing.Size(112, 23) + Me.btnAction.TabIndex = 10 + Me.btnAction.Text = "Activate Servers" + ' + 'nudPort + ' + Me.nudPort.Location = New System.Drawing.Point(139, 16) + Me.nudPort.Name = "nudPort" + Me.nudPort.Size = New System.Drawing.Size(48, 20) + Me.nudPort.TabIndex = 1 + Me.nudPort.Value = New Decimal(New Integer() {83, 0, 0, 0}) + ' + 'lblPort + ' + Me.lblPort.AutoSize = True + Me.lblPort.Location = New System.Drawing.Point(102, 18) + Me.lblPort.Name = "lblPort" + Me.lblPort.Size = New System.Drawing.Size(29, 13) + Me.lblPort.TabIndex = 0 + Me.lblPort.Text = "&Port:" + ' + 'Label2 + ' + Me.Label2.Location = New System.Drawing.Point(8, 200) + Me.Label2.Name = "Label2" + Me.Label2.Size = New System.Drawing.Size(32, 16) + Me.Label2.TabIndex = 1 + Me.Label2.Text = "Log" + ' + 'txtLog + ' + Me.txtLog.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _ + Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.txtLog.Location = New System.Drawing.Point(8, 224) + Me.txtLog.Multiline = True + Me.txtLog.Name = "txtLog" + Me.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both + Me.txtLog.Size = New System.Drawing.Size(528, 222) + Me.txtLog.TabIndex = 2 + Me.txtLog.WordWrap = False + ' + 'gbHttpServer + ' + Me.gbHttpServer.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.gbHttpServer.Controls.Add(Me.lblUrl) + Me.gbHttpServer.Controls.Add(Me.nudCount) + Me.gbHttpServer.Controls.Add(Me.lblCount) + Me.gbHttpServer.Controls.Add(Me.lblServerName) + Me.gbHttpServer.Controls.Add(Me.txtServerName) + Me.gbHttpServer.Controls.Add(Me.txtRoot) + Me.gbHttpServer.Controls.Add(Me.lblRoot) + Me.gbHttpServer.Controls.Add(Me.nudPort) + Me.gbHttpServer.Controls.Add(Me.lblPort) + Me.gbHttpServer.Controls.Add(Me.lblLink) + Me.gbHttpServer.Controls.Add(Me.btnAction) + Me.gbHttpServer.Location = New System.Drawing.Point(8, 48) + Me.gbHttpServer.Name = "gbHttpServer" + Me.gbHttpServer.Size = New System.Drawing.Size(528, 144) + Me.gbHttpServer.TabIndex = 0 + Me.gbHttpServer.TabStop = False + Me.gbHttpServer.Text = "HttpServer" + ' + 'lblUrl + ' + Me.lblUrl.AutoSize = True + Me.lblUrl.Enabled = False + Me.lblUrl.Location = New System.Drawing.Point(99, 120) + Me.lblUrl.Name = "lblUrl" + Me.lblUrl.Size = New System.Drawing.Size(32, 13) + Me.lblUrl.TabIndex = 8 + Me.lblUrl.Text = "URL:" + ' + 'nudCount + ' + Me.nudCount.Location = New System.Drawing.Point(139, 88) + Me.nudCount.Name = "nudCount" + Me.nudCount.Size = New System.Drawing.Size(48, 20) + Me.nudCount.TabIndex = 7 + Me.nudCount.Value = New Decimal(New Integer() {5, 0, 0, 0}) + ' + 'lblCount + ' + Me.lblCount.AutoSize = True + Me.lblCount.Location = New System.Drawing.Point(16, 90) + Me.lblCount.Name = "lblCount" + Me.lblCount.Size = New System.Drawing.Size(115, 13) + Me.lblCount.TabIndex = 6 + Me.lblCount.Text = "Listener Thread Count:" + ' + 'lblServerName + ' + Me.lblServerName.AutoSize = True + Me.lblServerName.Location = New System.Drawing.Point(59, 67) + Me.lblServerName.Name = "lblServerName" + Me.lblServerName.Size = New System.Drawing.Size(72, 13) + Me.lblServerName.TabIndex = 4 + Me.lblServerName.Text = "&Server Name:" + ' + 'txtServerName + ' + Me.txtServerName.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.txtServerName.Location = New System.Drawing.Point(139, 64) + Me.txtServerName.Name = "txtServerName" + Me.txtServerName.Size = New System.Drawing.Size(376, 20) + Me.txtServerName.TabIndex = 5 + Me.txtServerName.Text = "TextBox3" + ' + 'txtRoot + ' + Me.txtRoot.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.txtRoot.Location = New System.Drawing.Point(139, 40) + Me.txtRoot.Name = "txtRoot" + Me.txtRoot.Size = New System.Drawing.Size(376, 20) + Me.txtRoot.TabIndex = 3 + Me.txtRoot.Text = "TextBox2" + ' + 'lblRoot + ' + Me.lblRoot.AutoSize = True + Me.lblRoot.Location = New System.Drawing.Point(76, 43) + Me.lblRoot.Name = "lblRoot" + Me.lblRoot.Size = New System.Drawing.Size(55, 13) + Me.lblRoot.TabIndex = 2 + Me.lblRoot.Text = "&RootPath:" + ' + 'MainForm + ' + Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) + Me.ClientSize = New System.Drawing.Size(534, 442) + Me.Controls.Add(Me.gbHttpServer) + Me.Controls.Add(Me.txtLog) + Me.Controls.Add(Me.Label2) + Me.Controls.Add(Me.pictureBox1) + Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog + Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon) + Me.MinimumSize = New System.Drawing.Size(550, 480) + Me.Name = "MainForm" + Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen + Me.Text = "Internet Pack Sample Server" + CType(Me.pictureBox1, System.ComponentModel.ISupportInitialize).EndInit() + CType(Me.nudPort, System.ComponentModel.ISupportInitialize).EndInit() + Me.gbHttpServer.ResumeLayout(False) + Me.gbHttpServer.PerformLayout() + CType(Me.nudCount, System.ComponentModel.ISupportInitialize).EndInit() + Me.ResumeLayout(False) + Me.PerformLayout() + + End Sub + +#End Region + + Private fEchoServer As EchoServer + Private WithEvents fHttpServer As SimpleHttpServer + + Private Sub btn_Action_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAction.Click + If btnAction.Text = "Activate Servers" Then + ActivateServers() + Else + DeactivateServers() + End If + End Sub + + Delegate Sub StatusEventHandler(ByVal Message As String) + + Private Sub ThreadSaveSetStatus(ByVal aStatus As String) + AddLog(String.Format("Request to {0}", aStatus)) + End Sub + + Public Sub OnHttpRequest(ByVal aSender As Object, ByVal ea As OnHttpRequestArgs) Handles fHttpServer.OnHttpRequest + Dim myDelegate As StatusEventHandler + myDelegate = AddressOf ThreadSaveSetStatus + Invoke(myDelegate, ea.Request.Header.RequestPath) + End Sub + + Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed + DeactivateServers() + End Sub + + Private Sub ActivateServers() + AddLog("Trying to activate servers...") + fEchoServer = New EchoServer + Try + fEchoServer.Open() + AddLog("EchoServer is active.") + Catch ex As Exception + AddLog("Can't activate EchoServer. An exception occured: " + ex.Message) + End Try + + fHttpServer = New SimpleHttpServer + fHttpServer.Port = nudPort.Value + fHttpServer.RootPath = txtRoot.Text + fHttpServer.ServerName = txtServerName.Text + If (Not Me.fHttpServer.BindingV4 Is Nothing) Then + Me.fHttpServer.BindingV4.ListenerThreadCount = Me.nudCount.Value + End If + fHttpServer.Open() + AddLog(String.Format("SimpleHttpServer is active on {0} port.", fHttpServer.Port)) + SetEnable(False) + AddLog("Servers activated.") + btnAction.Text = "Deactivate Servers" + End Sub + + Private Sub SetEnable(ByVal mode As Boolean) + lblPort.Enabled = mode + nudPort.Enabled = mode + lblServerName.Enabled = mode + txtServerName.Enabled = mode + lblRoot.Enabled = mode + txtRoot.Enabled = mode + lblUrl.Enabled = Not mode + lblLink.Enabled = Not mode + End Sub + + Private Sub DeactivateServers() + AddLog("Trying to deactivate servers...") + If Not fEchoServer Is Nothing Then + fEchoServer.Close() + End If + AddLog("EchoServer is closed.") + If Not fHttpServer Is Nothing Then + fHttpServer.Close() + End If + AddLog("HttpServer is closed.") + SetEnable(True) + AddLog("Servers is deactivated") + btnAction.Text = "Activate Servers" + End Sub + + Private Sub lbl_Link_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblLink.Click + If (File.Exists(fHttpServer.RootPath + "\index.html")) Then + System.Diagnostics.Process.Start(lblLink.Text) + Else + MessageBox.Show(fHttpServer.RootPath + "\index.html can not be opened, because it does not exists.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning) + End If + End Sub + + Private Sub nudPort_ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles nudPort.ValueChanged + lblLink.Text = String.Format("http://localhost:{0}/index.html", nudPort.Value) + End Sub + + Private Sub AddLog(ByVal line As String) + txtLog.AppendText(Environment.NewLine & String.Format("{0}: {1}", DateTime.Now.ToLongTimeString(), line)) + End Sub + + Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load + txtRoot.Text = Path.Combine(Path.GetDirectoryName(Me.GetType().Assembly.Location), "HttpRoot") + txtServerName.Text = "Internet Pack HTTP Server" + nudPort.Value = 82 + nudCount.Value = 5 + End Sub +End Class \ No newline at end of file diff --git a/Samples/VB/Sample Server/SampleServer.2008.sln b/Samples/VB/Sample Server/SampleServer.2008.sln new file mode 100644 index 0000000..3d6d0cd --- /dev/null +++ b/Samples/VB/Sample Server/SampleServer.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SampleServer", "SampleServer.2008.vbproj", "{794F3617-0013-4A3F-912F-9B47029170C3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {794F3617-0013-4A3F-912F-9B47029170C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {794F3617-0013-4A3F-912F-9B47029170C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {794F3617-0013-4A3F-912F-9B47029170C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {794F3617-0013-4A3F-912F-9B47029170C3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/Sample Server/SampleServer.2008.vbproj b/Samples/VB/Sample Server/SampleServer.2008.vbproj new file mode 100644 index 0000000..185b00d --- /dev/null +++ b/Samples/VB/Sample Server/SampleServer.2008.vbproj @@ -0,0 +1,127 @@ + + + Local + 9.0.30729 + 2.0 + {794F3617-0013-4A3F-912F-9B47029170C3} + Debug + AnyCPU + App.ico + + + SampleServer + + + None + JScript + Grid + IE50 + false + WinExe + Binary + On + Off + SampleServer + SampleServer.MainForm + + + WindowsFormsWithCustomSubMain + + + 2.0 + + + bin\ + SampleServer.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + + + bin\ + SampleServer.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + + + + + + + + + + Always + + + Always + + + Always + + + Code + + + Form + + + MainForm.vb + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/Sample Server/SampleServer.2010.sln b/Samples/VB/Sample Server/SampleServer.2010.sln new file mode 100644 index 0000000..ae36942 --- /dev/null +++ b/Samples/VB/Sample Server/SampleServer.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SampleServer", "SampleServer.2010.vbproj", "{794F3617-0013-4A3F-912F-9B47029170C3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {794F3617-0013-4A3F-912F-9B47029170C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {794F3617-0013-4A3F-912F-9B47029170C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {794F3617-0013-4A3F-912F-9B47029170C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {794F3617-0013-4A3F-912F-9B47029170C3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/Sample Server/SampleServer.2010.vbproj b/Samples/VB/Sample Server/SampleServer.2010.vbproj new file mode 100644 index 0000000..fda4dd8 --- /dev/null +++ b/Samples/VB/Sample Server/SampleServer.2010.vbproj @@ -0,0 +1,133 @@ + + + Local + 10.0.20506 + 2.0 + {794F3617-0013-4A3F-912F-9B47029170C3} + Debug + AnyCPU + App.ico + + + SampleServer + + + None + JScript + Grid + IE50 + false + WinExe + Binary + On + Off + SampleServer + SampleServer.MainForm + + + WindowsFormsWithCustomSubMain + + + 3.5 + v2.0 + + + bin\ + SampleServer.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + AllRules.ruleset + + + bin\ + SampleServer.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + AllRules.ruleset + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + + + + + + + + + Always + + + Always + + + Always + + + + Code + + + Form + + + MainForm.vb + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/Sample Server/SampleServer.Sample.html b/Samples/VB/Sample Server/SampleServer.Sample.html new file mode 100644 index 0000000..2323841 --- /dev/null +++ b/Samples/VB/Sample Server/SampleServer.Sample.html @@ -0,0 +1,44 @@ + + + + + + + + + + +

+ Sample Server +

+ + +

Purpose

+

+This example shows how easy it is to create a simple custom Http server. The + +SimpleHttpServer class is a file-based HTTP Server that will make all files in (and below) the specified RootPath folder available via HTTP. + + +

Examine the Code

+
    +
  • look at the ActivateServers procedure to see how to create and configure the HTTP server. The +RootPath property should reference the folder in our file system which we want to expose. +
  • +
  • +See the OnHttpRequest handler. There we can catch and process any incoming requests that come to the server. +
  • +
+ +

Getting started

+ +
    +
  • Compile the project.
  • +
  • Run application.
  • +
  • Configure server properties and activate the servers.
  • +
  • Click link on the main window to open home page of our server in web browser.
  • +
+ + + + \ No newline at end of file diff --git a/Samples/VB/Virtual FTP/App.ico b/Samples/VB/Virtual FTP/App.ico new file mode 100644 index 0000000..4680a4e Binary files /dev/null and b/Samples/VB/Virtual FTP/App.ico differ diff --git a/Samples/VB/Virtual FTP/AssemblyInfo.vb b/Samples/VB/Virtual FTP/AssemblyInfo.vb new file mode 100644 index 0000000..b12d8c1 --- /dev/null +++ b/Samples/VB/Virtual FTP/AssemblyInfo.vb @@ -0,0 +1,32 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' 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: + + diff --git a/Samples/VB/Virtual FTP/MainForm.resx b/Samples/VB/Virtual FTP/MainForm.resx new file mode 100644 index 0000000..a726801 --- /dev/null +++ b/Samples/VB/Virtual FTP/MainForm.resx @@ -0,0 +1,582 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + Qk1GEgAAAAAAADYEAAAoAAAAeAAAAB4AAAABAAgAAAAAAAAAAADCHgAAwh4AAAABAAAAAQAAAAAA/wEB + Af8CAgL/AwMD/wQEBP8FBQX/BgYG/wcHB/8ICAj/CQkJ/woKCv8LCwv/DAwM/w0NDf8ODg7/Dw8P/xAQ + EP8RERH/EhIS/xMTE/8UFBT/FRUV/xYWFv8XFxf/GBgY/xkZGf8aGhr/Gxsb/xwcHP8dHR3/Hh4e/x8f + H/8gICD/ISEh/yIiIv8jIyP/JCQk/yUlJf8mJib/Jycn/ygoKP8pKSn/Kioq/ysrK/8sLCz/LS0t/y4u + Lv8vLy//MDAw/zExMf8yMjL/MzMz/zQ0NP81NTX/NjY2/zc3N/84ODj/OTk5/zo6Ov87Ozv/PDw8/z09 + Pf8+Pj7/Pz8//0BAQP9BQUH/QkJC/0NDQ/9ERET/RUVF/0ZGRv9HR0f/SEhI/0lJSf9KSkr/S0tL/0xM + TP9NTU3/Tk5O/09PT/9QUFD/UVFR/1JSUv9TU1P/VFRU/1VVVf9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb + W/9cXFz/XV1d/15eXv9fX1//YGBg/2FhYf9iYmL/Y2Nj/2RkZP9lZWX/ZmZm/2dnZ/9oaGj/aWlp/2pq + av9ra2v/bGxs/21tbf9ubm7/b29v/3BwcP9xcXH/cnJy/3Nzc/90dHT/dXV1/3Z2dv93d3f/eHh4/3l5 + ef96enr/e3t7/3x8fP99fX3/fn5+/39/f/+AgID/gYGB/4KCgv+Dg4P/hISE/4WFhf+Ghob/h4eH/4iI + iP+JiYn/ioqK/4uLi/+MjIz/jY2N/46Ojv+Pj4//kJCQ/5GRkf+SkpL/k5OT/5SUlP+VlZX/lpaW/5eX + l/+YmJj/mZmZ/5qamv+bm5v/nJyc/52dnf+enp7/n5+f/6CgoP+hoaH/oqKi/6Ojo/+kpKT/paWl/6am + pv+np6f/qKio/6mpqf+qqqr/q6ur/6ysrP+tra3/rq6u/6+vr/+wsLD/sbGx/7Kysv+zs7P/tLS0/7W1 + tf+2trb/t7e3/7i4uP+5ubn/urq6/7u7u/+8vLz/vb29/76+vv+/v7//wMDA/8HBwf/CwsL/w8PD/8TE + xP/FxcX/xsbG/8fHx//IyMj/ycnJ/8rKyv/Ly8v/zMzM/83Nzf/Ozs7/z8/P/9DQ0P/R0dH/0tLS/9PT + 0//U1NT/1dXV/9bW1v/X19f/2NjY/9nZ2f/a2tr/29vb/9zc3P/d3d3/3t7e/9/f3//g4OD/4eHh/+Li + 4v/j4+P/5OTk/+Xl5f/m5ub/5+fn/+jo6P/p6en/6urq/+vr6//s7Oz/7e3t/+7u7v/v7+//8PDw//Hx + 8f/y8vL/8/Pz//T09P/19fX/9vb2//f39//4+Pj/+fn5//r6+v/7+/v//Pz8//39/f/+/v7//////wAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu + LCspKCYkKDI5Pz07OTg2LCIWDw4MCwkIBhOr9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AAAw + LiwrKTlJU1FPTUpIRkRCQT88MB4ODAsJCAYEfff39/f39/f399Du9+Xb9/f32+X39/fO5vf3993L6vf3 + 5dv39/fb5ff399Du9/f37cvb9/f398/b9/f39+7Q9/f39/f3983q99D39/f3zcv39+Xb9/f3zff3AAAx + MC4+VFhWVVNRT01KSEZEQkE/PDozHQwLCQgGBF/39/f39/f39yDC94tW9/f3Vov391QmcffiPihBOYb3 + i1b39/dWi/f39yDC9/dfHkFETff3jx1E9/f398Ig9/f39/f2RDI5gyD397osMkE/94tW9/duN/f3AAAz + N1RfXVtYVlVTUU9NSkhGREJBPzw6OCwRCwkIBgRu9/f39/f39wC493g49/f3OHj3yQSw0fdKRLzGwNT3 + eDj39/c4ePf39wC494csqsbFw/f3FX3F9/f397gA9/f39/ePJsTAegD36B9nwca/93g697MKmvf3AABA + YWNhX11bWFZVU1FPTUpIRkRCQT88OjgzFgsJCAYEnPf39/f39wC693w+9/f3Pnz3twD399kA4ff39/f3 + fD739/c+fPf39wC69yGd9/f39/f3AL739/f397oA9/f39/ebDvf3vQD3owrj9/f393w29ydk8ff3AABp + ZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4NhsLCQgGE8r39/f39wC693xA9/f3QHz3ugD397cAR0BASVj3 + fEH39/c+fvf39wC69wA1QUBCW833ALv39/f397oA3t739/flRT5mWwD3dzr39/f393wUTzjf9/f3AABr + aWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6ODYWCwkIBkH39/f39wC693w49/f3OnT3ugD399IAeXp6WAD3 + fC339/c+e/f39wCz9w1Xfnl+AMT3ALr39/f397oAW1tVi/f30YN8XwD3jSP39/f393woG2j39/f3AABv + a2lmZGNhX11bWFZVU1FPTUpIRkRCQT88OjgzEQsJCAad9/f39wC693wTxPfoCp/3ywD39/Qqm/f3gUb3 + fBOp6fc+N+P3ugDY92pg9/fFEer3AMv39/f397oAjo5nK6H37Pf3dhv31Qmq9/f293xCsTSv9/f3AAB0 + b2tpZmRjYV9dW1hWVVNRT01KSEZEQkE/PDo4LgwLCQgk9/f39wC693IxaIFYJ+XYWwB5mPezNndvM7P3 + cj1JT/cvTXd5N03399hAank9ffd5AFt59/f397oA9/f3gBv3f4F8MYj394FBd3mF93w+93ghqff3AAB4 + dG9raWZkY2FfXVtYVlVTUU9NSkhGREJBPzw6OB8MCwkInvf39wC696q1nExSt/fJLgBAbvf3pU1Cmff3 + qrOiR/eC1oBAZdj39/fKXEB96fdAAC9A9/f397oA9/f3xAD3i0xAgej39/eWTUBx93w+9/dohff3AAB9 + eHRva2lmZGNhX11bWFZVU1FPTUpIRkJAQT88OjUQDAsJJvf39wC59/f39/f39/f33Bn39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3JNH39/f397gA6+vZZyz39/f39/f39/f39/f393w+9/f39/f3AACB + fXh0b2tpZmRjYV9dW1hWVVNRT01KSEYmNUE/PDohDgwLCcv39wi89/f39/f39/f38Zr39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f3tNb3m9n398ENjo5uH6b39/f39/f39/f39/f393g49/f39/f3AACF + gX14dG9raWZkY2FfXVtYVlVTUU9NSkg1GSU9Pzw1Dw4MC4H392XT9/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f3ui669+JzU1N0uff39/f39/f39/f39/f394tW9/f39/f3AACK + hYF9eHRva2lmZGNhX11bWFZVU1FPTUpIIBkaMz88GQ8ODEb39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f396pN9/f39/f39/f39/f39/f39/f39/f39+Xb9/f39/f3AACO + ioWBfXh0b2tpZmRjYV9dW1hWVVNRT01KNRsZFyA5JREPDgz39/f39/f39/f39/f3APf39w/o6F0+TYv3 + fHz39z669/cA9/eqLj4ufPf3umxNLk3o97o+9+hdPk2L9+hdLj6q96oufLo+Pmz39/f39/f39/f3AACS + aYWFgX14dG9raWZkY2FfXVtYVlVTUU9NRx4bGRcYHxMRDw7M9/f39/f39/f39/f3APf3yS73XYv39/f3 + fHz39z669/cA97ou2ffoXXz3ug/o94td97o+912L9/f3911s9/f39z6q9+j39y7J9/f39/f39/f3AABW + Yo6KhYFpSkhGREJAPz07OTg2NDMxMC4sKiAcGxkXFhQTEQ+99/f39/f39/f39/f3APf3fGz3D7q6urr3 + fHz39z669/cA902b9/f32Q/3uj739/cP97o+9w+6urq69w/39/f39z669/fofA/o9/f39/f39/f3AABJ + h5KOioV1RTw7OURCQD89Ozk4NzUzMTAuLB8eHBsZFxYUExGS9/f39/f39/f39/f3AHxsLtn3H3x8fAD3 + fHz39z669/cA9wD39/f39z66uj739/cP97o+9x98fHwA9x/o9/f39z669+guXdn39/f39/f39/f3AABg + m5eSjoqFgW1TOzxNYmZkY2FfXVtYVlVTRCEfHhwlPxcWFBOF9/f39/f39/f39/f3ALq6TZv3XYv3uj73 + fD736B9897of9wD39/f39z66ugDZ94tN97o+912L97o+92xs9/f39z6698ku9/f39/f39/f39/f3AACD + oJuXko6KhYF9dFtAOEJTZGNhX11bWFZVNSMhITdIRhkXFhSF9/f39/f39/f39/f3APf36A/36F0uPsn3 + fHxNH2ybPi669y669/f36A/ouj58Lk3Z97o+9+hdLj7J9+hsPj6qbA8ubPdsPk339/f39/f39/f3AACR + joqGg398d3RwbWllVD42N0hcYV9dW1hRJiQuSU1KSBsZFxaG9/f39/f39/f39/f3APf32Q/39/f39/f3 + 9/f39/f39/f396pN9/f3i133uj739/f39/f39/f39/f39/f39/f39z669/f39/f39/f39/f39/f3AABP + TkxLSUdGRUNBQD48Ozk4NjQzY2FfXVtAKkBTUU9NRBwbGRe/9/f39/f39/f39/f3AHx8H4v39/f39/f3 + 9/f39/f39/f39/d8H3w+Puj3uj739/f399kf9/f39/f39/f39/f399no9/f39/f39/f39/f39/f3AACZ + lZKOioeDf3x4dHFtamViXlxdZGNhX108UlZVU1FPQR4cGxnA9/f39/f39/f39/f32bq69/f39/f39/f3 + 9/f39/f39/f39/f36LrZ9/f3yWz39/f39/fo9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACQ + sa2opKCbl5KOioWBfXh0b2tpZmRjYVtdW1hWVVNRNx8eHBv39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABt + tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVtYVlVTMSEfHkX39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AABW + oLWxraikoJuXko6KhYF9eHRva2lmZGNhX11bWFZRJCMhH3D39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AACH + fbq1sa2opKCbl5KOioWBfXh0b2tpZmRjYV9dW1hCJiQjIbT39/f39/f39/f39/f39/f39/f39/f39/f3 + 9/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f39/f3AADH + qLe6tbGtqKSgm5eSjoqFgX14dG9raWZkY2FfXVsvKCYkMPf39/f39/f39/f39/f39/f39/f39/f39/f3 + 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 + CQAPDw8AGxsLABQUFAAYGBgAHR0dAC4uDAA8PAQANjYJADQ0DgA9PQsAKioTACIiGwArKysAMTEiADQ0 + NAA7OzEAODg4AD4+PgBAQAMAREQCAEREDgBNTQoASUkMAFNTBABZWQAARUUSAEFBFABWVhAAYWEAAGlp + AABtbQIAZmYJAG1tCgBycgAAe3sAAHV1CwB9fQ0AZGQUAHh4GwBYWCEAX18nAFlZKgBFRTwAc3MqAHt7 + KwB7ezUASEhIAExMTABUVEwAX19XAFtbWwBeXl4Ae3tMAGppUgByclMAenpUAGNjYwBlZWUAaWlpAH5+ + YgBxcXEAdXV1AH5+fgCDgwAAi4sAAJSUAACenggAi4sRAKmpGAC0tCoAuLg7AMC/OwCDg0AAi4pKAIGB + XACSkl8AhYVqAJubYACVlHUAo6JrAK6tcQCoqHwAy8pLAM7NXADW1VwA2dhsANvaewDh4W4A7ex/AIqK + igCSkpIAlZWVAJycnACioqIAqqqqAMXEhgDk44gA7u2MAPj3jwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg4OJ82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLv9OThv/d3cF/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2Vl + AP9iYgD/X18A/11dAP9aWgD/V1cA/1RUAP9JSQH/JiYI/wwMDP8LCwv/CQkJ/wgICP8GBgb/BAQE/wMD + A58AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7jzk5 + Of84ODj/NjY2/zQ0NP8zMzP/PT0r/3R0C/+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3Fx + AP9ubgD/a2sA/2hoAP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/z4+A/8UFAv/CwsL/wkJ + Cf8ICAj/BgYG/wQEBP8DAwOPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA+Pj5fPDw8/zs7O/85OTn/ODg4/zY2Nv9MTCf/iooD/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FR + AP9KSgD/HBwJ/wsLC/8JCQn/CAgI/wYGBv8EBAT/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEFBQS9AQEDvPj4+/zw8PP87Ozv/OTk5/1xcI/+WlgD/k5MA/5CQAP+OjgD/i4sA/4iI + AP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11d + AP9aWgD/V1cA/1RUAP9RUQD/Tk4A/yQkB/8LCwv/CQkJ/wgICP8GBgb/BAQE7wQEBC8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAENDQ79BQUH/QEBA/z4+Pv88PDz/VFQv/52dBv+ZmQH/lpYA/5OT + AP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2ho + AP9lZQD/YmIA/19fAP9dXQD/WloA/1dXAP9UVAD/UVEA/05OAP8cHAn/CwsL/wkJCf8ICAj/BgYG/wQE + BL8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkZGX0VFRf9DQ0P/QUFB/0BAQP9LSzn/np4U/6Gh + DP+dnQb/mZkB/5aWAP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0 + AP9xcQD/bm4A/2trAP9oaAD/ZWUA/2JiAP9fXwD/XV0A/1paAP9XVwD/VFQA/1FRAP9KSgD/FBQL/wsL + C/8JCQn/CAgI/wYGBv8FBQVfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAR0dH30ZGRv9FRUX/Q0ND/0FB + Qf+YmCP/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP9iYgD/X18A/11dAP9aWgD/V1cA/1RU + AP9RUQD/QkIC/wwMDP8LCwv/CQkJ/wgICP8GBgbfAAAAAAAAAAAAAAAAAAAAAAAAAABKSkpfSUlJ/0dH + R/9GRkb/RUVF/3R0N/+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9lZQD/YmIA/19f + AP9dXQD/WloA/1dXAP9UVAD/UVEA/yoqB/8MDAz/CwsL/wkJCf8ICAj/BwcHXwAAAAAAAAAAAAAAAAAA + AABMTEzfS0tL/0lJSf9HR0f/TU1F/7CwMP+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2tr + AP9oaAD/ZWUA/15eAf9bWwH/XV0A/1paAP9XVwD/VFQA/01NAP8SEg3/DAwM/wsLC/8JCQn/CAgI3wAA + AAAAAAAAAAAAAE9PTy9OTk7/TExM/0tLS/9JSUn/fHtB/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3 + AP90dAD/cXEA/25uAP9rawD/aGgA/2VlAP8wMBH/SUkH/11dAP9aWgD/V1cA/1RUAP8sLAj/Dg4O/wwM + DP8LCwv/CQkJ/wgICC8AAAAAAAAAAFFRUX9PT0//Tk5O/0xMTP9LS0v/tLNB/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP9/fwD/fHwA/3p6AP93dwD/dHQA/3FxAP9ubgD/a2sA/2hoAP9JSQn/GRkZ/y4uEP9YWAH/WloA/1dX + AP9MTAL/Dw8P/w4ODv8MDAz/CwsL/woKCn8AAAAAAAAAAFNTU79RUVH/T09P/05OTv9kZEz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP96egD/d3cA/3R0AP9xcQD/bm4A/2trAP9oaAD/JCQX/xkZ + Gf8cHBb/RkYH/1paAP9XVwD/Hh4O/w8PD/8ODg7/DAwM/wsLC78AAAAAAAAAAFRUVP9TU1P/UVFR/09P + T/+Ghk//y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP98fAD/enoA/3d3AP90dAD/cXEA/25u + AP9rawD/R0cM/xsbG/8ZGRn/FxcX/ycnEP9RUQL/MTEK/xEREf8PDw//Dg4O/wwMDP8AAAAAV1dXL1ZW + Vv9UVFT/U1NT/1FRUf+pqVT/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/fn45/6ysK/+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6 + AP93dwD/dHQA/3FxAP9ubgD/ZmYB/yEhGv8bGxv/GRkZ/xcXF/8aGhT/JiYP/xMTE/8RERH/Dw8P/w4O + Dv8NDQ0vWVlZP1hYWP9WVlb/VFRU/1NTU/+9vVr/09JX/8/OUf/Lykz/x8dG/6ysQv9eXkX/cnI//7i4 + L/+0tCn/sLAj/62sHf+HhyP/VlYx/1RUL/9SUiz/UFAq/05OKP9MTCf/Skom/0hIJf9GRiT/REQi/0JC + If9AQCD/Pj4f/z09Hv87Oxz/OTkb/zc3Gv81NRn/MzMY/yMjHP8cHBz/Gxsb/xkZGf8XFxf/FhYW/xQU + FP8TExP/ERER/w8PD/8ODg4/Wlpab1lZWf9YWFj/VlZW/1RUVP/a2WL/1tZc/9PSV//PzlH/i4tM/0tL + S/9JSUn/qag8/7y7NP+4uC//tLQp/7CwI/+YmCT/S0s5/zw8PP87Ozv/OTk5/1BQKv9OTij/TEwn/0pK + Jv9ISCX/RkYj/0REIv9DQyH/QUEg/z8/Hv89PR7/Ozsc/zk5G/83Nxr/NTUY/x8fH/8eHh7/HBwc/xsb + G/8ZGRn/FxcX/xYWFv8UFBT/ExMT/xEREf8QEBBvXFxcf1tbW/9ZWVn/WFhY/1ZWVv/e3Wj/2tli/729 + Wv9oaFH/Tk5O/0xMTP9qakn/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/46OIf9kZC3/Ozs7/z8/ + Nv9iYh//iooG/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/X18I/yEh + If8fHx//Hh4e/xwcHP8tLRT/WVkD/xcXF/8WFhb/FBQU/xMTE/8SEhJ/Xl5ef1xcXP9bW1v/WVlZ/1hY + WP/i4W7/mZle/1NTU/9RUVH/T09P/05OTv+bm0z/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62s + Hf+pqRf/np4V/3R0If9GRjP/ODg4/05OKP9vbxT/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8 + AP96egD/Q0MW/yMjI/8hISH/JCQd/0lJDf9oaAD/ZWUA/xkZGf8XFxf/FhYW/xQUFP8TExN/X19ff15e + Xv9cXFz/W1tb/1lZWf9paVv/VlZW/1RUVP9TU1P/UVFR/1hXUP+urlD/q6tL/6enR/+kpEL/oaE9/56e + OP+bmzT/l5cv/5SUKv+RkSX/jo4h/4qKHP+Hhxf/a2sg/0REMf82Njb/Ojox/1tbHP+Cggb/i4sA/4iI + AP+FhQD/goIA/39/AP9ycgX/JiYm/yQkJP83Nxr/Z2cE/25uAP9rawD/aGgA/xsbG/8ZGRn/FxcX/xYW + Fv8VFRV/YWFhP2BgYP9eXl7/XFxc/1tbW/9ZWVn/WFhY/1ZWVv9UVFT/U1NT/1FRUf9PT0//Tk5O/0xM + TP9LS0v/SUlJ/0dHR/9GRkb/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7/zk5Of84ODj/NjY2/zQ0 + NP8zMzP/jo4A/4uLAP+IiAD/hYUA/4KCAP9UVBT/LS0l/1VVEP93dwD/dHQA/3FxAP9ubgD/YWED/xwc + HP8bGxv/GRkZ/xcXF/8WFhY/YmJiP2FhYf9gYGD/Xl5e/1xcXP9tbV//WVlZ/1hYWP9WVlb/VFRU/1NT + U/+1tVr/srFV/6+uUf+rq0z/qKhH/6WkQ/+ioT7/np45/5ubNf+YmDD/lZQr/5KRJv+OjiH/i4sd/4eH + GP+EhBP/gYEP/35+Df+BgQn/kJAA/46OAP+LiwD/iIgA/4WFAP9LSxr/dHQF/3x8AP96egD/d3cA/3R0 + AP9xcQD/WloH/x4eHv8cHBz/Gxsb/xkZGf8YGBg/AAAAAGNjY/9hYWH/YGBg/15eXv+mpnD/v790/2Ji + W/9YWFj/VlZW/1RUVP+np1z/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7Cw + I/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4KCAv+FhQD/goIA/39/ + AP98fAD/enoA/3d3AP90dAD/SUkQ/x8fH/8eHh7/HBwc/xsbG/8AAAAAAAAAAGRkZM9jY2P/YWFh/2Bg + YP+Dg2n/8fCF/+Tjff+GhmT/WFhY/1ZWVv92dln/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/ + Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uL + AP+IiAD/hYUA/4KCAP9/fwD/fHwA/3p6AP93dwD/PDwY/yEhIf8fHx//Hh4e/xwcHM8AAAAAAAAAAGZm + Zp9kZGT/Y2Nj/2FhYf9gYGD/6+qI//Hwhf/t7H//xsVy/2pqXP9WVlb/u7tj/9rZYv/W1lz/09JX/8/O + Uf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aW + AP+TkwD/kJAA/46OAP+LiwD/iIgA/4WFAP+CggD/f38A/3x8AP90dAL/JCQk/yMjI/8hISH/Hx8f/x4e + Hp8AAAAAAAAAAGdnZ09mZmb/ZGRk/2NjY/9hYWH/tbV6//X0i//x8IX/7ex//+HgeP+WlmT/iopf/97d + aP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6Wl + Ev+hoQz/nZ0G/5mZAf+WlgD/k5MA/5CQAP+OjgD/i4sA/4iIAP+FhQD/goIA/39/AP9XVxH/JiYm/yQk + JP8jIyP/ISEh/yAgIE8AAAAAAAAAAAAAAABnZ2fvZmZm/2RkZP9jY2P/dXRo//n4kP/19Iv/8fCF/+3s + f//p6Xn/wsJt/9nYbP/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0 + Kf+wsCP/rawd/6mpF/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/jo4A/4uLAP+IiAD/hYUA/4KC + AP80NCT/KCgo/yYmJv8kJCT/IyMj7wAAAAAAAAAAAAAAAAAAAABpaWl/aGho/2ZmZv9kZGT/Y2Nj/8LB + gv/5+JD/9fSL//Hwhf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PD + QP+/vzr/vLs0/7i4L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP+TkwD/kJAA/46O + AP+LiwD/iIgA/2RkEP8rKyv/KSkp/ygoKP8mJib/JSUlfwAAAAAAAAAAAAAAAAAAAABqamofaWlp/2ho + aP9mZmb/ZGRk/2xsZv/z8pL/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PS + V//PzlH/y8pM/8fHRv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/5mZ + Af+WlgD/k5MA/5CQAP+OjgD/hYUD/zQ0K/8sLCz/Kysr/ykpKf8oKCj/JiYmHwAAAAAAAAAAAAAAAAAA + AAAAAAAAampqf2lpaf9oaGj/ZmZm/2RkZP+Tk3T//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lh + bv/e3Wj/2tli/9bWXP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mp + F/+lpRL/oaEM/52dBv+ZmQH/lpYA/5OTAP+QkAD/Tk4i/zAwMP8uLi7/LCws/ysrK/8pKSmPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2pqau9paWn/aGho/2ZmZv9kZGT/sbB+//z7lv/5+JD/9fSL//Hw + hf/t7H//6el5/+bldP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4 + L/+0tCn/sLAj/62sHf+pqRf/paUS/6GhDP+dnQb/mZkB/5aWAP9kZBr/MzMz/zExMf8wMDD/Li4u/yws + LO8rKysPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtra09qamr/aWlp/2hoaP9mZmb/ZGRk/8TE + hf/8+5b/+fiQ//X0i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fH + Rv/Dw0D/v786/7y7NP+4uC//tLQp/7CwI/+trB3/qakX/6WlEv+hoQz/nZ0G/3V1Ff82Njb/NDQ0/zMz + M/8xMTH/MDAw/y4uLk8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2uPampq/2lp + af9oaGj/ZmZm/2RkZP+6uoH//PuW//n4kP/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bW + XP/T0lf/z85R/8vKTP/Hx0b/w8NA/7+/Ov+8uzT/uLgv/7S0Kf+wsCP/rawd/6mpF/+lpRL/dHQg/zk5 + Of84ODj/NjY2/zQ0NP8zMzP/MTExjwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trz2pqav9paWn/aGho/2ZmZv9kZGT/k5N0//Pykv/5+JD/9fSL//Hwhf/t7H//6el5/+bl + dP/i4W7/3t1o/9rZYv/W1lz/09JX/8/OUf/Lykz/x8dG/8PDQP+/vzr/vLs0/7i4L/+0tCn/sLAj/6al + H/9fXzL/PDw8/zs7O/85OTn/ODg4/zY2Nv80NDTPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trD2tra89qamr/aWlp/2hoaP9mZmb/ZGRk/3Z2af/CwYL/+fiQ//X0 + i//x8IX/7ex//+npef/m5XT/4uFu/97daP/a2WL/1tZc/9PSV//PzlH/y8pM/8fHRv/Dw0D/v786/7y7 + NP+4uC//iYky/09PPf9AQED/Pj4+/zw8PP87Ozv/OTk5/zg4OM82NjYPAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGtraw9ra2vPampq/2lpaf9oaGj/ZmZm/2Rk + ZP9jY2P/iIhu/7++fv/19Iv/8fCF/+3sf//p6Xn/5uV0/+Lhbv/e3Wj/2tli/9bWXP/T0lf/z85R/8vK + TP/Hx0b/w8NA/5KSP/9jY0H/RUVF/0NDQ/9BQUH/QEBA/z4+Pv88PDz/Ozs7zzo6Og8AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pq + av9paWn/aGho/2ZmZv9kZGT/Y2Nj/2FhYf9paWP/lpZu/7m4df/R0Xj/6el5/+bldP/i4W7/3t1o/9rZ + Yv/W1lz/urlV/56eUP97e0z/UlJK/0lJSf9HR0f/RkZG/0VFRf9DQ0P/QUFB/0BAQP8+Pj6fAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGtra09qamrvaWlp/2hoaP9mZmb/ZGRk/2NjY/9hYWH/YGBg/15eXv9cXFz/W1tb/1lZ + Wf97e17/eXlc/1RUVP9TU1P/UVFR/09PT/9OTk7/TExM/0tLS/9JSUn/R0dH/0ZGRv9FRUX/Q0ND70FB + QU8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABra2sPampqn2lpaf9oaGj/ZmZm/2RkZP9jY2P/YWFh/2Bg + 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 + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0NDAv8YGAn/CQkJ/wcH + B/8FBQX/AwMDXwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQK8+Pj7/Ozs7/zk5Of9zcxT/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/0tL + AP8YGAn/CQkJ/wcHB/8FBQX/BAQELwAAAAAAAAAAAAAAAAAAAABFRUVPQkJC/0BAQP8+Pj7/fHsf/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xc + AP9YWAD/U1MA/0tLAP8YGAn/CQkJ/wcHB/8FBQXPAAAAAAAAAAAAAAAAAAAAAEdHR99FRUX/QkJC/3Bw + Mf+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lp + AP9kZAD/YGAA/1xcAP9YWAD/U1MA/0dHAf8QEAv/CQkJ/wcHB/8GBgZfAAAAAAAAAABLS0tfSUlJ/0dH + R/9MTEP/rKwp/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1 + AP9xcQD/bW0A/2lpAP9kZAD/YGAA/1xcAP9YWAD/U1MA/y8vBv8MDAz/CQkJ/wcHB98AAAAAAAAAAE5O + Ts9MTEz/SUlJ/4KCP/+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP9bWwP/TU0G/1xcAP9YWAD/T08A/xISDf8MDAz/CQkJ/wgI + CD9TU1MPUVFR/05OTv9MTEz/vLxB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/2lpAP8tLRP/NzcN/1dXAf9YWAD/Li4J/w4O + Dv8MDAz/CgoKj1VVVU9TU1P/UVFR/25uTv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/1FRCP8aGhr/HBwW/0VF + B/9HRwT/ERER/w4ODv8MDAzPV1dXf1VVVf9TU1P/kpJV/8/OUv/JyUn/xMNB/7a2Of+UlDb/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/3V1AP9xcQD/bW0A/yYm + Gf8aGhr/GBgY/ysrD/8TExP/ERER/w4ODv9aWlqPWFhY/1VVVf+fnlv/1dRa/8/OUv+qqkr/WVlI/5iY + PP+5uDD/s7Mn/62tH/9LSzn/Ozs7/zk5Of9PTyn/TEwn/0lJJf9GRiP/REQi/0FBIP8+Ph7/Ozsc/zg4 + Gv81NRj/Hx8f/x0dHf8aGhr/GBgY/xUVFf8TExP/ERER/1xcXL9aWlr/WFhY/728Zf/S0WL/iopV/05O + Tv9bW0v/xMNB/76+OP+5uDD/s7Mn/62tH/+HhyP/XFwu/1hYKP95eRH/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/enoA/2trBP8hISH/Hx8f/yYmGf9SUgb/KioR/xUVFf8TExP/Xl5ev1xcXP9aWlr/np5l/2dn + WP9TU1P/UVFR/5aWUP/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/lpUU/2trIP9JSS3/XV0d/4OD + Bv+LiwD/hoYA/4KCAP9+fgD/UFAT/yQkJP9ERBL/aGgB/2lpAP8oKBX/GBgY/xUVFf9hYWF/X19f/1xc + XP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dHR/9FRUX/QkJC/0BAQP8+Pj7/Ozs7/zk5 + Of83Nzf/NDQ0/4ODBv+LiwD/hoYA/4KCAP84OCH/ZWUJ/3V1AP9xcQD/bW0A/x0dHf8aGhr/GBgY/2Nj + Y39hYWH/X19f/4uKaf+Ih2X/WFhY/1VVVf+Wllr/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/gYEC/3x8Av9+fgD/enoA/3V1AP9nZwT/Hx8f/x0d + Hf8aGhr/ZWVlP2RkZP9hYWH/e3tn//Hwhf++vnP/YWFa/2ZmWP/a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KCAP9+fgD/enoA/1dX + Df8hISH/Hx8f/x0dHb8AAAAAZmZm/2RkZP9hYWH/4+KH//Hwhf/i4Xv/hIRh/6yrYv/a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+PAP+LiwD/hoYA/4KC + AP9+fgD/QEAa/yQkJP8hISH/Hx8fbwAAAABoaGifZmZm/2RkZP+bmnT/9vaN//Hwhf/r6nz/y8pv/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+XlwD/k5MA/4+P + AP+LiwD/hoYA/3d3Bf8oKCj/JiYm/yQkJP8hISEfAAAAAGpqaj9oaGj/ZmZm/2RkZP/p6I//9vaN//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/oqIO/5yc + Bf+XlwD/k5MA/4+PAP+LiwD/T08c/ysrK/8oKCj/JiYmvwAAAAAAAAAAAAAAAGpqar9oaGj/ZmZm/4GB + bv/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/62t + H/+opxb/oqIO/5ycBf+XlwD/k5MA/3JyD/8vLy//LS0t/ysrK/8pKSk/AAAAAAAAAAAAAAAAa2trL2pq + av9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/76+ + OP+5uDD/s7Mn/62tH/+opxb/oqIO/5ycBf+FhQr/Ojox/zIyMv8vLy//LS0trwAAAAAAAAAAAAAAAAAA + AAAAAAAAa2trb2pqav9oaGj/ZmZm/56eeP/8+5X/9vaN//Hwhf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/O + Uv/JyUn/xMNB/76+OP+5uDD/s7Mn/62tH/+opxb/j48W/z8/Nv83Nzf/NDQ0/zIyMt8wMDAPAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/4uKcf/p6I//9vaN//Hwhf/r6nz/5uV0/+Df + a//a2mP/1dRa/8/OUv/JyUn/xMNB/76+OP+5uDD/s7Mn/319Lf9ERDv/Ozs7/zk5Of83NzfvNDQ0LwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trn2pqav9oaGj/ZmZm/21tZ/+urnr/4+KH//Hw + hf/r6nz/5uV0/+Dfa//a2mP/1dRa/8/OUv/JyUn/xMNB/5iYPP9aWkH/QkJC/0BAQP8+Pj7/Ozs77zk5 + OS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trf2pqav9oaGj/ZmZm/2Rk + ZP9hYWH/e3tn/6amcP+9vXH/wsFs/728Zf+4uF7/m5pV/35+T/9UVEz/SUlJ/0dHR/9FRUX/QkJC/0BA + QM8+Pj4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trL2pq + ar9oaGj/ZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05OTv9MTEz/SUlJ/0dH + R+9ERER/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGpqak9oaGivZmZm/2RkZP9hYWH/X19f/1xcXP9aWlr/WFhY/1VVVf9TU1P/UVFR/05O + Tt9LS0t/SkpKDwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZ2cPZWVlT2NjY39hYWG/Xl5ev1xcXL9aWlq/V1dXn1VV + VW9SUlIvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAP//wAA//4A + AD/4AAAf8AAAD+AAAAfgAAADwAAAA8AAAAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIAAAACAAAAAgAAAAcAAAAHAAAAD4AAAA/AAAAf4AAAP/AAAH/4AAH//gAD//+AH/ygA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJycnPyIi + Iq8dHR3vGBgY/xQUFP8PDw//CwsLvwcHB18AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1NTUPMDAwrysr + K/8xMSL/RUUS/0REDv89PQv/NjYJ/xsbC/8KCgr/BgYGzwMDAy8AAAAAAAAAAAAAAAA/Pz8POTk5zzU1 + Nf9kZBT/g4MA/3t7AP9ycgD/aWkA/2FhAP9ZWQD/PDwE/w4OCf8GBgbvAwMDLwAAAAAAAAAAQ0NDjz4+ + Pv94eBv/lJQA/4uLAP+DgwD/e3sA/3JyAP9paQD/YWEA/1lZAP9ERAL/Dg4J/wYGBs8AAAAATExMH0hI + SP97ezb/qakY/56eCP+UlAD/i4sA/4ODAP97ewD/cnIA/2lpAP9hYQD/WVkA/0BAA/8KCgr/BwcHX1BQ + UI9MTEz/uLg7/7S0Kv+pqRj/np4I/5SUAP+LiwD/g4MA/3t7AP9ycgD/aWkA/1NTBP9ZWQD/GxsL/woK + Cs9VVVW/cnJT/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uLAP+DgwD/e3sA/3JyAP9JSQz/KioT/y4u + DP8PDw//W1tb75KSX//OzVz/e3tM/8C/O/+0tCr/e3sr/19fJ/99fQ3/dXUL/21tCv9mZgn/QUEU/yIi + G/80NA7/FBQU/19fX99+fmL/X19X/2ppUv+Likr/g4NA/3t7Nf9zcyr/WVkq/1hYIf+LiwD/g4MA/1ZW + EP9tbQL/TU0K/xgYGP9kZGS/hYVq/66tcf+BgVz/1tVc/8vKS//Avzv/tLQq/6mpGP+engj/lJQA/4uL + AP+DgwD/e3sA/0VFEv8dHR3/Z2dnb2RkZP/k44j/29p7/9nYbP/W1Vz/y8pL/8C/O/+0tCr/qakY/56e + CP+UlAD/i4sA/4ODAP8xMSL/IiIir2trax9paWn/lZR1//j3j//t7H//4eFu/9bVXP/Lykv/wL87/7S0 + Kv+pqRj/np4I/5SUAP9kZBT/Kysr/ygoKF8AAAAAa2trf2lpaf/FxIb/+PeP/+3sf//h4W7/1tVc/8vK + S//Avzv/tLQq/6mpGP+LixH/Ozsx/zAwML8AAAAAAAAAAAAAAABra2ufaWlp/6iofP/u7Yz/7ex//+Hh + bv/W1Vz/y8pL/8C/O/97ezb/RUU8/zk5Oc81NTUPAAAAAAAAAAAAAAAAAAAAAGtra39paWn/ZGRk/4WF + av+jomv/m5tg/3p6VP9UVEz/SEhI/0NDQ68/Pz8PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa2trH2ho + aI9kZGS/X19f/1tbW/9WVlbPUVFRn0xMTD8AAAAAAAAAAAAAAAAAAAAA8A8AAMADAACAAQAAgAEAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAEAAMABAADgAwAA8A8AAA== + + + \ No newline at end of file diff --git a/Samples/VB/Virtual FTP/MainForm.vb b/Samples/VB/Virtual FTP/MainForm.vb new file mode 100644 index 0000000..d26b36e --- /dev/null +++ b/Samples/VB/Virtual FTP/MainForm.vb @@ -0,0 +1,219 @@ +Imports RemObjects.InternetPack.Ftp.VirtualFtp +Imports System.IO + +Public Class MainForm + Inherits System.Windows.Forms.Form + + Private fRootFolder As VirtualFolder + Private fUserManager As IFtpUserManager + Private fFtpServer As VirtualFtpServer + + Const port As Integer = 4444 + +#Region " Windows Form Designer generated code " + + Public Sub New() + MyBase.New() + + Application.EnableVisualStyles() + + 'This call is required by the Windows Form Designer. + InitializeComponent() + + 'Add any initialization after the InitializeComponent() call + + End Sub + + 'Form overrides dispose to clean up the component list. + Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing Then + If Not (components Is Nothing) Then + components.Dispose() + End If + End If + MyBase.Dispose(disposing) + End Sub + + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + Friend WithEvents label1 As System.Windows.Forms.Label + Friend WithEvents pictureBox1 As System.Windows.Forms.PictureBox + Friend WithEvents txtLog As System.Windows.Forms.TextBox + Friend WithEvents GroupBox1 As System.Windows.Forms.GroupBox + Friend WithEvents btnStartStop As System.Windows.Forms.Button + Friend WithEvents llShortcut As System.Windows.Forms.LinkLabel + Private Sub InitializeComponent() + Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainForm)) + Me.label1 = New System.Windows.Forms.Label + Me.llShortcut = New System.Windows.Forms.LinkLabel + Me.pictureBox1 = New System.Windows.Forms.PictureBox + Me.txtLog = New System.Windows.Forms.TextBox + Me.GroupBox1 = New System.Windows.Forms.GroupBox + Me.btnStartStop = New System.Windows.Forms.Button + CType(Me.pictureBox1, System.ComponentModel.ISupportInitialize).BeginInit() + Me.GroupBox1.SuspendLayout() + Me.SuspendLayout() + ' + 'label1 + ' + Me.label1.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.label1.Location = New System.Drawing.Point(3, 39) + Me.label1.Name = "label1" + Me.label1.Size = New System.Drawing.Size(309, 24) + Me.label1.TabIndex = 5 + Me.label1.Text = "In order to login on ftp please use login: test; password: test." + Me.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter + ' + 'llShortcut + ' + Me.llShortcut.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.llShortcut.Location = New System.Drawing.Point(4, 63) + Me.llShortcut.Name = "llShortcut" + Me.llShortcut.Size = New System.Drawing.Size(300, 33) + Me.llShortcut.TabIndex = 4 + Me.llShortcut.TextAlign = System.Drawing.ContentAlignment.MiddleCenter + ' + 'pictureBox1 + ' + Me.pictureBox1.Image = CType(resources.GetObject("pictureBox1.Image"), System.Drawing.Image) + Me.pictureBox1.Location = New System.Drawing.Point(8, 8) + Me.pictureBox1.Name = "pictureBox1" + Me.pictureBox1.Size = New System.Drawing.Size(120, 30) + Me.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize + Me.pictureBox1.TabIndex = 3 + Me.pictureBox1.TabStop = False + ' + 'txtLog + ' + Me.txtLog.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _ + Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.txtLog.Location = New System.Drawing.Point(8, 16) + Me.txtLog.Multiline = True + Me.txtLog.Name = "txtLog" + Me.txtLog.ScrollBars = System.Windows.Forms.ScrollBars.Both + Me.txtLog.Size = New System.Drawing.Size(282, 108) + Me.txtLog.TabIndex = 6 + Me.txtLog.WordWrap = False + ' + 'GroupBox1 + ' + Me.GroupBox1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _ + Or System.Windows.Forms.AnchorStyles.Left) _ + Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.GroupBox1.Controls.Add(Me.txtLog) + Me.GroupBox1.Location = New System.Drawing.Point(8, 104) + Me.GroupBox1.Name = "GroupBox1" + Me.GroupBox1.Size = New System.Drawing.Size(298, 132) + Me.GroupBox1.TabIndex = 7 + Me.GroupBox1.TabStop = False + Me.GroupBox1.Text = "Log" + ' + 'btnStartStop + ' + Me.btnStartStop.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) + Me.btnStartStop.Location = New System.Drawing.Point(231, 9) + Me.btnStartStop.Name = "btnStartStop" + Me.btnStartStop.Size = New System.Drawing.Size(75, 23) + Me.btnStartStop.TabIndex = 8 + Me.btnStartStop.Text = "Start" + ' + 'MainForm + ' + Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) + Me.ClientSize = New System.Drawing.Size(314, 242) + Me.Controls.Add(Me.btnStartStop) + Me.Controls.Add(Me.GroupBox1) + Me.Controls.Add(Me.label1) + Me.Controls.Add(Me.llShortcut) + Me.Controls.Add(Me.pictureBox1) + Me.ForeColor = System.Drawing.SystemColors.ControlText + Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon) + Me.MinimumSize = New System.Drawing.Size(330, 280) + Me.Name = "MainForm" + Me.Text = "Virtual FTP" + CType(Me.pictureBox1, System.ComponentModel.ISupportInitialize).EndInit() + Me.GroupBox1.ResumeLayout(False) + Me.GroupBox1.PerformLayout() + Me.ResumeLayout(False) + Me.PerformLayout() + + End Sub + +#End Region + + Private Sub linkLabel1_LinkClicked(ByVal sender As System.Object, ByVal e As System.Windows.Forms.LinkLabelLinkClickedEventArgs) Handles llShortcut.LinkClicked + If llShortcut.Text <> "" Then + System.Diagnostics.Process.Start(llShortcut.Text) + End If + End Sub + + Public Sub StartServer(ByVal aPort As String) + Dim lDiskFolder As String + lDiskFolder = Path.GetDirectoryName(GetType(MainForm).Assembly.Location) & "..\..\..\" + + fRootFolder = New VirtualFolder(Nothing, "[ROOT]") + fRootFolder.Add(New VirtualFolder(Nothing, "virtual")) + fRootFolder.Add(New DiscFolder(Nothing, "drive-c", "c:\")) + fRootFolder.Add(New DiscFolder(Nothing, "disc", lDiskFolder)) + fRootFolder.Add(New EmptyFile(Nothing, "=== Welcome to the FTP ===")) + + fUserManager = New UserManager + CType(fUserManager, UserManager).AddUser("test", "test") + + fFtpServer = New VirtualFtpServer + fFtpServer.Port = aPort + fFtpServer.Timeout = 60 * 1000 '/* 1 minute */ + If Not IsNothing(fFtpServer.BindingV4) Then + fFtpServer.BindingV4.ListenerThreadCount = 10 + Else + fFtpServer.BindingV6.ListenerThreadCount = 10 + End If + fFtpServer.RootFolder = fRootFolder + fFtpServer.UserManager = fUserManager + fFtpServer.ServerName = "VirtualFTP Sample - powered by RemObjects Internet Pack for .NET" + + fFtpServer.Open() + + Debug.Write("VirtualFTP 0.3 BETA - started up") + End Sub + + Public Sub StopServer() + If Not IsNothing(fFtpServer) Then + fFtpServer.Close() + End If + End Sub + + Private Sub btnStartStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStartStop.Click + If btnStartStop.Text = "Start" Then + addToLog("Starting Virtual FTP on " & port.ToString() & " port...") + StartServer(port) + llShortcut.Text = String.Format("ftp://localhost:{0}/", port) + addToLog("Virtual FTP is running under " & Environment.OSVersion.ToString()) + btnStartStop.Text = "Stop" + Else + addToLog("Shutting down Virtual FTP ...") + StopServer() + llShortcut.Text = "" + addToLog("Virtual FTP is stopped.") + btnStartStop.Text = "Start" + End If + End Sub + + Private Sub addToLog(ByVal line As String) + txtLog.Text = txtLog.Text & _ + System.DateTime.Now.ToLongTimeString() & _ + ": " & _ + line & Environment.NewLine + End Sub + + Private Sub Form1_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed + StopServer() + End Sub +End Class diff --git a/Samples/VB/Virtual FTP/VirtualFTP.2008.sln b/Samples/VB/Virtual FTP/VirtualFTP.2008.sln new file mode 100644 index 0000000..7f5743f --- /dev/null +++ b/Samples/VB/Virtual FTP/VirtualFTP.2008.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "VirtualFTP", "VirtualFTP.2008.vbproj", "{9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/Virtual FTP/VirtualFTP.2008.vbproj b/Samples/VB/Virtual FTP/VirtualFTP.2008.vbproj new file mode 100644 index 0000000..3143d3e --- /dev/null +++ b/Samples/VB/Virtual FTP/VirtualFTP.2008.vbproj @@ -0,0 +1,120 @@ + + + Local + 8.0.50727 + 2.0 + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B} + Debug + AnyCPU + App.ico + + + VirtualFTP + + + None + JScript + Grid + IE50 + false + WinExe + Binary + On + Off + VirtualFTP + VirtualFTP.MainForm + + + WindowsFormsWithCustomSubMain + + + 2.0 + + + bin\ + VirtualFTP.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + + + bin\ + VirtualFTP.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + + + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + + + + + + + + + + Code + + + Form + + + MainForm.vb + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/Virtual FTP/VirtualFTP.2010.sln b/Samples/VB/Virtual FTP/VirtualFTP.2010.sln new file mode 100644 index 0000000..be1b584 --- /dev/null +++ b/Samples/VB/Virtual FTP/VirtualFTP.2010.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2008 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "VirtualFTP", "VirtualFTP.2010.vbproj", "{9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Samples/VB/Virtual FTP/VirtualFTP.2010.vbproj b/Samples/VB/Virtual FTP/VirtualFTP.2010.vbproj new file mode 100644 index 0000000..35b4a78 --- /dev/null +++ b/Samples/VB/Virtual FTP/VirtualFTP.2010.vbproj @@ -0,0 +1,126 @@ + + + Local + 10.0.20506 + 2.0 + {9FE33D2A-FD55-4CC7-AE8D-D941CAD6AD9B} + Debug + AnyCPU + App.ico + + + VirtualFTP + + + None + JScript + Grid + IE50 + false + WinExe + Binary + On + Off + VirtualFTP + VirtualFTP.MainForm + + + WindowsFormsWithCustomSubMain + + + 3.5 + v2.0 + + + bin\ + VirtualFTP.xml + 285212672 + + + + + true + true + true + false + false + false + false + 1 + 42016,42017,42018,42019,42032 + full + AllRules.ruleset + + + bin\ + VirtualFTP.xml + 285212672 + + + + + false + true + false + true + false + false + false + 1 + 42016,42017,42018,42019,42032 + none + AllRules.ruleset + + + + + + + + System + + + System.Data + + + System.Drawing + + + System.Windows.Forms + + + System.XML + + + + + + + + + + + + + + + Code + + + Form + + + MainForm.vb + + + + + + + + + + + + + \ No newline at end of file diff --git a/Samples/VB/Virtual FTP/VirtualFTP.Sample.html b/Samples/VB/Virtual FTP/VirtualFTP.Sample.html new file mode 100644 index 0000000..17a3adf --- /dev/null +++ b/Samples/VB/Virtual FTP/VirtualFTP.Sample.html @@ -0,0 +1,46 @@ + + + + + + + + + +
+

+ Virtual FTP Sample +

+ +
+

Purpose

+

+This sample shows how to create and configure virtual FTP. This supports building + a virtual folder structure, similar to IIS, based on real and non-existing folders.

+

+Note: +
+To connect to Virtual FTP, please use port 4444. (ftp://localhost:4444/) +
UserName=test +
Password=test +

+ +

Examine the Code

+
    +
  • Look at the StartServer function. There you can find how you can configure your ftp. +You can add a VirtualFolder or a DiskFolder. +
  • +
  • Also there you can see how to add the users allowed to  use this ftp.
  • +
+ +

Getting started

+
    +
  • Build the application.
  • +
  • Run the application and start the virtual FTP
  • +
  • With the help of the link on the form, open virtual FTP in your WebBrowser
  • +
  • Test virtual ftp.
  • +
+ + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack.2003.sln b/Source/RemObjects.InternetPack.2003.sln new file mode 100644 index 0000000..f79b1e9 --- /dev/null +++ b/Source/RemObjects.InternetPack.2003.sln @@ -0,0 +1,29 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.2003", "RemObjects.InternetPack\RemObjects.InternetPack.2003.csproj", "{7C02E26A-2522-4C8F-8CAC-4E65854A09DE}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.VirtualFTP.2003", "RemObjects.InternetPack.VirtualFTP\RemObjects.InternetPack.VirtualFTP.2003.csproj", "{E8E89EC1-4445-4853-9E0B-A18B044069B5}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {7C02E26A-2522-4C8F-8CAC-4E65854A09DE}.Debug.ActiveCfg = Debug|.NET + {7C02E26A-2522-4C8F-8CAC-4E65854A09DE}.Debug.Build.0 = Debug|.NET + {7C02E26A-2522-4C8F-8CAC-4E65854A09DE}.Release.ActiveCfg = Release|.NET + {7C02E26A-2522-4C8F-8CAC-4E65854A09DE}.Release.Build.0 = Release|.NET + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Debug.ActiveCfg = Debug|.NET + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Debug.Build.0 = Debug|.NET + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Release.ActiveCfg = Release|.NET + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Release.Build.0 = Release|.NET + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.2005.sln b/Source/RemObjects.InternetPack.2005.sln new file mode 100644 index 0000000..68c65e6 --- /dev/null +++ b/Source/RemObjects.InternetPack.2005.sln @@ -0,0 +1,25 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.2005", "RemObjects.InternetPack\RemObjects.InternetPack.2005.csproj", "{0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.VirtualFTP.2005", "RemObjects.InternetPack.VirtualFTP\RemObjects.InternetPack.VirtualFTP.2005.csproj", "{E8E89EC1-4445-4853-9E0B-A18B044069B5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Release|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.2008.sln b/Source/RemObjects.InternetPack.2008.sln new file mode 100644 index 0000000..95e7b0a --- /dev/null +++ b/Source/RemObjects.InternetPack.2008.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.2008", "RemObjects.InternetPack\RemObjects.InternetPack.2008.csproj", "{0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.VirtualFTP.2008", "RemObjects.InternetPack.VirtualFTP\RemObjects.InternetPack.VirtualFTP.2008.csproj", "{E8E89EC1-4445-4853-9E0B-A18B044069B5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Licensed|Any CPU = Licensed|Any CPU + Mono TRIAL|Any CPU = Mono TRIAL|Any CPU + Mono|Any CPU = Mono|Any CPU + Release|Any CPU = Release|Any CPU + TRIAL|Any CPU = TRIAL|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Licensed|Any CPU.ActiveCfg = Licensed|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Licensed|Any CPU.Build.0 = Licensed|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono TRIAL|Any CPU.ActiveCfg = Mono TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono TRIAL|Any CPU.Build.0 = Mono TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono|Any CPU.ActiveCfg = Mono|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono|Any CPU.Build.0 = Mono|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Release|Any CPU.Build.0 = Release|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.TRIAL|Any CPU.ActiveCfg = TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.TRIAL|Any CPU.Build.0 = TRIAL|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Licensed|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Licensed|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Mono TRIAL|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Mono TRIAL|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Mono|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Mono|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Release|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.TRIAL|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.TRIAL|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.2010.sln b/Source/RemObjects.InternetPack.2010.sln new file mode 100644 index 0000000..b32b641 --- /dev/null +++ b/Source/RemObjects.InternetPack.2010.sln @@ -0,0 +1,45 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.2010", "RemObjects.InternetPack\RemObjects.InternetPack.2010.csproj", "{0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.VirtualFTP.2010", "RemObjects.InternetPack.VirtualFTP\RemObjects.InternetPack.VirtualFTP.2010.csproj", "{E8E89EC1-4445-4853-9E0B-A18B044069B5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Licensed|Any CPU = Licensed|Any CPU + Mono TRIAL|Any CPU = Mono TRIAL|Any CPU + Mono|Any CPU = Mono|Any CPU + Release|Any CPU = Release|Any CPU + TRIAL|Any CPU = TRIAL|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Licensed|Any CPU.ActiveCfg = Licensed|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Licensed|Any CPU.Build.0 = Licensed|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono TRIAL|Any CPU.ActiveCfg = Mono TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono TRIAL|Any CPU.Build.0 = Mono TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono|Any CPU.ActiveCfg = Mono|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono|Any CPU.Build.0 = Mono|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Release|Any CPU.Build.0 = Release|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.TRIAL|Any CPU.ActiveCfg = TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.TRIAL|Any CPU.Build.0 = TRIAL|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Licensed|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Licensed|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Mono TRIAL|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Mono TRIAL|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Mono|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Mono|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.Release|Any CPU.Build.0 = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.TRIAL|Any CPU.ActiveCfg = Release|Any CPU + {E8E89EC1-4445-4853-9E0B-A18B044069B5}.TRIAL|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.CF.2003.sln b/Source/RemObjects.InternetPack.CF.2003.sln new file mode 100644 index 0000000..44c5b61 --- /dev/null +++ b/Source/RemObjects.InternetPack.CF.2003.sln @@ -0,0 +1,23 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{20D4826A-C6FA-45DB-90F4-C717570B9F32}") = "RemObjects.InternetPack.CF.2003", "RemObjects.InternetPack\RemObjects.InternetPack.CF.2003.csdproj", "{67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Debug.ActiveCfg = Debug|Pocket PC + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Debug.Build.0 = Debug|Pocket PC + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Debug.Deploy.0 = Debug|Pocket PC + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Release.ActiveCfg = Release|Pocket PC + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Release.Build.0 = Release|Pocket PC + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Release.Deploy.0 = Release|Pocket PC + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.CF.2005.sln b/Source/RemObjects.InternetPack.CF.2005.sln new file mode 100644 index 0000000..00d60e3 --- /dev/null +++ b/Source/RemObjects.InternetPack.CF.2005.sln @@ -0,0 +1,23 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.CF.2005", "RemObjects.InternetPack\RemObjects.InternetPack.CF.2005.csproj", "{67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.CF.2008.sln b/Source/RemObjects.InternetPack.CF.2008.sln new file mode 100644 index 0000000..279ceb5 --- /dev/null +++ b/Source/RemObjects.InternetPack.CF.2008.sln @@ -0,0 +1,23 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.CF.2008", "RemObjects.InternetPack\RemObjects.InternetPack.CF.2008.csproj", "{67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}" + ProjectSection(WebsiteProperties) = preProject + Debug.AspNetCompiler.Debug = "True" + Release.AspNetCompiler.Debug = "False" + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.MonoAndroid.2010.sln b/Source/RemObjects.InternetPack.MonoAndroid.2010.sln new file mode 100644 index 0000000..718a62f --- /dev/null +++ b/Source/RemObjects.InternetPack.MonoAndroid.2010.sln @@ -0,0 +1,31 @@ +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.MonoAndroid.2010", "RemObjects.InternetPack\RemObjects.InternetPack.MonoAndroid.2010.csproj", "{0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Licensed|Any CPU = Licensed|Any CPU + Mono TRIAL|Any CPU = Mono TRIAL|Any CPU + Mono|Any CPU = Mono|Any CPU + Release|Any CPU = Release|Any CPU + TRIAL|Any CPU = TRIAL|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Licensed|Any CPU.ActiveCfg = Licensed|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Licensed|Any CPU.Build.0 = Licensed|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono TRIAL|Any CPU.ActiveCfg = Mono TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono TRIAL|Any CPU.Build.0 = Mono TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono|Any CPU.ActiveCfg = Mono|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Mono|Any CPU.Build.0 = Mono|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.Release|Any CPU.Build.0 = Release|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.TRIAL|Any CPU.ActiveCfg = TRIAL|Any CPU + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E}.TRIAL|Any CPU.Build.0 = TRIAL|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.Monotouch.sln b/Source/RemObjects.InternetPack.Monotouch.sln new file mode 100644 index 0000000..41468d8 --- /dev/null +++ b/Source/RemObjects.InternetPack.Monotouch.sln @@ -0,0 +1,37 @@ +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemObjects.InternetPack.MonoTouch", "RemObjects.InternetPack\RemObjects.InternetPack.MonoTouch.csproj", "{4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|iPhoneSimulator = Debug|iPhoneSimulator + Licensed|iPhoneSimulator = Licensed|iPhoneSimulator + Release|iPhoneSimulator = Release|iPhoneSimulator + TRIAL|iPhoneSimulator = TRIAL|iPhoneSimulator + Debug|iPhone = Debug|iPhone + Licensed|iPhone = Licensed|iPhone + Release|iPhone = Release|iPhone + TRIAL|iPhone = TRIAL|iPhone + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Licensed|iPhoneSimulator.ActiveCfg = Licensed|iPhoneSimulator + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Licensed|iPhoneSimulator.Build.0 = Licensed|iPhoneSimulator + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.TRIAL|iPhoneSimulator.ActiveCfg = TRIAL|iPhoneSimulator + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.TRIAL|iPhoneSimulator.Build.0 = TRIAL|iPhoneSimulator + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Debug|iPhone.ActiveCfg = Debug|iPhone + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Debug|iPhone.Build.0 = Debug|iPhone + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Licensed|iPhone.ActiveCfg = Licensed|iPhone + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Licensed|iPhone.Build.0 = Licensed|iPhone + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Release|iPhone.ActiveCfg = Release|iPhone + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.Release|iPhone.Build.0 = Release|iPhone + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.TRIAL|iPhone.ActiveCfg = TRIAL|iPhone + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A}.TRIAL|iPhone.Build.0 = TRIAL|iPhone + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Source/RemObjects.InternetPack.SecureBlackbox/AssemblyInfo.cs b/Source/RemObjects.InternetPack.SecureBlackbox/AssemblyInfo.cs new file mode 100644 index 0000000..2865731 --- /dev/null +++ b/Source/RemObjects.InternetPack.SecureBlackbox/AssemblyInfo.cs @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + + (c)opyright RemObjects Software, Inc. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("RemObjects Internet Pack for .NET - SecureBlackbox Library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RemObjects Software")] +[assembly: AssemblyProduct("RemObjects Internet Pack for .NET")] +[assembly: AssemblyCopyright("Copyright RemObjects Software 2003-2012. All Rights Reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("6.0.0.0")] +[assembly: CLSCompliant(false)] +#if REMOBJECTS_SIGN_ASSEMBLY +[assembly: AssemblyKeyName("RemObjects")] +[assembly: System.Security.AllowPartiallyTrustedCallers()] +#endif \ No newline at end of file diff --git a/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2003.csproj b/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2003.csproj new file mode 100644 index 0000000..1864aa5 --- /dev/null +++ b/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2003.csproj @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2005.csproj b/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2005.csproj new file mode 100644 index 0000000..1864aa5 --- /dev/null +++ b/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2005.csproj @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2008.csproj b/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2008.csproj new file mode 100644 index 0000000..1864aa5 --- /dev/null +++ b/Source/RemObjects.InternetPack.SecureBlackbox/RemObjects.InternetPack.SecureBlackbox.2008.csproj @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/RemObjects.InternetPack.SecureBlackbox/SSLConnection.cs b/Source/RemObjects.InternetPack.SecureBlackbox/SSLConnection.cs new file mode 100644 index 0000000..6144734 --- /dev/null +++ b/Source/RemObjects.InternetPack.SecureBlackbox/SSLConnection.cs @@ -0,0 +1,369 @@ +using System; +using System.Net; +using System.Net.Sockets; +using RemObjects.InternetPack.Core; +using SecureBlackbox; +using SecureBlackbox.SSLSocket; +using SecureBlackbox.SSLSocket.Client; +using SecureBlackbox.SSLSocket.Server; + +namespace RemObjects.InternetPack.SecureBlackbox +{ + /// + /// SSLConnection is a base class for SSL-enabled connections + /// + public abstract class SSLConnection : RemObjects.InternetPack.Core.Connection + { + protected ElSSLSocket fDataSocket = null; + + public SSLConnection() : base((System.Net.Sockets.Socket)null) + { + } + + public SSLConnection(System.Net.Sockets.Socket aSocket) + : base((Socket)aSocket) + { + } + + public SSLConnection(RemObjects.InternetPack.Core.Binding aBinding) + :base(aBinding) + { + } + + protected System.Net.Sockets.Socket Socket + { + get + { + return base.DataSocket; + } + } + + #region ConnectionOverrides + + protected new ElSSLSocket DataSocket { get { return fDataSocket; } set { fDataSocket = value; } } + protected override int DataSocketAvailable + { + get { return DataSocket.Available; } + } + + protected override int DataSocketReceiveWhatsAvaiable(byte[] aBuffer, int aOffset, int aSize) + { + StartTimeoutTimer(); + try + { + byte[] tmp = new byte[aSize]; + int result = DataSocket.Receive(tmp); + Array.Copy(tmp, 0, aBuffer, aOffset, result); + return result; + } + finally + { + StopTimeoutTimer(); + } + } + + protected override int DataSocketSendAsMuchAsPossible(byte[] aBuffer, int aOffset, int aSize) + { + byte[] tmp = new byte[aSize]; + Array.Copy(aBuffer, aOffset, tmp, 0, aSize); + return DataSocket.Send(tmp); + } + #endregion + + #region Properties + public bool get_CipherSuites(short Index) + { + return DataSocket.get_CipherSuites(Index); + } + + public void set_CipherSuites(short Index, bool Value) + { + DataSocket.set_CipherSuites(Index, Value); + } + + public short CipherSuite + { + get + { + return DataSocket.CipherSuite; + } + set + { + DataSocket.CipherSuite = value; + } + } + + public short CurrentVersion + { + get + { + return DataSocket.CurrentVersion; + } + set + { + DataSocket.CurrentVersion = value; + } + } + + + public short Versions + { + get + { + return DataSocket.Versions; + } + set + { + DataSocket.Versions = value; + } + } + + public override bool DataSocketConnected + { + get + { + return DataSocket.Connected; + } + } + #endregion + } + + public class ClientSSLConnection : SSLConnection + { + public ClientSSLConnection( + System.Net.Sockets.Socket aSocket, + IPEndPoint remoteEndPoint, + IClientSSLConnectionSettings aSettings + ) + : base(aSocket) + { + fDataSocket = new ElClientSSLSocket(); + fDataSocket.Socket = aSocket; + InitializeSSLSocket(aSettings); + ((ElClientSSLSocket)fDataSocket).Connect(remoteEndPoint); + } + + public ClientSSLConnection( + RemObjects.InternetPack.Core.Binding aBinding, + IClientSSLConnectionSettings aSettings + ) + : base(new Socket(aBinding.AddressFamily, aBinding.SocketType, aBinding.Protocol)) + { + fDataSocket = new ElClientSSLSocket(); + fDataSocket.Socket = base.Socket; + InitializeSSLSocket(aSettings); + ((ElClientSSLSocket)fDataSocket).Connect(new IPEndPoint(aBinding.Address, aBinding.Port)); + } + + private void InitializeSSLSocket(IClientSSLConnectionSettings aSettings) + { + ((ElClientSSLSocket)fDataSocket).OnCertificateValidate += new SBClient.TSBValidateCertificateEvent(OnSecureClientCertificateValidate); + ((ElClientSSLSocket)fDataSocket).OnCertificateNeeded += new SBClient.TSBCertificateNeededEvent(OnSecureClientCertificateNeeded); + ((ElClientSSLSocket)fDataSocket).OnCertificateNeededEx += new SBClient.TSBCertificateNeededExEvent(OnSecureClientCertificateNeededEx); + ((ElClientSSLSocket)fDataSocket).OnCertificateChoose += new SBClient.TSBChooseCertificateEvent(OnSecureClientCertificateChoose); + + if (aSettings != null) + { + if (aSettings.get_OnCertificateValidate() != null) + this.OnCertificateValidate += aSettings.get_OnCertificateValidate(); + if (aSettings.get_OnCertificateNeeded() != null) + this.OnCertificateNeeded += aSettings.get_OnCertificateNeeded(); + if (aSettings.get_OnCertificateNeededEx() != null) + this.OnCertificateNeededEx += aSettings.get_OnCertificateNeededEx(); + if (aSettings.get_OnCertificateChoose() != null) + this.OnCertificateChoose += aSettings.get_OnCertificateChoose(); + + for (short i = SBConstants.Unit.SB_SUITE_FIRST; + i < SBConstants.Unit.SB_SUITE_LAST; i++) + set_CipherSuites(i, aSettings.get_CipherSuites(i)); + + this.CertStorage = aSettings.CertStorage; + this.Versions = aSettings.Versions; + } + } + + #region Socket operation overrides + protected override void DataSocketConnect(EndPoint aEndPoint) + { + ((ElClientSSLSocket) DataSocket).Connect(aEndPoint); } + + protected override void DataSocketClose() + { ((ElClientSSLSocket) DataSocket).Close(true); } + #endregion + + #region Properties and events + + public SBCustomCertStorage.TElCustomCertStorage CertStorage + { + get + { + return ((ElClientSSLSocket) DataSocket).CertStorage; + } + set + { + ((ElClientSSLSocket) DataSocket).CertStorage = value; + } + } + + public SBClient.TSBCloseReason CloseReason + { + get + { + return ((ElClientSSLSocket) DataSocket).CloseReason; + } + } + + public event SBClient.TSBValidateCertificateEvent OnCertificateValidate; + public event SBClient.TSBCertificateNeededEvent OnCertificateNeeded; + public event SBClient.TSBCertificateNeededExEvent OnCertificateNeededEx; + public event SBClient.TSBChooseCertificateEvent OnCertificateChoose; + + #endregion + + private void OnSecureClientCertificateValidate(Object sender, + SBX509.TElX509Certificate certificate, ref bool validate) + { + if (OnCertificateValidate != null) + OnCertificateValidate(this, certificate, ref validate); + } + + private void OnSecureClientCertificateNeeded(Object sender, + ref byte[] CertificateBuffer, ref int CertificateSize, + ref byte[] PrivateKeyBuffer, ref int PrivateKeySize, + SBClient.TClientCertificateType ClientCertificateType) + { + if (OnCertificateNeeded != null) + OnCertificateNeeded(this, ref CertificateBuffer, ref CertificateSize, + ref PrivateKeyBuffer, ref PrivateKeySize, ClientCertificateType); + } + + private void OnSecureClientCertificateNeededEx(Object sender, + ref SBX509.TElX509Certificate certificate) + { + if (OnCertificateNeededEx != null) + OnCertificateNeededEx(this, ref certificate); + } + + private void OnSecureClientCertificateChoose(object Sender, + SBX509.TElX509Certificate[] Certificates, ref int CertificateIndex) + { + if (OnCertificateChoose != null) + OnCertificateChoose(this, Certificates, ref CertificateIndex); + } + } + + + public class ServerSSLConnection : SSLConnection + { + public ServerSSLConnection(System.Net.Sockets.Socket aSocket, IServerSSLConnectionSettings aSettings) + { + fDataSocket = new ElServerSSLSocket(aSocket); + InitializeSSLSocket(aSettings); + ((ElServerSSLSocket)DataSocket).OpenSSLSession(); + } + + private void InitializeSSLSocket(IServerSSLConnectionSettings aSettings) + { + ((ElServerSSLSocket)fDataSocket).OnCertificateValidate += + new SBServer.TSBCertificateValidateEvent(OnSecureServerCertificateValidate); + + if (aSettings != null) + { + if (aSettings.get_OnCertificateValidate() != null) + this.OnCertificateValidate += aSettings.get_OnCertificateValidate(); + + for (short i = SBConstants.Unit.SB_SUITE_FIRST; + i < SBConstants.Unit.SB_SUITE_LAST; i++) + set_CipherSuites(i, aSettings.get_CipherSuites(i)); + + this.CertStorage = aSettings.CertStorage; + this.Versions = aSettings.Versions; + + this.ClientAuthentication = aSettings.ClientAuthentication; + this.ClientCertStorage = aSettings.ClientCertStorage; + this.ForceCertificateChain = aSettings.ForceCertificateChain; + this.SessionPool = aSettings.SessionPool; + } + } + + #region Socket operation overrides + protected override void DataSocketInitializeServerConnection() + { + } + + protected override void DataSocketClose() + { ((ElServerSSLSocket) DataSocket).Close(false); } + #endregion + + #region Properties and events + public bool ForceCertificateChain + { + get + { + return ((ElServerSSLSocket) DataSocket).ForceCertificateChain; + } + set + { + ((ElServerSSLSocket) DataSocket).ForceCertificateChain = value; + } + } + + public SBSessionPool.TElSessionPool SessionPool + { + get + { + return ((ElServerSSLSocket) DataSocket).SessionPool; + } + set + { + ((ElServerSSLSocket) DataSocket).SessionPool = value; + } + } + + public SBCustomCertStorage.TElMemoryCertStorage CertStorage + { + get + { + return ((ElServerSSLSocket) DataSocket).CertStorage; + } + set + { + ((ElServerSSLSocket) DataSocket).CertStorage = value; + } + } + + public SBCustomCertStorage.TElCustomCertStorage ClientCertStorage + { + get + { + return ((ElServerSSLSocket) DataSocket).ClientCertStorage; + } + set + { + ((ElServerSSLSocket) DataSocket).ClientCertStorage = value; + } + } + + public bool ClientAuthentication + { + get + { + return ((ElServerSSLSocket) DataSocket).ClientAuthentication; + } + set + { + ((ElServerSSLSocket) DataSocket).ClientAuthentication = value; + } + } + + public event SBServer.TSBCertificateValidateEvent OnCertificateValidate; + + #endregion + + private void OnSecureServerCertificateValidate(object Sender, SBX509.TElX509Certificate certificate, ref bool validate) + { + if (this.OnCertificateValidate != null) + this.OnCertificateValidate(this, certificate, ref validate); + } + } +} diff --git a/Source/RemObjects.InternetPack.SecureBlackbox/SSLConnectionFactory.cs b/Source/RemObjects.InternetPack.SecureBlackbox/SSLConnectionFactory.cs new file mode 100644 index 0000000..0a811e3 --- /dev/null +++ b/Source/RemObjects.InternetPack.SecureBlackbox/SSLConnectionFactory.cs @@ -0,0 +1,229 @@ +using System; +using System.Net; +using System.Net.Sockets; +using RemObjects.InternetPack.Core; +using SecureBlackbox; +using SecureBlackbox.SSLSocket.Server; +using SecureBlackbox.SSLSocket.Client; + +namespace RemObjects.InternetPack.SecureBlackbox +{ + public interface ISSLConnectionSettings + { + bool get_CipherSuites(short Index); + + + short Versions { get; } + } + + public interface IClientSSLConnectionSettings : ISSLConnectionSettings + { + SBCustomCertStorage.TElCustomCertStorage CertStorage { get; } + + SBClient.TSBValidateCertificateEvent get_OnCertificateValidate(); + SBClient.TSBCertificateNeededEvent get_OnCertificateNeeded(); + SBClient.TSBCertificateNeededExEvent get_OnCertificateNeededEx(); + SBClient.TSBChooseCertificateEvent get_OnCertificateChoose(); + } + + public interface IServerSSLConnectionSettings : ISSLConnectionSettings + { + bool ClientAuthentication { get; } + + SBCustomCertStorage.TElMemoryCertStorage CertStorage { get; } + + SBCustomCertStorage.TElCustomCertStorage ClientCertStorage { get; } + + bool ForceCertificateChain { get; } + + SBSessionPool.TElSessionPool SessionPool { get; } + + SBServer.TSBCertificateValidateEvent get_OnCertificateValidate(); + } + + /// + /// Summary description for SSLConnectionFactory. + /// + public abstract class SSLConnectionFactory : RemObjects.InternetPack.Core.IConnectionFactory, + ISSLConnectionSettings + { + private bool[] ciperSuites = new bool[SBConstants.Unit.SB_SUITE_LAST]; + private short versions = SBConstants.Unit.sbSSL2 | + SBConstants.Unit.sbSSL3 | + SBConstants.Unit.sbTLS1; + + #region IConnectionFactory Members + + public virtual Connection CreateServerConnection(System.Net.Sockets.Socket aSocket) + { + throw new InvalidOperationException(); + } + + public virtual Connection CreateClientConnection(RemObjects.InternetPack.Core.Binding aBinding) + { + throw new InvalidOperationException(); + } + + #endregion + + #region ISSLConnectionSettings Members + + public bool get_CipherSuites(short Index) + { + if (Index < SBConstants.Unit.SB_SUITE_FIRST) + throw new IndexOutOfRangeException(); + return ciperSuites[Index]; + } + + public void set_CipherSuites(short Index, bool Value) + { + if (Index < SBConstants.Unit.SB_SUITE_FIRST) + throw new IndexOutOfRangeException(); + ciperSuites[Index] = Value; + } + + public short Versions + { + get + { + return versions; + } + set + { + versions = value; + } + } + + #endregion + } + + public class SSLServerConnectionFactory : SSLConnectionFactory, IServerSSLConnectionSettings + { + private bool clientAuthentication = false; + private SBCustomCertStorage.TElMemoryCertStorage certStorage; + private SBCustomCertStorage.TElCustomCertStorage clientCertStorage; + private bool forceCertificateChain = false; + private SBSessionPool.TElSessionPool sessionPool; + + public event SBServer.TSBCertificateValidateEvent OnCertificateValidate; + + public SBServer.TSBCertificateValidateEvent get_OnCertificateValidate() + { + return OnCertificateValidate; + } + + public override Connection CreateServerConnection(System.Net.Sockets.Socket aSocket) + { + ServerSSLConnection conn = new ServerSSLConnection(aSocket, this); + return conn; + } + + public SBCustomCertStorage.TElMemoryCertStorage CertStorage + { + get + { + return certStorage; + } + set + { + certStorage = value; + } + } + + public bool ClientAuthentication + { + get + { + return clientAuthentication; + } + set + { + clientAuthentication = value; + } + } + + public SBCustomCertStorage.TElCustomCertStorage ClientCertStorage + { + get + { + return clientCertStorage; + } + set + { + clientCertStorage = value; + } + } + + public bool ForceCertificateChain + { + get + { + return forceCertificateChain; + } + set + { + forceCertificateChain = value; + } + } + + public SBSessionPool.TElSessionPool SessionPool + { + get + { + return sessionPool; + } + set + { + sessionPool = value; + } + } + } + + public class SSLClientConnectionFactory : SSLConnectionFactory, IClientSSLConnectionSettings + { + private SBCustomCertStorage.TElCustomCertStorage certStorage; + + public event SBClient.TSBValidateCertificateEvent OnCertificateValidate; + public event SBClient.TSBCertificateNeededEvent OnCertificateNeeded; + public event SBClient.TSBCertificateNeededExEvent OnCertificateNeededEx; + public event SBClient.TSBChooseCertificateEvent OnCertificateChoose; + + public override Connection CreateClientConnection(RemObjects.InternetPack.Core.Binding aBinding) + { + return new ClientSSLConnection(aBinding, this); + } + + public SBCustomCertStorage.TElCustomCertStorage CertStorage + { + get + { + return certStorage; + } + set + { + certStorage = value; + } + } + + public SBClient.TSBValidateCertificateEvent get_OnCertificateValidate() + { + return OnCertificateValidate; + } + + public SBClient.TSBCertificateNeededEvent get_OnCertificateNeeded() + { + return OnCertificateNeeded; + } + + public SBClient.TSBCertificateNeededExEvent get_OnCertificateNeededEx() + { + return OnCertificateNeededEx; + } + + public SBClient.TSBChooseCertificateEvent get_OnCertificateChoose() + { + return OnCertificateChoose; + } + + } +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/AssemblyInfo.cs b/Source/RemObjects.InternetPack.VirtualFTP/AssemblyInfo.cs new file mode 100644 index 0000000..26489e3 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/AssemblyInfo.cs @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("RemObjects Internet Pack for .NET - VirtualFtp Library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RemObjects Software, LLC")] +[assembly: AssemblyProduct("RemObjects Internet Pack for .NET")] +[assembly: AssemblyCopyright("Copyright RemObjects Software, LLC 2003-2012. All Rights Reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("2.0.0.0")] + +[assembly: CLSCompliant(true)] +#if REMOBJECTS_SIGN_ASSEMBLY +[assembly: AssemblyKeyName("RemObjectsSoftware")] +[assembly: System.Security.AllowPartiallyTrustedCallers()] +#endif \ No newline at end of file diff --git a/Source/RemObjects.InternetPack.VirtualFTP/DiscFile.cs b/Source/RemObjects.InternetPack.VirtualFTP/DiscFile.cs new file mode 100644 index 0000000..792997a --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/DiscFile.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using RemObjects.InternetPack; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + /// + /// Summary description for DiscFile. + /// + public class DiscFile : FtpFile + { + public DiscFile(IFtpFolder aParent, string aName, string aLocalPath) : base(aParent, aName) + { + fLocalPath = aLocalPath; + WorldRead = true; + } + + private string fLocalPath; public string LocalPath { get { return fLocalPath; } } + + public override DateTime Date { get { return File.GetLastWriteTime(LocalPath); } } public override int Size { get { return 5; } set { /* no-op */ } } + const int BUFFER_SIZE = 64*1024; + + public override void GetFile(Stream aToStream) { if (!File.Exists(LocalPath)) + throw new Exception("Error retrieving file from disk: file does not exist."); + + Stream lStream = new FileStream(LocalPath, FileMode.Open, FileAccess.Read, FileShare.Read); + try + { + byte[] lBuffer = new byte[BUFFER_SIZE]; + + int lBytesRead = lStream.Read(lBuffer, 0, BUFFER_SIZE); + while (lBytesRead > 0) { aToStream.Write(lBuffer,0,lBytesRead); lBytesRead = lStream.Read(lBuffer, 0, BUFFER_SIZE); + } + } + finally + { + lStream.Close(); + } + } public override void CreateFile(Stream aStream) { if (File.Exists(LocalPath)) + throw new Exception("Error savinf file to disk: file already exist."); + + Stream lStream = new FileStream(LocalPath, FileMode.CreateNew, FileAccess.Write, FileShare.None); + try + { + + byte[] lBuffer = new byte[BUFFER_SIZE]; + + int lBytesRead = aStream.Read(lBuffer, 0, BUFFER_SIZE); + while (lBytesRead > 0) { lStream.Write(lBuffer,0,lBytesRead); Size += lBytesRead; lBytesRead = aStream.Read(lBuffer, 0, BUFFER_SIZE); + } + /* ToDo: eliminate one call to receive by checking for lBytesRead == BUFFER_SIZE? */ + lStream.Close(); + Complete = true; + } + catch (ConnectionClosedException) + { + lStream.Close(); + Complete = true; + } + catch (Exception) + { + lStream.Close(); + File.Delete(LocalPath); + throw; + } + + } + } +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/DiscFolder.cs b/Source/RemObjects.InternetPack.VirtualFTP/DiscFolder.cs new file mode 100644 index 0000000..4f1f65b --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/DiscFolder.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; +using System.Collections; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + + public class DiscFolder : FtpFolder + { + public DiscFolder(IFtpFolder aParent, string aName, string aLocalPath) : base(aParent, aName) + { + fLocalPath = aLocalPath; + WorldRead = true; + WorldWrite = true; + } + + private string fLocalPath; public string LocalPath { get { return fLocalPath; } } + + public override IEnumerable SubFolders { get { return null; } } public override IEnumerable Files { get { return null; } } public override void DoListFolderItems(FtpListing aListing) { string[] lNames = Directory.GetDirectories(LocalPath); + foreach (string s in lNames) + { + FtpListingItem lListingItem = aListing.Add(); + lListingItem.Directory = true; + lListingItem.FileName = Path.GetFileName(s); + lListingItem.FileDate = Directory.GetLastWriteTime(Path.Combine(LocalPath,s)); + lListingItem.Size = 0; + lListingItem.User = "system"; + lListingItem.Group = "system"; + lListingItem.UserRead = UserRead; + lListingItem.UserWrite = UserWrite; + lListingItem.UserExec = UserRead; + lListingItem.GroupRead = GroupRead; + lListingItem.GroupWrite = GroupWrite; + lListingItem.GroupExec = GroupRead; + lListingItem.OtherRead = WorldRead; + lListingItem.OtherWrite = WorldWrite; + lListingItem.OtherExec = WorldRead; + } + + DirectoryInfo lDirectory = new DirectoryInfo(LocalPath); + FileInfo[] lFiles = lDirectory.GetFiles(); + foreach (FileInfo fi in lFiles) + { + FtpListingItem lListingItem = aListing.Add(); + lListingItem.Directory = false; + lListingItem.FileName = fi.Name; + lListingItem.FileDate = fi.LastWriteTime; + lListingItem.Size = fi.Length; + lListingItem.User = "system"; + lListingItem.Group = "system"; + lListingItem.UserRead = UserRead; + lListingItem.UserWrite = UserWrite; + lListingItem.UserExec = false; + lListingItem.GroupRead = GroupRead; + lListingItem.GroupWrite = GroupWrite; + lListingItem.GroupExec = false; + lListingItem.OtherRead = WorldRead; + lListingItem.OtherWrite = WorldWrite; + lListingItem.OtherExec = false; + } + + AddListingItems(aListing, SubFolders); + AddListingItems(aListing, Files); + } public override bool HasSubfolder(string aFolderName) { return Directory.Exists(Path.Combine(LocalPath,aFolderName)); } public override IFtpFolder GetSubFolder(string aFolderName, VirtualFtpSession aSession) { if (!AllowBrowse(aSession)) + throw new FtpException(550, String.Format("Cannot access folder \"{0}\", permission to access items in this folder denied.",aFolderName)); + if (!HasSubfolder(aFolderName)) + throw new FtpException(550, String.Format("A folder named \"{0}\" does not exist.",aFolderName)); + + return new DiscFolder(this, aFolderName, Path.Combine(LocalPath,aFolderName)); + } public override IFtpFolder CreateFolder(string aFolderName, VirtualFtpSession aSession) { if (!AllowMkDir(aSession)) + throw new FtpException(550, String.Format("Cannot create folder \"{0}\", permission to mkdir in this folder denied.",aFolderName)); if (HasSubfolder(aFolderName)) + throw new FtpException(550, String.Format("A folder named \"{0}\" already exist.",aFolderName)); + Directory.CreateDirectory(Path.Combine(LocalPath,aFolderName)); return new DiscFolder(this, aFolderName, Path.Combine(LocalPath,aFolderName)); } public override void DeleteFolder(string aFolderName, bool aRecursive, VirtualFtpSession aSession) { if (!AllowDeleteItems(aSession)) + throw new FtpException(550, String.Format("Cannot delete folder \"{0}\", permission to delete from this folder denied.",aFolderName)); if (!HasSubfolder(aFolderName)) + throw new FtpException(550, String.Format("A folder named \"{0}\" does not exist.",aFolderName)); + Directory.Delete(Path.Combine(LocalPath,aFolderName),aRecursive); } public override bool HasFile(string aFilename) { return File.Exists(Path.Combine(LocalPath,aFilename)); } public override IFtpFile GetFile(string aFilename, VirtualFtpSession aSession) { if (!AllowGet(aSession)) + throw new FtpException(550, String.Format("Cannot retrieve file \"{0}\", permission to get files from this folder denied.",aFilename)); if (!HasFile(aFilename)) + throw new FtpException(550, String.Format("A file named \"{0}\" does not exist.",aFilename)); + return new DiscFile(this, aFilename, Path.Combine(LocalPath, aFilename)); } public override IFtpFile CreateFile(string aFilename, VirtualFtpSession aSession) { if (!AllowPut(aSession)) + throw new FtpException(550, String.Format("Cannot upload file \"{0}\", permission to upload files to this folder denied.",aFilename)); if (HasFile(aFilename)) + throw new FtpException(550, String.Format("A file named \"{0}\" already exist.",aFilename)); + return new DiscFile(this, aFilename, Path.Combine(LocalPath, aFilename)); } + public override void DeleteFile(string aFilename, VirtualFtpSession aSession) { if (!AllowDeleteItems(aSession)) + throw new FtpException(550, String.Format("Cannot delete file \"{0}\", permission to delete from this folder denied.",aFilename)); if (!HasFile(aFilename)) + throw new FtpException(550, String.Format("A file named \"{0}\" does not exist.",aFilename)); + File.Delete(Path.Combine(LocalPath,aFilename)); } public override void RenameFileOrFolder(string aOldFilename, string aNewFilename, VirtualFtpSession aSession) { } public override void RemoveItem(IFtpItem aItem) { } + public override bool Invalid { get { return !Directory.Exists(LocalPath); } } + + } + +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/EmptyFile.cs b/Source/RemObjects.InternetPack.VirtualFTP/EmptyFile.cs new file mode 100644 index 0000000..30db509 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/EmptyFile.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + public class EmptyFile : FtpFile + { + public EmptyFile(IFtpFolder aParent, string aName) : base(aParent, aName) + { + UserRead = false; + UserWrite = false; + Complete = false; + } + + public override int Size { get { return 0; } set { /* no-op */ } } + + public override void GetFile(Stream aToStream) { /* no-op */ } public override void CreateFile(Stream aStream) { /* no-op */ } + } +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/FtpFile.cs b/Source/RemObjects.InternetPack.VirtualFTP/FtpFile.cs new file mode 100644 index 0000000..7234cdd --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/FtpFile.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + + public abstract class FtpFile : FtpItem, IFtpFile + { + protected FtpFile(IFtpFolder aParent, string aName) : base(aParent, aName) + { + } + /* ToDo: add file rights */ + public abstract void GetFile(Stream aToStream); public abstract void CreateFile(Stream aStream); + private bool fComplete; public bool Complete { get { return fComplete; } set { fComplete = value; } } + + public override void FillFtpListingItem(FtpListingItem aItem) + { + base.FillFtpListingItem(aItem); + aItem.Directory = false; + aItem.Size = Size; + aItem.UserRead = Complete && UserRead; + aItem.UserWrite = Complete && UserWrite; + aItem.UserExec = false; + aItem.GroupRead = Complete && GroupRead; + aItem.GroupWrite = Complete && GroupWrite; + aItem.GroupExec = false; + aItem.OtherRead = Complete && WorldRead; + aItem.OtherWrite = Complete && WorldWrite; + aItem.OtherExec = false; + } + + + public virtual bool AllowGet(VirtualFtpSession aSession) { return Complete && AllowRead(aSession); + } public virtual bool AllowAppend(VirtualFtpSession aSession) { return Complete && AllowWrite(aSession); + } public virtual bool AllowDelete(VirtualFtpSession aSession) { return Complete && AllowWrite(aSession); + } public virtual bool AllowRename(VirtualFtpSession aSession) { return Complete && AllowWrite(aSession); + } } + +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/FtpFolder.cs b/Source/RemObjects.InternetPack.VirtualFTP/FtpFolder.cs new file mode 100644 index 0000000..b3518c6 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/FtpFolder.cs @@ -0,0 +1,116 @@ +using System; +using System.Text; +using System.Collections; +using System.IO; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + public abstract class FtpFolder : FtpItem, IFtpFolder + { + protected FtpFolder(IFtpFolder aParent, string aName) : base(aParent, aName) + { + fSubFolderList = new Hashtable(); + } + + public override int Size { get { return 0; } set { /* no-op */ } } + private Hashtable fSubFolderList; protected Hashtable SubFolderList { get { return fSubFolderList; } } + + #region IFtpFolder implementation + + public abstract IEnumerable SubFolders { get ; } public abstract IEnumerable Files { get ; } public abstract bool HasSubfolder(string aFolderName); public abstract IFtpFolder GetSubFolder(string aFolderName, VirtualFtpSession aSession); public abstract IFtpFolder CreateFolder(string aFolderName, VirtualFtpSession aSession); public abstract void DeleteFolder(string aFolderName, bool aRecursive, VirtualFtpSession aSession); public abstract bool HasFile(string aFileame); public abstract IFtpFile GetFile(string aFilename, VirtualFtpSession aSession); public abstract IFtpFile CreateFile(string aFilename, VirtualFtpSession aSession); public abstract void DeleteFile(string aFilename, VirtualFtpSession aSession); public abstract void RenameFileOrFolder(string aOldFilename, string aNewFilename, VirtualFtpSession aSession); public abstract void RemoveItem(IFtpItem aItem); public IFtpFolder DigForSubFolder(string aFullPath, VirtualFtpSession aSession) { if (aFullPath.Length == 0) + return this; + int p = aFullPath.IndexOf('/'); + if (p >= 0) + { + string lSubFolder = aFullPath.Substring(0,p); + + IFtpFolder lFolder = GetSubFolder(lSubFolder, aSession); + + if (lFolder != null) + { + aFullPath = aFullPath.Substring(p+1); + return lFolder.DigForSubFolder(aFullPath, aSession); + } + + else + { + return null; + } + } + else { return GetSubFolder(aFullPath, aSession); } } public void FindBaseFolderForFilename(string aPath, out IFtpFolder aFolder, out string aFilename, VirtualFtpSession aSession) { if (aPath.IndexOf('/') != -1) + { + if (aPath.StartsWith("/")) + { + aFolder = Root; + aPath = aPath.Substring(1); /* remove / */ + } + else + { + aFolder = this; + } + int p = aPath.IndexOf('/'); while (p >= 0) + { + string lFolderName = aPath.Substring(0,p); + aFolder = aFolder.GetSubFolder(lFolderName, aSession); + + if (aFolder == null || !aFolder.AllowBrowse(aSession)) + throw new FtpException(550, String.Format("Folder \"{0}\" does not exists, or access denied.", aPath)); + + aPath = aPath.Substring(p+1); + p = aPath.IndexOf('/'); } + } + else + { + aFolder = this; + } + + aFilename = aPath; + } public string FullPath { get { + if (Parent != null) + { + return Parent.FullPath+Name+"/"; + } + else { return "/"; } } } public virtual bool AllowBrowse(VirtualFtpSession aSession) { return AllowRead(aSession); + } public virtual bool AllowGet(VirtualFtpSession aSession) { return AllowRead(aSession); + } public virtual bool AllowPut(VirtualFtpSession aSession) { return AllowWrite(aSession); + } public virtual bool AllowMkDir(VirtualFtpSession aSession) { return AllowWrite(aSession); + } + public virtual bool AllowDeleteItems(VirtualFtpSession aSession) { return AllowWrite(aSession); + } + public virtual bool AllowRenameItems(VirtualFtpSession aSession) { return AllowWrite(aSession); + } + public virtual bool AllowDeleteThis(VirtualFtpSession aSession) { return AllowWrite(aSession); + } #endregion + + #region FolderListing + public void ListFolderItems(FtpListing aListing) { /* Add this folder (.) */ + FtpListingItem lListingItem; + lListingItem = aListing.Add(); + FillFtpListingItem(lListingItem, "."); + + /* Add parent folder (..) */ + if (Parent != null) + { + lListingItem = aListing.Add(); + Parent.FillFtpListingItem(lListingItem, ".."); + } + + DoListFolderItems(aListing); + } public virtual void DoListFolderItems(FtpListing aListing) { AddListingItems(aListing, SubFolders); + AddListingItems(aListing, Files); + } protected void AddListingItems(FtpListing aListing, IEnumerable aFtpItems) + { + if (aFtpItems != null) + { + foreach (IFtpItem lFtpItem in aFtpItems) + { + FtpListingItem lListingItem = aListing.Add(); + lFtpItem.FillFtpListingItem(lListingItem); + } + } + } + #endregion + + } + +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/FtpInterfaces.cs b/Source/RemObjects.InternetPack.VirtualFTP/FtpInterfaces.cs new file mode 100644 index 0000000..5d2d4e4 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/FtpInterfaces.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections; +using System.IO; +using System.Net; +using RemObjects.InternetPack; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + + public interface IFtpItem + { + IFtpFolder Parent { get; set; } string Name { get; set; } DateTime Date { get; } string OwningUser { get; set; } string OwningGroup { get; set; } bool AllowRead(VirtualFtpSession aSession); bool AllowWrite(VirtualFtpSession aSession); void FillFtpListingItem(FtpListingItem aItem); void FillFtpListingItem(FtpListingItem aItem, string aAsName); void Invalidate(); } + + public interface IFtpFolder: IFtpItem + { + IFtpFolder Root { get ; } IEnumerable SubFolders { get ; } IEnumerable Files { get ; } bool HasSubfolder(string aFolderName); IFtpFolder GetSubFolder(string aFolderName, VirtualFtpSession aSession); IFtpFolder CreateFolder(string aFolderName, VirtualFtpSession aSession); void DeleteFolder(string aFolderName, bool aRecursive, VirtualFtpSession aSession); void ListFolderItems(FtpListing aListing); IFtpFolder DigForSubFolder(string aFullPath, VirtualFtpSession aSession); void FindBaseFolderForFilename(string aPath, out IFtpFolder aFolder, out string aFilename, VirtualFtpSession aSession); string FullPath { get ; } bool HasFile(string aFilename); IFtpFile GetFile(string aFilename, VirtualFtpSession aSession); IFtpFile CreateFile(string aFilename, VirtualFtpSession aSession); void DeleteFile(string aFilename, VirtualFtpSession aSession); void RenameFileOrFolder(string aOldFilename, string aNewFilename, VirtualFtpSession aSession); void RemoveItem(IFtpItem aItem); bool AllowBrowse(VirtualFtpSession aSession); bool AllowGet(VirtualFtpSession aSession); bool AllowPut(VirtualFtpSession aSession); bool AllowMkDir(VirtualFtpSession aSession); bool AllowDeleteItems(VirtualFtpSession aSession); bool AllowRenameItems(VirtualFtpSession aSession); bool AllowDeleteThis(VirtualFtpSession aSession); } + + public interface IFtpFile: IFtpItem + { + int Size { get ; } /* ToDo: add file rights */ void GetFile(Stream aToStream); void CreateFile(Stream aFromStream); + bool AllowGet(VirtualFtpSession aSession); bool AllowAppend(VirtualFtpSession aSession); bool AllowDelete(VirtualFtpSession aSession); bool AllowRename(VirtualFtpSession aSession); } + + public interface IFtpUserManager + { + bool CheckIP(EndPoint aRemote, EndPoint aLocal); + bool CheckLogin(string aUsername, string aPassword, VirtualFtpSession aSession); } + + +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/FtpItem.cs b/Source/RemObjects.InternetPack.VirtualFTP/FtpItem.cs new file mode 100644 index 0000000..a4308d3 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/FtpItem.cs @@ -0,0 +1,82 @@ +using System; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + + public abstract class FtpItem : IFtpItem + { + protected FtpItem(IFtpFolder aParent, string aName) + { + fParent = aParent; + fName = aName; + + UserRead = true; + UserWrite = true; + GroupRead = true; + + OwningUser = "system"; + OwningGroup = "system"; + + Date = DateTime.Now; + } + + #region General Item attributes + private IFtpFolder fParent; public IFtpFolder Parent { get { return fParent; } set { fParent = value; } } + public IFtpFolder Root { get { IFtpFolder lParent; if (this is IFtpFolder) + { + lParent = this as IFtpFolder; } else { lParent = Parent; } + while (lParent.Parent != null) lParent = lParent.Parent; return lParent; } } + + private string fName; public virtual string Name { get { return fName; } set { fName = value; } } + private DateTime fDate; + public virtual DateTime Date { get { return fDate; } set { fDate = value; } } + public abstract int Size { get; set; } #endregion + #region Rights + private string fOwningUser; public virtual string OwningUser { get { return fOwningUser; } set { fOwningUser = value; } } + + private string fOwningGroup; public virtual string OwningGroup { get { return fOwningGroup; } set { fOwningGroup = value; } } + + public virtual bool AllowRead(VirtualFtpSession aSession) { if (Invalid) return false; + if (aSession.IsFileAdmin) return true; + if (aSession.Username == OwningUser && UserRead) return true; + if (WorldRead) return true; + return false; + } public virtual bool AllowWrite(VirtualFtpSession aSession) { if (Invalid) return false; + if (aSession.IsFileAdmin) return true; + if (aSession.Username == OwningUser && UserWrite) return true; + if (WorldWrite) return true; + return false; + } + private bool[] fRights = new bool[6]; public virtual bool UserRead { get { return fRights[0]; } set { fRights[0] = value; } } public virtual bool UserWrite { get { return fRights[1]; } set { fRights[1] = value; } } public virtual bool GroupRead { get { return fRights[2]; } set { fRights[2] = value; } } public virtual bool GroupWrite { get { return fRights[3]; } set { fRights[3] = value; } } public virtual bool WorldRead { get { return fRights[4]; } set { fRights[4] = value; } } public virtual bool WorldWrite { get { return fRights[5]; } set { fRights[5] = value; } } #endregion + + #region Invalidation + private bool fInvalid; public virtual bool Invalid { get { return fInvalid; } set { fInvalid = value; } } + public virtual void Invalidate() { Invalid = false; } #endregion + public virtual void FillFtpListingItem(FtpListingItem aItem, string aAsName) + { + FillFtpListingItem(aItem); + aItem.FileName = aAsName; + } + + public virtual void FillFtpListingItem(FtpListingItem aItem) + { + aItem.Directory = (this is IFtpFolder); + aItem.FileName = Name; + aItem.FileDate = Date; + aItem.Size = Size; + aItem.User = OwningUser; + aItem.Group = OwningGroup; + aItem.UserRead = UserRead; + aItem.UserWrite = UserWrite; + aItem.UserExec = aItem.Directory && UserRead; + aItem.GroupRead = GroupRead; + aItem.GroupWrite = GroupWrite; + aItem.GroupExec = aItem.Directory && GroupRead; + aItem.OtherRead = WorldRead; + aItem.OtherWrite = WorldWrite; + aItem.OtherExec = aItem.Directory && WorldRead; + } + + } + +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/Glyphs/VirtualFtpServer.bmp b/Source/RemObjects.InternetPack.VirtualFTP/Glyphs/VirtualFtpServer.bmp new file mode 100644 index 0000000..3775f7d Binary files /dev/null and b/Source/RemObjects.InternetPack.VirtualFTP/Glyphs/VirtualFtpServer.bmp differ diff --git a/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2003.csproj b/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2003.csproj new file mode 100644 index 0000000..cb267db --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2003.csproj @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2005.csproj b/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2005.csproj new file mode 100644 index 0000000..f25a174 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2005.csproj @@ -0,0 +1,136 @@ + + + Local + 8.0.50727 + 2.0 + {E8E89EC1-4445-4853-9E0B-A18B044069B5} + Debug + AnyCPU + + + + + RemObjects.InternetPack.VirtualFTP + + + JScript + Grid + IE50 + false + Library + RemObjects.InternetPack.VirtualFTP + OnBuildSuccess + + + + + + + bin\Debug\ + false + 285212672 + false + + + REMOBJECTS_SIGN_ASSEMBLY;DEBUG;TRACE + + + true + 4096 + false + + + false + false + false + false + 4 + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + + + true + false + false + false + 4 + + + + System + + + System.Data + + + System.XML + + + RemObjects.InternetPack.2005 + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Component + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2008.csproj b/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2008.csproj new file mode 100644 index 0000000..90e888c --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2008.csproj @@ -0,0 +1,134 @@ + + + Local + 9.0.30729 + 2.0 + {E8E89EC1-4445-4853-9E0B-A18B044069B5} + Debug + AnyCPU + + + + + RemObjects.InternetPack.VirtualFTP + + + JScript + Grid + IE50 + false + Library + RemObjects.InternetPack.VirtualFTP + OnBuildSuccess + + + + + + + bin\Debug\ + false + 285212672 + false + + + REMOBJECTS_SIGN_ASSEMBLY;DEBUG;TRACE + + + true + 4096 + false + 1699 + false + false + false + false + 4 + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + 1699 + true + false + false + false + 4 + + + + System + + + System.Data + + + System.XML + + + RemObjects.InternetPack.2008 + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Component + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2010.csproj b/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2010.csproj new file mode 100644 index 0000000..3ed881d --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/RemObjects.InternetPack.VirtualFTP.2010.csproj @@ -0,0 +1,135 @@ + + + + Local + 9.0.30729 + 2.0 + {E8E89EC1-4445-4853-9E0B-A18B044069B5} + Debug + AnyCPU + + + + + RemObjects.InternetPack.VirtualFTP + + + JScript + Grid + IE50 + false + Library + RemObjects.InternetPack.VirtualFTP + OnBuildSuccess + + + + + + + bin\Debug\ + false + 285212672 + false + + + REMOBJECTS_SIGN_ASSEMBLY;DEBUG;TRACE + + + true + 4096 + false + 1699 + false + false + false + false + 4 + + + ..\..\Bin\ + false + 285212672 + false + + + TRACE;REMOBJECTS_SIGN_ASSEMBLY + + + false + 4096 + false + 1699 + true + false + false + false + 4 + + + + System + + + System.Data + + + System.XML + + + RemObjects.InternetPack.2010 + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E} + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Code + + + Component + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack.VirtualFTP/Session.cs b/Source/RemObjects.InternetPack.VirtualFTP/Session.cs new file mode 100644 index 0000000..26ed381 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/Session.cs @@ -0,0 +1,38 @@ +using System; +using RemObjects.InternetPack.Ftp; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + public class VirtualFtpSession : FtpSession + { + public VirtualFtpSession() + { + } + + public override String Directory + { + get + { + return CurrentFolder.FullPath; + } + set + { + /* ToDo: browse to folder */ + base.Directory = value; + } + } + + + private bool fIsSuperUser; public bool IsSuperUser { get { return fIsSuperUser; } set { fIsSuperUser = value; } } + + private bool fIsFileAdmin; + public bool IsFileAdmin + { + get { return fIsFileAdmin; } + set { fIsFileAdmin = value; } + } + + private IFtpFolder fCurrentFolder; public IFtpFolder CurrentFolder { get { return fCurrentFolder; } set { fCurrentFolder = value; } } + + } +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/UserManager.cs b/Source/RemObjects.InternetPack.VirtualFTP/UserManager.cs new file mode 100644 index 0000000..afa4b51 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/UserManager.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using System.Net; +using RemObjects.InternetPack; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + public class User + { + public User(string aUsername, string aPassword) { fUsername = aUsername; fPassword = aPassword; } + + private string fUsername; public string Username { get { return fUsername; } set { fUsername = value; } } + + private string fPassword; public string Password { get { return fPassword; } set { fPassword = value; } } + } + + public class UserManager: Hashtable, IFtpUserManager + { + public UserManager(): base () + { + } + + public virtual bool CheckIP(EndPoint aRemote, EndPoint aLocal) + { + return true; + } + + public virtual bool CheckLogin(string aUsername, string aPassword, VirtualFtpSession aSession) + { + User lUser = (User)this[aUsername.ToLower()]; + if (lUser == null) return false; + if (lUser.Password != aPassword) return false; + /* ToDo: add additional checks */ + return true; + } + + public void AddUser(string aUsername, string aPassword) + { + Add(aUsername.ToLower(),new User(aUsername, aPassword)); + } + + public void AddUser(User aUser) + { + Add(aUser.Username.ToLower(), aUser); + } + + } +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/VirtualFolder.cs b/Source/RemObjects.InternetPack.VirtualFTP/VirtualFolder.cs new file mode 100644 index 0000000..24353c4 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/VirtualFolder.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using System.Collections; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ + + public class VirtualFolder : FtpFolder + { + public VirtualFolder(IFtpFolder aParent, string aName) : this(aParent, aName, "system", "system", false) + { + fFileList = new Hashtable(); + WorldRead = true; + WorldWrite = false; + Date = DateTime.Now; + } + + public VirtualFolder(IFtpFolder aParent, string aName, string aOwningUser, string aOwningGroup, bool aWorldWritable) : base(aParent, aName) + { + fFileList = new Hashtable(); + WorldRead = true; + WorldWrite = aWorldWritable; + OwningUser = aOwningUser; + OwningGroup = aOwningGroup; + Date = DateTime.Now; + } + + #region Elements + private Hashtable fFileList; protected Hashtable FileList { get { return fFileList; } } + + public override IEnumerable SubFolders { get { return SubFolderList.Values; } } public override IEnumerable Files { get { return FileList.Values; } } public void Add(IFtpItem aItem) + { + lock (this) + { + if (aItem is IFtpFolder) + { + aItem.Parent = this; + SubFolderList.Add(aItem.Name.ToLower(), aItem); + } + else if (aItem is IFtpFile) + { + aItem.Parent = this; + FileList.Add(aItem.Name.ToLower(), aItem); + } + } + } + #endregion #region Has* public override bool HasSubfolder(string aFolderName) { return SubFolderList.ContainsKey(aFolderName.ToLower()); } public override bool HasFile(string aFileame) { return FileList.ContainsKey(aFileame.ToLower()); } #endregion #region Get* public override IFtpFolder GetSubFolder(string aFolderName, VirtualFtpSession aSession) { if (!AllowBrowse(aSession)) + throw new FtpException(550, String.Format("Cannot access folder \"{0}\", permission to access items in this folder denied.",aFolderName)); IFtpFolder lFolder = SubFolderList[aFolderName.ToLower()] as IFtpFolder; if (lFolder != null && !lFolder.AllowBrowse(aSession)) + throw new FtpException(550, String.Format("Cannot access folder \"{0}\", permission to browse folder denied.",aFolderName)); return lFolder; } public override IFtpFile GetFile(string aFilename, VirtualFtpSession aSession) { if (!HasFile(aFilename)) + throw new FtpException(String.Format("A file named \"{0}\" does not exists.",aFilename)); if (!AllowBrowse(aSession)) + throw new FtpException(550,String.Format("Cannot access file \"{0}\", permission to access files in this folder denied.",aFilename)); IFtpFile lFile = FileList[aFilename.ToLower()] as IFtpFile; if (!lFile.AllowRead(aSession)) + throw new FtpException(550, String.Format("Cannot access file \"{0}\", permission to access file denied.",aFilename)); return lFile; } #endregion + #region Create* + public override sealed IFtpFolder CreateFolder(string aFolderName, VirtualFtpSession aSession) { if (!AllowMkDir(aSession)) + throw new FtpException(550, String.Format("Cannot create folder \"{0}\", permission to mkdir in this folder denied.",aFolderName)); return DoCreateFolder(aFolderName, aSession); } public override sealed IFtpFile CreateFile(string aFilename, VirtualFtpSession aSession) { if (!AllowPut(aSession)) + throw new FtpException(550, String.Format("Cannot create file \"{0}\", permission to upload to this folder denied.",aFilename)); return DoCreateFile(aFilename, aSession); } + #endregion + + #region Create* virtuals + protected virtual IFtpFolder DoCreateFolder(string aFolderName, VirtualFtpSession aSession) { throw new FtpException(550, String.Format("You cannot create subfolders in a {0}.",this.GetType().Name)); } protected virtual IFtpFile DoCreateFile(string aFilename, VirtualFtpSession aSession) { throw new FtpException(550, String.Format("You cannot create files in a {0}.",this.GetType().Name)); } #endregion + #region Delete* + public override void DeleteFolder(string aFolderName, bool aRecursive, VirtualFtpSession aSession) { if (!HasSubfolder(aFolderName)) + throw new FtpException(String.Format("A folder named \"{0}\" does not exists.",aFolderName)); + if (!AllowDeleteItems(aSession)) + throw new FtpException(550, String.Format("Cannot delete folder \"{0}\", permission to delete from this folder denied.",aFolderName)); + IFtpFolder lFolder = GetSubFolder(aFolderName, aSession); + if (!lFolder.AllowDeleteThis(aSession)) + throw new FtpException(550, String.Format("Cannot delete folder \"{0}\", permission to delete folder denied.",aFolderName)); + lock(this) + { + lFolder.Invalidate(); + SubFolderList.Remove(aFolderName.ToLower()); } } public override void DeleteFile(string aFilename, VirtualFtpSession aSession) { if (!HasFile(aFilename)) + throw new FtpException(String.Format("A file named \"{0}\" does not exists.",aFilename)); + if (!AllowDeleteItems(aSession)) + throw new FtpException(550, String.Format("Cannot delete fike \"{0}\", permission to delete from this folder denied.",aFilename)); + IFtpFile lFile = GetFile(aFilename, aSession); + if (!lFile.AllowDelete(aSession)) + throw new FtpException(550, String.Format("Cannot delete file \"{0}\", permission to delete file denied.",aFilename)); + lock(this) + { + lFile.Invalidate(); + FileList.Remove(aFilename.ToLower()); } } public override void RemoveItem(IFtpItem aItem) { if (FileList.ContainsValue(aItem)) { FileList.Remove(aItem.Name); } } + #endregion public override void RenameFileOrFolder(string aOldFilename, string aNewFilename, VirtualFtpSession aSession) { if (HasSubfolder(aOldFilename)) + { + if (!AllowRenameItems(aSession)) + throw new FtpException(550, String.Format("Cannot rename folder \"{0}\", permission to rename in this folder denied.",aOldFilename)); } + else if (HasFile(aOldFilename)) + { + if (!AllowRenameItems(aSession)) + throw new FtpException(550, String.Format("Cannot rename file \"{0}\", permission to rename in this folder denied.",aOldFilename)); + IFtpFile lFile = GetFile(aOldFilename, aSession); + if (!lFile.AllowRename(aSession)) + throw new FtpException(550, String.Format("Cannot rename file \"{0}\", permission to rename file denied.",aOldFilename)); + lock (this) + { + lFile.Name = aNewFilename; + } + } + else + { + throw new FtpException(String.Format("A file or folder named \"{0}\" does not exists.",aOldFilename)); } } } + +} diff --git a/Source/RemObjects.InternetPack.VirtualFTP/VirtualFtpServer.cs b/Source/RemObjects.InternetPack.VirtualFTP/VirtualFtpServer.cs new file mode 100644 index 0000000..9914320 --- /dev/null +++ b/Source/RemObjects.InternetPack.VirtualFTP/VirtualFtpServer.cs @@ -0,0 +1,204 @@ +using System; +using System.IO; +using System.Net; +using System.Collections; +using RemObjects.InternetPack; +using RemObjects.InternetPack.CommandBased; +using RemObjects.InternetPack.Ftp; + +namespace RemObjects.InternetPack.Ftp.VirtualFtp +{ +#if FULLFRAMEWORK + [ System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.VirtualFtpServer.bmp") ] +#endif + public class VirtualFtpServer : FtpServer + { + public VirtualFtpServer() + { + } + + #region Properties + private IFtpFolder fRootFolder; + public IFtpFolder RootFolder + { + get { return fRootFolder; } + set { fRootFolder = value; } + } + + private IFtpUserManager fUserManager; + public IFtpUserManager UserManager + { + get { return fUserManager; } + set { fUserManager = value; } + } + #endregion + + protected override Type GetDefaultSessionClass() + { + return typeof(VirtualFtpSession); + } + + #region Login & IP Check + protected override void InvokeOnUserLogin(FtpUserLoginEventArgs ea) + { +#if DEBUGSERVER + Debug.EnterMethod("Login({0})", ea.UserName); + try + { +#endif + if (fUserManager == null) return; + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + ea.LoginOk = fUserManager.CheckLogin(ea.UserName, ea.Password, lSession); +#if DEBUGSERVER + Debug.Write("Login result " + ea.LoginOk); +#endif + base.InvokeOnUserLogin(ea); +#if DEBUGSERVER + } + finally + { + Debug.ExitMethod("Login({0}):{1}", ea.UserName, ea.LoginOk); + } +#endif + } + + protected override void InvokeOnClientConnected(SessionEventArgs ea) + { + ((VirtualFtpSession)ea.Session).CurrentFolder = fRootFolder; + if (fUserManager == null) return; + if (!fUserManager.CheckIP(ea.Connection.RemoteEndPoint, ea.Connection.LocalEndPoint)) + { + ea.Connection.Disconnect(); + throw new Exception("Access not allowed from this IP."); + } + base.InvokeOnClientConnected(ea); + } + #endregion + + #region Folder handling + protected override void InvokeOnGetListing(FtpGetListingArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; +#if DEBUGSERVER + Debug.Write("Getting Listing for {0}", lSession.CurrentFolder.FullPath); +#endif + lSession.CurrentFolder.ListFolderItems(ea.Listing); + + base.InvokeOnGetListing(ea); + } + + protected override void InvokeOnChangeDirectory(FtpChangeDirectoryArgs ea) + { + string lPath = ea.NewDirectory; + + if (lPath.IndexOf('/') != 0) + throw new Exception(String.Format("Not an absolute path: \"{0}\"", lPath)); + + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + IFtpFolder lFolder = fRootFolder.DigForSubFolder(lPath.Substring(1), lSession); + + if (lFolder != null) + { + ((VirtualFtpSession)ea.Session).CurrentFolder = lFolder; + } + ea.ChangeDirOk = (lFolder != null); + base.InvokeOnChangeDirectory(ea); + } + + protected override void InvokeOnMakeDirectory(FtpFileEventArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + + IFtpFolder lFolder; + string lFilename; + + lSession.CurrentFolder.FindBaseFolderForFilename(ea.FileName, out lFolder, out lFilename, lSession); + lFolder.CreateFolder(lFilename, lSession); + ea.Ok = true; + + } + + protected override void InvokeOnDeleteDirectory(FtpFileEventArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + IFtpFolder lFolder; + string lFilename; + + lSession.CurrentFolder.FindBaseFolderForFilename(ea.FileName, out lFolder, out lFilename, lSession); + lFolder.DeleteFolder(lFilename, false, lSession); + ea.Ok = true; + } + #endregion + + #region File Handling + protected override void InvokeOnCanStoreFile(FtpTransferEventArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + IFtpFolder lFolder; + string lFilename; + + lSession.CurrentFolder.FindBaseFolderForFilename(ea.FileName, out lFolder, out lFilename, lSession); + ea.Ok = lFolder.AllowPut(lSession); + } + + protected override void InvokeOnCanRetrieveFile(FtpTransferEventArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + IFtpFolder lFolder; + string lFilename; + + lSession.CurrentFolder.FindBaseFolderForFilename(ea.FileName, out lFolder, out lFilename, lSession); + IFtpFile lFile = lFolder.GetFile(lFilename, lSession); + ea.Ok = (lFile != null && lFile.AllowRead(lSession)); + } + + protected override void InvokeOnStoreFile(FtpTransferEventArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + IFtpFolder lFolder; + string lFilename; + + lSession.CurrentFolder.FindBaseFolderForFilename(ea.FileName, out lFolder, out lFilename, lSession); + IFtpFile lFile = lFolder.CreateFile(lFilename, lSession); + lFile.CreateFile(ea.DataChannel); + ea.Ok = true; + } + + protected override void InvokeOnRetrieveFile(FtpTransferEventArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + IFtpFolder lFolder; + string lFilename; + + lSession.CurrentFolder.FindBaseFolderForFilename(ea.FileName, out lFolder, out lFilename, lSession); + IFtpFile lFile = lFolder.GetFile(lFilename, lSession); + lFile.GetFile(ea.DataChannel); + ea.Ok = true; + } + + protected override void InvokeOnRename(FtpRenameEventArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + IFtpFolder lFolder; + string lFilename; + + lSession.CurrentFolder.FindBaseFolderForFilename(ea.FileName, out lFolder, out lFilename, lSession); + lFolder.RenameFileOrFolder(lFilename, ea.NewFileName, lSession); + ea.Ok = true; + } + + protected override void InvokeOnDelete(FtpFileEventArgs ea) + { + VirtualFtpSession lSession = (VirtualFtpSession)ea.Session; + IFtpFolder lFolder; + string lFilename; + + lSession.CurrentFolder.FindBaseFolderForFilename(ea.FileName, out lFolder, out lFilename, lSession); + lFolder.DeleteFile(lFilename, lSession); + ea.Ok = true; + } + #endregion + + } + +} diff --git a/Source/RemObjects.InternetPack/Asn1.cs b/Source/RemObjects.InternetPack/Asn1.cs new file mode 100644 index 0000000..4bb1071 --- /dev/null +++ b/Source/RemObjects.InternetPack/Asn1.cs @@ -0,0 +1,1387 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; + +namespace RemObjects.InternetPack.Ldap +{ + // http://tools.ietf.org/html/rfc3641 << asn1 (encoding of values) + // http://en.wikipedia.org/wiki/Basic_Encoding_Rules (actual wire format) + + static class Asn1 // static + { + // Universal Class Types + public const Byte EOC = 0; + public const Byte BOOLEAN = 1; + public const Byte INTEGER = 2; + public const Byte BITSTRING = 3; + public const Byte OCTETSTRING = 4; + public const Byte NULL = 5; + public const Byte OBJECTIDENTIFIER = 6; + public const Byte OBJECTDESCRIPTOR = 7; + public const Byte EXTERNAL = 8; + public const Byte REAL = 9; + public const Byte ENUMERATED = 10; + public const Byte EMBEDDEDPDV = 11; + public const Byte UTF8STRING = 12; + public const Byte RELATIVEOID = 13; + public const Byte SEQUENCE = 48; + public const Byte SET = 49; + public const Byte NUMERICSTRING = 18; + public const Byte PRINTABLESTRING = 19; + public const Byte T61STRING = 20; + public const Byte VIDEOTEXSTRING = 21; + public const Byte IA5STRING = 22; + public const Byte UTCTIME = 23; + public const Byte GENERALIZEDTIME = 24; + public const Byte GRAPHICSTRING = 25; + public const Byte VISIBLESTRING = 26; + public const Byte GENERALSTRING = 27; + public const Byte UNIVERSALSTRING = 28; + public const Byte CHARACTERSTRING = 29; + public const Byte BMPSTRING = 30; + public const Byte LDAPOID = 0x80; + public const Byte LDAPVALUE = 0x81; + + public const Byte LDAPSTARTRANGE = LDAPBINDREQ; + public const Byte LDAPENDRANGE = LDAPEXTRESP; + public const Byte LDAPBINDREQ = 0x60; + public const Byte LDAPBINDRESP = 0x61; + public const Byte LDAPUNBINDREQ = 0x42; + + public const Byte LDAPSEARCHREQ = 0x63; + public const Byte LDAPSEARCHENTRY = 0x64; + public const Byte LDAPSEARCHDONE = 0x65; + public const Byte LDAPSEARCHREF = 0x73; + public const Byte LDAPMODIFYREQ = 0x66; + + public const Byte LDAPMODIFYRESP = 0x67; + public const Byte LDAPADDREQ = 0x68; + public const Byte LDAPADDRESP = 0x69; + public const Byte LDAPDELREQ = 0x4A; + public const Byte LDAPDELRESP = 0x6B; + public const Byte LDAPMODIFYDNREQ = 0x6C; + public const Byte LDAPMODIFYDNRESP = 0x6D; + public const Byte LDAPCOMPAREREQ = 0x6E; + public const Byte LDAPCOMPARERESP = 0x6F; + public const Byte LDAPABANDONREQ = 0x70; + public const Byte LDAPEXTREQ = 0x77; + public const Byte LDAPEXTRESP = 0x78; + + public const Byte LDAPREFERER = 0xA3; + public const Byte LDAPBINDPASSWORD = 0x80; + public const Byte LDAPBINDSASL = 0xA3; + + public const Byte LDAPFILTERSUBSTRING_INIT = 0xa0; + public const Byte LDAPFILTERSUBSTRING_ANY = 0xa1; + public const Byte LDAPFILTERSUBSTRING_FINAL = 0xa2; + + public const Byte LDAPFILTERAND = 0xa0; + public const Byte LDAPFILTEROR = 0xa1; + public const Byte LDAPFILTERNOT = 0xa2; + public const Byte LDAPFILTEREQUALITYMATCH = 0xa3; + public const Byte LDAPFILTERSUBSTRING = 0xa4; + public const Byte LDAPFILTERGREATEROREQUAL = 0xa5; + public const Byte LDAPFILTERLESSOREQUAL = 0xa6; + public const Byte LDAPFILTERPRESENT = 0x87; + public const Byte LDAPFILTERAPPROXMATCH = 0xa8; + public const Byte LDAPFILTEREXTENSIBLEMATCH = 0xa9; + // http://www.shrubbery.net/mibs/RFC1155-SMI.txt + + private const Byte APPLICATION = 0x40; + public const Byte IPADDRESS = APPLICATION + 0; // 4 bytes + public const Byte COUNTER = APPLICATION + 1; // 4 bytes; but should be read as regular integer + public const Byte GAUGE = APPLICATION + 2; // 4 bytes; but should be read as regular integer + public const Byte TIMETICKS = APPLICATION + 3; // 4 bytes; but should be read as regular integer + public const Byte OPAQUE = APPLICATION + 4; // octet String + + + public static void Decode(BinaryReader reader, out Byte classType, out Int32 length) + { + classType = reader.ReadByte(); + Int32 lLength = reader.ReadByte(); + if (0 == (lLength & 0x80)) + { + length = lLength; + } + else + { + lLength &= ~0x80; + length = 0; + for (Int32 i = 0; i < lLength; i++) + length = length << 8 | reader.ReadByte(); + } + } + + public static void Encode(BinaryWriter writer, Byte classType, Int32 length) + { + writer.Write(classType); + // now write the length + if (length < 0x80) + { + writer.Write((Byte)length); + } + else + { + Byte t1 = (Byte)length; + Byte t2 = (Byte)(length >> 8); + Byte t3 = (Byte)(length >> 16); + Byte t4 = (Byte)(length >> 24); + if (t4 != 0) + { + writer.Write((Byte)0x84); + writer.Write(t4); + writer.Write(t3); + writer.Write(t2); + writer.Write(t1); + } + else if (t3 != 0) + { + writer.Write((Byte)0x83); + writer.Write(t3); + writer.Write(t2); + writer.Write(t1); + } + else if (t2 != 0) + { + writer.Write((Byte)0x82); + writer.Write(t2); + writer.Write(t1); + } + else + { + writer.Write((Byte)0x81); + writer.Write(t1); + } + // Long form. Two to 127 octets. + // Bit 8 of first octet has value "1" and bits 7-1 give + // the number of additional length octets. Second and + // following octets give the length, base 256, most + // significant digit first. + } + } + + public static Int32 DecodeInt(BinaryReader reader, Int32 maxLength) + { + switch (maxLength) + { + case 1: + return reader.ReadSByte(); + + case 2: + return (Int16)((UInt16)reader.ReadByte() << 8 | reader.ReadByte()); + + case 3: + { + UInt32 lResult = ((UInt32)reader.ReadByte() << 16 | (UInt32)reader.ReadByte() << 8 | reader.ReadByte()); + if (0 != (lResult & (1 << 23))) + lResult = lResult | 0xff000000; + + return (Int32)lResult; + } + + case 4: + return (Int32)((UInt32)reader.ReadByte() << 24 | (UInt32)reader.ReadByte() << 16 | (UInt32)reader.ReadByte() << 8 | reader.ReadByte()); + + default: + { + UInt32 lResult = reader.ReadByte(); + if (0 != (lResult & 0x80)) + lResult = lResult | 0xffffff00; + for (Int32 i = 1; i < maxLength; i++) + lResult = lResult << 8 | reader.ReadByte(); + + return (Int32)lResult; + } + } + } + + public static UInt32 DecodeUInt(BinaryReader reader, Int32 maxLength) + { + switch (maxLength) + { + case 1: + return reader.ReadByte(); + + case 2: + return (UInt16)((UInt16)reader.ReadByte() << 8 | (UInt16)reader.ReadByte()); + + case 3: + return ((UInt32)reader.ReadByte() << 16 | (UInt32)reader.ReadByte() << 8 | reader.ReadByte()); + + case 4: + return ((UInt32)reader.ReadByte() << 24 | (UInt32)reader.ReadByte() << 16 | (UInt32)reader.ReadByte() << 8 | reader.ReadByte()); + + default: + UInt32 lResult = 0; + for (Int32 i = 0; i < maxLength; i++) + lResult = lResult << 8 | reader.ReadByte(); + + return lResult; + } + } + + // http://www.faqs.org/rfcs/rfc1960.html + internal enum RFC1960Token + { + OpeningParenthesis, + ClosingParenthesis, + And, + Or, + Not, + Equal, + AproxEqual, + GreaterOrEqual, + LessOrEqual, + Present, + Any, + Value, + Error, + EOF // won't be returned + } + + private static void RFC1960Next(String value, ref Int32 pos, ref Int32 len, out RFC1960Token tok, out String result) + { + pos += len; + len = 0; + result = null; + + switch (value[pos]) + { + case '\0': + tok = RFC1960Token.EOF; + break; + case '(': + tok = RFC1960Token.OpeningParenthesis; + len = 1; + break; + case ')': + tok = RFC1960Token.ClosingParenthesis; + len = 1; + break; + case '&': + tok = RFC1960Token.And; + len = 1; + break; + case '|': + tok = RFC1960Token.Or; + len = 1; + break; + case '!': + tok = RFC1960Token.Not; + len = 1; + break; + case '=': + switch (value[pos + 1]) + { + case '~': + len = 2; + tok = RFC1960Token.AproxEqual; + break; + case '*': + len = 2; + tok = RFC1960Token.Present; + break; + default: + len = 1; + tok = RFC1960Token.Equal; + break; + } + break; + case '>': + if (value[pos + 1] == '=') + { + len = 2; + tok = RFC1960Token.GreaterOrEqual; + break; + } + len = 0; + tok = RFC1960Token.Error; + break; + case '<': + if (value[pos + 1] == '=') + { + len = 2; + tok = RFC1960Token.LessOrEqual; + break; + } + len = 0; + tok = RFC1960Token.Error; + break; + + case '*': + len = 1; + tok = RFC1960Token.Any; + break; + default: + { + tok = RFC1960Token.Value; + StringBuilder sb = new StringBuilder(8); + Boolean stop = false; + while (!stop) + { + switch (value[pos + len]) + { + case '<': + case '~': + case '>': + case '=': + case '\0': + case '*': + case '(': + case ')': + case '/': + stop = true; + break; + case '\\': + sb.Append((char)(Byte.Parse(value[pos + len + 1].ToString()) * 16 + + Byte.Parse(value[pos + len + 2].ToString()))); + len += 2; + break; + default: + sb.Append(value[pos + len]); + break; + + + } + len++; + } + len--; + result = sb.ToString(); + break; + } + + } + } + + internal static BerValue ParseFilter(String filter, ref Int32 pos, ref Int32 len, ref RFC1960Token tok, ref String res) + { + if (tok != RFC1960Token.OpeningParenthesis) + throw new LdapException("Opening parenthesis expected at " + pos); + RFC1960Next(filter, ref pos, ref len, out tok, out res); + BerValue result; + switch (tok) + { + case RFC1960Token.And: + result = ParseAnd(filter, ref pos, ref len, ref tok, ref res); + break; + case RFC1960Token.Or: + result = ParseOr(filter, ref pos, ref len, ref tok, ref res); + break; + case RFC1960Token.Not: + result = ParseNot(filter, ref pos, ref len, ref tok, ref res); + break; + default: + result = ParseSimple(filter, ref pos, ref len, ref tok, ref res); + break; + } + + if (tok != RFC1960Token.ClosingParenthesis) + throw new LdapException("Opening parenthesis expected at " + pos); + RFC1960Next(filter, ref pos, ref len, out tok, out res); + return result; + } + + private static String ParseValue(String filter, ref Int32 pos, ref Int32 len, ref RFC1960Token tok, ref String res) + { + if (tok != RFC1960Token.Value) + throw new LdapException("Value expected at " + pos); + String s = res; + do + { + RFC1960Next(filter, ref pos, ref len, out tok, out res); + switch (tok) + { + case RFC1960Token.Value: + s += res; + break; + case RFC1960Token.And: + case RFC1960Token.AproxEqual: + case RFC1960Token.Equal: + case RFC1960Token.GreaterOrEqual: + case RFC1960Token.LessOrEqual: + case RFC1960Token.Not: + case RFC1960Token.Or: + case RFC1960Token.Present: + s += filter.Substring(pos, len); + break; + default: + return s; + } + } while (true); + } + + private static BerValue ParseAnd(String filter, ref Int32 pos, ref Int32 len, ref RFC1960Token tok, ref String res) + { + RFC1960Next(filter, ref pos, ref len, out tok, out res); + return new BerSequence(Asn1.LDAPFILTERAND, ParseFilterList(filter, ref pos, ref len, ref tok, ref res)); + } + + private static BerValue ParseOr(String filter, ref Int32 pos, ref Int32 len, ref RFC1960Token tok, ref String res) + { + RFC1960Next(filter, ref pos, ref len, out tok, out res); + return new BerSequence(Asn1.LDAPFILTEROR, ParseFilterList(filter, ref pos, ref len, ref tok, ref res)); + } + + private static BerValue ParseNot(String filter, ref Int32 pos, ref Int32 len, ref RFC1960Token tok, ref String res) + { + RFC1960Next(filter, ref pos, ref len, out tok, out res); + return new BerSequence(Asn1.LDAPFILTERNOT, ParseFilter(filter, ref pos, ref len, ref tok, ref res)); + } + + private static BerValue[] ParseFilterList(String filter, ref Int32 pos, ref Int32 len, ref RFC1960Token tok, ref String res) + { + List val = new List(); + val.Add(ParseFilter(filter, ref pos, ref len, ref tok, ref res)); + while (tok == RFC1960Token.OpeningParenthesis) + val.Add(ParseFilter(filter, ref pos, ref len, ref tok, ref res)); + return val.ToArray(); + } + + private static BerValue ParseSimple(String filter, ref Int32 pos, ref Int32 len, ref RFC1960Token tok, ref String res) + { + if (tok != RFC1960Token.Value) + throw new LdapException("Attribute name expected at " + pos); + String attributename = res; + BerValue result; + RFC1960Next(filter, ref pos, ref len, out tok, out res); + switch (tok) + { + // ::= + // ::= | | | + // ::= '=' + // ::= '~=' + // ::= '>=' + // ::= '<=' + case RFC1960Token.Equal: + { + RFC1960Next(filter, ref pos, ref len, out tok, out res); + + String currval = ParseValue(filter, ref pos, ref len, ref tok, ref res); + + if (tok != RFC1960Token.Any) + { + result = new BerSequence(Asn1.LDAPFILTEREQUALITYMATCH, new BerString(OCTETSTRING, attributename), new BerString(OCTETSTRING, currval)); + break; + } + BerSequence items = new BerSequence(); + items.Items.Add(new BerString(LDAPFILTERSUBSTRING_INIT, currval)); + currval = null; + while (tok == RFC1960Token.Any) + { + if (currval != null) + { + items.Items.Add(new BerString(LDAPFILTERSUBSTRING_ANY, currval)); + } + RFC1960Next(filter, ref pos, ref len, out tok, out res); + currval = ParseValue(filter, ref pos, ref len, ref tok, ref res); + } + items.Items.Add(new BerString(LDAPFILTERSUBSTRING_FINAL, currval)); + result = new BerSequence(Asn1.LDAPFILTERSUBSTRING, new BerString(OCTETSTRING, attributename), items); + break; + } + // supports * too + case RFC1960Token.AproxEqual: + RFC1960Next(filter, ref pos, ref len, out tok, out res); + result = new BerSequence(Asn1.LDAPFILTERAPPROXMATCH, new BerString(OCTETSTRING, attributename), new BerString(OCTETSTRING, ParseValue(filter, ref pos, ref len, ref tok, ref res))); + break; + case RFC1960Token.GreaterOrEqual: + RFC1960Next(filter, ref pos, ref len, out tok, out res); + result = new BerSequence(Asn1.LDAPFILTERGREATEROREQUAL, new BerString(OCTETSTRING, attributename), new BerString(OCTETSTRING, ParseValue(filter, ref pos, ref len, ref tok, ref res))); + break; + case RFC1960Token.LessOrEqual: + RFC1960Next(filter, ref pos, ref len, out tok, out res); + result = new BerSequence(Asn1.LDAPFILTERLESSOREQUAL, new BerString(OCTETSTRING, attributename), new BerString(OCTETSTRING, ParseValue(filter, ref pos, ref len, ref tok, ref res))); + break; + case RFC1960Token.Present: + RFC1960Next(filter, ref pos, ref len, out tok, out res); + result = new BerString(Asn1.LDAPFILTERPRESENT, attributename); + break; + default: + throw new LdapException("expression expected at " + pos); + } + return result; + } + + internal static BerValue ParseFilter(String filter) + { + filter += "\0\0\0\0"; + Int32 len = 0; + Int32 pos = 0; + String res; + RFC1960Token tok; + RFC1960Next(filter, ref pos, ref len, out tok, out res); + BerValue val = ParseFilter(filter, ref pos, ref len, ref tok, ref res); + + if (tok != RFC1960Token.EOF) + throw new LdapException("EOF expected at " + pos); + return val; + } + } + + enum BerType + { + Integer, + UInteger, + Enumerated, + Boolean, + BitString, + String, + IpAddress, + Sequence, + Other + } + + abstract class BerValue + { + public BerValue() + { + } + + public abstract BerType Type { get; } + + public abstract Byte TypeTag { get; } + + public abstract Int32 Length { get; } + + protected abstract void IntWrite(BinaryWriter writer); + + protected abstract void IntRead(BinaryReader reader, Byte code, Int32 length); + + public void Write(BinaryWriter writer) + { + Asn1.Encode(writer, TypeTag, Length); + this.IntWrite(writer); + } + + public Int32 TotalLength + { + get + { + Int32 lLength = this.Length; + if (lLength < 0x80) + lLength++; + else if ((lLength & 0xff000000) != 0) + lLength += 5; + else if ((lLength & 0x00ff0000) != 0) + lLength += 4; + else if ((lLength & 0x0000ff00) != 0) + lLength += 3; + else + lLength += 2; + lLength++; // char code + + return lLength; + } + } + + public static BerValue Read(BinaryReader reader) + { + Int32 lLength; + Byte lClassType; + Asn1.Decode(reader, out lClassType, out lLength); + + return Read(reader, lLength, lClassType); + } + + public static BerValue Read(Byte[] val, Int32 len, Byte code) + { + return Read(new BinaryReader(new MemoryStream(val, false)), len, code); + } + + public static BerValue Read(BinaryReader reader, Int32 length, Byte code) + { + BerValue lResult; + + switch (code) + { + case Asn1.BITSTRING: + lResult = new BerBinary(); + break; + + case Asn1.ENUMERATED: + lResult = new BerEnumerated(); + break; + + case Asn1.BOOLEAN: + lResult = new BerBoolean(); + break; + + case Asn1.INTEGER: + lResult = new BerInteger(); + break; + + case Asn1.COUNTER: + case Asn1.GAUGE: + case Asn1.TIMETICKS: + lResult = new BerUInteger(); + break; + + case Asn1.LDAPREFERER: + case Asn1.SET: + case Asn1.SEQUENCE: + lResult = new BerSequence(); + break; + + case Asn1.IPADDRESS: + lResult = new BerIpAddress(); + break; + + case Asn1.OPAQUE: + case Asn1.OCTETSTRING: + lResult = new BerString(); + break; + + default: + if (code >= Asn1.LDAPSTARTRANGE && code <= Asn1.LDAPENDRANGE) + lResult = new BerSequence(); + else + lResult = new BerOther(); + break; + } + + lResult.IntRead(reader, code, length); + + return lResult; + } + } + + class BerInteger : BerValue + { + public BerInteger() + { + } + + public BerInteger(Int32 val) + { + this.fValue = val; + } + + public override BerType Type + { + get + { + return BerType.Integer; + } + } + + public override Int32 Length + { + get + { + if (this.fValue < 0) + { + if (this.fValue >= -128) + return 1; + + if (this.fValue >= -32768) + return 2; + + if (this.fValue >= -8388608) + return 3; + + return 4; + } + else + { + if (this.fValue < 128) + return 1; + + if (this.fValue < 32768) + return 2; + + if (this.fValue < 8388608) + return 3; + + return 4; + } + } + } + + public override Byte TypeTag + { + get + { + return Asn1.INTEGER; + } + } + + protected override void IntRead(BinaryReader reader, Byte code, Int32 length) + { + this.fValue = Asn1.DecodeInt(reader, length); + } + + protected override void IntWrite(BinaryWriter writer) + { + if (this.fValue < 0) + { + if (this.fValue >= -128) + { + writer.Write((Byte)this.fValue); + } + else if (this.fValue >= -32768) + { + UInt16 lValue = (UInt16)this.fValue; + writer.Write((Byte)(lValue >> 8)); + writer.Write((Byte)(lValue)); + } + else if (fValue >= -8388608) + { + UInt32 lValue = (UInt32)this.fValue; + writer.Write((Byte)(lValue >> 16)); + writer.Write((Byte)(lValue >> 8)); + writer.Write((Byte)(lValue)); + } + else + { + UInt32 lValue = (UInt32)this.fValue; + writer.Write((Byte)(lValue >> 24)); + writer.Write((Byte)(lValue >> 16)); + writer.Write((Byte)(lValue >> 8)); + writer.Write((Byte)(lValue)); + } + } + else + { + if (this.fValue < 128) + { + writer.Write((Byte)this.fValue); + } + else if (fValue < 32768) + { + UInt16 newval = (UInt16)fValue; + writer.Write((Byte)(newval >> 8)); + writer.Write((Byte)(newval)); + } + else if (this.fValue < 8388608) + { + UInt32 lValue = (UInt32)this.fValue; + writer.Write((Byte)(lValue >> 16)); + writer.Write((Byte)(lValue >> 8)); + writer.Write((Byte)(lValue)); + } + else + { + UInt32 lValue = (UInt32)this.fValue; + writer.Write((Byte)(lValue >> 24)); + writer.Write((Byte)(lValue >> 16)); + writer.Write((Byte)(lValue >> 8)); + writer.Write((Byte)(lValue)); + } + } + // Primitive. Contents octets give the value of the integer, base 256, in two's + // complement form, most significant digit first, with the minimum number of + // octets. The value 0 is encoded as a single 00 octet. + + // Some example BER encodings (which also happen to be DER encodings) are given in Table 3. + // Integer + // value BER encoding + // 0 02 01 00 + // 127 02 01 7F + // 128 02 02 00 80 + // 256 02 02 01 00 + // -128 02 01 80 + // -129 02 02 FF 7F + } + + public Int32 Value + { + get + { + return this.fValue; + } + set + { + this.fValue = value; + } + } + private Int32 fValue; + + public override String ToString() + { + return Value.ToString(); + } + } + + class BerUInteger : BerValue + { + public BerUInteger() + { + this.fTypeTag = Asn1.COUNTER; + } + + public override BerType Type + { + get + { + return BerType.UInteger; + } + } + + protected override void IntRead(BinaryReader reader, Byte code, Int32 length) + { + this.fTypeTag = code; + this.fValue = Asn1.DecodeUInt(reader, length); + } + + protected override void IntWrite(BinaryWriter writer) + { + if (this.fValue < 256) + { + writer.Write((Byte)this.fValue); + } + else if (this.fValue < 65536) + { + writer.Write((Byte)(this.fValue >> 8)); + writer.Write((Byte)this.fValue); + } + else if (this.fValue < 16777216) + { + writer.Write((Byte)(this.fValue >> 16)); + writer.Write((Byte)(this.fValue >> 8)); + writer.Write((Byte)this.fValue); + } + else + { + writer.Write((Byte)(this.fValue >> 24)); + writer.Write((Byte)(this.fValue >> 16)); + writer.Write((Byte)(this.fValue >> 8)); + writer.Write((Byte)this.fValue); + } + } + + public override Int32 Length + { + get + { + if (this.fValue < 256) + return 1; + + if (this.fValue < 65536) + return 2; + + if (this.fValue < 16777216) + return 3; + + return 4; + } + } + + public override Byte TypeTag + { + get + { + return this.fTypeTag; + } + } + + public Byte TypeCodeTag + { + get + { + return this.fTypeTag; + } + set + { + this.fTypeTag = value; + } + } + private Byte fTypeTag; + + public UInt32 Value + { + get + { + return this.fValue; + } + set + { + this.fValue = value; + } + } + private UInt32 fValue; + + public override String ToString() + { + return this.Value.ToString(); + } + } + + + class BerBinary : BerValue + { + public BerBinary() + { + } + + public override BerType Type + { + get + { + return BerType.BitString; + } + } + + protected override void IntRead(BinaryReader reader, Byte code, Int32 length) + { + this.fValue = reader.ReadBytes(length); + } + + protected override void IntWrite(BinaryWriter writer) + { + writer.Write(this.fValue); + } + + public override Int32 Length + { + get + { + return this.fValue.Length; + } + } + + public override Byte TypeTag + { + get + { + return Asn1.BITSTRING; + } + } + + public Byte[] Value + { + get + { + return this.fValue; + } + set + { + this.fValue = value; + } + } + private Byte[] fValue; + + public override String ToString() + { + return (this.Value == null) ? "" : Convert.ToString(this.Value); + } + } + + class BerBoolean : BerInteger + { + public BerBoolean() + { + } + + public BerBoolean(Boolean value) + { + this.Value = value; + } + + public new Boolean Value + { + get + { + return base.Value != 0; + } + set + { + if (value) + base.Value = 0xff; + else + base.Value = 0; + } + } + + public override BerType Type + { + get + { + return BerType.Boolean; + } + } + + public override Byte TypeTag + { + get + { + return Asn1.BOOLEAN; + } + } + + public override String ToString() + { + return this.Value.ToString(); + } + } + + class BerEnumerated : BerInteger + { + public BerEnumerated() + { + } + + public BerEnumerated(Int32 value) + { + this.Value = value; + } + + public override BerType Type + { + get + { + return BerType.Enumerated; + } + } + + public override Byte TypeTag + { + get + { + return Asn1.ENUMERATED; + } + } + } + + class BerSequence : BerValue + { + public BerSequence() + { + this.fItems = new List(); + this.fTypeTag = Asn1.SEQUENCE; + } + + public BerSequence(Byte code, params BerValue[] args) + : this() + { + this.fTypeTag = code; + if (args != null) + this.fItems.AddRange(args); + } + + public IList Items + { + get + { + return this.fItems; + } + } + private List fItems; + + public override BerType Type + { + get + { + return BerType.Sequence; + } + } + + public override Byte TypeTag + { + get + { + return this.fTypeTag; + } + } + + public Byte TypeCodeTag + { + get + { + return this.fTypeTag; + } + set + { + this.fTypeTag = value; + } + } + private Byte fTypeTag; + + public override Int32 Length + { + get + { + Int32 lLength = 0; + for (Int32 i = 0; i < this.fItems.Count; i++) + lLength += this.fItems[i].TotalLength; + + return lLength; + } + } + + protected override void IntWrite(BinaryWriter writer) + { + for (Int32 i = 0; i < this.fItems.Count; i++) + this.fItems[i].Write(writer); + } + + protected override void IntRead(BinaryReader reader, Byte code, Int32 length) + { + this.fTypeTag = code; + Byte[] lData = reader.ReadBytes(length); + this.fItems.Clear(); + + BinaryReader lReader = new BinaryReader(new MemoryStream(lData, false)); + while (lReader.PeekChar() >= 0) + this.fItems.Add(BerValue.Read(lReader)); + } + + public override String ToString() + { + StringBuilder lResult = new StringBuilder(); + lResult.Append("{"); + + for (Int32 i = 0; i < this.Items.Count; i++) + { + if (i != 0) + lResult.Append(", "); + + lResult.Append(this.Items[i].ToString()); + } + lResult.Append("}"); + + return lResult.ToString(); + } + } + + class BerString : BerValue + { + public BerString() + { + this.fTypeTag = Asn1.OCTETSTRING; + } + + public BerString(Byte typeTag, String value) + : this() + { + this.fValue = value; + this.fTypeTag = typeTag; + } + + public override BerType Type + { + get + { + return BerType.String; + } + } + + public override Int32 Length + { + get + { + return this.fValue == null ? 0 : Encoding.UTF8.GetByteCount(this.fValue); + } + } + + protected override void IntWrite(BinaryWriter writer) + { + if (this.fValue != null) + writer.Write(Encoding.UTF8.GetBytes(this.fValue)); + } + + protected override void IntRead(BinaryReader reader, Byte code, Int32 length) + { + this.fTypeTag = code; + Byte[] lValue = reader.ReadBytes(length); + this.fValue = Encoding.UTF8.GetString(lValue, 0, lValue.Length); + } + + public override Byte TypeTag + { + get + { + return this.fTypeTag; + } + } + + public String Value + { + get + { + return this.fValue; + } + set + { + this.fValue = value; + } + } + private String fValue; + + public Byte TypeCodeTag + { + get + { + return this.fTypeTag; + } + set + { + this.fTypeTag = value; + } + } + private Byte fTypeTag; + + public override String ToString() + { + if (this.Value == null) + return ""; + + return "\"" + this.Value.Replace("\"", "\"\"") + "\""; + } + } + + class BerIpAddress : BerValue + { + public BerIpAddress() + { + } + + public override BerType Type + { + get + { + return BerType.IpAddress; + } + } + + public override Byte TypeTag + { + get + { + return Asn1.IPADDRESS; + } + } + + public override Int32 Length + { + get + { + if (this.fValue.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) + return 16; + + return 4; + } + } + + protected override void IntWrite(BinaryWriter writer) + { + Byte[] lValue = this.fValue.GetAddressBytes(); + + if (this.fValue.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) + writer.Write(lValue, 0, 16); + else + writer.Write(lValue, 0, 4); + } + + protected override void IntRead(BinaryReader reader, Byte code, Int32 length) + { + this.fValue = new IPAddress(reader.ReadBytes(length)); + } + + public IPAddress Value + { + get + { + return this.fValue; + } + set + { + this.fValue = value; + } + } + private IPAddress fValue; + + public override String ToString() + { + if (this.Value == null) + return ""; + + return this.Value.ToString(); + } + } + + class BerOther : BerValue + { + public BerOther() + { + } + + public override BerType Type + { + get + { + return BerType.Other; + } + } + + public override Int32 Length + { + get + { + return fValue.Length; + } + } + + protected override void IntWrite(BinaryWriter writer) + { + writer.Write(fValue); + } + + protected override void IntRead(BinaryReader reader, Byte code, Int32 length) + { + this.fTypeTag = code; + this.fValue = reader.ReadBytes(length); + } + + public override Byte TypeTag + { + get + { + return this.fTypeTag; + } + } + + public Byte[] Value + { + get + { + return this.fValue; + } + set + { + this.fValue = value; + } + } + private Byte[] fValue; + + public Byte TypeCodeTag + { + get + { + return this.fTypeTag; + } + set + { + this.fTypeTag = value; + } + } + private Byte fTypeTag; + + public override String ToString() + { + if (this.Value == null) + return ""; + + return Convert.ToString(this.Value); + } + } +} diff --git a/Source/RemObjects.InternetPack/AssemblyInfo.cs b/Source/RemObjects.InternetPack/AssemblyInfo.cs new file mode 100644 index 0000000..49fd3e7 --- /dev/null +++ b/Source/RemObjects.InternetPack/AssemblyInfo.cs @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("RemObjects Internet Pack for .NET - Core Library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("RemObjects Software, LLC")] +[assembly: AssemblyProduct("RemObjects Internet Pack for .NET")] +[assembly: AssemblyCopyright("Copyright RemObjects Software, LLC 2003-2012. All Rights Reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("2.0.0.0")] + +[assembly: CLSCompliant(true)] +#if REMOBJECTS_SIGN_ASSEMBLY && !COMPACTFRAMEWORK +[assembly: AssemblyKeyName("RemObjectsSoftware")] +#if !COMPACTFRAMEWORK +[assembly: System.Security.AllowPartiallyTrustedCallers()] +#endif +#endif \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/AsyncHttpServer.cs b/Source/RemObjects.InternetPack/AsyncHttpServer.cs new file mode 100644 index 0000000..cd68dc7 --- /dev/null +++ b/Source/RemObjects.InternetPack/AsyncHttpServer.cs @@ -0,0 +1,736 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.IO; +using System.Net.Sockets; +using System.Text; + +namespace RemObjects.InternetPack.Http +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.HttpServer.bmp")] +#endif + public class AsyncHttpServer : AsyncServer + { + public AsyncHttpServer() + { + this.DefaultPort = 80; + this.KeepAlive = true; + this.ServerName = sServerName; + this.MaxPostSize = 4194304; + } + +#if FULLFRAMEWORK + public AsyncHttpServer(IContainer container) + : this() + { + if (container != null) + container.Add(this); + } +#endif + + public override Type GetWorkerClass() + { + return typeof(AsyncHttpWorker); + } + + #region Properties + [DefaultValue(true), Category("Server")] + public Boolean KeepAlive + { + get + { + return fKeepAlive; + } + set + { + fKeepAlive = value; + } + } + private Boolean fKeepAlive; + + [DefaultValue(true), Category("Server")] + public Boolean ValidateRequests + { + get + { + return fValidateRequests; + } + set + { + fValidateRequests = value; + } + } + private Boolean fValidateRequests; + + [DefaultValue(sServerName), Category("Server")] + public String ServerName + { + get + { + return fServerName; + } + set + { + fServerName = value; + } + } + private String fServerName; + + public const String sServerName = "Internet Pack HTTP Server"; + + [DefaultValue(4194304), Category("Server")] + public Int32 MaxPostSize + { + get + { + return fMaxPostSize; + } + set + { + fMaxPostSize = value; + } + } + private Int32 fMaxPostSize; + #endregion + + public event OnAsyncHttpRequestHandler OnBeforeHaveData; + public event OnAsyncHttpRequestHandler OnHttpRequest; + public event OnAsyncHttpRequestHandler OnHttpResponseSent; + public event OnAsyncHttpRequestHandler OnHttpResponseFailed; + + internal protected virtual void TriggerOnBeforeHaveData(Connection connection, AsyncHttpContext context) + { + if (this.OnBeforeHaveData != null) + { + OnAsyncHttpRequestArgs lEventArgs = new OnAsyncHttpRequestArgs(connection, context); + this.OnBeforeHaveData(this, lEventArgs); + } + } + + internal protected virtual void TriggerOnHttpRequest(Connection connection, AsyncHttpContext context) + { + if (this.OnHttpRequest != null) + { + OnAsyncHttpRequestArgs lEventArgs = new OnAsyncHttpRequestArgs(connection, context); + this.OnHttpRequest(this, lEventArgs); + } + } + + internal protected virtual void TriggerOnHttpResponseSent(Connection connection, AsyncHttpContext context) + { + if (this.OnHttpResponseSent != null) + { + OnAsyncHttpRequestArgs lEventArgs = new OnAsyncHttpRequestArgs(connection, context); + this.OnHttpResponseSent(this, lEventArgs); + } + } + + internal protected virtual void TriggerOnHttpResponseFailed(Connection connection, AsyncHttpContext context) + { + if (this.OnHttpResponseFailed != null) + { + OnAsyncHttpRequestArgs lEventArgs = new OnAsyncHttpRequestArgs(connection, context); + this.OnHttpResponseFailed(this, lEventArgs); + } + } + + protected internal virtual AsyncHttpContext NewContext(AsyncHttpWorker worker) + { + return new AsyncHttpContext(worker); + } + } + + public delegate void OnAsyncHttpRequestHandler(Object sender, OnAsyncHttpRequestArgs e); + + public class OnAsyncHttpRequestArgs : ConnectionEventArgs + { + public OnAsyncHttpRequestArgs(Connection connection, AsyncHttpContext context) + : base(connection) + { + this.fContext = context; + } + + public AsyncHttpContext Context + { + get + { + return this.fContext; + } + } + private readonly AsyncHttpContext fContext; + } + + public class AsyncHttpRequest : HttpRequestResponse + { + protected override Boolean Client + { + get + { + return false; + } + } + + protected override Boolean Server + { + get + { + return true; + } + } + + public Byte[] ContentBytes + { + get + { + return this.fContentBytes; + } + set + { + this.fContentBytes = value; + } + } + private Byte[] fContentBytes; + } + + public class AsyncHttpContext + { + public AsyncHttpContext(AsyncHttpWorker worker) + { + this.fWorker = worker; + this.fCurrentRequest = new AsyncHttpRequest(); + this.fCurrentResponse = new HttpServerResponse(); + } + + private readonly AsyncHttpWorker fWorker; + + #region Properties + public Connection Connection + { + get + { + return this.fWorker.DataConnection; + } + } + + public AsyncHttpRequest CurrentRequest + { + get + { + return this.fCurrentRequest; + } + } + private readonly AsyncHttpRequest fCurrentRequest; + + public HttpServerResponse CurrentResponse + { + get + { + return this.fCurrentResponse; + } + } + private readonly HttpServerResponse fCurrentResponse; + + public Boolean ResponseSent + { + get + { + return this.fResponseSent; + } + set + { + this.fResponseSent = value; + } + } + private Boolean fResponseSent; + + public Object UserData + { + get + { + return this.fUserData; + } + set + { + this.fUserData = value; + } + } + private Object fUserData; + #endregion + + public void SendResponse() + { + if (this.ResponseSent) + return; + + this.fResponseSent = true; + this.CurrentResponse.FinalizeHeader(); + this.fWorker.SendResponse(); + } + } + + public class AsyncHttpWorker : AsyncWorker + { + private AsyncHttpContext fContext; + + protected override void DoSetup() + { + this.DataConnection.MaxLineLength = 8096; + this.DataConnection.MaxLineLengthEnabled = true; + this.DataConnection.AsyncDisconnect += new EventHandler(cbDisconnect); + this.fContext = ((AsyncHttpServer)Owner).NewContext(this); + try + { + this.DataConnection.BeginReadLine(new AsyncCallback(HeaderFirstLineCallback), null); + } + catch (SocketException) + { + } + catch (ObjectDisposedException) + { + } + } + + private void cbDisconnect(Object sender, EventArgs e) + { + this.Done(); + } + + #region header reading + private void HeaderFirstLineCallback(IAsyncResult ar) + { + fContext.ResponseSent = false; + + String lFirst; + try + { + lFirst = this.DataConnection.EndReadLine(ar); + } + catch (ConnectionClosedException) + { + return; + } + catch (SocketException) + { + return; + } + catch (ObjectDisposedException) + { + return; + } + + try + { + this.fContext.CurrentRequest.Header.FirstHeader = lFirst; + this.fContext.CurrentRequest.Header.ParseFirstLine(); + } + catch (HttpHeaderException) + { + this.SendInvalidRequest(); + return; + } + catch (UrlParserException) + { + this.SendInvalidRequest(); + return; + } + + try + { + this.DataConnection.BeginReadLine(new AsyncCallback(HeaderLinesCallback), null); + } + catch (SocketException) + { + } + catch (ObjectDisposedException) + { + } + } + + private void HeaderLinesCallback(IAsyncResult ar) + { + String lHeaderLine = null; + try + { + lHeaderLine = this.DataConnection.EndReadLine(ar); + } + catch (ConnectionClosedException) + { + return; + } + catch (SocketException) + { + return; + } + catch (ObjectDisposedException) + { + return; + } + + // HTTP Request Type is already known + String lHttpMethod = this.fContext.CurrentRequest.Header.RequestType; + Boolean lRequireBody = (lHttpMethod == "POST") || (lHttpMethod == "PUT") || (lHttpMethod == "MERGE"); + + Boolean lHaveData = true; + while (lHaveData) + { + lHaveData = false; + if (lHeaderLine == "") + { + // we've got the last line. Process it + if (lRequireBody) + { + Int64 lContentLength; +#if FULLFRAMEWORK + if (!Int64.TryParse(fContext.CurrentRequest.Header.GetHeaderValue("Content-Length"), out lContentLength)) +#else + if (!LongHelper.TryParse(fContext.CurrentRequest.Header.GetHeaderValue("Content-Length"), out lContentLength)) +#endif + lContentLength = 0; + + if (lContentLength > ((AsyncHttpServer)this.Owner).MaxPostSize) + { + this.SendInvalidRequest(new Exception("Content-Length too large")); + return; + } + + try + { + ((AsyncHttpServer)this.Owner).TriggerOnBeforeHaveData(this.DataConnection, this.fContext); + } + catch (Exception ex) + { + this.SendInvalidRequest(ex); + return; + } + + if (this.fContext.ResponseSent) + return; // already triggered the required functions. + + try + { + Byte[] lData = new Byte[(Int32)lContentLength]; + DataConnection.BeginRead(lData, 0, (Int32)lContentLength, new AsyncCallback(WantBodyCallback), lData); + } + catch (SocketException) + { + } + catch (ObjectDisposedException) + { + } + + return; + } + else + { + try + { + ((AsyncHttpServer)Owner).TriggerOnHttpRequest(DataConnection, fContext); + return; + } + catch (Exception ex) + { + this.SendInvalidRequest(ex); + return; + } + } + } + + if (fContext.CurrentRequest.Header.Count >= fContext.CurrentRequest.Header.MaxHeaderLines && fContext.CurrentRequest.Header.MaxHeaderLinesEnabled) + { + SendInvalidRequest(); + return; + } + + Int32 lPosition = lHeaderLine.IndexOf(":"); + if (lPosition == -1) + { + SendInvalidRequest(); + return; + } + + String lName = lHeaderLine.Substring(0, lPosition); + String lValue = null; + if (lHeaderLine.Length > lPosition + 1) + lValue = lHeaderLine.Substring(lPosition + 2); + + fContext.CurrentRequest.Header.SetHeaderValue(lName, lValue); + lHeaderLine = DataConnection.BufferReadLine(); + if (lHeaderLine != null) + { + lHaveData = true; + continue; + } + + try + { + DataConnection.BeginReadLine(new AsyncCallback(HeaderLinesCallback), null); + } + catch (SocketException) + { + } + catch (ObjectDisposedException) + { + } + } + } + + private void WantBodyCallback(IAsyncResult ar) + { + try + { + this.DataConnection.EndRead(ar); + } + catch (ConnectionClosedException) + { + return; + } + catch (SocketException) + { + return; + } + catch (ObjectDisposedException) + { + return; + } + + this.fContext.CurrentRequest.ContentBytes = (Byte[])ar.AsyncState; + + try + { + ((AsyncHttpServer)Owner).TriggerOnHttpRequest(this.DataConnection, this.fContext); + } + catch (Exception ex) + { + this.SendInvalidRequest(ex); + return; + } + } + #endregion + + private void SendInvalidRequest(Exception ex) + { + this.fContext.CurrentResponse.Header.SetHeaderValue("Server", ((AsyncHttpServer)Owner).ServerName); + + if (ex != null) + this.fContext.CurrentResponse.SendError(500, "Server Error while processing HTTP request.", ex); + else + this.fContext.CurrentResponse.SendError(500, "Server Error while processing HTTP request."); + + if (this.fContext.CurrentResponse.ContentBytes == null) + { + if (this.fContext.CurrentResponse.ContentString != null) + this.fContext.CurrentResponse.ContentBytes = this.fContext.CurrentResponse.Encoding.GetBytes(fContext.CurrentResponse.ContentString); + else + this.fContext.CurrentResponse.ContentBytes = new Byte[0]; + } + + this.fContext.CurrentResponse.FinalizeHeader(); + Byte[] lHeader = Encoding.ASCII.GetBytes(this.fContext.CurrentResponse.Header.ToString()); + Byte[] lData = new Byte[fContext.CurrentResponse.ContentBytes.Length + lHeader.Length]; + + Array.Copy(lHeader, 0, lData, 0, lHeader.Length); + Array.Copy(fContext.CurrentResponse.ContentBytes, 0, lData, lHeader.Length, fContext.CurrentResponse.ContentBytes.Length); + + this.DataConnection.BeginWrite(lData, 0, lData.Length, new AsyncCallback(InvalidRequestCallback), null); + } + + private void SendInvalidRequest() + { + this.SendInvalidRequest(null); + } + + private void InvalidRequestCallback(IAsyncResult ar) + { + try + { + this.DataConnection.EndWrite(ar); + this.DataConnection.TriggerAsyncDisconnect(); + this.DataConnection.Disconnect(); + } + catch (ConnectionClosedException) + { + return; + } + catch (SocketException) + { + return; + } + catch (ObjectDisposedException) + { + return; + } + } + + public override void Done() + { + base.Done(); + } + + private Int32 fBodyOffset; + private Byte[] fBodyBuffer; + + public void SendResponse() + { + try + { + fContext.CurrentResponse.KeepAlive = ((AsyncHttpServer)AsyncOwner).KeepAlive; + Byte[] lHeader = Encoding.ASCII.GetBytes(fContext.CurrentResponse.Header.ToString()); + + if (lHeader.Length >= 4096 || fContext.CurrentResponse.ContentSource == ContentSource.ContentNone) + { + fBodyOffset = 0; + + switch (fContext.CurrentResponse.ContentSource) + { + case ContentSource.ContentBytes: + DataConnection.BeginWrite(lHeader, 0, fBodyOffset, new AsyncCallback(ResponseBodyCallback), fContext.CurrentResponse.ContentBytes); + break; + + case ContentSource.ContentString: + Byte[] lBuffer = fContext.CurrentResponse.Encoding.GetBytes(fContext.CurrentResponse.ContentString); + DataConnection.BeginWrite(lHeader, 0, fBodyOffset, new AsyncCallback(ResponseBodyCallback), lBuffer); + break; + + case ContentSource.ContentStream: + fContext.CurrentResponse.ContentStream.Position = 0; + DataConnection.BeginWrite(lHeader, 0, fBodyOffset, new AsyncCallback(ResponseBodyCallback), fContext.CurrentResponse.ContentStream); + break; + + default: + DataConnection.BeginWrite(lHeader, 0, fBodyOffset, new AsyncCallback(ResponseBodyCallback), null); + break; + } + } + else + { + if (fBodyBuffer == null) + fBodyBuffer = new Byte[4096]; + + Array.Copy(lHeader, 0, fBodyBuffer, 0, lHeader.Length); + fBodyOffset = fBodyBuffer.Length - lHeader.Length; + + switch (fContext.CurrentResponse.ContentSource) + { + case ContentSource.ContentBytes: + if (fBodyOffset > fContext.CurrentResponse.ContentBytes.Length) + fBodyOffset = fContext.CurrentResponse.ContentBytes.Length; + Array.Copy(fContext.CurrentResponse.ContentBytes, 0, fBodyBuffer, lHeader.Length, fBodyOffset); + DataConnection.BeginWrite(fBodyBuffer, 0, fBodyOffset + lHeader.Length, new AsyncCallback(ResponseBodyCallback), fContext.CurrentResponse.ContentBytes); + break; + + case ContentSource.ContentString: + Byte[] lBuffer = fContext.CurrentResponse.Encoding.GetBytes(fContext.CurrentResponse.ContentString); + if (fBodyOffset > lBuffer.Length) + fBodyOffset = lBuffer.Length; + Array.Copy(lBuffer, 0, fBodyBuffer, lHeader.Length, fBodyOffset); + DataConnection.BeginWrite(fBodyBuffer, 0, fBodyOffset + lHeader.Length, new AsyncCallback(ResponseBodyCallback), lBuffer); + break; + + case ContentSource.ContentStream: + fContext.CurrentResponse.ContentStream.Position = 0; + fBodyOffset = fContext.CurrentResponse.ContentStream.Read(fBodyBuffer, lHeader.Length, fBodyOffset); + DataConnection.BeginWrite(fBodyBuffer, 0, fBodyOffset + lHeader.Length, new AsyncCallback(ResponseBodyCallback), fContext.CurrentResponse.ContentStream); + break; + + default: + DataConnection.BeginWrite(lHeader, 0, fBodyOffset, new AsyncCallback(ResponseBodyCallback), null); + break; + } + } + } + catch (ConnectionClosedException) + { + ((AsyncHttpServer)Owner).TriggerOnHttpResponseFailed(DataConnection, fContext); + return; + } + catch (SocketException) + { + ((AsyncHttpServer)Owner).TriggerOnHttpResponseFailed(DataConnection, fContext); + return; + } + catch (ObjectDisposedException) + { + ((AsyncHttpServer)Owner).TriggerOnHttpResponseFailed(DataConnection, fContext); + return; + } + } + + private void ResponseBodyCallback(IAsyncResult ar) + { + try + { + DataConnection.EndWrite(ar); + + if (ar.AsyncState is Stream) + { + Stream lData = (Stream)ar.AsyncState; + Int32 lLen = lData.Read(fBodyBuffer, 0, fBodyBuffer.Length); + if (lLen != 0) + { + DataConnection.BeginWrite(fBodyBuffer, 0, lLen, new AsyncCallback(ResponseBodyCallback), lData); + return; + } + } + else if (ar.AsyncState is Byte[]) + { + Byte[] lData = (Byte[])ar.AsyncState; + Int32 lLen = fBodyBuffer.Length; + if (fBodyOffset + lLen > lData.Length) + lLen = lData.Length - fBodyOffset; + if (lLen != 0) + { + Array.Copy(lData, fBodyOffset, fBodyBuffer, 0, lLen); + fBodyOffset += lLen; + DataConnection.BeginWrite(fBodyBuffer, 0, lLen, new AsyncCallback(ResponseBodyCallback), lData); + return; + } + } + } + catch (ConnectionClosedException) + { + ((AsyncHttpServer)Owner).TriggerOnHttpResponseFailed(DataConnection, fContext); + return; + } + catch (SocketException) + { + ((AsyncHttpServer)Owner).TriggerOnHttpResponseFailed(DataConnection, fContext); + return; + } + catch (ObjectDisposedException) + { + ((AsyncHttpServer)Owner).TriggerOnHttpResponseFailed(DataConnection, fContext); + return; + } + + AsyncHttpContext lOldContext = fContext; + + fContext = ((AsyncHttpServer)Owner).NewContext(this); + try + { + DataConnection.BeginReadLine(new AsyncCallback(HeaderFirstLineCallback), null); + } + catch (SocketException) + { + ((AsyncHttpServer)Owner).TriggerOnHttpResponseFailed(DataConnection, fContext); + return; + } + + catch (ObjectDisposedException) + { + ((AsyncHttpServer)Owner).TriggerOnHttpResponseFailed(DataConnection, fContext); + return; + } + ((AsyncHttpServer)Owner).TriggerOnHttpResponseSent(DataConnection, lOldContext); + + return; + } + } +} diff --git a/Source/RemObjects.InternetPack/AsyncServer.cs b/Source/RemObjects.InternetPack/AsyncServer.cs new file mode 100644 index 0000000..e2a9b4a --- /dev/null +++ b/Source/RemObjects.InternetPack/AsyncServer.cs @@ -0,0 +1,203 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Net.Sockets; + +namespace RemObjects.InternetPack +{ + public abstract class AsyncServer : Server + { + private ArrayList fWorkers; + + public override void Open() + { + try + { + if (CloseConnectionsOnShutdown) + this.fWorkers = new ArrayList(); + else + this.fWorkers = null; + + Int32 lActualPort = this.Port; + + if (this.BindingV4 != null && this.BindV4) + { + this.BindingV4.BindUnthreaded(); + this.BindingV4.ListeningSocket.Listen(this.BindingV4.MaxWaitConnections); + this.BindingV4.ListeningSocket.BeginAccept(new AsyncCallback(AcceptCallback), this.BindingV4.ListeningSocket); + lActualPort = ((System.Net.IPEndPoint)this.BindingV4.ListeningSocket.LocalEndPoint).Port; + } + + if (this.BindingV6 != null && this.BindV6) + { + this.BindingV6.BindUnthreaded(); + this.BindingV6.ListeningSocket.Listen(this.BindingV6.MaxWaitConnections); + this.BindingV6.ListeningSocket.BeginAccept(new AsyncCallback(AcceptCallback), this.BindingV6.ListeningSocket); + lActualPort = ((System.Net.IPEndPoint)this.BindingV6.ListeningSocket.LocalEndPoint).Port; + } + + if (lActualPort != this.Port) + this.Port = lActualPort; + + this.fActive = true; + } + catch (Exception) + { + this.Close(); + throw; + } + } + + private void AcceptCallback(IAsyncResult ar) + { + try + { + Socket lSocket; + try + { + lSocket = ((Socket)ar.AsyncState).EndAccept(ar); + } + catch (System.ObjectDisposedException) + { + return; // sometimes the object is gc'd before we even get here. + } + catch (SocketException) + { + return; + } + + Object lObject = Activator.CreateInstance(this.GetWorkerClass()); + + IAsyncWorker lWorker = lObject as IAsyncWorker; + lWorker.Owner = this; + + if (this.ConnectionFactory != null) + { + lWorker.DataConnection = ConnectionFactory.CreateServerConnection(lSocket); + } + else if (this.ConnectionClass != null) + { + lWorker.DataConnection = (Connection)Activator.CreateInstance(ConnectionClass); + lWorker.DataConnection.Init(lSocket); + } +#if FULLFRAMEWORK + else if (this.SslOptions.Enabled) + { + lWorker.DataConnection = this.SslOptions.CreateServerConnection(lSocket); + } +#endif + else + { + lWorker.DataConnection = new Connection(lSocket); + } + +#if FULLFRAMEWORK + if (this.TimeoutEnabled) + { + lWorker.DataConnection.TimeoutEnabled = true; + lWorker.DataConnection.Timeout = this.Timeout; + } +#endif + if (this.MaxLineLengthEnabled) + { + lWorker.DataConnection.MaxLineLengthEnabled = true; + lWorker.DataConnection.MaxLineLength = this.MaxLineLength; + } + + if (this.fWorkers != null) + { + lock (this.fWorkers) + { + this.fWorkers.Add(lWorker); + } + } + + if (lWorker.DataConnection.BeginInitializeServerConnection(new AsyncCallback(SetupCallback), lWorker) == null) + lWorker.Setup(); + + ((Socket)ar.AsyncState).BeginAccept(new AsyncCallback(AcceptCallback), ar.AsyncState); + + } + catch + { + // Is not a perfect solution but we must not allow exceptions to escape + return; + } + } + + private void SetupCallback(IAsyncResult ar) + { + try + { + ((IAsyncWorker)ar.AsyncState).DataConnection.EndInitializeServerConnection(ar); + } + catch (Exception) // we are running async; if something escapes here the whole app goes down. + { + ((IAsyncWorker)ar.AsyncState).DataConnection.Dispose(); + return; + } + + ((IAsyncWorker)ar.AsyncState).Setup(); + } + + public override void Close() + { + try + { + if (this.BindingV4 != null && this.BindV4) + this.BindingV4.Unbind(false); + + if (this.BindingV6 != null && this.BindV6) + this.BindingV6.Unbind(false); + } + catch (Exception) + { + // avoid GC/Socket errors + } + + if (this.fWorkers != null) + { + lock (this.fWorkers) + { + for (Int32 i = this.fWorkers.Count - 1; i >= 0; i--) + { + try + { + ((IAsyncWorker)this.fWorkers[i]).DataConnection.Close(); + } + catch (SocketException) + { + } + catch (ObjectDisposedException) + { + } + } + } + } + this.fActive = false; + } + + public override Type GetWorkerClass() + { + return typeof(AsyncWorker); + } + + public void ClientClosed(IAsyncWorker worker) + { + if (this.fWorkers == null) + return; + + lock (this.fWorkers) + { + this.fWorkers.Remove(worker); + } + } + } +} diff --git a/Source/RemObjects.InternetPack/Bindings.cs b/Source/RemObjects.InternetPack/Bindings.cs new file mode 100644 index 0000000..f5dbe8a --- /dev/null +++ b/Source/RemObjects.InternetPack/Bindings.cs @@ -0,0 +1,374 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace RemObjects.InternetPack +{ +#if FULLFRAMEWORK + [TypeConverter(typeof(BindingConverter))] +#endif + public class Binding + { + public Binding() + { + this.AddressFamily = System.Net.Sockets.AddressFamily.InterNetwork; + this.Protocol = System.Net.Sockets.ProtocolType.Tcp; + } + + public Binding(AddressFamily addressFamily) + { + this.AddressFamily = addressFamily; + this.Protocol = System.Net.Sockets.ProtocolType.Tcp; + } + + #region Properties + public Int32 Port + { + get + { + return this.fPort; + } + set + { + this.fPort = value; + } + } + private Int32 fPort; + + public Int32 DefaultPort + { + get + { + return this.fDefaultPort; + } + set + { + this.fDefaultPort = value; + } + } + private Int32 fDefaultPort; + + public IPAddress Address + { + get + { + return this.fAddress; + } + set + { + this.fAddress = value; + } + } + private IPAddress fAddress; + + public AddressFamily AddressFamily + { + get + { + return this.fAddressFamily; + } + set + { + this.fAddressFamily = value; + } + } + private AddressFamily fAddressFamily; + + public SocketType SocketType + { + get + { + return this.fSocketType; + } + protected set + { + this.fSocketType = value; + } + } + private SocketType fSocketType; + + public ProtocolType Protocol + { + get + { + return this.fProtocol; + } + set + { + this.fProtocol = value; + + switch (this.Protocol) + { + case ProtocolType.Tcp: + fSocketType = System.Net.Sockets.SocketType.Stream; + break; + + case ProtocolType.Udp: + fSocketType = SocketType.Dgram; + break; + } + } + } + private ProtocolType fProtocol; + #endregion + + public Boolean ShouldSerializePort() + { + return (this.Port != this.DefaultPort); + } + } + + public class ServerBinding : Binding + { + public ServerBinding() + { + this.fListenerThreadCount = 1; + this.fMaxWaitConnections = 10; + this.fEnableNagle = false; + } + + #region Properties + [Browsable(false)] + public Socket ListeningSocket + { + get + { + return this.fListeningSocket; + } + } + private Socket fListeningSocket; + + [Browsable(false)] + public IPEndPoint EndPoint + { + get + { + return this.fEndPoint; + } + } + private IPEndPoint fEndPoint; + + [DefaultValue(10)] + public Int32 MaxWaitConnections + { + get + { + return this.fMaxWaitConnections; + } + set + { + this.fMaxWaitConnections = value; + } + } + private Int32 fMaxWaitConnections; + + [DefaultValue(1)] + public Int32 ListenerThreadCount + { + get + { + return this.fListenerThreadCount; + } + set + { + this.fListenerThreadCount = value; + } + } + private Int32 fListenerThreadCount; + + [DefaultValue(false)] + public Boolean EnableNagle + { + get + { + return this.fEnableNagle; + } + set + { + this.fEnableNagle = value; + } + } + private Boolean fEnableNagle = false; + #endregion + + private Thread[] fListenThreads; + + public virtual void Bind(IListener listener) + { + this.fEndPoint = new IPEndPoint(this.Address, this.Port); + this.fListeningSocket = new Socket(this.AddressFamily, this.SocketType, this.Protocol); + if (!this.EnableNagle) + this.fListeningSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); + this.fListeningSocket.Bind(this.fEndPoint); + this.fListeningSocket.Listen(this.MaxWaitConnections); + this.fListenThreads = new Thread[ListenerThreadCount]; + + for (Int32 i = 0; i < this.fListenThreads.Length; i++) + { + listener.ListeningSocket = this.fListeningSocket; + fListenThreads[i] = new Thread(new ThreadStart(listener.Listen)); +#if FULLFRAMEWORK + fListenThreads[i].Name = String.Format("Internet Pack Listener {0} for {1}", i, this.EndPoint.ToString()); +#endif + fListenThreads[i].Start(); + } + } + + public virtual void Unbind() + { + this.Unbind(true); + } + + public virtual void Unbind(Boolean block) + { + if (this.fListeningSocket == null) + return; + + this.fListeningSocket.Close(); +#if FULLFRAMEWORK + if (block && this.fListenThreads != null) + for (Int32 i = 0; i < this.fListenThreads.Length; i++) + this.fListenThreads[i].Join(); +#endif + } + + public virtual void BindUnthreaded() + { + this.fEndPoint = new IPEndPoint(this.Address, this.Port); + this.fListeningSocket = new Socket(this.AddressFamily, this.SocketType, this.Protocol); + if (!this.EnableNagle) + this.fListeningSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); + this.fListeningSocket.Bind(this.fEndPoint); + this.fListeningSocket.Listen(0); + } + + public virtual Connection Accept() + { + return new Connection(this.fListeningSocket.Accept()); + } + } + + #region DualBinding + internal class DualServerBinding : ServerBinding + { + public DualServerBinding() + : base() + { + this.fBindingV6 = new ServerBinding(); + } + + #region Properties + [Browsable(false)] + public Socket ListeningSocketV4 + { + get + { + return this.ListeningSocket; + } + } + private ServerBinding fBindingV6; + + [Browsable(false)] + public Socket ListeningSocketV6 + { + get + { + return this.fBindingV6.ListeningSocket; + } + } + + [Browsable(false)] + public IPEndPoint EndPointV4 + { + get + { + return this.EndPoint; + } + } + + [Browsable(false)] + public IPEndPoint EndPointV6 + { + get + { + return this.fBindingV6.EndPoint; + } + } + #endregion + + public override void Bind(IListener listener) + { + base.Bind(listener); + this.fBindingV6.Bind(listener); + } + + public override void Unbind(Boolean block) + { + base.Unbind(block); + this.fBindingV6.Unbind(block); + } + + public override void BindUnthreaded() + { + base.BindUnthreaded(); + this.fBindingV6.BindUnthreaded(); + } + } + #endregion + + #region Binding Lists + public class Bindings + { + } + + public class ServerBindings : Bindings + { + public void Bind(Listener listener) + { + } + + public void Unbind(Boolean block) + { + } + } + #endregion + + #region BindingConverter class +#if FULLFRAMEWORK + class BindingConverter : ExpandableObjectConverter + { + public override Boolean CanConvertFrom(ITypeDescriptorContext context, Type type) + { + return base.CanConvertFrom(context, type); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value) + { + return base.ConvertFrom(context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, Object value, Type destinationType) + { + if (destinationType == typeof(String) && (value is Binding)) + { + Binding lBinding = (Binding)value; + return lBinding.Address.ToString() + ":" + lBinding.Port.ToString(); + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } +#endif + #endregion +} diff --git a/Source/RemObjects.InternetPack/BoundIncomingStream.cs b/Source/RemObjects.InternetPack/BoundIncomingStream.cs new file mode 100644 index 0000000..6563c12 --- /dev/null +++ b/Source/RemObjects.InternetPack/BoundIncomingStream.cs @@ -0,0 +1,114 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.IO; + +namespace RemObjects.InternetPack +{ + public class BoundIncomingStream : Stream + { + public BoundIncomingStream(Connection connection, Int32 size) + { + this.fConnection = connection; + this.fSize = size; + this.fDataLeft = size; + } + + private Connection fConnection; + private Int32 fSize; + private Int32 fDataLeft; + + #region System.IO.Stream Methods + public override void Flush() + { + Byte[] lBuffer = new Byte[256]; + while (this.fDataLeft > 0) + this.fDataLeft -= this.Read(lBuffer, 0, lBuffer.Length); + } + + public override void Close() + { + this.Flush(); + } + + public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count) + { + if (count > this.fDataLeft) + count = this.fDataLeft; + + if (count <= 0) + return 0; + + Int32 lResult = this.fConnection.Receive(buffer, offset, count); + this.fDataLeft -= lResult; + + return lResult; + } + + public override Int64 Seek(Int64 offset, SeekOrigin origin) + { + throw new Exception(String.Format("{0} does not support Seek", this.GetType().FullName)); + } + + public override void SetLength(Int64 length) + { + throw new Exception(String.Format("{0} does not support SetLength", this.GetType().FullName)); + } + + public override void Write(Byte[] buffer, Int32 offset, Int32 count) + { + throw new Exception(String.Format("{0} is a read-only Stream", this.GetType().FullName)); + } + + public override Boolean CanRead + { + get + { + return true; + } + } + + public override Boolean CanSeek + { + get + { + return false; + } + } + + public override Boolean CanWrite + { + get + { + return false; + } + } + + public override Int64 Length + { + get + { + return this.fSize; + } + } + + public override Int64 Position + { + get + { + return this.fSize - this.fDataLeft; + } + set + { + throw new Exception(String.Format("{0} does not support Seek", this.GetType().FullName)); + } + } + #endregion + } +} diff --git a/Source/RemObjects.InternetPack/CFCompatibility.cs b/Source/RemObjects.InternetPack/CFCompatibility.cs new file mode 100644 index 0000000..abe0486 --- /dev/null +++ b/Source/RemObjects.InternetPack/CFCompatibility.cs @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; + +namespace RemObjects.InternetPack +{ + public class StringCollection : ArrayList + { + public new string this[Int32 index] + { + get + { + return (string)base[index]; + } + set + { + base[index] = value; + } + } + } + + class TypeConverterAttribute : Attribute + { + public TypeConverterAttribute(Type type) + { + } + } + + class BrowsableAttribute : Attribute + { + public BrowsableAttribute(Boolean browsable) + { + } + } + + class CategoryAttribute : Attribute + { + public CategoryAttribute(String category) + { + } + } + + class SerializableAttribute : Attribute + { + public SerializableAttribute() + { + } + } + + public static class IntHelper + { + public static Boolean TryParse(String text, out Int32 value) + { + try + { + value = Int32.Parse(text); + return true; + } + catch + { + value = 0; + return false; + } + } + } + + public static class LongHelper + { + public static Boolean TryParse(String text, out Int64 value) + { + try + { + value = Int64.Parse(text); + return true; + } + catch + { + value = 0; + return false; + } + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Client.cs b/Source/RemObjects.InternetPack/Client.cs new file mode 100644 index 0000000..56f5e12 --- /dev/null +++ b/Source/RemObjects.InternetPack/Client.cs @@ -0,0 +1,419 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.Net; +using System.Net.Sockets; +using RemObjects.InternetPack.Dns; + +namespace RemObjects.InternetPack +{ + public abstract class Client : System.ComponentModel.Component + { + protected Client() + { + this.EnableNagle = false; + this.fBindingV4 = new Binding(); + this.fBindingV6 = new Binding(AddressFamily.InterNetworkV6); + this.fDnsResolveType = DnsResolveType.Once; +#if FULLFRAMEWORK + this.fSslOptions = new SslConnectionFactory(); +#endif + } + + protected virtual void ResolveHostNameIfNeeded() + { + if (String.IsNullOrEmpty(this.HostName)) + return; + + if ((this.HostAddress == null) || (this.DnsResolveType == DnsResolveType.Always)) + this.ResolveHostName(); + } + + protected virtual void ResolveHostName() + { + String lHostName = this.HostName; + this.TriggerOnResolveHostName(ref lHostName); + + if (lHostName == null) + throw new Exception("No Hostname set"); + + this.HostAddress = DnsLookup.ResolveFirst(this.HostName); + this.TriggerOnResolvedHostName(lHostName, this.HostAddress); + } + + #region Properties + [DefaultValue(DnsResolveType.Once)] + public DnsResolveType DnsResolveType + { + get + { + return fDnsResolveType; + } + set + { + fDnsResolveType = value; + } + } + private DnsResolveType fDnsResolveType; + + [Obsolete("Please use BindingV4 and BindingV6 instead", false)] + public Binding Binding + { + get + { + return this.BindingV4; + } + } + + public Binding BindingV4 + { + get + { + return this.fBindingV4; + } + } + private Binding fBindingV4; + + public Binding BindingV6 + { + get + { + return fBindingV6; + } + } + private Binding fBindingV6; + + public Int32 Port + { + get + { + return this.fPort; + } + set + { + this.fPort = value; + } + } + private Int32 fPort; + + public String HostName + { + get + { + return this.fHostName; + } + set + { + if (this.fHostName != value) + { + this.fHostName = value; + this.fHostAddress = null; + } + } + } + private String fHostName; + + public IPAddress HostAddress + { + get + { + return this.fHostAddress; + } + set + { + this.fHostAddress = value; + if (this.fHostAddress != null) + this.fHostName = this.fHostAddress.ToString(); + else + this.fHostName = null; + } + } + private IPAddress fHostAddress; + + [Browsable(false)] + public Type ConnectionClass + { + get + { + return this.fConnectionClass; + } + set + { + + if (value != null && !value.IsSubclassOf(typeof(Connection))) + throw new Exception(String.Format("The assigned Type '{0}' is not a descendant of Connection", value.FullName)); + + this.fConnectionClass = value; + } + } + private Type fConnectionClass; + + [Browsable(false)] + public IConnectionFactory ConnectionFactory + { + get + { + return this.fConnectionFactory; + } + set + { + this.fConnectionFactory = value; + } + } + private IConnectionFactory fConnectionFactory; + + protected ConnectionPool ConnectionPool + { + get + { + return this.fConnectionPool; + } + set + { + if (value != this.fConnectionPool) + { + if (this.fConnectionPool != null) + this.fConnectionPool.Dispose(); + } + + this.fConnectionPool = value; + } + } + private ConnectionPool fConnectionPool; + +#if FULLFRAMEWORK + public SslConnectionFactory SslOptions + { + get + { + return this.fSslOptions; + } + set + { + this.fSslOptions = value; + } + } + private SslConnectionFactory fSslOptions; +#endif + + [DefaultValue(false)] + public Boolean EnableNagle + { + get + { + return this.fEnableNagle; + } + set + { + this.fEnableNagle = value; + } + } + private Boolean fEnableNagle; + #endregion + + #region Events + public delegate void OnResolveHostNameHandler(Object sender, OnResolveHostNameArgs e); + + public class OnResolveHostNameArgs : EventArgs + { + public String HostName + { + get + { + return this.fHostName; + } + set + { + this.fHostName = value; + } + } + private String fHostName; + } + + public event OnResolveHostNameHandler OnResolveHostName; + + protected virtual void TriggerOnResolveHostName(ref String hostname) + { + if (this.OnResolveHostName == null) + return; + + OnResolveHostNameArgs lEventArgs = new OnResolveHostNameArgs(); + lEventArgs.HostName = hostname; + + this.OnResolveHostName(this, lEventArgs); + + hostname = lEventArgs.HostName; + } + + public delegate void OnResolvedHostNameHandler(Object sender, OnResolvedHostNameArgs e); + + public class OnResolvedHostNameArgs : EventArgs + { + public String HostName + { + get + { + return this.fHostName; + } + set + { + this.fHostName = value; + } + } + private String fHostName; + + public IPAddress IPAddress + { + get + { + return fIPAddress; + } + set + { + fIPAddress = value; + } + } + private IPAddress fIPAddress; + } + + public event OnResolvedHostNameHandler OnResolvedHostName; + + protected void TriggerOnResolvedHostName(String hostname, IPAddress address) + { + if (this.OnResolvedHostName == null) + return; + + OnResolvedHostNameArgs lEventArgs = new OnResolvedHostNameArgs(); + lEventArgs.HostName = hostname; + lEventArgs.IPAddress = address; + hostname = lEventArgs.HostName; + this.OnResolvedHostName(this, lEventArgs); + } + #endregion + + #region Methods + public virtual Connection Connect() + { + this.ResolveHostNameIfNeeded(); + + return this.Connect(this.HostAddress, this.Port); + } + + public virtual Connection Connect(String hostname, Int32 port) + { + return this.Connect(Dns.DnsLookup.ResolveFirst(hostname), port); + } + + public virtual Connection Connect(IPAddress host, Int32 port) + { + return this.GetConnection(host, port); + } + + public virtual Connection ConnectNew(String hostname, Int32 port) + { + IPAddress lHostAddress = Dns.DnsLookup.ResolveFirst(hostname); + + return GetNewConnection(lHostAddress, port); + } + + public virtual Connection ConnectNew(IPAddress host, Int32 port) + { + return GetNewConnection(host, port); + } + + protected virtual Connection GetConnection(IPAddress host, Int32 port) + { + if (this.fConnectionPool != null) + return this.fConnectionPool.GetConnection(new IPEndPoint(host, port)); + + Binding lBinding; + switch (host.AddressFamily) + { + case AddressFamily.InterNetwork: + lBinding = this.fBindingV4; + break; + + case AddressFamily.InterNetworkV6: + lBinding = this.fBindingV6; + break; + + default: + lBinding = new Binding(host.AddressFamily); + break; + } + + Connection lConnection = this.NewConnection(lBinding); + lConnection.Connect(host, port); + + return lConnection; + } + + private Connection GetNewConnection(IPAddress host, Int32 port) + { + if (this.fConnectionPool != null) + return this.fConnectionPool.GetNewConnection(new IPEndPoint(host, port)); + + return this.GetConnection(host, port); + } + + protected virtual Connection NewConnection(Binding binding) + { + if (this.fConnectionFactory != null) + return this.fConnectionFactory.CreateClientConnection(binding); + + if (this.fConnectionClass != null) + return (Connection)Activator.CreateInstance(this.fConnectionClass); + +#if FULLFRAMEWORK + if (this.SslOptions.Enabled) + { + Connection lSslConnection = this.SslOptions.CreateClientConnection(binding); + lSslConnection.EnableNagle = this.EnableNagle; + + return lSslConnection; + } +#endif + Connection lConnection = new Connection(binding); + lConnection.EnableNagle = this.EnableNagle; + + return lConnection; + } + + public static Connection Connect(String hostname, Int32 port, Binding binding) + { + IPAddress lHostAddress = Dns.DnsLookup.ResolveFirst(hostname); + + return Client.Connect(lHostAddress, port, binding); + } + + public static Connection Connect(IPAddress host, Int32 port, Binding binding) + { + Connection lConnection = new Connection(binding); + lConnection.Connect(host, port); + + return lConnection; + } + + protected virtual void ReleaseConnection(Connection connection) + { + if (fConnectionPool != null) + fConnectionPool.ReleaseConnection(connection); + else + connection.Dispose(); + } + #endregion + } + + public enum DnsResolveType + { + Once, + Always + } +} diff --git a/Source/RemObjects.InternetPack/Client.resx b/Source/RemObjects.InternetPack/Client.resx new file mode 100644 index 0000000..dd0ea4d --- /dev/null +++ b/Source/RemObjects.InternetPack/Client.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/Source/RemObjects.InternetPack/CommandBasedClient.cs b/Source/RemObjects.InternetPack/CommandBasedClient.cs new file mode 100644 index 0000000..a689d99 --- /dev/null +++ b/Source/RemObjects.InternetPack/CommandBasedClient.cs @@ -0,0 +1,245 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Text; + +namespace RemObjects.InternetPack.CommandBased +{ + public abstract class CommandBasedClient : Client + { + protected CommandBasedClient() + { + } + + #region Properties + public Connection CurrentConnection + { + get + { + return this.fCurrentConnection; + } + } + private Connection fCurrentConnection = null; + + public String LastResponseText + { + get + { + return this.fLastResponseText; + } + } + private String fLastResponseText; + + public Int32 LastResponseNo + { + get + { + return this.fLastResponseNo; + } + } + private Int32 fLastResponseNo; + + public Boolean Connected + { + get + { + return (this.fCurrentConnection != null) && (this.fCurrentConnection.Connected); + } + } + #endregion + + protected Boolean WaitForResponse(params Int32[] validResponses) + { + String lDataString = fCurrentConnection.ReadLine(); + + this.SendLog(LogDirection.Receive, lDataString); + + String[] lResp; + Boolean lMultline; + Int32 i1 = lDataString.IndexOf('-'); + Int32 i2 = lDataString.IndexOf(' '); + + if (i1 == -1 || (i1 > i2 && !(i2 == -1))) + { + lResp = new String[2]; + lResp[0] = lDataString.Substring(0, i2); + lResp[1] = lDataString.Substring(i2 + 1); + lMultline = false; + } + else + { + lResp = new String[2]; + lResp[0] = lDataString.Substring(0, i1); + lResp[1] = lDataString.Substring(i1 + 1); + lMultline = true; + } + + try + { + this.fLastResponseNo = Int32.Parse(lResp[0]); + } + catch + { + throw new Exception("Invalid response from server"); + } + + if (lMultline) + { + String lStopSign = lResp[0] + ' '; + StringBuilder lFullResponse = new StringBuilder(lResp[1]); + + while (true) + { + lDataString = fCurrentConnection.ReadLine(); + SendLog(LogDirection.Receive, lDataString); + + if (lDataString.StartsWith(lStopSign)) + { + lFullResponse.Append(lDataString.Substring(lStopSign.Length)); + break; + } + + lFullResponse.Append(lDataString); + lFullResponse.Append('\n'); + } + fLastResponseText = lFullResponse.ToString(); + } + else + { + fLastResponseText = lResp[1]; + } + + for (Int32 i = 0; i < validResponses.Length; i++) + { + + if (fLastResponseNo == validResponses[i]) + return true; + } + + return false; + } + + protected Boolean SendAndWaitForResponse(String command, params Int32[] validResponses) + { + if (!this.Connected) + throw new Exception("Connection not open"); + + this.SendLog(LogDirection.Send, command); + this.fCurrentConnection.WriteLine(command); + + return this.WaitForResponse(validResponses); + } + + public virtual void Open() + { + if (this.fCurrentConnection != null) + { + if (this.fCurrentConnection.Connected) + this.fCurrentConnection.Close(); + this.fCurrentConnection = null; + } + + this.fCurrentConnection = this.Connect(); + } + + public virtual void Close() + { + if (this.fCurrentConnection != null) + { + this.fCurrentConnection.Close(); + this.fCurrentConnection = null; + } + } + + #region Log handling + public event ClientLogEvent OnLog; + + protected virtual void SendLog(LogDirection direction, String message) + { + if (this.OnLog != null) + this.OnLog(this, new ClientLogArgs(direction, message)); + } + + protected virtual void SendLog(LogDirection direction, String format, params Object[] parameters) + { + if (this.OnLog != null) + this.OnLog(this, new ClientLogArgs(direction, format, parameters)); + } + #endregion + } + + public enum LogDirection + { + Status, + Send, + Receive + }; + + #region Delegates and arguments + public class ClientLogArgs + { + public ClientLogArgs(LogDirection direction, String message) + { + this.Direction = direction; + + if (message.StartsWith("PASS")) + message = "PASS (hidden)"; + this.Text = message; + } + + public ClientLogArgs(LogDirection direction, String format, params Object[] parameters) + : this(direction, String.Format(format, parameters)) + { + } + + #region Properties + public LogDirection Direction + { + get + { + return this.fDirection; + } + set + { + this.fDirection = value; + } + } + private LogDirection fDirection; + + public String Text + { + get + { + return this.fText; + } + set + { + this.fText = value; + } + } + private String fText; + #endregion + } + + public delegate void ClientLogEvent(Object sender, ClientLogArgs e); + #endregion + + #region Exceptions +#if FULLFRAMEWORK + [System.Serializable()] +#endif + public class CmdResponseException : Exception + { + public CmdResponseException(String error, Int32 responseCode, String responseText) : + base(String.Format("{0}: {1} {2}", error, responseCode, responseText)) + { + } + } + #endregion +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/CommandBasedServer.cs b/Source/RemObjects.InternetPack/CommandBasedServer.cs new file mode 100644 index 0000000..00da42b --- /dev/null +++ b/Source/RemObjects.InternetPack/CommandBasedServer.cs @@ -0,0 +1,315 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Net; + +namespace RemObjects.InternetPack.CommandBased +{ + + public class SessionEventArgs : EventArgs + { + public SessionEventArgs(Object session, Connection connection, Server server) + { + this.fConnection = connection; + this.fSession = session; + this.fServer = server; + } + + private readonly Object fSession; + public Object Session + { + get + { + return this.fSession; + } + } + + private readonly Connection fConnection; + public Connection Connection + { + get + { + return this.fConnection; + } + } + + private readonly Server fServer; + public Server Server + { + get + { + return this.fServer; + } + } + } + + public class CommandEventArgs : SessionEventArgs + { + public CommandEventArgs(Object session, Connection connection, Server server) + : base(session, connection, server) + { + } + + public String Command + { + get + { + return this.fCommand; + } + set + { + this.fCommand = value; + } + } + private String fCommand; + + public String AllParameters + { + get + { + return this.fAllParameters; + } + set + { + this.fAllParameters = value; + } + } + private String fAllParameters; + + public String[] Parameters + { + get + { + return this.fParameters; + } + set + { + this.fParameters = value; + } + } + private String[] fParameters; + } + + public delegate void OnCommandHandler(Object sender, CommandEventArgs e); + + public delegate void OnClientConnectedHandler(Object sender, SessionEventArgs e); + + public delegate void OnClientDisconnectedHandler(Object sender, SessionEventArgs e); + + public abstract class CommandBasedServer : Server + { + protected CommandBasedServer() + { + this.UnknownCommand = "500 {0}: Command not understood"; + this.Greeting = "GREETING"; + this.fCommands = new Dictionary(16); + this.InitCommands(); + } + + #region Properties + public Dictionary Commands + { + get + { + return this.fCommands; + } + } + private readonly Dictionary fCommands; + + public String UnknownCommand + { + get + { + return this.fUnknownCommand; + } + set + { + this.fUnknownCommand = value; + } + } + private String fUnknownCommand; + + public String Greeting + { + get + { + return this.fGreeting; + } + set + { + this.fGreeting = value; + } + } + private String fGreeting; + + public Type SessionClass + { + get + { + return this.fSessionClass; + } + set + { + this.fSessionClass = value; + } + } + private Type fSessionClass; + #endregion + + #region Events + public event OnClientConnectedHandler OnClientConnected; + + public event OnClientDisconnectedHandler OnClientDisconnected; + + protected internal virtual void InvokeOnClientConnected(SessionEventArgs e) + { + if (this.OnClientConnected != null) + this.OnClientConnected(this, e); + } + + protected internal virtual void InvokeOnClientDisconnected(SessionEventArgs e) + { + if (this.OnClientDisconnected != null) + this.OnClientDisconnected(this, e); + } + #endregion + + protected internal virtual CommandBasedSession CreateSession() + { + if (this.SessionClass != null) + return (CommandBasedSession)Activator.CreateInstance(this.SessionClass); + + return (CommandBasedSession)Activator.CreateInstance(this.GetDefaultSessionClass()); + } + + protected abstract void InitCommands(); + + public void AddCustomCommand(String name, OnCommandHandler handler) + { + this.fCommands.Add(name, handler); + } + + public override Type GetWorkerClass() + { + return typeof(CommandBasedWorker); + } + + protected virtual Type GetDefaultSessionClass() + { + return typeof(CommandBasedSession); + } + + protected static String CleanStringForCommandResponse(String rawValue) + { + return rawValue.Replace("\n", "").Replace("\r", ""); + } + + protected internal virtual void HandleCommandException(Connection connection, Exception ex) + { + /* On error, just close the connection, descendand classes may implement + * additional/different behavior */ + connection.Close(); + } + } + + public class CommandBasedSession + { + public IPEndPoint RemoteEndPoint + { + get + { + return this.fRemoteEndPoint; + } + set + { + this.fRemoteEndPoint = value; + } + } + private IPEndPoint fRemoteEndPoint; + + public IPEndPoint LocalEndPoint + { + get + { + return this.fLocalEndPoint; + } + set + { + this.fLocalEndPoint = value; + } + } + private IPEndPoint fLocalEndPoint; + } + + class CommandBasedWorker : Worker + { + private Char[] SPACE = { ' ' }; + + protected override void DoWork() + { + CommandBasedServer lServer = (CommandBasedServer)Owner; + CommandBasedSession lSession = lServer.CreateSession(); + lSession.RemoteEndPoint = ((IPEndPoint)DataConnection.RemoteEndPoint); + lSession.LocalEndPoint = ((IPEndPoint)DataConnection.RemoteEndPoint); + + try + { + lServer.InvokeOnClientConnected(new SessionEventArgs(lSession, DataConnection, lServer)); + DataConnection.WriteLine(lServer.Greeting); + CommandEventArgs lArgs = new CommandEventArgs(lSession, DataConnection, lServer); + String lCmdLine; + + while (DataConnection.Connected) + { + lCmdLine = DataConnection.ReadLine().Trim(); + + if (lCmdLine.Length == 0) + continue; + Int32 tempidx = lCmdLine.IndexOf(' '); + + if (tempidx == -1) + { + lArgs.Command = lCmdLine.ToUpper(); + lArgs.AllParameters = ""; + lArgs.Parameters = new String[0]; + } + else + { + lArgs.Command = lCmdLine.Substring(0, tempidx).ToUpper(); + lArgs.AllParameters = lCmdLine.Substring(tempidx + 1).Trim(); + lArgs.Parameters = lArgs.AllParameters.Split(SPACE); + } + OnCommandHandler handler = (OnCommandHandler)lServer.Commands[lArgs.Command]; + + if (handler == null) + { + DataConnection.WriteLine(String.Format(lServer.UnknownCommand, lArgs.Command)); + } + else + { + try + { + handler(lServer, lArgs); + } + catch (Exception ex) + { + lServer.HandleCommandException(DataConnection, ex); + } + } + } + } + catch (Exception) + { + } + + lServer.InvokeOnClientDisconnected(new SessionEventArgs(lSession, DataConnection, lServer)); + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Connection.cs b/Source/RemObjects.InternetPack/Connection.cs new file mode 100644 index 0000000..09871fe --- /dev/null +++ b/Source/RemObjects.InternetPack/Connection.cs @@ -0,0 +1,1800 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace RemObjects.InternetPack +{ + public interface IConnectionTimeouts + { + Int32 Timeout { get; set; } + Boolean TimeoutEnabled { get; set; } + } + + public class Connection : Stream, IDisposable, IConnectionTimeouts + { + public Connection(Socket socket) + { + this.fDataSocket = socket; + + if (this.fDataSocket != null && !EnableNagle) + this.fDataSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); + + this.Initialize(); + + this.DataSocketInitializeServerConnection(); + } + + public Connection(Binding binding) + { + this.Init(binding); + this.Initialize(); + } + + private const Int32 READLINE_BUFFER_SIZE = 1024; + private const Int32 BUFFER_SIZE = 1024;//64 * 1024; + + private Boolean fTimeoutTimerEnabled; + private Boolean fBufferedAsync = true; + private System.Threading.Timer fTimeoutTimer; + + public void Init(Socket socket) + { + this.fDataSocket = socket; + + if (!this.EnableNagle) + this.fDataSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); + + this.Initialize(); + this.DataSocketInitializeServerConnection(); + } + + public void Init(Binding binding) + { + this.fBinding = binding; + this.fDataSocket = new Socket(binding.AddressFamily, binding.SocketType, binding.Protocol); + + if (!this.EnableNagle) + this.fDataSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); + } + + protected virtual void Initialize() + { + this.Timeout = DEFAULT_TIMEOUT; + this.MaxLineLength = DEFAULT_MAX_LINE_LENGTH; + } + + public override String ToString() + { + return String.Format("{0} Local: {1} Remote {2}", this.GetType().Name, this.LocalEndPoint, this.RemoteEndPoint); + } + + #region Properties + public const Int32 DEFAULT_TIMEOUT = 5 * 60; /* 5 minutes */ + public const Int32 DEFAULT_MAX_LINE_LENGTH = 4096; + + public Binding Binding + { + get + { + if (this.fBinding != null) + return this.fBinding; + + return new Binding(DataSocket.AddressFamily); + } + } + private Binding fBinding; + + public EndPoint LocalEndPoint + { + get + { + return this.DataSocket.LocalEndPoint; + } + } + + public EndPoint RemoteEndPoint + { + get + { + return this.DataSocket.RemoteEndPoint; + } + } + + public Encoding Encoding + { + get + { +#if MONOANDROID + if (this.fEncoding == null) + this.fEncoding = Encoding.UTF8; +#else + if (this.fEncoding == null) + this.fEncoding = Encoding.Default; +#endif + return this.fEncoding; + } + set + { + this.fEncoding = value; + } + } + private Encoding fEncoding; + + public virtual Boolean EnableNagle + { + get + { + return fEnableNagle; + } + set + { + fEnableNagle = value; + } + } + private Boolean fEnableNagle = false; + + public virtual Boolean Secure + { + get + { + return false; + } + } + + public Int32 Timeout + { + get + { + return this.fTimeout; + } + set + { + this.fTimeout = value; + + if (this.fTimeoutTimer != null) + { + this.fTimedOut = false; + this.fTimeoutTimer.Change(this.fTimeout * 1000, this.fTimeout * 1000); + } + } + } + private Int32 fTimeout; + + public Boolean TimedOut + { + get + { + return this.fTimedOut & this.TimeoutEnabled; + } + } + private Boolean fTimedOut; + + public Boolean BufferedAsync + { + get + { + return this.fBufferedAsync; + } + set + { + this.fBufferedAsync = value; + } + } + + public Boolean TimeoutEnabled + { + get + { + return this.fTimeoutTimer != null; + } + set + { + if (value && this.fTimeoutTimer == null) + { + this.fTimedOut = false; + this.fTimeoutTimer = new System.Threading.Timer(new System.Threading.TimerCallback(this.TimeoutElapsed), null, this.Timeout * 1000, this.Timeout * 1000); + } + else if (value && this.fTimeoutTimer != null) + { + this.fTimedOut = false; + this.fTimeoutTimer.Dispose(); + this.fTimeoutTimer = null; + } + } + } + + public Int32 MaxLineLength + { + get + { + return fMaxLineLength; + } + set + { + fMaxLineLength = value; + } + } + private Int32 fMaxLineLength; + + public Boolean MaxLineLengthEnabled + { + get + { + return fMaxLineLengthEnabled; + } + set + { + fMaxLineLengthEnabled = value; + } + } + private Boolean fMaxLineLengthEnabled; + #endregion + + #region Timeouts & Limits + private void TimeoutElapsed(Object sender) + { + if (this.fTimeoutTimerEnabled) + { + this.fTimedOut = true; + this.Abort(); + } + } + + protected void StartTimeoutTimer() + { + if (this.TimeoutEnabled) + { + this.fTimeoutTimerEnabled = true; + this.fTimedOut = false; + this.fTimeoutTimer.Change(this.Timeout * 1000, this.Timeout * 1000); + } + } + + protected void StopTimeoutTimer() + { + if (this.TimeoutEnabled) + { + this.fTimeoutTimerEnabled = false; + this.fTimeoutTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); + } + } + #endregion + + #region DataSocket access + public virtual System.Net.Sockets.Socket DataSocket + { + get + { + return fDataSocket; + } + } + private System.Net.Sockets.Socket fDataSocket; + + public virtual Boolean DataSocketConnected + { + get + { + return DataSocket.Connected; + } + } + + public virtual Int32 DataSocketAvailable + { + get + { + return this.DataSocket.Available; + } + } + + public Boolean Connected + { + get + { + return this.DataSocketConnected; + } + } + + public EndPoint OriginalEndpoint + { + get + { + return this.fOriginalEndpoint; + } + protected set + { + this.fOriginalEndpoint = value; + } + } + private EndPoint fOriginalEndpoint; + + internal protected virtual void InitializeServerConnection() + { + } + + internal protected virtual IAsyncResult BeginInitializeServerConnection(AsyncCallback callback, Object state) + { + return null; + } + + internal protected virtual void EndInitializeServerConnection(IAsyncResult ar) + { + } + + protected virtual Int32 DataSocketReceiveWhatsAvaiable(Byte[] buffer, Int32 offset, Int32 size) + { + StartTimeoutTimer(); + try + { + Int32 lReadBytes = DataSocket.Receive(buffer, offset, size, SocketFlags.None); + + if (lReadBytes == 0) + DataSocket.Close(); + + TriggerOnBytesReceived(lReadBytes); + return lReadBytes; + } + catch (ObjectDisposedException) + { + DataSocket.Close(); + return 0; + } + catch (SocketException) + { + DataSocket.Close(); + return 0; + } + finally + { + StopTimeoutTimer(); + } + } + + protected virtual Int32 DataSocketSendAsMuchAsPossible(Byte[] buffer, Int32 offset, Int32 size) + { + Int32 lSentBytes = DataSocket.Send(buffer, offset, size, SocketFlags.None); + + TriggerOnBytesSent(lSentBytes); + + return lSentBytes; + } + + protected virtual void DataSocketConnect(EndPoint endPoint) + { + fOriginalEndpoint = endPoint; + DataSocket.Connect(endPoint); + } + + protected virtual void DataSocketInitializeServerConnection() + { + } + + public void Abort() + { + if (DataSocket.Connected) + { + DataSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, new LingerOption(false, 0)); + DataSocket.Close(); + } + } + + protected virtual void DataSocketClose() + { + if (DataSocket.Connected) + { + try + { + DataSocket.Shutdown(SocketShutdown.Both); + } + catch (ObjectDisposedException) + { + } + catch (SocketException) + { + } + DataSocket.Close(); + } + } + + protected virtual void DataSocketClose(Boolean dispose) + { + try + { + if (DataSocket.Connected) + DataSocket.Shutdown(SocketShutdown.Both); + } + catch (Exception) + { + } + + if (dispose) + DataSocket.Close(); + } + + protected Int32 DataSocketReceive(Byte[] buffer, Int32 offset, Int32 size) + { + Int32 lTotalReadBytes = 0; + + do + { + Int32 lReadBytes = DataSocketReceiveWhatsAvaiable(buffer, offset, size); + if (lReadBytes == 0) + return lTotalReadBytes; + + size -= lReadBytes; + offset += lReadBytes; + lTotalReadBytes += lReadBytes; + } + while (size > 0); + + return lTotalReadBytes; + } + + protected Int32 DataSocketSend(Byte[] buffer, Int32 offset, Int32 size) + { + Int32 lTotalSentBytes = 0; + + while (size > 0) + { + Int32 lSentBytes = DataSocketSendAsMuchAsPossible(buffer, offset, size); + size -= lSentBytes; + offset += lSentBytes; + lTotalSentBytes += lSentBytes; + } + return lTotalSentBytes; + } + + protected Int32 DataSocketSend(Byte[] buffer) + { + return DataSocketSend(buffer, 0, buffer.Length); + } + #endregion + + #region Connection Methods + public virtual void Connect(EndPoint endPoint) + { + DataSocketConnect(endPoint); + } + + public virtual void Connect(IPAddress address, Int32 port) + { + this.DataSocketConnect(new IPEndPoint(address, port)); + } + + public virtual void Disconnect() + { + this.DataSocketClose(); + } + #endregion + + #region Data Access + public Int32 Available + { + get + { + Int32 lAvailable = DataSocketAvailable; + + if (fBuffer != null) + lAvailable += fBufferEnd - fBufferStart; + + return lAvailable; + } + } + + private Int32 Receive(Byte[] buffer, Int32 offset, Int32 size, Boolean block) + { + + if (fBuffer != null) + { + // still bytes in buffer? if yes, get them first + Int32 lSize = fBufferEnd - fBufferStart; + + if (lSize > size) + { + // more bytes in buffer than we need? + Array.Copy(fBuffer, fBufferStart, buffer, offset, size); + fBufferStart += size; + + return size; + } + else + { + // less (or same) number of bytes in buffer then we need? + Buffer.BlockCopy(fBuffer, fBufferStart, buffer, offset, lSize); + fBuffer = null; + + if (size > lSize && (DataSocketAvailable > 0 || block)) + { + // if more bytes werer requested, and bytes are available, get them + return lSize + DataSocketReceive(buffer, offset + lSize, size - lSize); + } + + return lSize; + } + } + else + { + /* otherwise, get data directly from socket */ + if (block) + return DataSocketReceive(buffer, offset, size); + + return DataSocketReceiveWhatsAvaiable(buffer, offset, size); + } + } + + public Int32 Receive(Byte[] buffer, Int32 offset, Int32 size) + { + return Receive(buffer, offset, size, true); + } + + public Int32 ReceiveWhatsAvailable(Byte[] buffer, Int32 offset, Int32 size) + { + return Receive(buffer, offset, size, false); + } + + public Int32 Receive(Byte[] buffer) + { + return Receive(buffer, 0, buffer.Length, true); + } + + private class DataBlock + { + private readonly Byte[] fBuffer; + private Int32 fActualSize = -1; + + public DataBlock(Int32 size) + { + this.fBuffer = new Byte[size]; + } + + public Byte[] Buffer + { + get + { + return this.fBuffer; + } + } + + public Int32 ActualSize + { + get + { + if (this.fActualSize == -1) + return this.fBuffer.Length; + + return this.fActualSize; + } + set + { + this.fActualSize = value; + } + } + } + + /// + /// Get data from connection until all bytes have been read + /// (i.e., until Receive() returns 0). Uses a constant buffersize. + /// + /// + public Byte[] ReceiveAllRemaining() + { + return ReceiveAllRemaining(BUFFER_SIZE); + } + + /// + /// Get data from connection until all bytes have been read + /// (i.e., until Receive() returns 0). Uses a buffersize passed as a parameter. + /// + /// Size of receive buffer used for each pass + /// + public Byte[] ReceiveAllRemaining(Int32 bufferSize) + { + Byte[] result = null; + + ArrayList lBlocks = new ArrayList(); + + Int32 lRead = 0; + Int32 lTotal = 0; + + Boolean lDone; + do + { + lDone = false; + + Int32 lBytesToRead = bufferSize; + + DataBlock lBuffer = new DataBlock(lBytesToRead); + + try + { + lRead = Receive(lBuffer.Buffer, 0, lBytesToRead); + } + catch (ObjectDisposedException) + { + lRead = 0; + } + catch (SocketException) + { + lRead = 0; + } + + if (lRead > 0) + { + lBuffer.ActualSize = lRead; + lBlocks.Add(lBuffer); + lTotal += lRead; + } + else + { + lDone = true; + } + } + while (!lDone); + + if (lTotal > 0) + { + Int32 lCurrent = 0; + + Byte[] lNewBuffer = new Byte[lTotal]; + + for (Int32 i = 0; i < lBlocks.Count; i++) + { + DataBlock lData = (DataBlock)(lBlocks[i]); + Buffer.BlockCopy(lData.Buffer, 0, lNewBuffer, lCurrent, lData.ActualSize); + lCurrent += lData.ActualSize; + } + + result = lNewBuffer; + } + + return result; + } + + public void ReceiveToStream(Stream stream) + { + ReceiveToStream(stream, -1, BUFFER_SIZE); + } + + public void ReceiveToStream(Stream stream, Int64 size) + { + ReceiveToStream(stream, size, BUFFER_SIZE); + } + + public void ReceiveToStream(Stream stream, Int64 size, Int32 bufferSize) + { + Int32 lRead = 0; + Int32 lTotal = 0; + + Boolean lDone; + do + { + lDone = false; + + Int64 lBytesToRead = bufferSize; + if (size > -1 && size - lTotal < bufferSize) + lBytesToRead = size - lTotal; + + Byte[] lBuffer = new Byte[lBytesToRead]; + + lRead = Receive(lBuffer, 0, (Int32)lBytesToRead); + + if (lRead > 0) + { + stream.Write(lBuffer, 0, lRead); + lTotal += lRead; + } + else + { + lDone = true; + } + + if (size > -1 && lTotal >= size) + lDone = true; + } + while (!lDone); + } + + public void SendFromStream(Stream stream) + { + SendFromStream(stream, -1, BUFFER_SIZE); + } + + public void SendFromStream(Stream stream, Int64 size) + { + SendFromStream(stream, size, BUFFER_SIZE); + } + + public void SendFromStream(Stream stream, Int64 size, Int32 bufferSize) + { + if (size == -1) + size = stream.Length - stream.Position; + + Int32 lSent = 0; + Int32 lTotal = 0; + + Boolean lDone; + do + { + lDone = false; + + Int64 lBytesToSend = bufferSize; + if (size - lTotal < bufferSize) + lBytesToSend = size - lTotal; + + Byte[] lBuffer = new Byte[lBytesToSend]; + stream.Read(lBuffer, 0, (Int32)lBytesToSend); + + lSent = Send(lBuffer, 0, (Int32)lBytesToSend); + + lTotal += lSent; + + if (lTotal >= size) + lDone = true; + } + while (!lDone); + } + + public Int32 Send(Byte[] buffer, Int32 offset, Int32 size) + { + return DataSocketSend(buffer, offset, size); + } + + public Int32 Send(Byte[] buffer) + { + return DataSocketSend(buffer, 0, buffer.Length); + } + + public virtual void SkipBytes(Int32 aSize) + { + if (aSize <= 0) + return; + + Byte[] lBuffer = new Byte[BUFFER_SIZE]; + while (aSize > 0) + { + // lBytesRead can be 0 if connection is closed + Int32 lBytesRead = Read(lBuffer, 0, aSize > BUFFER_SIZE ? BUFFER_SIZE : aSize); + + if (lBytesRead == 0) + break; + + aSize -= lBytesRead; + } + } + #endregion + + #region System.IO.Stream Methods + public override void Flush() + { + } + + public override void Close() + { + this.DataSocketClose(); + base.Close(); + + if (fTimeoutTimer != null) + { + fTimeoutTimer.Dispose(); + fTimeoutTimer = null; + } + } + + public void Close(Boolean dispose) + { + DataSocketClose(dispose); + + if (dispose) + base.Close(); + } + + public override Int32 Read(Byte[] buffer, Int32 offset, Int32 size) + { + return Receive(buffer, offset, size, true); + } + + public override Int64 Seek(Int64 offset, SeekOrigin origin) + { + throw new Exception(String.Format("{0} does not support seeking", this.GetType().FullName)); + } + + public override void SetLength(Int64 length) + { + throw new Exception(String.Format("{0} does not support SetLength", this.GetType().FullName)); + } + + public override void Write(Byte[] buffer, Int32 offset, Int32 size) + { + DataSocketSend(buffer, offset, size); + } + + public override Boolean CanRead + { + get + { + return true; + } + } + + public override Boolean CanSeek + { + get + { + return false; + } + } + + public override Boolean CanWrite + { + get + { + return true; + } + } + + public override Int64 Length + { + get + { + return fPosition + DataSocketAvailable; + } + } + + public override Int64 Position + { + get + { + return fPosition; + } + set + { + Seek(value, SeekOrigin.Begin); + fPosition = value; + } + } + private Int64 fPosition; + #endregion + + #region Async + private class AsyncRequest : IAsyncResult + { + public Byte[] AsyncBuffer + { + get + { + return fAsyncBuffer; + } + set + { + fAsyncBuffer = value; + } + } + private Byte[] fAsyncBuffer; + + public Int32 AsyncOffset + { + get + { + return fAsyncOffset; + } + set + { + fAsyncOffset = value; + } + } + private Int32 fAsyncOffset; + + public Int32 AsyncCount + { + get + { + return fAsyncCount; + } + set + { + fAsyncCount = value; + } + } + private Int32 fAsyncCount; + + public AsyncCallback AsyncCallback + { + get + { + return fAsyncCallback; + } + set + { + fAsyncCallback = value; + } + } + private AsyncCallback fAsyncCallback; + + public Object AsyncState + { + get + { + return fState; + } + set + { + fState = value; + } + } + private Object fState; + + public Int32 AsyncRest + { + get + { + return fAsyncRest; + } + set + { + fAsyncRest = value; + } + } + private Int32 fAsyncRest; + + Object IAsyncResult.AsyncState + { + get + { + return fState; + } + } + + public Boolean CompletedSynchronously + { + get + { + return false; + } + } + + public System.Threading.WaitHandle AsyncWaitHandle + { + get + { + return null; + } + } + + public Boolean IsCompleted + { + get + { + return fAsyncRest == 0; + } + } + } + + /// + /// Raised when an async read fails and the socket is disconnected; Will not work when not using async reading + /// + public event EventHandler AsyncDisconnect; + + /// + /// Raised when there is data received but it's not complete yet. can be used for timeout handling. + /// + public event EventHandler AsyncHaveIncompleteData; + + internal protected virtual void TriggerAsyncDisconnect() + { + if (AsyncDisconnect != null) + AsyncDisconnect(this, EventArgs.Empty); + } + + internal protected virtual void TriggerAsyncHaveIncompleteData() + { + if (AsyncHaveIncompleteData != null) + AsyncHaveIncompleteData(this, EventArgs.Empty); + } + + private class BufferAsyncResult : IAsyncResult + { + public BufferAsyncResult(Byte[] buffer, Int32 offset, Int32 count, Object aState) + { + fBuffer = buffer; + fOffset = offset; + fCount = count; + fAsyncState = aState; + } + + #region IAsyncResult Members + public Object AsyncState + { + get + { + return fAsyncState; + } + } + public readonly Object fAsyncState; + + public System.Threading.WaitHandle AsyncWaitHandle + { + get + { + return null; + } + } + + public Boolean CompletedSynchronously + { + get + { + return true; + } + } + + public Boolean IsCompleted + { + get + { + return true; + } + } + + public Byte[] Buffer + { + get + { + return this.fBuffer; + } + set + { + this.fBuffer = value; + } + } + public Byte[] fBuffer; + + public Int32 Offset + { + get + { + return this.fOffset; + } + set + { + this.fOffset = value; + } + } + public Int32 fOffset; + + public Int32 Count + { + get + { + return this.fCount; + } + set + { + this.fCount = value; + } + } + public Int32 fCount; + #endregion + } + + protected virtual IAsyncResult IntBeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) + { + if (fBuffer != null && fBufferEnd - fBufferStart > 0) + { + IAsyncResult lAr = new BufferAsyncResult(buffer, offset, count, state); + callback(lAr); + + return lAr; + } + + return fDataSocket.BeginReceive(buffer, offset, count, SocketFlags.None, callback, state); + } + + protected virtual Int32 IntEndRead(IAsyncResult ar) + { + if (ar is BufferAsyncResult) + { + BufferAsyncResult lBufferResult = (BufferAsyncResult)ar; + if (fBufferEnd - fBufferStart > lBufferResult.fCount) + { + for (Int32 i = 0; i < lBufferResult.fCount; i++) + lBufferResult.fBuffer[lBufferResult.fOffset + i] = fBuffer[i + fBufferStart]; + + fBufferStart += lBufferResult.fCount; + + return lBufferResult.fCount; + } + else + { + Int32 lSize = fBufferEnd - fBufferStart; + for (Int32 i = 0; i < lSize; i++) + lBufferResult.fBuffer[lBufferResult.fOffset + i] = fBuffer[i + fBufferStart]; + + fBufferStart = 0; + fBufferEnd = 0; + + return lSize; + } + } + + return fDataSocket.EndReceive(ar); + } + + protected virtual void IntEndWrite(IAsyncResult ar) + { + this.fDataSocket.EndSend(ar); + } + + protected virtual IAsyncResult IntBeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) + { + return this.fDataSocket.BeginSend(buffer, offset, count, SocketFlags.None, callback, state); + } + + // returns null if there's nothing in the buffer; returns "" if it's an empty line else the line in the buffer + public String BufferReadLine() + { + if (!BufferedAsync) + throw new Exception("BufferedAsync must be true to use BeginReadLine"); + + if (fBuffer != null) + { + for (Int32 i = fBufferStart; i < fBufferEnd; i++) + { + if (fBuffer[i] == 10) + { + Int32 lLen = i - fBufferStart; + if (lLen > 0 && fBuffer[i - 1] == 13) + lLen--; + + String lData = Encoding.GetString(fBuffer, fBufferStart, lLen); + fBufferStart = i + 1; + + return lData; + } + } + } + + return null; + } + + public IAsyncResult BeginReadLine(AsyncCallback callback, Object state) + { + if (!BufferedAsync) + throw new Exception("BufferedAsync must be true to use BeginReadLine"); + + AsyncReadLineRequest lRequest; + if (fBuffer != null) + { + for (Int32 i = fBufferStart; i < fBufferEnd; i++) + { + if (fBuffer[i] == 10) + { + Int32 lLen = i - fBufferStart; + + if (lLen > 0 && fBuffer[i - 1] == 13) + lLen--; + + lRequest = new AsyncReadLineRequest(state, callback); + lRequest.Data.Write(fBuffer, fBufferStart, lLen); + + lRequest.State = AsyncReadLineState.SyncDone; + fBufferStart = i + 1; + + callback(lRequest); + + return lRequest; + } + } + } + + lRequest = new AsyncReadLineRequest(state, callback); + if (fBuffer != null) + lRequest.Data.Write(fBuffer, fBufferStart, fBufferEnd - fBufferStart); + else + fBuffer = new Byte[BUFFER_SIZE]; + + fBufferStart = 0; + fBufferEnd = 0; + + try + { + IntBeginRead(fBuffer, 0, fBuffer.Length, new AsyncCallback(IntReadLineCallback), lRequest); + } + catch (ObjectDisposedException) // disconnect from this side + { + TriggerAsyncDisconnect(); + throw; + } + catch (SocketException) // disconnect + { + TriggerAsyncDisconnect(); + throw; + } + + return lRequest; + } + + private enum AsyncReadLineState + { + Start, + Done, + MaxLineLengthReached, + SyncDone + } + + private class AsyncReadLineRequest : IAsyncResult + { + public AsyncReadLineRequest(Object state, AsyncCallback callback) + { + fAsyncState = state; + fCallback = callback; + this.fData = new MemoryStream(1024); + } + + #region IAsyncResult Members + public Object AsyncState + { + get + { + return fAsyncState; + } + } + private readonly Object fAsyncState; + + public System.Threading.WaitHandle AsyncWaitHandle + { + get + { + return null; + } + } + + public Boolean CompletedSynchronously + { + get + { + return fState == AsyncReadLineState.SyncDone; + } + } + + public Boolean IsCompleted + { + get + { + return fState >= AsyncReadLineState.Done; + } + } + + public AsyncReadLineState State + { + get + { + return this.fState; + } + set + { + this.fState = value; + } + } + private AsyncReadLineState fState; + + public MemoryStream Data + { + get + { + return this.fData; + } + } + private readonly MemoryStream fData; + + public AsyncCallback Callback + { + get + { + return this.fCallback; + } + } + private readonly AsyncCallback fCallback; + #endregion + } + + private void IntReadLineCallback(IAsyncResult ar) + { + AsyncReadLineRequest lRequest = (AsyncReadLineRequest)ar.AsyncState; + Int32 lCount; + try + { + lCount = IntEndRead(ar); + if (lCount == 0) + { + TriggerAsyncDisconnect(); + return; + } + + for (Int32 i = 0; i < lCount; i++) + { + if (fBuffer[i] == 10) + { + if (i > 0 && fBuffer[i - 1] == 13) + lRequest.Data.Write(fBuffer, 0, i - 1); + else + lRequest.Data.Write(fBuffer, 0, i); + + fBufferStart = i + 1; + fBufferEnd = lCount; + lRequest.State = AsyncReadLineState.Done; + lRequest.Callback(lRequest); + + return; + } + } + + if (lRequest.Data.Length > fMaxLineLength && fMaxLineLengthEnabled) + { + fBufferStart = 0; + fBufferEnd = lCount; + lRequest.State = AsyncReadLineState.MaxLineLengthReached; + lRequest.Callback(lRequest); + + return; + } + + lRequest.Data.Write(fBuffer, 0, lCount); + TriggerAsyncHaveIncompleteData(); + IntBeginRead(fBuffer, 0, fBuffer.Length, new AsyncCallback(IntReadLineCallback), lRequest); + } + catch (ObjectDisposedException) // disconnect from this side + { + TriggerAsyncDisconnect(); + return; + } + catch (SocketException) // disconnect + { + TriggerAsyncDisconnect(); + return; + } + catch (ArgumentException) + { + return; + } + catch (Exception) // different platforms throw different exceptions + { + TriggerAsyncDisconnect(); + return; + } + } + + public String EndReadLine(IAsyncResult ar) + { + AsyncReadLineRequest lRequest = (AsyncReadLineRequest)ar; + try + { + if (lRequest.State == AsyncReadLineState.MaxLineLengthReached) + { + TriggerAsyncDisconnect(); + Disconnect(); + throw new ConnectionClosedException("Size limit for ReadLine() was exceeded."); + } + + Byte[] lBuffer = lRequest.Data.GetBuffer(); + if (lRequest.Data.Length > 0 && lBuffer[lRequest.Data.Length - 1] == 13) + return Encoding.GetString(lBuffer, 0, (Int32)lRequest.Data.Length - 1); // on the rare occasion that the split is between the #13 and the #10 + + return Encoding.GetString(lBuffer, 0, (Int32)lRequest.Data.Length); + } + finally + { + lRequest.Data.Close(); + } + } + + public override IAsyncResult BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) + { + if (!fBufferedAsync) + return IntBeginRead(buffer, offset, count, callback, state); + + AsyncRequest lRequest = new AsyncRequest(); + lRequest.AsyncBuffer = buffer; + lRequest.AsyncOffset = offset; + lRequest.AsyncCount = count; + lRequest.AsyncRest = count; + lRequest.AsyncCallback = callback; + lRequest.AsyncState = state; + + try + { + IntBeginRead(buffer, offset, count, new AsyncCallback(IntReadCallback), lRequest); + } + catch (ObjectDisposedException) // disconnect from this side + { + TriggerAsyncDisconnect(); + throw; + } + catch (SocketException) // disconnect + { + TriggerAsyncDisconnect(); + throw; + } + + return lRequest; + } + + private void IntReadCallback(IAsyncResult ar) + { + AsyncRequest lRequest = (AsyncRequest)ar.AsyncState; + Int32 lCount; + + try + { + lCount = IntEndRead(ar); + } + catch (ObjectDisposedException) // disconnect from this side + { + TriggerAsyncDisconnect(); + return; + } + catch (SocketException) // disconnect + { + TriggerAsyncDisconnect(); + return; + } + catch (ArgumentException) // disconnect + { + return; + } + + if (lCount == 0) + { + TriggerAsyncDisconnect(); + return; + } + + lRequest.AsyncRest = lRequest.AsyncRest - lCount; + if (lRequest.AsyncRest > 0) + { + lRequest.AsyncOffset = lRequest.AsyncOffset + lCount; + try + { + TriggerAsyncHaveIncompleteData(); + IntBeginRead(lRequest.AsyncBuffer, lRequest.AsyncOffset, lRequest.AsyncRest, new AsyncCallback(IntReadCallback), lRequest); + } + catch (ObjectDisposedException) // disconnect from this side + { + TriggerAsyncDisconnect(); + return; + } + catch (SocketException) // disconnect + { + TriggerAsyncDisconnect(); + return; + } + } + else + { + lRequest.AsyncCallback(lRequest); + } + } + + public override Int32 EndRead(IAsyncResult ar) + { + if (fBufferedAsync) + return ((AsyncRequest)ar).AsyncCount - ((AsyncRequest)ar).AsyncRest; + + return IntEndRead(ar); + } + + public override IAsyncResult BeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) + { + try + { + return IntBeginWrite(buffer, offset, count, callback, state); + } + catch (SocketException) // disconnect + { + TriggerAsyncDisconnect(); + throw; + } + } + + public override void EndWrite(IAsyncResult ar) + { + try + { + IntEndWrite(ar); + } + catch (SocketException) // disconnect + { + TriggerAsyncDisconnect(); + throw; + } + catch (ObjectDisposedException) + { + TriggerAsyncDisconnect(); + throw; + } + } + + public virtual IAsyncResult BeginConnect(EndPoint endPoint, AsyncCallback callback, Object state) + { + return fDataSocket.BeginConnect(endPoint, callback, state); + } + + public virtual IAsyncResult BeginConnect(IPAddress address, Int32 port, AsyncCallback callback, Object state) + { + return fDataSocket.BeginConnect(new IPEndPoint(address, port), callback, state); + } + + public virtual void EndConnect(IAsyncResult ar) + { + fDataSocket.EndConnect(ar); + } + #endregion + + #region ReadLine/WriteLine + private Byte[] fBuffer; + private Int32 fBufferStart; + private Int32 fBufferEnd; + + public virtual String ReadLine() + { + Boolean lDone = false; + String lResult = ""; + + while (!lDone) + { + if (fBuffer == null) + { + fBuffer = new Byte[READLINE_BUFFER_SIZE]; + fBufferStart = 0; + fBufferEnd = DataSocketReceiveWhatsAvaiable(fBuffer, 0, fBuffer.Length); + + if (fBufferEnd == 0) + throw new ConnectionClosedException(); + } + + Int32 i = fBufferStart; + while (i < fBufferEnd && !lDone) + { + + if (fBuffer[i] == 10) + lDone = true; + + i++; + } + + if (lDone) + { + if ((i - 2 >= fBufferStart) && fBuffer[i - 2] == 13) + { + // if last character is 13, of yes we have a Windows style CRLF line end and must discard the CR, too + lResult = lResult + Encoding.GetString(fBuffer, fBufferStart, i - fBufferStart - 2); + } + else + { + // else just discard the 10 (LF) + lResult = lResult + Encoding.GetString(fBuffer, fBufferStart, i - fBufferStart - 1); + } + } + else + { + lResult = lResult + Encoding.GetString(fBuffer, fBufferStart, i - fBufferStart); + } + + if (lDone) + { + /* Keep remaining buffer for future reads. */ + fBufferStart = i; + + if (fBufferStart == fBufferEnd) + fBuffer = null; + } + else + { + fBuffer = null; + } + + // Enforce Line Length checking + if (MaxLineLengthEnabled && lResult.Length > MaxLineLength) + { + Disconnect(); + throw new ConnectionClosedException("Size limit for ReadLine() was exceeded."); + } + } + + return lResult; + } + + public Byte[] CRLF = { 13, 10 }; + public Byte[] LF = { 10 }; + + public virtual void WriteLine(String line, params Object[] args) + { + WriteLine(String.Format(line, args)); + } + + public virtual void WriteLineLF(String aLine, params Object[] args) + { + WriteLineLF(String.Format(aLine, args)); + } + + public virtual void WriteLine(String line) + { + Byte[] lBytes = Encoding.GetBytes(line); + + if (lBytes != null && lBytes.Length > 0) + DataSocketSend(lBytes, 0, lBytes.Length); + + DataSocketSend(CRLF, 0, 2); + } + + public virtual void WriteLineLF(String line) + { + Byte[] lBytes = Encoding.GetBytes(line); + + if (lBytes != null && lBytes.Length > 0) + DataSocketSend(lBytes, 0, lBytes.Length); + DataSocketSend(LF, 0, 1); + } + #endregion + + #region Connection Pooling properties + public DateTime LastUsed + { + get + { + return fLastUsed; + } + set + { + fLastUsed = value; + } + } + private DateTime fLastUsed; + + public ConnectionPool Pool + { + get + { + return fPool; + } + set + { + fPool = value; + } + } + private ConnectionPool fPool; + #endregion + + #region IDisposable Members +#if FULLFRAMEWORK + public new void Dispose() +#endif +#if COMPACTFRAMEWORK + public void Dispose() +#endif + { + if (this.Connected) + this.Disconnect(); + + if (this.fTimeoutTimer != null) + { + this.fTimeoutTimer.Dispose(); + this.fTimeoutTimer = null; + } + } + #endregion + + #region Statistics + public void ResetStatistics() + { + fBytesSent = 0; + + /* if we have still bytes in the buffer, we'll still want to count then against future "received" counts */ + if (fBuffer != null) + fBytesReceived = fBufferEnd - fBufferStart; + else + fBytesReceived = 0; + } + + public Int64 BytesSent + { + get + { + return fBytesSent; + } + } + private Int64 fBytesSent; + + public Int64 BytesReceived + { + get + { + return fBytesReceived; + } + } + private Int64 fBytesReceived; + + public event EventHandler OnBytesSent; + + public event EventHandler OnBytesReceived; + + protected void TriggerOnBytesSent(Int64 count) + { + fBytesSent += count; + if (OnBytesSent != null) + OnBytesSent(this, EventArgs.Empty); + } + protected void TriggerOnBytesReceived(Int64 count) + { + fBytesReceived += count; + if (OnBytesReceived != null) + OnBytesReceived(this, EventArgs.Empty); + } + #endregion + } + + #region Delegates + public delegate void ConnectionEventHandler(Object sender, ConnectionEventArgs e); + + public class ConnectionEventArgs : EventArgs + { + public ConnectionEventArgs(Connection connection) + : base() + { + this.fConnection = connection; + } + + public Connection Connection + { + get + { + return this.fConnection; + } + } + private readonly Connection fConnection; + } + #endregion + + [Serializable] + public class ConnectionClosedException : System.Exception + { + public ConnectionClosedException() + : base("Connection was closed.") + { + } + + public ConnectionClosedException(Boolean timeout) + : base(timeout ? "Timeout executing operation" : "Connection was closed.") + { + this.fTimeout = timeout; + } + + public ConnectionClosedException(String message) + : base("Connection was closed; " + message) + { + } + + public ConnectionClosedException(Exception innerException) + : base("Connection was closed.", innerException) + { + } + + public Boolean Timeout + { + get + { + return fTimeout; + } + } + private readonly Boolean fTimeout; + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/ConnectionPool.cs b/Source/RemObjects.InternetPack/ConnectionPool.cs new file mode 100644 index 0000000..866b987 --- /dev/null +++ b/Source/RemObjects.InternetPack/ConnectionPool.cs @@ -0,0 +1,333 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace RemObjects.InternetPack +{ + public class ConnectionPool : IDisposable + { + public ConnectionPool(Binding bindingV4, Binding bindingV6) + : base() + { + this.fBindingV4 = bindingV4; + this.fBindingV6 = bindingV6; + this.fCleanupTimer = new Timer(new TimerCallback(Cleanup), null, 30000, 30000); + this.fMaxQueuePerHost = 5; + this.fTimeout = 260; + } + + public ConnectionPool() + : this(new Binding(), new Binding(AddressFamily.InterNetworkV6)) + { + } + + private Timer fCleanupTimer; + private Hashtable fCache = new Hashtable(); + private Binding fBindingV4; + private Binding fBindingV6; + + [Obsolete("Please use BindingV4 and BindingV6 instead", false)] + public Binding Binding + { + get + { + return this.fBindingV4; + } + } + + public Binding BindingV4 + { + get + { + return this.fBindingV4; + } + } + + public Binding BindingV6 + { + get + { + return this.fBindingV6; + } + } + + public Int32 MaxQueuePerHost + { + get + { + return this.fMaxQueuePerHost; + } + set + { + this.fMaxQueuePerHost = value; + } + } + private Int32 fMaxQueuePerHost; + + public Int32 Timeout + { + get + { + return this.fTimeout; + } + set + { + this.fTimeout = value; + } + } + private Int32 fTimeout; + + public IConnectionFactory ConnectionFactory + { + get + { + return this.fConnectionFactory; + } + set + { + this.fConnectionFactory = value; + } + } + private IConnectionFactory fConnectionFactory; + + public Type ConnectionClass + { + get + { + return this.fConnectionClass; + } + set + { + + if (value != null && !value.IsSubclassOf(typeof(Connection))) + throw new Exception(String.Format("The assigned Type '{0}' is not a descendant of Connection", value.FullName)); + this.fConnectionClass = value; + } + } + private Type fConnectionClass; + + public void Cleanup(Object state) + { + DateTime ExpireTime = DateTime.Now.AddSeconds(-this.fTimeout); + lock (this.fCache) + { + foreach (DictionaryEntry entry in this.fCache) + { + ConnectionQueue lQueue = (ConnectionQueue)entry.Value; + Boolean lModified = false; + + for (Int32 i = lQueue.UnderlyingArray.Length - 1; i >= 0; i--) + { + if (lQueue.UnderlyingArray[i] != null && lQueue.UnderlyingArray[i].LastUsed < ExpireTime) + { + lModified = true; + lQueue.UnderlyingArray[i].Dispose(); + lQueue.UnderlyingArray[i] = null; + } + } + if (lModified) + lQueue.RemoveNulls(); + } + } + } + + public virtual Connection GetConnection(EndPoint endPoint) + { + String lHost = endPoint.ToString(); + + lock (this.fCache) + { + ConnectionQueue lQueue = this.fCache.ContainsKey(lHost) ? (ConnectionQueue)this.fCache[lHost] : null; + if (lQueue != null && lQueue.Count > 0) + return lQueue.Dequeue(); + } + + return this.GetNewConnection(endPoint); + } + + public virtual Connection GetNewConnection(EndPoint endPoint) + { + Binding lBinding; + switch (endPoint.AddressFamily) + { + case AddressFamily.InterNetwork: + lBinding = fBindingV4; + break; + + case AddressFamily.InterNetworkV6: + lBinding = fBindingV6; + break; + + default: + lBinding = new Binding(endPoint.AddressFamily); + break; + } + + Connection lConnection; + if (this.fConnectionFactory != null) + lConnection = this.fConnectionFactory.CreateClientConnection(lBinding); + else if (fConnectionClass != null) + lConnection = (Connection)Activator.CreateInstance(this.fConnectionClass); + else + lConnection = new Connection(lBinding); + + lConnection.Connect(endPoint); + lConnection.LastUsed = DateTime.Now; + lConnection.Pool = this; + + return lConnection; + } + + public virtual void ReleaseConnection(Connection connection) + { + String lHost = connection.OriginalEndpoint.ToString(); + + if (!connection.Connected) + { + connection.Dispose(); + return; + } + + lock (this.fCache) + { + ConnectionQueue lQueue; + if (this.fCache.ContainsKey(lHost)) + lQueue = (ConnectionQueue)this.fCache[lHost]; + else + { + lQueue = new ConnectionQueue(this.fMaxQueuePerHost == 0 ? 8 : fMaxQueuePerHost); + this.fCache.Add(lHost, lQueue); + } + + if (lQueue.Count < this.fMaxQueuePerHost || this.fMaxQueuePerHost < 1) + { + ((Connection)connection).LastUsed = DateTime.Now; + lQueue.Enqueue((Connection)connection); + } + else + { + connection.Dispose(); + } + } + } + + #region IDisposable Members + public virtual void Dispose() + { + lock (this.fCache) + { + foreach (DictionaryEntry entry in this.fCache) + { + while (((ConnectionQueue)entry.Value).Count > 0) + { + try + { + ((ConnectionQueue)entry.Value).Dequeue().Dispose(); + } + catch (System.Net.Sockets.SocketException) // ignore socket errors + { + } + } + this.fCache.Clear(); + } + } + + if (this.fCleanupTimer != null) + { + this.fCleanupTimer.Dispose(); + this.fCleanupTimer = null; + } + } + #endregion + } + + static class DefaultPool + { + static public ConnectionPool ConnectionPool + { + get + { + return fConnectionPool; + } + } + static private ConnectionPool fConnectionPool = new ConnectionPool(); + } + + class ConnectionQueue + { + public ConnectionQueue(Int32 capacity) + { + this.fCapacity = capacity; + this.fArray = new Connection[this.fCapacity]; + this.fCount = 0; + this.fHead = 0; + this.fTail = 0; + } + + private Int32 fCapacity; + private Int32 fHead; + private Int32 fTail; + + public Connection[] UnderlyingArray + { + get + { + return this.fArray; + } + } + private Connection[] fArray; + + public Int32 Count + { + get + { + return this.fCount; + } + } + private Int32 fCount; + + public void RemoveNulls() + { + this.fHead = 0; + this.fTail = 0; + this.fCount = 0; + + for (Int32 i = 0; i < this.fCapacity; i++) + if (this.fArray[i] != null) + this.Enqueue(this.fArray[i]); + } + + public void Enqueue(Connection connection) + { + if (this.fCount == fCapacity || connection == null) + throw new ArgumentOutOfRangeException(); + + this.fArray[this.fTail] = connection; + this.fTail = (this.fTail + 1) % this.fCapacity; + this.fCount++; + } + + public Connection Dequeue() + { + if (this.fCount == 0) + throw new ArgumentOutOfRangeException(); + + Connection lConnection = this.fArray[this.fHead]; + this.fArray[this.fHead] = null; + this.fCount--; + this.fHead = (this.fHead + 1) % this.fCapacity; + + return lConnection; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Dns.cs b/Source/RemObjects.InternetPack/Dns.cs new file mode 100644 index 0000000..e70affc --- /dev/null +++ b/Source/RemObjects.InternetPack/Dns.cs @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Net; + +namespace RemObjects.InternetPack.Dns +{ + public class DnsLookup + { + public static IPAddress ResolveFirst(String hostname) + { + IPAddress[] lAddresses = ResolveAll(hostname); + if (lAddresses.Length == 0) + throw new DnsResolveException(String.Format("Could not resolve HostName {0}", hostname)); + + return lAddresses[0]; + } + + public static IPAddress ResolveRandom(String hostname) + { + IPAddress[] lAddresses = ResolveAll(hostname); + if (lAddresses.Length == 0) + throw new DnsResolveException(String.Format("Could not resolve hostname {0}", hostname)); + + Random lRandom = new Random(); + return lAddresses[lRandom.Next(lAddresses.Length)]; + } + + public static IPAddress[] ResolveAll(String hostname) + { + IPAddress lAddress = TryStringAsIPAddress(hostname); + if (lAddress != null) + return new IPAddress[] { lAddress }; + + IPHostEntry lEntry = System.Net.Dns.GetHostEntry(hostname); + return lEntry.AddressList; + } + + public static IPAddress TryStringAsIPAddress(String hostname) + { + if (hostname.StartsWith("[")) // ipv6 + { + try + { + return IPAddress.Parse(hostname); + } + catch (FormatException) + { + return null; + } + } + + String[] lFields = hostname.Split('.'); + if (lFields.Length != 4) + return null; + + for (Int32 i = 0; i < 4; i++) + { + if (lFields[i].Length > 3) + return null; + + for (Int32 j = 0; j < lFields[i].Length; j++) + if (lFields[i][j] < '0' && lFields[i][j] > '9') + return null; + } + try + { + return IPAddress.Parse(hostname); + } + catch (FormatException) + { + return null; + } + } + + public static String ReverseResolve(IPAddress address) + { + IPHostEntry lEntry = System.Net.Dns.GetHostEntry(address); + + return lEntry.HostName; + } + } + +#if FULLFRAMEWORK + [System.Serializable()] +#endif + public class DnsResolveException : Exception + { + public DnsResolveException(String message) + : base(message) + { + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/EchoServer.cs b/Source/RemObjects.InternetPack/EchoServer.cs new file mode 100644 index 0000000..80b0ec7 --- /dev/null +++ b/Source/RemObjects.InternetPack/EchoServer.cs @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; + +namespace RemObjects.InternetPack.StandardServers +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.EchoServer.bmp")] +#endif + public class EchoServer : Server + { + public EchoServer() + { + this.DefaultPort = 7; // Default port for echo service + this.Port = this.DefaultPort; + } + + public override Type GetWorkerClass() + { + return typeof(EchoWorker); + } + } + + public class EchoWorker : Worker + { + protected override void DoWork() + { + Int32 lReceived; + + do + { + Byte[] lReceiveBuffer = new Byte[256]; /* a tiny buffer is good enough for echo server */ + lReceived = this.DataConnection.Read(lReceiveBuffer, 0, lReceiveBuffer.Length); + + if (lReceived > 0) + this.DataConnection.Write(lReceiveBuffer, 0, lReceived); + } + while (lReceived > 0); + } + } +} diff --git a/Source/RemObjects.InternetPack/Events.cs b/Source/RemObjects.InternetPack/Events.cs new file mode 100644 index 0000000..c7ab3cd --- /dev/null +++ b/Source/RemObjects.InternetPack/Events.cs @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; + +namespace RemObjects.InternetPack.Events +{ + public delegate void TransferStartEventHandler(Object sender, TransferStartEventArgs e); + public delegate void TransferEndEventHandler(Object sender, TransferEndEventArgs e); + public delegate void TransferProgressEventHandler(Object sender, TransferProgressEventArgs e); + + public enum TransferDirection + { + Send, + Receive + } + + public class TransferEventArgs + { + public TransferEventArgs(TransferDirection direction) + : base() + { + this.fDirection = direction; + } + + public TransferDirection TransferDirection + { + get + { + return this.fDirection; + } + } + private TransferDirection fDirection; + } + + public class TransferStartEventArgs : TransferEventArgs + { + public TransferStartEventArgs(TransferDirection direction, Int64 total) + : base(direction) + { + this.fTotal = total; + } + + public Int64 Total + { + get + { + return this.fTotal; + } + } + private Int64 fTotal; + } + + public class TransferEndEventArgs : TransferEventArgs + { + public TransferEndEventArgs(TransferDirection direction) + : base(direction) + { + } + } + + public class TransferProgressEventArgs : TransferEventArgs + { + public TransferProgressEventArgs(TransferDirection direction, Int64 current) + : base(direction) + { + this.fCurrent = current; + } + + public Int64 Current + { + get + { + return this.fCurrent; + } + } + private Int64 fCurrent; + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/FtpClient.cs b/Source/RemObjects.InternetPack/FtpClient.cs new file mode 100644 index 0000000..fbbdc20 --- /dev/null +++ b/Source/RemObjects.InternetPack/FtpClient.cs @@ -0,0 +1,534 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using RemObjects.InternetPack.CommandBased; +using RemObjects.InternetPack.Events; + +namespace RemObjects.InternetPack.Ftp +{ + // ftp://ftp.rfc-editor.org/in-notes/rfc959.txt +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.FtpClient.bmp")] +#endif + public class FtpClient : CommandBasedClient + { + public FtpClient() + { + this.Passive = false; + this.AutoRetrieveListing = true; + this.fCurrentDirectory = String.Empty; + this.fCurrentDirectoryContents = new FtpListing(); + } + + #region Private fields + private IPAddress fDataAddress; + private Int32 fDataPort; + private Connection fDataConnection; + private SimpleServer fDataServer; + private String fCurrentDirectory; + #endregion + + #region Properties + public String UserName + { + get + { + return this.fUserName; + } + set + { + this.fUserName = value; + } + } + private String fUserName; + + public String Password + { + get + { + return this.fPassword; + } + set + { + this.fPassword = value; + } + } + private String fPassword; + + public String Account + { + get + { + return this.fAccount; + } + set + { + this.fAccount = value; + } + } + private String fAccount; + + public Boolean Passive + { + get + { + return this.fInPassiveMode; + } + set + { + this.fInPassiveMode = value; + } + } + private Boolean fInPassiveMode; + + public Boolean ShowHiddenFiles + { + get + { + return this.fShowHiddenFiles; + } + set + { + this.fShowHiddenFiles = value; + } + } + private Boolean fShowHiddenFiles; + + public Encoding Encoding + { + get + { + if (this.fEncoding == null) + this.fEncoding = Encoding.UTF8; + + return this.fEncoding; + } + set + { + this.fEncoding = value; + } + } + private Encoding fEncoding; + + public Boolean AutoRetrieveListing + { + get + { + return this.fAutoRetrieveListing; + } + set + { + this.fAutoRetrieveListing = value; + } + } + private Boolean fAutoRetrieveListing; + + public FtpListing CurrentDirectoryContents + { + get + { + return this.fCurrentDirectoryContents; + } + } + private FtpListing fCurrentDirectoryContents; + #endregion + + public override void Open() + { + base.Open(); + + this.CurrentConnection.Encoding = Encoding; + + if (!this.WaitForResponse(220)) + { + this.Close(); + throw new CmdResponseException("Invalid connection reply", this.LastResponseNo, this.LastResponseText); + } + } + + public override void Close() + { + base.Close(); + + if (this.fDataConnection != null && this.fDataConnection.Connected) + this.fDataConnection.Close(); + } + + public void Login() + { + if (!this.SendAndWaitForResponse("USER " + this.UserName, 331, 230)) + throw new CmdResponseException("Login unsuccessful", this.LastResponseNo, this.LastResponseText); + + switch (this.LastResponseNo) + { + case 331: + if (!this.SendAndWaitForResponse("PASS " + this.Password, 230, 332)) + throw new CmdResponseException("Login unsuccessful", this.LastResponseNo, this.LastResponseText); + + switch (this.LastResponseNo) + { + case 232: + SendAccount(); + break; + + case 230: + break; + } + break; + + case 230: + break; + } + } + + public void SendAccount() + { + if (this.Account.Length == 0) + throw new Exception("Account cannot be blank"); + + if (!this.SendAndWaitForResponse("ACCT " + this.Account, 230, 202)) + throw new CmdResponseException("Account command unsuccessful", this.LastResponseNo, this.LastResponseText); + } + + public void Quit() + { + if (!this.SendAndWaitForResponse("QUIT", 221)) + throw new CmdResponseException("Quit unsuccessful", this.LastResponseNo, this.LastResponseText); + } + + public void ChangeDirectory(String directory) + { + if (!this.SendAndWaitForResponse("CWD " + directory, 250)) + throw new CmdResponseException("Error changing directory", this.LastResponseNo, this.LastResponseText); + + if (this.AutoRetrieveListing) + this.List(); + } + + public void ChangeToParentDirectory() + { + if (!this.SendAndWaitForResponse("CDUP", 250)) + throw new CmdResponseException("Error changing directory", this.LastResponseNo, this.LastResponseText); + + if (this.AutoRetrieveListing) + this.List(); + } + + public String GetCurrentDirectory() + { + if (!this.SendAndWaitForResponse("PWD", 257)) + throw new CmdResponseException("Could not retrieve current directory", this.LastResponseNo, this.LastResponseText); + + StringBuilder lResult = new StringBuilder(); + + Int32 i = 1; + while (i < this.LastResponseText.Length) + { + if (this.LastResponseText[i] == '"') + { + if (i < this.LastResponseText.Length - 1 && this.LastResponseText[i + 1] == '"') + { + lResult.Append('"'); + i++; // skip extra doubled quote + } + else + { + break; + } + } + else + { + lResult.Append(this.LastResponseText[i]); + } + i++; + } + + this.fCurrentDirectory = lResult.ToString(); + + return this.fCurrentDirectory; + } + + public void RemoveDirectory(String directory) + { + if (!this.SendAndWaitForResponse("RMD " + directory, 250)) + throw new CmdResponseException("Error removing directory", LastResponseNo, LastResponseText); + + if (this.AutoRetrieveListing) + this.List(); + } + + public void RemoveDirectory(String directory, Boolean recursive) + { + if (recursive) + { + this.ChangeDirectory(directory); + try + { + this.List(true); + foreach (FtpListingItem ftpItem in this.CurrentDirectoryContents) + { + if (ftpItem.FileName != "..") + { + if (ftpItem.Directory) + this.RemoveDirectory(ftpItem.FileName); + else + this.Delete(ftpItem.FileName); + } + } + } + finally + { + this.ChangeToParentDirectory(); + } + } + + this.RemoveDirectory(directory); + } + + public void MakeDirectory(String directory) + { + if (!this.SendAndWaitForResponse("MKD " + directory, 257)) + throw new CmdResponseException("Error making directory", LastResponseNo, LastResponseText); + + if (this.AutoRetrieveListing) + this.List(); + } + + public void StartPassiveConnection() + { + if (!this.SendAndWaitForResponse("PASV", 227)) + throw new CmdResponseException("Could not set passive mode", this.LastResponseNo, this.LastResponseText); + + Match lMatch = Regex.Match(LastResponseText, @"(?\d+),(?\d+),(?\d+),(?\d+),(?\d+),(?\d+)"); + GroupCollection lGroups = lMatch.Groups; + + this.fDataAddress = IPAddress.Parse(String.Format("{0}.{1}.{2}.{3}", lGroups["A1"].Value, lGroups["A2"].Value, lGroups["A3"].Value, lGroups["A4"].Value)); + this.fDataPort = (Byte.Parse(lGroups["P1"].Value) * 256) + Byte.Parse(lGroups["P2"].Value); + + this.SendLog(LogDirection.Status, "Connecting to {0}:{1}", this.fDataAddress, this.fDataPort); + this.fDataConnection = this.NewConnection(CurrentConnection.Binding); + this.fDataConnection.Connect(this.fDataAddress, this.fDataPort); + this.fDataConnection.Encoding = Encoding; + this.SendLog(LogDirection.Status, "Connected to {0} port {1}", this.fDataAddress, this.fDataPort); + + this.fDataConnection.OnBytesReceived += this.TriggerOnBytesReceived; + this.fDataConnection.OnBytesSent += this.InternalOnBytesSent; + } + + public void StartActiveConnection() + { + if (this.fDataConnection != null) + { + if (this.fDataConnection.Connected) + this.fDataConnection.Close(); + this.fDataConnection = null; + } + + if (this.fDataServer == null) + { + this.fDataServer = new SimpleServer(); + this.fDataServer.Binding.Address = ((IPEndPoint)CurrentConnection.LocalEndPoint).Address; + this.fDataServer.Open(); + } + + Byte[] lAddress; +#if FULLFRAMEWORK + lAddress = ((IPEndPoint)this.fDataServer.Binding.ListeningSocket.LocalEndPoint).Address.GetAddressBytes(); +#endif +#if COMPACTFRAMEWORK + IPAddress lIPAddress = ((IPEndPoint)this.fDataServer.Binding.ListeningSocket.LocalEndPoint).Address; + String[] lIPAddressstr = lIPAddress.ToString().Split(new Char[] {'.'}); + lAddress = new Byte[lIPAddressstr.Length]; + for (Int32 i = 0; i < lIPAddressstr.Length; i++) + lAddress[i] = Byte.Parse(lIPAddressstr[i]); +#endif + + Int32 lPort = ((IPEndPoint)this.fDataServer.Binding.ListeningSocket.LocalEndPoint).Port; + String lPortCommand = String.Format("PORT {0},{1},{2},{3},{4},{5}", lAddress[0], lAddress[1], lAddress[2], lAddress[3], unchecked((Byte)(lPort >> 8)), unchecked((Byte)lPort)); + if (!SendAndWaitForResponse(lPortCommand, 200)) + throw new CmdResponseException("Error in PORT command", LastResponseNo, LastResponseText); + } + + private void RetrieveDataConnection() + { + if (!this.Passive) + { + if (this.fDataServer == null) + throw new Exception("DataServer is not assigned"); + + Connection lConnection = this.fDataServer.WaitForConnection(); + this.fDataServer.Close(); + this.fDataServer = null; + this.fDataConnection = lConnection; + this.fDataConnection.Encoding = this.Encoding; + } + } + + public String List() + { + return this.List(false); + } + + public String List(Boolean showHiddenFiles) + { + String lResult; + + this.SetType("A"); + this.CheckDataConnection(); + + if (!this.SendAndWaitForResponse(this.ShowHiddenFiles || showHiddenFiles ? "LIST -a" : "LIST", 125, 150)) + throw new CmdResponseException("Could not start LIST command", LastResponseNo, LastResponseText); + + this.RetrieveDataConnection(); + Byte[] lResponse = this.fDataConnection.ReceiveAllRemaining(); + this.fDataConnection.Close(); + + if (lResponse != null) + { + try + { + lResult = this.fDataConnection.Encoding.GetString(lResponse, 0, lResponse.Length); + this.CurrentDirectoryContents.Parse(lResult, this.fCurrentDirectory != "/"); + } + catch + { + // we don't want any exception here + lResult = null; + this.fCurrentDirectoryContents.Clear(); + } + } + else + { + lResult = null; + this.CurrentDirectoryContents.Clear(); + } + this.WaitForResponse(226); + + this.TriggerOnNewListing(); + + return lResult; + } + + public void Delete(String filename) + { + if (!this.SendAndWaitForResponse("DELE " + filename, 250)) + throw new CmdResponseException("Error deleting file", this.LastResponseNo, this.LastResponseText); + + if (this.AutoRetrieveListing) + this.List(); + } + + public void SetType(String type) + { + if (!SendAndWaitForResponse("TYPE " + type, 200)) + throw new CmdResponseException("Error sending TYPE command", this.LastResponseNo, this.LastResponseText); + } + + private void CheckDataConnection() + { + if (this.fDataConnection == null || (this.fDataConnection != null && !this.fDataConnection.Connected)) + { + if (this.Passive) + this.StartPassiveConnection(); + else + this.StartActiveConnection(); + } + } + + public void Retrieve(FtpListingItem item, Stream stream) + { + this.Retrieve(item.FileName, item.Size, stream); + } + + public void Retrieve(String filename, Int64 size, Stream stream) + { + this.SetType("I"); + this.CheckDataConnection(); + + if (!this.SendAndWaitForResponse("RETR " + filename, 150, 125)) + throw new CmdResponseException("Error retrieving file", this.LastResponseNo, this.LastResponseText); + + this.TriggerOnTransferStart(this, new TransferStartEventArgs(TransferDirection.Receive, size)); + + this.RetrieveDataConnection(); + this.fDataConnection.ReceiveToStream(stream, size); + this.fDataConnection.Close(); + + this.WaitForResponse(226); + } + + public void Store(String filename, Stream stream) + { + this.SetType("I"); + this.CheckDataConnection(); + + if (!this.SendAndWaitForResponse("STOR " + filename, 150, 125)) + throw new CmdResponseException("Error storing file", LastResponseNo, LastResponseText); + + this.TriggerOnTransferStart(this, new TransferStartEventArgs(TransferDirection.Receive, stream.Length)); + + this.RetrieveDataConnection(); + this.fDataConnection.SendFromStream(stream); + this.fDataConnection.Close(); + + this.WaitForResponse(226); + + if (this.AutoRetrieveListing) + this.List(); + } + + public void Rename(String from, String to) + { + if (!this.SendAndWaitForResponse("RNFR " + from, 350)) + throw new CmdResponseException("Error renaming file", this.LastResponseNo, this.LastResponseText); + + if (!this.SendAndWaitForResponse("RNTO " + to, 250)) + throw new CmdResponseException("Error renaming file", this.LastResponseNo, this.LastResponseText); + + if (this.AutoRetrieveListing) + this.List(); + } + + #region Events + public event TransferStartEventHandler OnTransferStart; + + protected virtual void TriggerOnTransferStart(Object sender, TransferStartEventArgs e) + { + if (this.OnTransferStart != null) + this.OnTransferStart(sender, e); + } + + public event TransferProgressEventHandler OnTransferProgress; + + protected virtual void TriggerOnBytesReceived(Object sender, EventArgs e) + { + if (this.OnTransferProgress != null) + this.OnTransferProgress(sender, new TransferProgressEventArgs(TransferDirection.Receive, ((Connection)sender).BytesReceived)); + } + + protected virtual void InternalOnBytesSent(Object sender, EventArgs e) + { + if (this.OnTransferProgress != null) + this.OnTransferProgress(sender, new TransferProgressEventArgs(TransferDirection.Send, ((Connection)sender).BytesSent)); + } + + public event EventHandler OnNewListing; + + public virtual void TriggerOnNewListing() + { + if (this.OnNewListing != null) + this.OnNewListing(this, new EventArgs()); + } + #endregion + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/FtpListing.cs b/Source/RemObjects.InternetPack/FtpListing.cs new file mode 100644 index 0000000..862cf41 --- /dev/null +++ b/Source/RemObjects.InternetPack/FtpListing.cs @@ -0,0 +1,728 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Text; +using System.Text.RegularExpressions; + +namespace RemObjects.InternetPack.Ftp +{ + public class FtpListingItem + { + #region Constructors + public FtpListingItem() + { + this.Directory = false; + this.UserRead = true; + this.UserWrite = true; + this.UserExec = true; + this.GroupRead = true; + this.GroupWrite = true; + this.GroupExec = true; + this.OtherRead = true; + this.OtherWrite = true; + this.OtherExec = true; + this.SubItemCount = 1; + this.User = "user"; + this.Group = "group"; + this.Size = 0; + this.FileDate = DateTime.MinValue; + } + + public FtpListingItem(String item) + : this() + { + this.Parse(item); + } + #endregion + + #region Properties + public Boolean Directory + { + get + { + return this.fDirectory; + } + set + { + this.fDirectory = value; + } + } + private Boolean fDirectory; + + public Int32 ImageIndex + { + get + { + if (!this.Directory) + return 2; + + if (this.FileName == "..") + return 0; + + return 1; + } + } + + public Boolean UserRead + { + get + { + return fUserRead; + } + set + { + fUserRead = value; + } + } + private Boolean fUserRead; + + public Boolean UserWrite + { + get + { + return fUserWrite; + } + set + { + fUserWrite = value; + } + } + private Boolean fUserWrite; + + public Boolean UserExec + { + get + { + return fUserExec; + } + set + { + fUserExec = value; + } + } + private Boolean fUserExec; + + public Boolean GroupRead + { + get + { + return fGroupRead; + } + set + { + fGroupRead = value; + } + } + private Boolean fGroupRead; + + public Boolean GroupWrite + { + get + { + return fGroupWrite; + } + set + { + fGroupWrite = value; + } + } + private Boolean fGroupWrite; + + public Boolean GroupExec + { + get + { + return fGroupExec; + } + set + { + fGroupExec = value; + } + } + private Boolean fGroupExec; + + public Boolean OtherRead + { + get + { + return fOtherRead; + } + set + { + fOtherRead = value; + } + } + private Boolean fOtherRead; + + public Boolean OtherWrite + { + get + { + return fOtherWrite; + } + set + { + fOtherWrite = value; + } + } + private Boolean fOtherWrite; + + public Boolean OtherExec + { + get + { + return fOtherExec; + } + set + { + fOtherExec = value; + } + } + private Boolean fOtherExec; + + public Int32 SubItemCount + { + get + { + return fSubItemCount; + } + set + { + fSubItemCount = value; + } + } + private Int32 fSubItemCount; + + public String User + { + get + { + return fUser; + } + set + { + fUser = value; + } + } + private String fUser; + + public String Group + { + get + { + return fGroup; + } + set + { + fGroup = value; + } + } + private String fGroup; + + private Int64 fSize = 0; + public Int64 Size + { + get + { + return fSize; + } + set + { + fSize = value; + } + } + + public DateTime FileDate + { + get + { + return fFileDate; + } + set + { + fFileDate = value; + } + } + private DateTime fFileDate; + + public String FileName + { + get + { + return fFileName; + } + set + { + fFileName = value; + } + } + private String fFileName; + #endregion + + public static String LeadZero(String value, Int32 length) + { + StringBuilder lResult = new StringBuilder(); + length -= value.Length; + while (length > 0) + { + lResult.Append('0'); + length--; + } + lResult.Append(value); + + return lResult.ToString(); + } + + public static String FtpDateToString(DateTime date) + { + Boolean lShowYear = (DateTime.Now - date).Days > 180; + String lResult; + + switch (date.Month) + { + + case 1: + lResult = "Jan"; + break; + + case 2: + lResult = "Feb"; + break; + + case 3: + lResult = "Mar"; + break; + + case 4: + lResult = "Apr"; + break; + + case 5: + lResult = "May"; + break; + + case 6: + lResult = "Jun"; + break; + + case 7: + lResult = "Jul"; + break; + + case 8: + lResult = "Aug"; + break; + + case 9: + lResult = "Sep"; + break; + + case 10: + lResult = "Oct"; + break; + + case 11: + lResult = "Nov"; + break; + + case 12: + lResult = "Dec"; + break; + + default: + return ""; + } + + if (date.Day > 9) + lResult += " " + date.Day; + else + lResult += " " + date.Day; + + if (lShowYear) + { + lResult += " " + LeadZero(date.Year.ToString(), 4); + } + else + { + lResult += " "; + if (date.Hour < 10) { lResult += "0"; } + lResult += date.Hour.ToString(); + lResult += ":"; + if (date.Minute < 10) { lResult += "0"; } + lResult += date.Minute.ToString(); + } + + return lResult; + } + + public override String ToString() + { + Char[] lRights = new Char[] { 'd', 'r', 'w', 'x', 'r', 'w', 'x', 'r', 'w', 'x' }; + + if (!this.Directory) + lRights[0] = '-'; + + if (!this.UserRead) + lRights[1] = '-'; + + if (!this.UserWrite) + lRights[2] = '-'; + + if (!this.UserExec) + lRights[3] = '-'; + + if (!this.GroupRead) + lRights[4] = '-'; + + if (!this.GroupWrite) + lRights[5] = '-'; + + if (!this.GroupExec) + lRights[6] = '-'; + + if (!this.OtherRead) + lRights[7] = '-'; + + if (!this.OtherWrite) + lRights[8] = '-'; + + if (!this.OtherExec) + lRights[9] = '-'; + + return String.Format("{0} {1,3} {2,8} {3,8} {4,7} {5} {6}", new String(lRights), this.SubItemCount, this.User, + this.Group, this.fSize, FtpListingItem.FtpDateToString(this.FileDate), this.FileName); + } + + public void Parse(String item) + { + Regex lRegEx = new Regex(@"\s+"); + + // there is two modes possible Unix mode or MS-DOS mode + if (item.StartsWith("d") || item.StartsWith("-")) + { + /* + Unix Mode + ====================================================================== + drwxr-xr-x 3 65025 100 4096 Dec 10 12:13 1 1 + drwxr-xr-x 2 65025 100 4096 Dec 10 12:13 2 + -rw-r--r-- 1 65025 100 35 Dec 10 12:33 root.txt + -rw-r--r-- 1 65025 100 43 Dec 10 12:33 root2.txt + + + where + 0 - access + 1 - sub item count + 2 - owner + 3 - group + 4 - size + 5 - Month + 6 - day + 7 - Time or Year + 8 - Filename + */ + String[] lSplittedData = lRegEx.Split(item, 9); + + // Copy splitted data to result + // Problem is that at least one FTP server doesn;t return Group segment + // So we have to compensate this + String[] lSegments = new String[9]; + for (Int32 i = 0; i < 3; i++) + lSegments[i] = lSplittedData[i]; + for (Int32 i = 1; i <= 6; i++) + lSegments[9 - i] = lSplittedData[lSplittedData.Length - i]; + + this.Directory = lSegments[0][0] != '-'; + this.UserRead = lSegments[0][1] != '-'; + this.UserWrite = lSegments[0][2] != '-'; + this.UserExec = lSegments[0][3] != '-'; + this.GroupRead = lSegments[0][4] != '-'; + this.GroupWrite = lSegments[0][5] != '-'; + this.GroupExec = lSegments[0][6] != '-'; + this.OtherRead = lSegments[0][7] != '-'; + this.OtherWrite = lSegments[0][8] != '-'; + this.OtherExec = lSegments[0][9] != '-'; + + this.SubItemCount = Int32.Parse(lSegments[1]); + this.User = lSegments[2]; + this.Group = lSegments[3]; + this.Size = Int64.Parse(lSegments[4]); + + String lMonthShortName = lSegments[5]; + String lDay = lSegments[6]; + String lTimeOrYear = lSegments[7]; + + this.FileDate = FtpListingItem.StringToFtpDate(lMonthShortName, lDay, lTimeOrYear); + + this.FileName = lSegments[8]; + } + else + { + /* + MS-DOS Mode + ====================================================================== + 01-14-08 01:35PM 1 1 + 01-14-08 01:35PM 2 + 01-14-08 01:36PM 35 root.txt + 01-14-08 01:36PM 43 root2.txt + + where + + 0 - date + 1 - time + 2 - Size or IsDir + 3 - Filename + */ + String[] lSegments = lRegEx.Split(item, 4); + this.Directory = (lSegments[2] == ""); + + this.Size = this.Directory ? 0 : Int64.Parse(lSegments[2]); + + String lDateStr = lSegments[0]; + String lTimeStr = lSegments[1]; + this.FileDate = FtpListingItem.StringToFtpDate(lDateStr, lTimeStr); + + this.FileName = lSegments[3]; + } + } + + public static DateTime StringToFtpDate(String value) + { + String[] lParts = Regex.Split(value, @"\w+"); + + return FtpListingItem.StringToFtpDate(lParts[0], lParts[1], lParts[2]); + } + + public static DateTime StringToFtpDate(String dateString, String timeString) + { + Int32 lMonth = 1; + Int32 lDay = 1; + Int32 lYear = 1; + Int32 lHour = 0; + Int32 lMinutes = 0; + + //01-14-08 01:35PM + try + { + #region Parsing Date (Should be MM-DD-YY) + String[] lDate = dateString.Split(new Char[] { '-' }); + if (lDate.Length == 3) + { + lMonth = Convert.ToInt32(lDate[0]); + lDay = Convert.ToInt32(lDate[1]); + lYear = Convert.ToInt32(lDate[2]); + + if (lDate[2].Length == 2) + { + Int32 lCentury = (DateTime.Now.Year / 100) * 100; + if ((lYear + 50) > 100) + lCentury -= 100; + lYear = lCentury + lYear; + } + } + #endregion + + #region Parsing Time (Should be HH:MMAM/PM) + String[] lTime = timeString.Split(new Char[] { ':' }); + if (lTime.Length == 2) + { + lHour = Convert.ToInt32(lTime[0]); + if (lTime[1].Length == 4) + { + Boolean lPM = lTime[1].Substring(2) == "PM"; + if (lPM && lHour < 12) + lHour += 12; + if (!lPM && lHour == 12) + lHour = 0; + lTime[1] = lTime[1].Substring(0, 2); + } + lMinutes = Convert.ToInt32(lTime[1]); + } + #endregion + } + catch (Exception) // don't need any exception here. + { + return new DateTime(0); + } + + return new DateTime(lYear, lMonth, lDay, lHour, lMinutes, 0); + } + + public static DateTime StringToFtpDate(String monthName, String day, String yearOrTime) + { + Int32 lYear; + Int32 lMonth; + Int32 lDay; + Int32 lHour = 0; + Int32 lMinute = 0; + Int32 lSecond = 0; + + try + { + switch (monthName) + { + case "Jan": + lMonth = 1; + break; + + case "Feb": + lMonth = 2; + break; + + case "Mar": + lMonth = 3; + break; + + case "Apr": + lMonth = 4; + break; + + case "May": + lMonth = 5; + break; + + case "Jun": + lMonth = 6; + break; + + case "Jul": + lMonth = 7; + break; + + case "Aug": + lMonth = 8; + break; + + case "Sep": + lMonth = 9; + break; + + case "Oct": + lMonth = 10; + break; + + case "Nov": + lMonth = 11; + break; + + case "Dec": + lMonth = 12; + break; + + default: + return new DateTime(0); + } + + lDay = Int32.Parse(day); + lYear = DateTime.Now.Year; + + if (yearOrTime.IndexOf(":") == -1) // this is a year, not a time + { + lYear = Int32.Parse(yearOrTime); + } + else + { + // no year, either this year or last + Int32 lCurrentMonth = DateTime.Now.Month; + if (lCurrentMonth < lMonth) + lYear -= 1; + + String[] lTimes = Regex.Split(yearOrTime, ":"); + lHour = Int32.Parse(lTimes[0]); + lMinute = Int32.Parse(lTimes[1]); + if (lTimes.Length > 2) + lSecond = Int32.Parse(lTimes[2]); + } + } + catch (Exception) // don't need any exception here. + { + return new DateTime(0); + } + + return new DateTime(lYear, lMonth, lDay, lHour, lMinute, lSecond, 999); + } + } + + public class FtpListing : ArrayList + { + public new FtpListingItem this[Int32 index] + { + get + { + return (FtpListingItem)base[index]; + } + } + + public FtpListingItem Add() + { + FtpListingItem lResult = new FtpListingItem(); + this.Add(lResult); + + return lResult; + } + + public void Parse(String list, Boolean includeUpDir) + { + this.Clear(); + + Boolean lFoundUpDir = false; + + String[] lItems = Regex.Split(list, @"(?:\r\n|\r|\n)"); + + for (Int32 i = 0; i < lItems.Length; i++) + { + String lItem = lItems[i].Trim(); + + if (String.IsNullOrEmpty(lItem)) + continue; + + if (lItem.StartsWith("total", StringComparison.OrdinalIgnoreCase)) + continue; + + try + { + FtpListingItem lNewItem = new FtpListingItem(); + lNewItem.Parse(lItem); + + this.Add(lNewItem); + + if (lNewItem.Directory && lNewItem.FileName == "..") + lFoundUpDir = true; + } + catch (IndexOutOfRangeException) + { + } + catch (FormatException) + { + } + } + + if (includeUpDir && !lFoundUpDir) + { + FtpListingItem lUpItem = new FtpListingItem(); + lUpItem.Directory = true; + lUpItem.FileName = ".."; + this.Insert(0, lUpItem); + } + } + + public override String ToString() + { + StringBuilder lResult = new StringBuilder(); + + for (Int32 i = 0; i < this.Count; i++) + { + lResult.Append(this.ToString()); + lResult.Append("\r\n"); + } + + return lResult.ToString(); + } + } +} diff --git a/Source/RemObjects.InternetPack/FtpServer.cs b/Source/RemObjects.InternetPack/FtpServer.cs new file mode 100644 index 0000000..0908639 --- /dev/null +++ b/Source/RemObjects.InternetPack/FtpServer.cs @@ -0,0 +1,1978 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.IO; +using System.Net; +using System.Text; +using System.Threading; +using RemObjects.InternetPack.CommandBased; + +namespace RemObjects.InternetPack.Ftp +{ + // ftp://ftp.rfc-editor.org/in-notes/rfc959.txt + #region Event Types + public delegate void OnFtpUserLoginHandler(Object sender, FtpUserLoginEventArgs e); + + public delegate void OnFtpUserAccountHandler(Object sender, FtpUserAccountArgs e); + + public delegate void OnFtpChangeDirectoryHandler(Object sender, FtpChangeDirectoryArgs e); + + public delegate void OnFtpGetListingHandler(Object sender, FtpGetListingArgs e); + + public delegate void OnFtpRenameHandler(Object sender, FtpRenameEventArgs e); + + public delegate void OnFtpFileHandler(Object sender, FtpFileEventArgs e); + + public delegate void OnFtpTransferHandler(Object sender, FtpTransferEventArgs e); + + public class FtpUserLoginEventArgs : SessionEventArgs + { + public FtpUserLoginEventArgs(Object session, Connection connection, Server server) + : base(session, connection, server) + { + this.LoginOk = false; + this.NeedAccount = false; + } + + public String UserName + { + get + { + return this.fUserName; + } + set + { + this.fUserName = value; + } + } + private String fUserName; + + public String Password + { + get + { + return this.fPassword; + } + set + { + this.fPassword = value; + } + } + private String fPassword; + + public Boolean LoginOk + { + get + { + return this.fLoginOk; + } + set + { + this.fLoginOk = value; + } + } + private Boolean fLoginOk; + + public Boolean NeedAccount + { + get + { + return this.fNeedAccount; + } + set + { + this.fNeedAccount = value; + } + } + private Boolean fNeedAccount; + } + + public class FtpUserAccountArgs : SessionEventArgs + { + public FtpUserAccountArgs(Object session, Connection connection, Server server) + : base(session, connection, server) + { + } + + public String AccountName + { + get + { + return this.fAccountName; + } + set + { + this.fAccountName = value; + } + } + private String fAccountName; + + public Boolean LoginOk + { + get + { + return this.fLoginOk; + } + set + { + this.fLoginOk = value; + } + } + private Boolean fLoginOk; + } + + public class FtpChangeDirectoryArgs : SessionEventArgs + { + public FtpChangeDirectoryArgs(Object session, Connection connection, Server server) + : base(session, connection, server) + { + } + + public String NewDirectory + { + get + { + return this.fNewDirectory; + } + set + { + this.fNewDirectory = value; + } + } + private String fNewDirectory; + + public Boolean ChangeDirOk + { + get + { + return this.fChangeDirOk; + } + set + { + this.fChangeDirOk = value; + } + } + private Boolean fChangeDirOk; + } + + public class FtpGetListingArgs : SessionEventArgs + { + public FtpGetListingArgs(Object session, Connection connection, Server server) + : base(session, connection, server) + { + this.fListing = new FtpListing(); + } + + public FtpListing Listing + { + get + { + return this.fListing; + } + } + private FtpListing fListing; + } + + public class FtpFileEventArgs : SessionEventArgs + { + public FtpFileEventArgs(Object session, Connection connection, Server server) + : base(session, connection, server) + { + } + + public String FileName + { + get + { + return this.fFileName; + } + set + { + this.fFileName = value; + } + } + private String fFileName; + + public Boolean Ok + { + get + { + return this.fOk; + } + set + { + this.fOk = value; + } + } + private Boolean fOk; + } + + public class FtpRenameEventArgs : FtpFileEventArgs + { + public FtpRenameEventArgs(Object session, Connection connection, Server server) + : base(session, connection, server) + { + } + + public String NewFileName + { + get + { + return this.fNewFileName; + } + set + { + this.fNewFileName = value; + } + } + private String fNewFileName; + } + + public class FtpTransferEventArgs : FtpFileEventArgs + { + public FtpTransferEventArgs(Object session, Connection connection, Server server) + : base(session, connection, server) + { + } + + public Boolean Append + { + get + { + return this.fAppend; + } + set + { + this.fAppend = value; + } + } + private Boolean fAppend; + + public Int64 RestartPoint + { + get + { + return this.fRestartPoint; + } + set + { + this.fRestartPoint = value; + } + } + private Int64 fRestartPoint; + + public Stream DataChannel + { + get + { + return this.fDataChannel; + } + set + { + this.fDataChannel = value; + } + } + private Stream fDataChannel; + } + #endregion + +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.FtpServer.bmp")] +#endif + public class FtpServer : CommandBasedServer + { + public FtpServer() + { + this.Port = 21; + this.ServerName = "FTP Server Ready"; + this.QuitReply = "221 Bye"; + this.SystemType = "UNIX"; + this.AsciiEnter = "\r\n"; + this.Detailed500Errors = true; + } + + #region Properties + [DefaultValue("211 Bye"), Category("Server")] + public String QuitReply + { + get + { + + return this.fQuitReply; + } + set + { + this.fQuitReply = value; + } + } + private String fQuitReply; + + [DefaultValue("UNIX"), Category("Server")] + public String SystemType + { + get + { + return this.fSystemType; + } + set + { + this.fSystemType = value; + } + } + private String fSystemType; + + public String AsciiEnter + { + get + { + return this.fAsciiEnter; + } + set + { + this.fAsciiEnter = value; + } + } + private String fAsciiEnter; + + [Category("Server")] + public String ServerName + { + get + { + return this.fServerName; + } + set + { + this.fServerName = value; + this.Greeting = "220 " + value; + } + } + private String fServerName; + + [DefaultValue(true), Category("Server")] + public Boolean Detailed500Errors + { + get + { + return this.fDetailed500Errors; + } + set + { + this.fDetailed500Errors = value; + } + } + private Boolean fDetailed500Errors; + #endregion + + #region Events + public event OnFtpUserLoginHandler OnUserLogin; + public event OnFtpUserAccountHandler OnAccount; + public event OnFtpChangeDirectoryHandler OnChangeDirectory; + public event OnFtpGetListingHandler OnGetListing; + public event OnFtpRenameHandler OnRename; + public event OnFtpFileHandler OnDelete; + public event OnFtpFileHandler OnMakeDirectory; + public event OnFtpFileHandler OnDeleteDirectory; + public event OnFtpTransferHandler OnStoreFile; + public event OnFtpTransferHandler OnCanStoreFile; + public event OnFtpTransferHandler OnRetrieveFile; + public event OnFtpTransferHandler OnCanRetrieveFile; + + internal protected virtual void InvokeOnUserLogin(FtpUserLoginEventArgs e) + { + if (this.OnUserLogin != null) + this.OnUserLogin(this, e); + } + + internal protected virtual void InvokeOnAccount(FtpUserAccountArgs e) + { + if (this.OnAccount != null) + this.OnAccount(this, e); + } + + internal protected virtual void InvokeOnChangeDirectory(FtpChangeDirectoryArgs e) + { + if (this.OnChangeDirectory != null) + this.OnChangeDirectory(this, e); + } + + internal protected virtual void InvokeOnGetListing(FtpGetListingArgs e) + { + if (this.OnGetListing != null) + this.OnGetListing(this, e); + } + + internal protected virtual void InvokeOnRename(FtpRenameEventArgs e) + { + if (this.OnRename != null) + this.OnRename(this, e); + } + + internal protected virtual void InvokeOnDelete(FtpFileEventArgs e) + { + if (this.OnDelete != null) + this.OnDelete(this, e); + } + + internal protected virtual void InvokeOnMakeDirectory(FtpFileEventArgs e) + { + if (this.OnMakeDirectory != null) + this.OnMakeDirectory(this, e); + } + + internal protected virtual void InvokeOnDeleteDirectory(FtpFileEventArgs e) + { + if (this.OnDeleteDirectory != null) + this.OnDeleteDirectory(this, e); + } + + internal protected virtual void InvokeOnStoreFile(FtpTransferEventArgs e) + { + if (this.OnStoreFile != null) + this.OnStoreFile(this, e); + } + + internal protected virtual void InvokeOnRetrieveFile(FtpTransferEventArgs e) + { + if (this.OnRetrieveFile != null) + this.OnRetrieveFile(this, e); + } + + internal protected virtual void InvokeOnCanStoreFile(FtpTransferEventArgs e) + { + if (this.OnCanStoreFile != null) + this.OnCanStoreFile(this, e); + } + + internal protected virtual void InvokeOnCanRetrieveFile(FtpTransferEventArgs e) + { + if (this.OnCanRetrieveFile != null) + this.OnCanRetrieveFile(this, e); + } + + protected internal override void InvokeOnClientDisconnected(SessionEventArgs e) + { + base.InvokeOnClientDisconnected(e); + + FtpSession lSession = (FtpSession)e.Session; + if (lSession.ActiveConnection != null) + { + try + { + lSession.ActiveConnection.Close(); + } + catch + { + } + lSession.ActiveConnection = null; + } + + if (lSession.PassiveServer != null) + { + try + { + lSession.PassiveServer.Close(); + } + catch + { + } + lSession.PassiveServer = null; + } + + if (lSession.TransferThread != null) + { + try + { + lSession.TransferThread.Abort(); + } + catch + { + } + lSession.TransferThread = null; + } + } + #endregion + + public static Boolean ValidFilename(String value) + { + if (value.IndexOfAny(new Char[] { '\\', '|', '>', '<', '*', '?', ':', '"' }) != -1) + return false; + + return true; + } + + public static String ValidateDirectory(String value) + { + StringBuilder lResult = new StringBuilder(); + lResult.Append('/'); + + String[] lTemp = value.Split(new Char[] { '/' }); + for (Int32 i = lTemp.Length - 1; i >= 0; i--) + { + if (lTemp[i] == ".") + { + lTemp[i] = ""; + + continue; + } + + if (lTemp[i] == "..") + { + lTemp[i] = ""; + + if (i > 0) + lTemp[i - 1] = ""; + + continue; + } + + if (lTemp[i].IndexOfAny(new Char[] { '\\', '|', '>', '<', '*', '?', ':', '"' }) != -1) + { + if (i > 0) + lTemp[i - 1] = ""; + } + } + + for (Int32 i = 0; i < lTemp.Length; i++) + { + if (lTemp[i].Length == 0) + continue; + + lResult.Append(lTemp[i]); + lResult.Append('/'); + } + + if (lResult.Length == 1) + return lResult.ToString(); + + String lTemp2 = lResult.ToString(); + return lTemp2.Substring(0, lTemp2.Length - 1); // remove the last / + } + + protected override Type GetDefaultSessionClass() + { + return typeof(FtpSession); + } + + protected override void InitCommands() + { + this.Commands.Add("QUIT", new OnCommandHandler(Cmd_QUIT)); + this.Commands.Add("NOOP", new OnCommandHandler(Cmd_NOOP)); + this.Commands.Add("USER", new OnCommandHandler(Cmd_USER)); + this.Commands.Add("ALLO", new OnCommandHandler(Cmd_NOOP)); + this.Commands.Add("PASS", new OnCommandHandler(Cmd_PASS)); + this.Commands.Add("ACCT", new OnCommandHandler(Cmd_ACCT)); + this.Commands.Add("CWD", new OnCommandHandler(Cmd_CWD)); + this.Commands.Add("CDUP", new OnCommandHandler(Cmd_CDUP)); + this.Commands.Add("PWD", new OnCommandHandler(Cmd_PWD)); + this.Commands.Add("SYST", new OnCommandHandler(Cmd_SYST)); + this.Commands.Add("TYPE", new OnCommandHandler(Cmd_TYPE)); + this.Commands.Add("PORT", new OnCommandHandler(Cmd_PORT)); + this.Commands.Add("REST", new OnCommandHandler(Cmd_REST)); + this.Commands.Add("LIST", new OnCommandHandler(Cmd_LIST)); + this.Commands.Add("PASV", new OnCommandHandler(Cmd_PASV)); + this.Commands.Add("RNFR", new OnCommandHandler(Cmd_RNFR)); + this.Commands.Add("RNTO", new OnCommandHandler(Cmd_RNTO)); + this.Commands.Add("DELE", new OnCommandHandler(Cmd_DELE)); + this.Commands.Add("RMD", new OnCommandHandler(Cmd_RMD)); + this.Commands.Add("MKD", new OnCommandHandler(Cmd_MKD)); + this.Commands.Add("STOR", new OnCommandHandler(Cmd_STOR)); + this.Commands.Add("APPE", new OnCommandHandler(Cmd_APPE)); + this.Commands.Add("RETR", new OnCommandHandler(Cmd_RETR)); + this.Commands.Add("ABOR", new OnCommandHandler(Cmd_ABOR)); + + /* Aliases */ + this.Commands.Add("CD", new OnCommandHandler(Cmd_CWD)); + } + + /* + STRU // only support file + MODE // stream, maybe block if needed + + NLST [ ] nlist + SITE site specific commands + STAT [ ] status + */ + + private static Boolean ValidateConnection(FtpSession session) + { + if (!session.Passive) + return (session.ActiveConnection != null && session.ActiveConnection.Connected); + + if (session.PassiveServer == null) + return false; + + try + { + Connection lConnection = session.PassiveServer.WaitForConnection(); + session.PassiveServer.Close(); + session.PassiveServer = null; + session.ActiveConnection = lConnection; + + return true; + } + catch + { + session.PassiveServer = null; + session.ActiveConnection = null; + + return false; + } + } + + public static void Cmd_NOOP(Object sender, CommandEventArgs e) + { + e.Connection.WriteLine("200 OK"); + } + + public static void Cmd_RNFR(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if ((e.AllParameters.Length == 0) || (!ValidFilename(e.AllParameters))) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + + String lNewDir = e.AllParameters; + + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + lSession.RenameFrom = lNewDir; + e.Connection.WriteLine("350 Ready for destination name."); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_RNTO(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if ((e.AllParameters.Length == 0) || (!ValidFilename(e.AllParameters))) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + + if (lSession.RenameFrom == null) + { + e.Connection.WriteLine("500 Bad sequence of commands."); + return; + } + + String lNewDir = e.AllParameters; + + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + + FtpRenameEventArgs lEventArgs = new FtpRenameEventArgs(e.Session, e.Connection, e.Server); + lEventArgs.FileName = lSession.RenameFrom; + lSession.RenameFrom = null; + lEventArgs.NewFileName = lNewDir; + try + { + ((FtpServer)e.Server).InvokeOnRename(lEventArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + e.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.Ok) + e.Connection.WriteLine("250 Rename successful"); + else + e.Connection.WriteLine("550 Permission Denied"); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_ABOR(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.Transfer && lSession.TransferThread != null) + { + lSession.TransferThread.Abort(); + lSession.TransferThread = null; + e.Connection.WriteLine("250 ABOR successful"); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_DELE(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if ((e.AllParameters.Length == 0) || (!ValidFilename(e.AllParameters))) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + String lNewDir = e.AllParameters; + + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + + FtpFileEventArgs lEventArgs = new FtpFileEventArgs(e.Session, e.Connection, e.Server); + lEventArgs.FileName = lNewDir; + try + { + ((FtpServer)e.Server).InvokeOnDelete(lEventArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + e.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.Ok) + e.Connection.WriteLine("250 Delete successful"); + else + e.Connection.WriteLine("550 Permission Denied"); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_RMD(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if ((e.AllParameters.Length == 0) || (!ValidFilename(e.AllParameters))) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + + String lNewDir = e.AllParameters; + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + + FtpFileEventArgs lEventArgs = new FtpFileEventArgs(e.Session, e.Connection, e.Server); + lEventArgs.FileName = lNewDir; + try + { + ((FtpServer)e.Server).InvokeOnDeleteDirectory(lEventArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + e.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.Ok) + e.Connection.WriteLine("250 RMD command succesfully"); + else + e.Connection.WriteLine("550 Permission Denied"); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_MKD(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if ((e.AllParameters.Length == 0) || (!ValidFilename(e.AllParameters))) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + + String lNewDir = e.AllParameters; + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + + FtpFileEventArgs lEventArgs = new FtpFileEventArgs(e.Session, e.Connection, e.Server); + lEventArgs.FileName = lNewDir; + try + { + ((FtpServer)e.Server).InvokeOnMakeDirectory(lEventArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + e.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.Ok) + e.Connection.WriteLine(String.Format("257 \"{0}\" - Directory successfully created", lEventArgs.FileName)); + else + e.Connection.WriteLine("550 Permission Denied"); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_STOR(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if ((e.AllParameters.Length == 0) || (!ValidFilename(e.AllParameters))) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + + String lNewDir = e.AllParameters; + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + + FtpTransferEventArgs lEventArgs = new FtpTransferEventArgs(e.Session, e.Connection, e.Server); + lEventArgs.FileName = lNewDir; + lEventArgs.Append = false; + lEventArgs.RestartPoint = 0; + lSession.RestartPoint = 0; + + try + { + ((FtpServer)lEventArgs.Server).InvokeOnCanStoreFile(lEventArgs); + } + catch (FtpException ex) + { + lEventArgs.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + lEventArgs.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.Ok) + { + if (lSession.Passive) + e.Connection.WriteLine("125 Data connection already open; transfer starting"); + + if (!ValidateConnection(lSession)) + { + e.Connection.WriteLine("425 Unable to build data connection"); + return; + } + + if (!lSession.Passive) + e.Connection.WriteLine("150 Opening data connection"); + } + else + { + lEventArgs.Connection.WriteLine("550 Permission Denied"); + return; + } + lSession.State = FtpState.Transfer; + + if (lSession.TransferThread != null) + { + lSession.TransferThread.Abort(); + lSession.TransferThread = null; + } + lSession.TransferThread = new FtpTransferThread(lEventArgs, true); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_APPE(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if ((e.AllParameters.Length == 0) || (!ValidFilename(e.AllParameters))) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + + String lNewDir = e.AllParameters; + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + + FtpTransferEventArgs lArgs = new FtpTransferEventArgs(e.Session, e.Connection, e.Server); + lArgs.FileName = lNewDir; + lArgs.Append = true; + lArgs.RestartPoint = 0; + lSession.RestartPoint = 0; + + try + { + ((FtpServer)lArgs.Server).InvokeOnCanStoreFile(lArgs); + } + catch (FtpException ex) + { + lArgs.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + lArgs.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lArgs.Ok) + { + if (lSession.Passive) + e.Connection.WriteLine("125 Data connection already open; transfer starting"); + + if (!ValidateConnection(lSession)) + { + e.Connection.WriteLine("425 Unable to build data connection"); + return; + } + if (!lSession.Passive) + e.Connection.WriteLine("150 Opening data connection"); + } + else + { + lArgs.Connection.WriteLine("550 Permission Denied"); + return; + } + lSession.State = FtpState.Transfer; + + if (lSession.TransferThread != null) + { + lSession.TransferThread.Abort(); + lSession.TransferThread = null; + } + lSession.TransferThread = new FtpTransferThread(lArgs, true); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_RETR(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if ((e.AllParameters.Length == 0)) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + return; + } + + String lNewDir = e.AllParameters; + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + + FtpTransferEventArgs lEventArgs = new FtpTransferEventArgs(e.Session, e.Connection, e.Server); + lEventArgs.FileName = lNewDir; + lEventArgs.Append = false; + lEventArgs.RestartPoint = lSession.RestartPoint; + lEventArgs.DataChannel = lSession.ActiveConnection; + lSession.RestartPoint = 0; + + try + { + ((FtpServer)lEventArgs.Server).InvokeOnCanRetrieveFile(lEventArgs); + } + catch (FtpException ex) + { + lEventArgs.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + lEventArgs.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.Ok) + { + if (lSession.Passive) + e.Connection.WriteLine("125 Data connection already open; transfer starting"); + + if (!ValidateConnection(lSession)) + { + e.Connection.WriteLine("425 Unable to build data connection"); + return; + } + + if (!lSession.Passive) + e.Connection.WriteLine("150 Opening data connection"); + + lSession.State = FtpState.Transfer; + if (lSession.TransferThread != null) + { + lSession.TransferThread.Abort(); + lSession.TransferThread = null; + } + lSession.TransferThread = new FtpTransferThread(lEventArgs, false); + } + else + { + lEventArgs.Connection.WriteLine("550 Permission Denied"); + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_PASV(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if (lSession.ActiveConnection != null) + { + if (lSession.ActiveConnection.Connected) + lSession.ActiveConnection.Close(); + + lSession.ActiveConnection = null; + } + + if (lSession.PassiveServer == null) + { + lSession.PassiveServer = new SimpleServer(); + + lSession.PassiveServer.Binding.Address = ((IPEndPoint)e.Connection.LocalEndPoint).Address; + lSession.PassiveServer.Open(); + } + lSession.Passive = true; + + Byte[] lAddress; +#if FULLFRAMEWORK + lAddress = ((IPEndPoint)lSession.PassiveServer.Binding.ListeningSocket.LocalEndPoint).Address.GetAddressBytes(); +#endif +#if COMPACTFRAMEWORK + IPAddress lIPAddress = ((IPEndPoint)lSession.PassiveServer.Binding.ListeningSocket.LocalEndPoint).Address; + String[] lIPAddressstr = lIPAddress.ToString().Split(new Char[] {'.'}); + lAddress = new Byte[lIPAddressstr.Length]; + for (Int32 i = 0; i < lIPAddressstr.Length; i++) + lAddress[i] = Byte.Parse(lIPAddressstr[i]); +#endif + + Int32 lPort = ((IPEndPoint)lSession.PassiveServer.Binding.ListeningSocket.LocalEndPoint).Port; + e.Connection.WriteLine("227 Entering Passive Mode ({0},{1},{2},{3},{4},{5}).", lAddress[0], lAddress[1], lAddress[2], lAddress[3], + unchecked((Byte)(lPort >> 8)), unchecked((Byte)lPort)); + + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_LIST(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + + if (lSession.Passive) + e.Connection.WriteLine("125 Data connection already open; transfer starting"); + + if (!ValidateConnection(lSession)) + { + e.Connection.WriteLine("425 Unable to build data connection"); + } + else + { + FtpGetListingArgs lListingArgs = new FtpGetListingArgs(e.Session, e.Connection, e.Server); + if (!lSession.Passive) + e.Connection.WriteLine("150 Opening data connection"); + try + { + ((FtpServer)e.Server).InvokeOnGetListing(lListingArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + + for (Int32 i = 0; i < lListingArgs.Listing.Count; i++) + { + FtpListingItem lItem = lListingArgs.Listing[i]; + lSession.ActiveConnection.WriteLine(lItem.ToString()); + } + + lSession.ActiveConnection.Close(); + lSession.ActiveConnection = null; + + try + { + e.Connection.WriteLine("226 Transfer complete."); + } + catch + { + e.Connection.WriteLine("425 Error while transfering"); + } + } + + lSession.RestartPoint = 0; + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_TYPE(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if (e.Parameters.Length != 1) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + switch (e.Parameters[0]) + { + case "I": + e.Connection.WriteLine("200 Type set to I"); + lSession.Image = true; + break; + + case "A": + e.Connection.WriteLine("200 Type set to A"); + lSession.Image = false; + break; + + default: + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + break; + } + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_REST(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if (e.Parameters.Length != 1) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + + try + { + Int64 lRestPoint = Int64.Parse(e.Parameters[0]); + lSession.RestartPoint = lRestPoint; + e.Connection.WriteLine(String.Format("350 Restarting at {0}. Send STORE or RETRIEVE to initiate transfer.", lRestPoint)); + } + catch + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_PORT(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if (e.Parameters.Length != 1) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + String[] lNewPort = e.Parameters[0].Split(new Char[] { ',' }); + + if (lNewPort.Length != 6) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + IPAddress lNewIp; + Int32 lNewPortw; + try + { + lNewIp = IPAddress.Parse(String.Format("{0}.{1}.{2}.{3}", lNewPort[0], lNewPort[1], lNewPort[2], lNewPort[3])); + lNewPortw = Byte.Parse(lNewPort[4]) << 8 | Byte.Parse(lNewPort[5]); + + lSession.Passive = false; + try + { + lSession.ActiveConnection = Client.Connect(lNewIp, lNewPortw, new Binding()); + e.Connection.WriteLine("200 PORT command succesful"); + } + catch + { + e.Connection.WriteLine("500 Illegal PORT command "); + } + } + catch + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + } + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_QUIT(Object sender, CommandEventArgs e) + { + e.Connection.WriteLine(((FtpServer)e.Server).QuitReply); + e.Connection.Close(); + } + + public static void Cmd_PWD(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if (e.Parameters.Length != 0) + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + else + e.Connection.WriteLine(String.Format("257 \"{0}\" is current directory.", lSession.Directory)); + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_CWD(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if (e.Parameters.Length == 0) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + String lNewDir = e.AllParameters; + if (lNewDir.StartsWith("/")) + { + lNewDir = ValidateDirectory(lNewDir); + } + else + { + String lTemp = lSession.Directory; + if (!lTemp.EndsWith("/")) + lTemp = lTemp + "/"; + lNewDir = ValidateDirectory(lTemp + lNewDir); + } + + FtpChangeDirectoryArgs lArgs = new FtpChangeDirectoryArgs(e.Session, e.Connection, e.Server); + lArgs.NewDirectory = lNewDir; + + try + { + ((FtpServer)e.Server).InvokeOnChangeDirectory(lArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + e.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lArgs.ChangeDirOk) + { + e.Connection.WriteLine("250 CWD command successful, new folder is '{0}'.", lSession.Directory); + lSession.Directory = lNewDir; + } + else + { + e.Connection.WriteLine("550 Permission Denied."); + } + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_CDUP(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + { + if (e.Parameters.Length != 0) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + String lNewDir = lSession.Directory; + if (lNewDir.Length > 2) + { + Int32 lNewLen = lNewDir.LastIndexOf('/', lNewDir.Length - 2); + if (lNewLen <= 0) + lNewLen = 1; + lNewDir = lNewDir.Substring(0, lNewLen); + if (lNewDir.Length == 0) + lNewDir = "/"; + } + + FtpChangeDirectoryArgs lEventArgs = new FtpChangeDirectoryArgs(e.Session, e.Connection, e.Server); + lEventArgs.NewDirectory = lNewDir; + try + { + ((FtpServer)e.Server).InvokeOnChangeDirectory(lEventArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + e.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.ChangeDirOk) + { + e.Connection.WriteLine("250 CDUP command successful."); + lSession.Directory = lNewDir; + } + else + { + e.Connection.WriteLine("550 Permission Denied."); + } + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_SYST(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.LoggedIn) + e.Connection.WriteLine("215 " + ((FtpServer)e.Server).SystemType); + else + e.Connection.WriteLine("503 Bad sequence of commands."); + } + + public static void Cmd_USER(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.Start || lSession.State == FtpState.PasswordRequired) + { + if (e.Parameters.Length != 1) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + lSession.Username = e.Parameters[0]; + e.Connection.WriteLine("331 User name okay, need password."); + lSession.State = FtpState.PasswordRequired; + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_ACCT(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.AccountRequired) + { + if (e.Parameters.Length != 1) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + FtpUserAccountArgs lEventArgs = new FtpUserAccountArgs(e.Session, e.Connection, e.Server); + lEventArgs.AccountName = e.Parameters[0]; + try + { + ((FtpServer)e.Server).InvokeOnAccount(lEventArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + e.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.LoginOk) + { + e.Connection.WriteLine("230 User logged in, proceed."); + lSession.State = FtpState.LoggedIn; + } + else + { + lSession.State = FtpState.Start; + e.Connection.WriteLine("530 Unable to login"); + } + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + public static void Cmd_PASS(Object sender, CommandEventArgs e) + { + FtpSession lSession = (FtpSession)e.Session; + + if (lSession.State == FtpState.PasswordRequired) + { + if (e.Parameters.Length != 1) + { + e.Connection.WriteLine("501 Syntax error in parameters or arguments."); + } + else + { + FtpUserLoginEventArgs lEventArgs = new FtpUserLoginEventArgs(e.Session, e.Connection, e.Server); + lEventArgs.UserName = lSession.Username; + lEventArgs.Password = e.Parameters[0]; + try + { + ((FtpServer)e.Server).InvokeOnUserLogin(lEventArgs); + } + catch (FtpException ex) + { + e.Connection.WriteLine(ex.ToString()); + return; + } + catch + { + e.Connection.WriteLine("500 Internal Error"); + return; + } + + if (lEventArgs.LoginOk) + { + if (lEventArgs.NeedAccount) + { + lSession.State = FtpState.AccountRequired; + e.Connection.WriteLine("332 Need account for login."); + } + else + { + lSession.State = FtpState.LoggedIn; + e.Connection.WriteLine("230 User logged in, proceed."); + } + } + else + { + lSession.State = FtpState.Start; + e.Connection.WriteLine("530 Unable to login"); + } + } + } + else + { + e.Connection.WriteLine("503 Bad sequence of commands."); + } + } + + internal protected override void HandleCommandException(Connection connection, Exception exception) + { + if (this.Detailed500Errors) + { + connection.WriteLine(String.Format("500 Internal Error: ({0}) {1}", exception.GetType().FullName, CleanStringForCommandResponse(exception.Message))); + } + else + { + connection.WriteLine("500 Internal Error"); + } + + // Don't call base, as we wanna keep the connection open. + } + } + + public enum FtpState + { + Start, // Connection is opened + PasswordRequired, // USER command was given + AccountRequired, // PASS command was ok, server requires ACCOUNT + LoggedIn, + Transfer + }; + + public class FtpSession : CommandBasedSession + { + public FtpSession() + { + this.Directory = "/"; + this.RenameFrom = null; + this.UserData = null; + this.Image = false; + this.State = FtpState.Start; + this.RestartPoint = 0; + this.PassiveServer = null; + this.ActiveConnection = null; + this.TransferThread = null; + this.Passive = false; + this.Username = null; + } + + #region Properties + public virtual String Directory + { + get + { + return this.fDirectory; + } + set + { + this.fDirectory = value; + } + } + private String fDirectory; + + public virtual String RenameFrom + { + get + { + return this.fRenameFrom; + } + set + { + this.fRenameFrom = value; + } + } + private String fRenameFrom; + + public Object UserData + { + get + { + return this.fUserData; + } + set + { + this.fUserData = value; + } + } + private Object fUserData; + + public Boolean Image + { + get + { + return this.fImage; + } + set + { + this.fImage = value; + } + } + private Boolean fImage; + + public FtpState State + { + get + { + return this.fState; + } + set + { + this.fState = value; + } + } + private FtpState fState; + + public Int64 RestartPoint + { + get + { + return this.fRestartPoint; + } + set + { + this.fRestartPoint = value; + } + } + private Int64 fRestartPoint; + + public SimpleServer PassiveServer + { + get + { + return this.fPassiveServer; + } + set + { + this.fPassiveServer = value; + } + } + private SimpleServer fPassiveServer; + + public Connection ActiveConnection + { + get + { + return this.fActiveConnection; + } + set + { + this.fActiveConnection = value; + } + } + private Connection fActiveConnection; + + internal FtpTransferThread TransferThread + { + get + { + return this.fTransferThread; + } + set + { + this.fTransferThread = value; + } + } + private FtpTransferThread fTransferThread; + + public Boolean Passive + { + get + { + return this.fPassive; + } + set + { + this.fPassive = value; + + if (!this.fPassive && this.fPassiveServer != null) + { + this.fPassiveServer.Close(); + this.fPassiveServer = null; + } + } + } + private Boolean fPassive; + + public String Username + { + get + { + return this.fUsername; + } + set + { + this.fUsername = value; + } + } + private String fUsername; + #endregion + } + + #region 'FtpException' custom exception class + + [Serializable] + public class FtpException : System.ApplicationException + { + public FtpException() + : base() + { + this.fCode = 500; + } + + public FtpException(String message) + : base(message) + { + this.fCode = 500; + } + + public FtpException(String message, Exception e) + : base(message, e) + { + this.fCode = 500; + } + + public FtpException(Int32 code, String message) + : this(message) + { + this.fCode = code; + } + + public FtpException(String message, Int32 code) + : this(message) + { + this.fCode = code; + } + + public FtpException(String message, Int32 code, Exception e) + : this(message, e) + { + this.fCode = code; + } + + public Int32 Code + { + get + { + return this.fCode; + } + } + private Int32 fCode; + + public override String ToString() + { + return String.Format("{0} {1}", this.fCode, this.Message); + } + } + #endregion + + #region Transfer Thread + class FtpTransferThread + { + private FtpTransferEventArgs fEventArgs; + private Boolean fStore; + + public FtpTransferThread(FtpTransferEventArgs e, Boolean store) + { + this.fEventArgs = e; + this.fStore = store; + + new Thread(new ThreadStart(Execute)).Start(); + } + + public void Abort() + { + try + { + ((FtpSession)this.fEventArgs.Session).ActiveConnection.Close(); + } + catch + { + } + } + + public static String GetFirstLine(String value) + { + if (value.IndexOf("\r\n") != -1) + { + value = value.Substring(0, value.IndexOf("\r\n")); + } + + if (value.IndexOf("\n") != -1) + { + value = value.Substring(0, value.IndexOf("\n")); + } + + return value; + } + + private void Execute() + { + Boolean lOk = true; + + try + { + if (fStore) + { + this.fEventArgs.DataChannel = ((FtpSession)this.fEventArgs.Session).ActiveConnection; + ((FtpServer)this.fEventArgs.Server).InvokeOnStoreFile(this.fEventArgs); + } + else + { + this.fEventArgs.DataChannel = ((FtpSession)this.fEventArgs.Session).ActiveConnection; + ((FtpServer)this.fEventArgs.Server).InvokeOnRetrieveFile(this.fEventArgs); + } + + } + catch (FtpException ex) + { + this.fEventArgs.Connection.WriteLine(ex.ToString()); + lOk = false; + } + catch (Exception ex) + { + this.fEventArgs.Connection.WriteLine("500 Internal Error " + FtpTransferThread.GetFirstLine(ex.Message)); + lOk = false; + } + + ((FtpSession)this.fEventArgs.Session).State = FtpState.LoggedIn; + try + { + ((FtpSession)this.fEventArgs.Session).ActiveConnection.Close(); + } + catch + { + } + + ((FtpSession)this.fEventArgs.Session).ActiveConnection = null; + + if (lOk) + this.fEventArgs.Connection.WriteLine("226 Transfer complete"); + + ((FtpSession)this.fEventArgs.Session).TransferThread = null; + } + } + #endregion +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Glyphs/EchoServer.bmp b/Source/RemObjects.InternetPack/Glyphs/EchoServer.bmp new file mode 100644 index 0000000..54f9360 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/EchoServer.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/FtpClient.bmp b/Source/RemObjects.InternetPack/Glyphs/FtpClient.bmp new file mode 100644 index 0000000..3bd24e6 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/FtpClient.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/FtpServer.bmp b/Source/RemObjects.InternetPack/Glyphs/FtpServer.bmp new file mode 100644 index 0000000..7eefede Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/FtpServer.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/HttpClient.bmp b/Source/RemObjects.InternetPack/Glyphs/HttpClient.bmp new file mode 100644 index 0000000..2258607 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/HttpClient.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/HttpServer.bmp b/Source/RemObjects.InternetPack/Glyphs/HttpServer.bmp new file mode 100644 index 0000000..7522782 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/HttpServer.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/NntpServer.bmp b/Source/RemObjects.InternetPack/Glyphs/NntpServer.bmp new file mode 100644 index 0000000..a5cef6d Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/NntpServer.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/Pop3Client.bmp b/Source/RemObjects.InternetPack/Glyphs/Pop3Client.bmp new file mode 100644 index 0000000..2e645ab Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/Pop3Client.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/Pop3Server.bmp b/Source/RemObjects.InternetPack/Glyphs/Pop3Server.bmp new file mode 100644 index 0000000..4e907c6 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/Pop3Server.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/SimpleHttpServer.bmp b/Source/RemObjects.InternetPack/Glyphs/SimpleHttpServer.bmp new file mode 100644 index 0000000..7522782 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/SimpleHttpServer.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/SmtpClient.bmp b/Source/RemObjects.InternetPack/Glyphs/SmtpClient.bmp new file mode 100644 index 0000000..43df211 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/SmtpClient.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/SmtpServer.bmp b/Source/RemObjects.InternetPack/Glyphs/SmtpServer.bmp new file mode 100644 index 0000000..d24e668 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/SmtpServer.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/TcpClient.bmp b/Source/RemObjects.InternetPack/Glyphs/TcpClient.bmp new file mode 100644 index 0000000..2541626 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/TcpClient.bmp differ diff --git a/Source/RemObjects.InternetPack/Glyphs/TcpServer.bmp b/Source/RemObjects.InternetPack/Glyphs/TcpServer.bmp new file mode 100644 index 0000000..70f7d21 Binary files /dev/null and b/Source/RemObjects.InternetPack/Glyphs/TcpServer.bmp differ diff --git a/Source/RemObjects.InternetPack/HttpClient.cs b/Source/RemObjects.InternetPack/HttpClient.cs new file mode 100644 index 0000000..42cc871 --- /dev/null +++ b/Source/RemObjects.InternetPack/HttpClient.cs @@ -0,0 +1,528 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.Globalization; +using System.IO; + +namespace RemObjects.InternetPack.Http +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Client), "Glyphs.HttpClient.bmp")] +#endif + public class HttpClient : Client + { + public HttpClient() + { + this.Version = DEFAULT_HTTP_VERSION; + this.UserAgent = DEFAULT_HTTP_USERAGENT; + this.Accept = DEFAULT_HTTP_ACCEPT; + this.Timeout = DEFAULT_TIMEOUT; + this.TimeoutEnabled = true; + this.KeepAlive = true; + this.fUrl = new UrlParser(); + this.fProxySettings = new HttpProxySettings(); +#if FULLFRAMEWORK + this.SslOptions = new HttpsConnectionFactory(this.fProxySettings); +#endif + } + + private const String DEFAULT_HTTP_VERSION = "1.1"; + private const String DEFAULT_HTTP_USERAGENT = "RemObjects Internet Pack HTTP Client"; + private const String DEFAULT_HTTP_ACCEPT = "text/html, image/gif, image/jpeg, image/png, */*"; + private const Int32 DEFAULT_TIMEOUT = 1 * 60; /* 1 minute */ + private const String CHARSET_KEY = "charset="; + + private Connection fConnection; + private String fConnectionUrl; + + #region Properties + [Category("Http"), DefaultValue(DEFAULT_HTTP_VERSION)] + public String Version + { + get + { + return fVersion; + } + set + { + fVersion = value; + } + } + private String fVersion; + + // This property is not used anywhere + // But we cannot just drop it for compatibility reasons + [Category("Http")] + public UrlParser Url + { + get + { + return this.fUrl; + } + set + { + this.fUrl = value; + } + } + private UrlParser fUrl; + + [Category("Http"), DefaultValue(true)] + public Boolean KeepAlive + { + get + { + return fKeepAlive; + } + set + { + if (UseConnectionPooling && !value) + UseConnectionPooling = false; + + fKeepAlive = value; + + if (!fKeepAlive) + DisposeHttpConnection(); + } + } + private Boolean fKeepAlive; + + [Browsable(false)] + public ConnectionPool CustomConnectionPool + { + get + { + return base.ConnectionPool; + } + set + { + base.ConnectionPool = value; + if (!KeepAlive && value != null) + KeepAlive = true; + } + } + + [Category("Http"), DefaultValue(false)] + public Boolean UseConnectionPooling + { + get + { + return base.ConnectionPool != null; + } + set + { + if (!value) + { + base.ConnectionPool = null; + return; + } + + if (base.ConnectionPool == null) + base.ConnectionPool = DefaultPool.ConnectionPool; + + if (!KeepAlive) + KeepAlive = true; + } + } + + [Category("Http"), DefaultValue(DEFAULT_HTTP_USERAGENT)] + public String UserAgent + { + get + { + return fUserAgent; + } + set + { + fUserAgent = value; + } + } + private String fUserAgent; + + [Category("Http"), DefaultValue(DEFAULT_HTTP_ACCEPT)] + public String Accept + { + get + { + return fAccept; + } + set + { + fAccept = value; + } + } + private String fAccept; + + [Category("Http"), DefaultValue(DEFAULT_TIMEOUT)] + public Int32 Timeout + { + get + { + return fTimeout; + } + set + { + fTimeout = value; + } + } + private Int32 fTimeout; + + [Category("Http"), DefaultValue(true)] + public Boolean TimeoutEnabled + { + get + { + return fTimeoutEnabled; + } + set + { + fTimeoutEnabled = value; + } + } + private Boolean fTimeoutEnabled; + + [Category("Basic authentication")] + public String UserName + { + get + { + return fUserName; + } + set + { + fUserName = value; + } + } + private String fUserName = ""; + + [Category("Basic authentication")] + public String Password + { + get + { + return fPassword; + } + set + { + fPassword = value; + } + } + private String fPassword = ""; + +#if FULLFRAMEWORK + [Category("Http Proxy settings")] + [Browsable(true)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] +#else + [Browsable(false)] +#endif + public HttpProxySettings ProxySettings + { + get + { + return this.fProxySettings; + } + } + private HttpProxySettings fProxySettings; + #endregion + + #region Methods + public String Get(String url) + { + return Get(url, null); + } + + private static System.Text.Encoding GetEncodingFromContentType(String contentType) + { + Int32 lStartPos = contentType.IndexOf(HttpClient.CHARSET_KEY); + if (lStartPos == -1) + return System.Text.Encoding.ASCII; + + lStartPos += HttpClient.CHARSET_KEY.Length; + Int32 lEndPos = contentType.IndexOf(";", lStartPos); + if (lEndPos == -1) + lEndPos = contentType.Length; + + String lCharsetName = contentType.Substring(lStartPos, lEndPos - lStartPos).Trim(); + + if (String.IsNullOrEmpty(lCharsetName)) + return System.Text.Encoding.ASCII; + + lCharsetName = lCharsetName.ToLower(CultureInfo.InvariantCulture); + + if (String.Equals(lCharsetName, "utf-7", StringComparison.Ordinal)) + return System.Text.Encoding.UTF7; + + if (String.Equals(lCharsetName, "utf-8", StringComparison.Ordinal)) + return System.Text.Encoding.UTF8; + + if (String.Equals(lCharsetName, "unicode", StringComparison.Ordinal)) + return System.Text.Encoding.Unicode; + + if (String.Equals(lCharsetName, "unicodeFFFE", StringComparison.Ordinal)) + return System.Text.Encoding.BigEndianUnicode; + + return System.Text.Encoding.ASCII; + } + + private static void SetAuthorizationHeader(HttpHeaders headers, String header, String username, String password) + { + if (String.IsNullOrEmpty(username)) + return; + + Byte[] lByteData = System.Text.Encoding.UTF8.GetBytes(username + ":" + password); + String lAuthData = "Basic " + Convert.ToBase64String(lByteData, 0, lByteData.Length); + + headers.SetHeaderValue(header, lAuthData); + } + + public String Get(String url, System.Text.Encoding encoding) + { + using (HttpClientResponse response = GetResponse(url)) + { + response.Encoding = (encoding != null) ? encoding : GetEncodingFromContentType(response.Header.ContentType); + return response.ContentString; + } + } + + public Byte[] GetBytes(String url) + { + using (HttpClientResponse response = GetResponse(url)) + return response.ContentBytes; + } + + public HttpClientResponse GetResponse(String url) + { + HttpClientRequest lRequest = new HttpClientRequest(); + lRequest.Url.Parse(url); + lRequest.Header.RequestType = "GET"; + lRequest.Header.SetHeaderValue("Accept", Accept); + lRequest.Header.SetHeaderValue("User-Agent", UserAgent); + lRequest.KeepAlive = KeepAlive; + + return this.Dispatch(lRequest); + } + + public String Post(String url, Byte[] content) + { + HttpClientRequest lRequest = new HttpClientRequest(); + lRequest.Url.Parse(url); + lRequest.RequestType = RequestType.Post; + lRequest.Header.SetHeaderValue("Accept", Accept); + lRequest.Header.SetHeaderValue("User-Agent", UserAgent); + lRequest.KeepAlive = KeepAlive; + lRequest.ContentBytes = content; + + using (HttpClientResponse response = this.Dispatch(lRequest)) + return response.ContentString; + } + + public String Post(String url, Stream content) + { + HttpClientRequest lRequest = new HttpClientRequest(); + lRequest.Url.Parse(url); + lRequest.RequestType = RequestType.Post; + lRequest.Header.SetHeaderValue("Accept", Accept); + lRequest.Header.SetHeaderValue("User-Agent", UserAgent); + lRequest.KeepAlive = KeepAlive; + lRequest.ContentStream = content; + + using (HttpClientResponse response = this.Dispatch(lRequest)) + return response.ContentString; + } + + public void Abort() + { + if (this.fConnection == null) + return; + + if (this.fConnection.Connected) + try + { + this.fConnection.Disconnect(); + } + catch (Exception) + { + } + + this.DisposeHttpConnection(); + } + + public HttpClientResponse TryDispatch(HttpClientRequest request) + { + HttpClient.SetAuthorizationHeader(request.Header, "Authorization", this.UserName, this.Password); + + String lHostname = request.Url.Hostname; + Int32 lPort = request.Url.Port; + Boolean lSslConnection = String.Equals(request.Url.Protocol, "https", StringComparison.OrdinalIgnoreCase); + + // Settings for connection thru Http Proxy + // Note that Request should think that it uses direct connection when SSL is enabled because + // proxy server tunnels SSL data AS IS, without adjusting its HTTP headers + request.UseProxy = this.ProxySettings.UseProxy && !lSslConnection; + if (this.ProxySettings.UseProxy) + { + lHostname = this.ProxySettings.ProxyHost; + lPort = this.ProxySettings.ProxyPort; + + HttpClient.SetAuthorizationHeader(request.Header, "Proxy-Authorization", this.ProxySettings.UserName, this.ProxySettings.Password); + } + + Connection lConnection = this.GetHttpConnection(lSslConnection, request.Url.Hostname, request.Url.Port, lHostname, lPort); + + try + { + request.WriteHeaderToConnection(lConnection); + } + catch (ConnectionClosedException) + { + lConnection = this.GetNewHttpConnection(lHostname, lPort); + request.WriteHeaderToConnection(lConnection); + } + catch (System.Net.Sockets.SocketException) + { + lConnection = this.GetNewHttpConnection(lHostname, lPort); + request.WriteHeaderToConnection(lConnection); + } + + request.WriteBodyToConnection(lConnection); + + lConnection.Timeout = Timeout; + lConnection.TimeoutEnabled = TimeoutEnabled; + + HttpClientResponse lResponse; + do + { + HttpHeaders lHeaders = HttpHeaders.Create(lConnection); + if (lHeaders == null) + throw new ConnectionClosedException(); + lResponse = new HttpClientResponse(lConnection, lHeaders); + } + while (lResponse.Header.ResponseCode == "100"); // 100 CONTINUE means useless response. + + if (!lResponse.KeepAlive) + { + this.fConnectionUrl = null; + this.fConnection = null; + } + + if (lResponse.Code == 407) + throw new HttpException("Proxy authorization failed", lResponse); + + return lResponse; + } + + public HttpClientResponse Dispatch(HttpClientRequest request) + { + HttpClientResponse lResponse = this.TryDispatch(request); + + if (lResponse.Code >= 400) + { + if (lResponse.HasContentLength) + throw new HttpException(lResponse.ContentString, lResponse); + + throw new HttpException(lResponse.Header.ToString(), lResponse); + } + + return lResponse; + } + + private void DisposeHttpConnection() + { + if (this.fConnection != null) + { + this.fConnection.Dispose(); + this.fConnection = null; + } + + this.fConnectionUrl = null; + } + + protected Connection GetHttpConnection(Boolean enableSSL, String targetHost, Int32 targetPort, String connectionHost, Int32 connectionPort) + { +#if FULLFRAMEWORK + if (enableSSL) + { + this.SslOptions.Enabled = true; + this.SslOptions.TargetHostName = targetHost; + ((HttpsConnectionFactory)this.SslOptions).TargetPort = targetPort; + } + else + { + this.SslOptions.Enabled = false; + } +#endif + + if (!this.KeepAlive || (this.ConnectionPool != null)) + return this.Connect(connectionHost, connectionPort); // pooling class will make sure we use the right connection + + String lUrl = connectionHost + ':' + connectionPort; + + if (this.fConnection != null) + { + if ((this.fConnectionUrl == lUrl) && this.fConnection.Connected) + return this.fConnection; + + this.fConnection.Dispose(); + } + + this.fConnectionUrl = lUrl; + this.fConnection = this.Connect(connectionHost, connectionPort); + + return this.fConnection; + } + + private Connection GetNewHttpConnection(String hostname, Int32 port) + { + if (base.ConnectionPool != null) + return this.ConnectNew(hostname, port); + + this.DisposeHttpConnection(); + + this.fConnectionUrl = hostname + ':' + port; + this.fConnection = this.ConnectNew(hostname, port); + + return this.fConnection; + } + #endregion + } + + [Serializable] + public class HttpException : Exception + { + public HttpException(HttpClientResponse response) + : base() + { + this.fResponse = response; + } + + public HttpException(String message, HttpClientResponse response) + : base(message) + { + this.fResponse = response; + } + + public HttpException(String message, HttpClientResponse response, Exception innerException) + : base(message, innerException) + { + this.fResponse = response; + } + + #region Properties + public HttpClientResponse Response + { + get + { + return this.fResponse; + } + } + private readonly HttpClientResponse fResponse; + #endregion + } +} diff --git a/Source/RemObjects.InternetPack/HttpHeader.cs b/Source/RemObjects.InternetPack/HttpHeader.cs new file mode 100644 index 0000000..19178ad --- /dev/null +++ b/Source/RemObjects.InternetPack/HttpHeader.cs @@ -0,0 +1,556 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace RemObjects.InternetPack.Http +{ + public class HttpHeader + { + #region Private fields + List fValues = new List(); + #endregion + + public HttpHeader(String name, String value) + { + this.Name = name; + this.fValues.Add(value); + } + + public HttpHeader(String line) + { + Int32 lPos = line.IndexOf(":"); + if (lPos == -1) + throw new HttpHeaderException("Invalid HTTP Header Line \"" + line + "\""); + + this.Name = line.Substring(0, lPos); + this.fValues.Add(line.Substring(lPos + 2)); + } + + #region ToString + public override String ToString() + { + if (this.Count == 1) + return String.Format("{0}: {1}", this.Name, this.fValues[0]); + + StringBuilder lResult = new StringBuilder(); + for (Int32 i = 0; i < this.Count; i++) + { + if (i > 0) + lResult.Append("\r\n"); + + lResult.Append(String.Format("{0}: {1}", this.Name, this.fValues[i])); + } + + return lResult.ToString(); + } + #endregion + + #region Properties + public String Name + { + get + { + return this.fName; + } + set + { + this.fName = value; + } + } + private String fName; + + public Int32 Count + { + get + { + return this.fValues.Count; + } + } + + public String Value + { + get + { + if (this.Count == 1) + return this.fValues[0]; + + StringBuilder lResult = new StringBuilder(); + for (Int32 i = 0; i < this.Count; i++) + { + if (i > 0) + lResult.Append(","); + + lResult.Append(String.Format("\"{0}\"", this.fValues[i])); + } + + return lResult.ToString(); + } + set + { + this.fValues.Clear(); + this.fValues.Add(value); + } + } + #endregion + + public String Get(Int32 index) + { + return this.fValues[index].ToString(); + } + + public void Add(String item) + { + this.fValues.Add(item); + } + } + + class HttpHeaderEnumerator : IEnumerator + { + public HttpHeaderEnumerator(IEnumerator parent) + { + this.fParent = parent; + } + + private readonly IEnumerator fParent; + + #region IEnumerator Members + public void Reset() + { + this.fParent.Reset(); + } + + public object Current + { + get + { + return ((DictionaryEntry)(this.fParent.Current)).Value; + } + } + + public Boolean MoveNext() + { + return this.fParent.MoveNext(); + } + #endregion + } + + public class HttpHeaders : IEnumerable + { + public HttpHeaders() + { + this.fHeaders = new Hashtable(StringComparer.OrdinalIgnoreCase); + this.Initialize(); + } + + // Cannot use Dictionary<> here because non-generic Enumerator is exposed + private Hashtable fHeaders; + + public static HttpHeaders Create(Connection connection) + { + HttpHeaders lResult = new HttpHeaders(); + if (!lResult.ReadHeader(connection)) + return null; + + return lResult; + } + + protected virtual void Initialize() + { + this.MaxHeaderLines = 100; + this.MaxHeaderLinesEnabled = true; + } + + #region Private Helper Methods + public void ParseFirstLine() + { + if (this.fFirstHeader.Length == 0) + throw new HttpHeaderException("HTTP Header is empty"); ; + + String lHeaderLine = this.fFirstHeader; + String[] lRequestHeaderValues = lHeaderLine.Split(' '); + + if (lRequestHeaderValues.Length < 3) + throw new HttpHeaderException("Invalid HTTP Header Line \"" + lHeaderLine + "\""); + + if (lHeaderLine.StartsWith("HTTP/")) + { + // HTTP Response + this.fResponseCode = lRequestHeaderValues[1]; + + StringBuilder lResponseText = new StringBuilder(24); + for (Int32 i = 2; i < lRequestHeaderValues.Length; i++) + { + if (i > 2) + lResponseText.Append(' '); + lResponseText.Append(lRequestHeaderValues[i]); + } + this.fResponseText = lResponseText.ToString(); + } + else + { + // HTTP Request + this.fRequestType = lRequestHeaderValues[0]; + this.fRequestPath = lRequestHeaderValues[1]; + this.fRequestVersion = lRequestHeaderValues[2]; + + if (this.fRequestVersion.StartsWith("HTTP/")) + this.fRequestVersion = fRequestVersion.Substring(5); + } + } + #endregion + + #region Properties + public String RequestType + { + get + { + return fRequestType; + } + set + { + fRequestType = value; + } + } + private String fRequestType; + + public String RequestPath + { + get + { + return fRequestPath; + } + set + { + fRequestPath = value; + } + } + private String fRequestPath; + + public String RequestVersion + { + get + { + return fRequestVersion; + } + set + { + fRequestVersion = value; + } + } + private String fRequestVersion; + + public String ResponseCode + { + get + { + return fResponseCode; + } + } + private String fResponseCode; + + public String ResponseText + { + get + { + return this.fResponseText; + } + } + private String fResponseText; + + public Int32 MaxHeaderLines + { + get + { + return fMaxHeaderLines; + } + set + { + fMaxHeaderLines = value; + } + } + private Int32 fMaxHeaderLines; + + public Boolean MaxHeaderLinesEnabled + { + get + { + return fMaxHeaderLinesEnabled; + } + set + { + fMaxHeaderLinesEnabled = value; + } + } + private Boolean fMaxHeaderLinesEnabled; + + public String ContentType + { + get + { + if (ContainsHeaderValue(CONTENT_TYPE)) + return GetHeaderValue(CONTENT_TYPE); + + return ""; + } + set + { + SetHeaderValue(CONTENT_TYPE, value); + } + } + private const String CONTENT_TYPE = "Content-Type"; + + public String FirstHeader + { + get + { + return fFirstHeader; + } + set + { + fFirstHeader = value; + } + } + private String fFirstHeader; + + public Int32 Count + { + get + { + return fHeaders.Count; + } + } + + public HttpHeader this[String key] + { + get + { + if (this.fHeaders.ContainsKey(key)) + return (HttpHeader)this.fHeaders[key]; + + return null; + } + } + #endregion + + #region Methods + // This method returns: + // 1. NULL if incoming datastream doesn't contain at least 4 bytes + // 2. Empty String is HTTP method is unknown + // 3. HTTP method name + private static String ReadHttpMethodName(Connection connection) + { + Byte[] lBuffer = new Byte[4]; + + if (connection.Receive(lBuffer, 0, 4) < 4) + return null; + + String lHttpMethodName = Encoding.ASCII.GetString(lBuffer, 0, 4); + + if ((lHttpMethodName == "POST") || (lHttpMethodName == "GET ") || (lHttpMethodName == "HTTP") || + (lHttpMethodName == "HEAD") || (lHttpMethodName == "PUT ")) + return lHttpMethodName; + + if (lHttpMethodName == "MERG") + { + connection.Read(lBuffer, 0, 1); + if (lBuffer[0] != (Byte)'E') + return String.Empty; + + return "MERGE"; + } + + if (lHttpMethodName == "DELE") + { + connection.Read(lBuffer, 0, 2); + if (lBuffer[0] != (Byte)'T' || lBuffer[1] != (Byte)'E') + return String.Empty; + + return "DELETE"; + } + + if (lHttpMethodName == "OPTI") + { + connection.Read(lBuffer, 0, 3); + if (lBuffer[0] != (Byte)'O' || lBuffer[1] != (Byte)'N' || lBuffer[2] != (Byte)'S') + return String.Empty; + + return "OPTIONS"; + } + + return String.Empty; + } + + public Boolean ReadHeader(Connection connection) + { + this.fFirstHeader = String.Empty; + + String lStart = HttpHeaders.ReadHttpMethodName(connection); + + if (lStart == null) + return false; + + if (String.Equals(lStart, String.Empty, StringComparison.Ordinal)) + throw new HttpRequestInvalidException(500, "Invalid HTTP Request, 'POST', 'MERGE', 'GET', 'DELETE', 'PUT' or 'HEAD' header expected."); + + String lHeaderLine; + do + { + lHeaderLine = connection.ReadLine(); + + if (!String.IsNullOrEmpty(lHeaderLine)) + { + if (this.fFirstHeader.Length == 0) + { + this.fFirstHeader = lStart + lHeaderLine; + } + else + { + Int32 lPos = lHeaderLine.IndexOf(":"); + if (lPos == -1) + throw new HttpHeaderException("Invalid HTTP Header Line \"" + lHeaderLine + "\""); + + String lName = lHeaderLine.Substring(0, lPos); + String lValue = null; + + // There should be a space after the ":" , but at least one custome said that this is not + // always true + // So we check if there is space after the ":" + if (lHeaderLine.Length > lPos + 1) + { + if (lHeaderLine[lPos + 1] == ' ') + lValue = lHeaderLine.Substring(lPos + 2); + else + lValue = lHeaderLine.Substring(lPos + 1); + } + + HttpHeader lHeader = this[lName]; + if (lHeader == null) + { + lHeader = new HttpHeader(lName, lValue); + fHeaders.Add(lName, lHeader); + } + else + { + lHeader.Add(lValue); + } + } + } + + if (this.MaxHeaderLinesEnabled && this.fHeaders.Count > this.MaxHeaderLines - 1) // -1 because FirstHeader is not in hashtable + { + connection.Disconnect(); + throw new HttpHeaderException(String.Format("Too many header lines received (maximum is set to {0})", MaxHeaderLines)); + } + } + while (!String.IsNullOrEmpty(lHeaderLine)); + + this.ParseFirstLine(); + + return true; + } + + public void WriteHeader(Connection connection) + { + connection.WriteLine(fFirstHeader); + + foreach (HttpHeader header in this) + { + connection.WriteLine(header.ToString()); + } + connection.WriteLine(""); + } + + public void SetResponseHeader(String version, Int32 resultCode, String resultText) + { + fFirstHeader = String.Format("HTTP/{0} {1} {2}", version, resultCode, resultText); + } + + public void SetRequestHeader(String version, String requestType, String requestPath) + { + fFirstHeader = String.Format("{0} {1} HTTP/{2}", requestType, requestPath, version); + } + + public Boolean ContainsHeaderValue(String key) + { + return this.fHeaders.ContainsKey(key); + } + + public void SetHeaderValue(String name, String value) + { + HttpHeader lHeader = this[name]; + + if (lHeader == null) + { + lHeader = new HttpHeader(name, value); + fHeaders[name] = lHeader; + } + else + { + lHeader.Value = value; + } + } + + public String GetHeaderValue(String name) + { + HttpHeader lHeader = this[name]; + + if (lHeader != null) + return lHeader.Value; + + return null; + } + + public override String ToString() + { + StringBuilder lResult = new StringBuilder(); + lResult.Append(fFirstHeader); + lResult.Append("\r\n"); + + foreach (HttpHeader header in this) + { + lResult.Append(header.ToString()); + lResult.Append("\r\n"); + } + + lResult.Append("\r\n"); + + return lResult.ToString(); + } + #endregion + + #region IEnumerable Members + public IEnumerator GetEnumerator() + { + return new HttpHeaderEnumerator(this.fHeaders.GetEnumerator()); + } + #endregion + } + + [Serializable] + public class HttpHeaderException : System.ApplicationException + { + public HttpHeaderException() + : base() + { + } + + public HttpHeaderException(String message) + : base(message) + { + } + + public HttpHeaderException(String message, Exception innerException) + : base(message, innerException) + { + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/HttpProxySettings.cs b/Source/RemObjects.InternetPack/HttpProxySettings.cs new file mode 100644 index 0000000..000a436 --- /dev/null +++ b/Source/RemObjects.InternetPack/HttpProxySettings.cs @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; + +namespace RemObjects.InternetPack.Http +{ +#if FULLFRAMEWORK + [TypeConverter(typeof(ExpandableObjectConverter))] +#endif + public sealed class HttpProxySettings + { + private const String DEFAULT_HOSTNAME = @"192.168.1.1"; + private const Int32 DEFAULT_PORT = 3128; + + public HttpProxySettings() + { + this.UseProxy = false; + this.ProxyHost = HttpProxySettings.DEFAULT_HOSTNAME; + this.ProxyPort = HttpProxySettings.DEFAULT_PORT; + this.UserName = String.Empty; + this.Password = String.Empty; + } + + [DefaultValue(false)] + public Boolean UseProxy + { + get + { + return this.fUseProxy; + } + set + { + this.fUseProxy = value; + } + } + private Boolean fUseProxy; + + [DefaultValue(HttpProxySettings.DEFAULT_HOSTNAME)] + public String ProxyHost + { + get + { + return this.fProxyHost; + } + set + { + this.fProxyHost = value; + } + } + private String fProxyHost; + + [DefaultValue(HttpProxySettings.DEFAULT_PORT)] + public Int32 ProxyPort + { + get + { + return this.fProxyPort; + } + set + { + this.fProxyPort = value; + } + } + private Int32 fProxyPort; + + [DefaultValue("")] + public String UserName + { + get + { + return this.fUserName; + } + set + { + this.fUserName = value; + } + } + private String fUserName; + + [DefaultValue("")] + public String Password + { + get + { + return this.fPassword; + } + set + { + this.fPassword = value; + } + } + private String fPassword; + + public void CopyProperties(HttpProxySettings source) + { + this.UseProxy = source.UseProxy; + this.ProxyHost = source.ProxyHost; + this.ProxyPort = source.ProxyPort; + this.UserName = source.UserName; + this.Password = source.Password; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/HttpRequestResponse.cs b/Source/RemObjects.InternetPack/HttpRequestResponse.cs new file mode 100644 index 0000000..ad545fd --- /dev/null +++ b/Source/RemObjects.InternetPack/HttpRequestResponse.cs @@ -0,0 +1,1073 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Text; +using RemObjects.InternetPack.Events; + +namespace RemObjects.InternetPack.Http +{ + #region Request/Response Base Classes + public abstract class HttpRequestResponse + { + protected HttpRequestResponse() + { + this.Header = new HttpHeaders(); + this.Encoding = Encoding.ASCII; + } + + protected HttpRequestResponse(HttpHeaders header) + { + this.Header = header; + this.Encoding = Encoding.ASCII; + } + + public HttpHeaders Header + { + get + { + return fHeader; + } + set + { + fHeader = value; + } + } + private HttpHeaders fHeader; + + public Encoding Encoding + { + get + { + return fEncoding; + } + set + { + fEncoding = value; + } + } + private Encoding fEncoding; + + protected abstract Boolean Client { get; } + + protected abstract Boolean Server { get; } + + #region Events + public event TransferStartEventHandler OnTransferStart; + + public event TransferEndEventHandler OnTransferEnd; + + public event TransferProgressEventHandler OnTransferProgress; + + public void CloneEvents(HttpRequestResponse source) + { + OnTransferStart = source.OnTransferStart; + OnTransferEnd = source.OnTransferEnd; + OnTransferProgress = source.OnTransferProgress; + } + + protected Boolean HasOnTransferProgress + { + get + { + return this.OnTransferProgress != null; + } + } + + protected virtual void TriggerOnTransferStart(TransferDirection direction, Int64 size) + { + if (OnTransferStart != null) + OnTransferStart(this, new TransferStartEventArgs(direction, size)); + } + + protected virtual void TriggerOnTransferEnd(TransferDirection direction) + { + if (OnTransferEnd != null) + OnTransferEnd(this, new TransferEndEventArgs(direction)); + } + + protected virtual void TriggerOnTransferProgress(TransferDirection direction, Int64 position) + { + if (OnTransferProgress != null) + OnTransferProgress(this, new TransferProgressEventArgs(direction, position)); + } + #endregion + } + + public abstract class HttpIncomingRequestResponse : HttpRequestResponse + { + protected HttpIncomingRequestResponse(Connection connection, HttpHeaders headers) + : base(headers) + { + this.DataConnection = connection; + } + + public Connection DataConnection + { + get + { + return fDataConnection; + } + private set + { + this.fDataConnection = value; + } + } + private Connection fDataConnection; + + const Int32 BUFFER_SIZE = 64 * 1024; + + #region Properties: Content + public Byte[] ContentBytes + { + get + { + if (fContentBytes == null) + { + if (HasContentLength) /* Server must always have on ContentLength */ + { + fContentBytes = new Byte[ContentLength]; + Int32 lRead = ContentStream.Read(fContentBytes, 0, fContentBytes.Length); + if (lRead != fContentBytes.Length) + throw new Exception("Unexpected end of response"); + } + else + { + Boolean lChunked = Chunked; + Boolean lKeepAlive = KeepAlive; + if (lKeepAlive && !lChunked) + throw new Exception("Content-Length or Chunked Transfer-Encoding required for Keep-Alive."); + + MemoryStream lResult = new MemoryStream(); + + if (lChunked) + { + Int32 lNextChunkSize = ReadChunkSize(); + while (lNextChunkSize > 0) + { + Byte[] lBuffer = new Byte[lNextChunkSize]; + Int32 lRead = DataConnection.Receive(lBuffer, 0, lNextChunkSize); + if (lRead != lNextChunkSize) throw new Exception("Unexpected end of HTTP chunk."); + + // todo: refactor, duped below + lResult.Write(lBuffer, 0, lRead); + + if (DataConnection.ReadLine() != "") throw new Exception("Invalid data at end of HTTP chunk."); + lNextChunkSize = ReadChunkSize(); + } + + // skip footer + while (DataConnection.ReadLine() != "") ; + } + else + { + Int32 lRead = 0; + do + { + Byte[] lBuffer = new Byte[BUFFER_SIZE]; + lRead = DataConnection.Read(lBuffer, 0, BUFFER_SIZE); + lResult.Write(lBuffer, 0, lRead); + } + while (lRead > 0); + } + + fContentBytes = lResult.ToArray(); + + if (!lKeepAlive) + DataConnection.Close(); + } + } + + return fContentBytes; + } + } + private Byte[] fContentBytes; + + public Stream ContentStream + { + get + { + if (fContentStream == null) + { + if (HasContentLength || Server) + fContentStream = new HttpIncomingStream(this); + else + fContentStream = DataConnection; + } + return fContentStream; + } + } + private Stream fContentStream; + + public String ContentString + { + get + { + if (fContentString == null) + fContentString = Encoding.GetString(ContentBytes, 0, ContentBytes.Length); + + return fContentString; + } + } + private String fContentString; + + public Boolean HasContentLength + { + get + { + String lLength = Header.GetHeaderValue("Content-Length"); + if (!String.IsNullOrEmpty(lLength)) + { + try + { + Int32.Parse(lLength); + return true; + } + catch (Exception) + { + } + } + + return false; + } + } + + public Int32 ContentLength + { + get + { + if (fContentLength == -1) + { + String lLength = Header.GetHeaderValue("Content-Length"); + if (String.IsNullOrEmpty(lLength)) + throw new HttpHeaderException("No Content-Lengh specified in header."); + + fContentLength = Int32.Parse(lLength); + } + + return fContentLength; + } + } + private Int32 fContentLength = -1; + + public Boolean KeepAlive + { + get + { + String lConnection = Header.GetHeaderValue("Connection"); + + return lConnection != null && lConnection.ToLower(CultureInfo.InvariantCulture) == "keep-alive"; + } + } + + public Boolean Chunked + { + get + { + String lTransferEncoding = Header.GetHeaderValue("Transfer-Encoding"); + + return lTransferEncoding != null && lTransferEncoding.ToLower() == "chunked"; + } + } + #endregion + + public Boolean FlushContent() + { + if (!HasContentLength) + return true; //nothing to flush + + if (fContentBytes != null) + return true; // we already read entire response + + if (fContentStream != null) + { + if (fContentStream is HttpIncomingStream) + return (fContentStream as HttpIncomingStream).FlushContent(); + + return true; + } + + if (ContentLength > 16 * 1204) + return false; + + DataConnection.SkipBytes(ContentLength); + return true; + } + + public virtual void Validate() + { + if (Header.RequestVersion == "1.1") + { + if (Header.GetHeaderValue("Transfer-Encoding") == "chunked") + throw new HttpRequestInvalidException(500, "Bad Request: Chunked encoding not supported, yet"); + } + } + + private Int32 ReadChunkSize() + { + String lLine = DataConnection.ReadLine(); + Int32 lPos = lLine.IndexOf(';'); + if (lPos >= 0) + lLine = lLine.Substring(0, lPos); + lLine = lLine.Trim(); + + return Int32.Parse(lLine, NumberStyles.AllowHexSpecifier); + } + + } + + public abstract class HttpOutgoingRequestResponse : HttpRequestResponse + { + protected internal HttpOutgoingRequestResponse() + : base() + { + CloseStream = true; + ContentSource = ContentSource.ContentNone; + } + + protected internal HttpOutgoingRequestResponse(HttpHeaders header) + : base(header) + { + CloseStream = true; + ContentSource = ContentSource.ContentNone; + } + + #region Properties: Content + public ContentSource ContentSource + { + get + { + return fContentSource; + } + set + { + fContentSource = value; + } + } + private ContentSource fContentSource; + + public Byte[] ContentBytes + { + get + { + return fContentBytes; + } + set + { + fContentBytes = value; + ContentSource = ContentSource.ContentBytes; + } + } + private Byte[] fContentBytes; + + public Stream ContentStream + { + get + { + return fContentStream; + } + set + { + fContentStream = value; + ContentSource = ContentSource.ContentStream; + } + } + private Stream fContentStream; + + public String ContentString + { + get + { + return fContentString; + } + set + { + fContentString = value; + ContentSource = ContentSource.ContentString; + } + } + private String fContentString; + + public Boolean CloseStream + { + get + { + return fCloseStream; + } + set + { + fCloseStream = value; + } + } + private Boolean fCloseStream; + + public Boolean KeepAlive + { + get + { + String lConnection = Header.GetHeaderValue("Connection"); + return (lConnection != null && lConnection == "Keep-Alive"); //ToDo: use global String; + } + set + { + if (value) + Header.SetHeaderValue("Connection", "Keep-Alive"); + else + Header.SetHeaderValue("Connection", "Close"); + } + } + #endregion + + #region Event Handlers + protected virtual void HandleOnBytesSent(Object sender, EventArgs e) + { + TriggerOnTransferProgress(TransferDirection.Send, ((Connection)sender).BytesSent); + } + #endregion + + #region Write to Connection + const Int32 BUFFER_SIZE = 256 * 1024; /* for now */ + + public virtual void FinalizeHeader() + { + if (ContentSource == ContentSource.ContentString) + ContentBytes = Encoding.GetBytes(ContentString); + + switch (ContentSource) + { + case ContentSource.ContentString: + case ContentSource.ContentBytes: + if (ContentBytes != null) + Header.SetHeaderValue("Content-Length", ContentBytes.Length.ToString()); + else + Header.SetHeaderValue("Content-Length", 0.ToString()); + break; + + case ContentSource.ContentStream: + if (ContentStream != null) + Header.SetHeaderValue("Content-Length", (ContentStream.Length - ContentStream.Position).ToString()); + else + Header.SetHeaderValue("Content-Length", 0.ToString()); + break; + + case ContentSource.ContentNone: + Header.SetHeaderValue("Content-Length", 0.ToString()); + break; + } + } + + public virtual void WriteToConnection(Connection connection) + { + WriteHeaderToConnection(connection); + WriteBodyToConnection(connection); + } + + public virtual void WriteHeaderToConnection(Connection connection) + { + FinalizeHeader(); + Header.WriteHeader(connection); + } + + public virtual void WriteBodyToConnection(Connection connection) + { + try + { + Byte[] lBuffer; + + if (HasOnTransferProgress) + { + connection.OnBytesSent += new EventHandler(HandleOnBytesSent); + connection.ResetStatistics(); + } + + switch (ContentSource) + { + case ContentSource.ContentBytes: + TriggerOnTransferStart(TransferDirection.Send, ContentBytes.Length); + connection.Send(ContentBytes); + TriggerOnTransferEnd(TransferDirection.Send); + break; + + case ContentSource.ContentStream: + TriggerOnTransferStart(TransferDirection.Send, ContentStream.Length); + lBuffer = new Byte[BUFFER_SIZE]; + Int32 lBytesRead; + do + { + lBytesRead = ContentStream.Read(lBuffer, 0, BUFFER_SIZE); + if (lBytesRead != 0) connection.Send(lBuffer, 0, lBytesRead); + } + while (lBytesRead > 0); + + if (CloseStream) + ContentStream.Close(); + + TriggerOnTransferEnd(TransferDirection.Send); + break; + + case ContentSource.ContentNone: + /* Nothing to do */ + break; + } + + if (HasOnTransferProgress) + connection.OnBytesSent -= new EventHandler(HandleOnBytesSent); + } + finally + { + fContentBytes = null; + fContentStream = null; + fContentString = null; + } + } + #endregion + } + #endregion + + public class HttpServerRequest : HttpIncomingRequestResponse + { + protected internal HttpServerRequest(Connection connection, HttpHeaders headers) + : base(connection, headers) + { + String lPath = Header.RequestPath; + Int32 lStart = lPath.IndexOf("?"); + + if (lStart > -1) + { + fQuery = lPath.Substring(lStart + 1); + fPath = lPath.Substring(0, lStart); + } + else + { + fPath = lPath; + } + } + + protected override Boolean Client + { + get + { + return false; + } + } + + protected override Boolean Server + { + get + { + return true; + } + } + + public override void Validate() + { + base.Validate(); + if (Header.RequestVersion == "1.1") + { + if (Header.GetHeaderValue("Host") == null) + throw new HttpRequestInvalidException(500, "Bad Request: No Host Header"); + } + } + + private String fPath; + private String fQuery; + + public QueryString QueryString + { + get + { + if (fQueryString == null) + fQueryString = new QueryString(fQuery); + return fQueryString; + } + } + private QueryString fQueryString; + + public String Path + { + get + { + return fPath; + } + } + } + + public class HttpServerResponse : HttpOutgoingRequestResponse + { + protected internal HttpServerResponse() + : base(new HttpHeaders()) + { + Code = 200; + ResponseText = "OK"; + } + + protected override Boolean Client + { + get + { + return false; + } + } + + protected override Boolean Server + { + get + { + return true; + } + } + + #region Properties + public Int32 Code + { + get + { + return fCode; + } + set + { + fCode = value; + } + } + private Int32 fCode; + + public String ResponseText + { + get + { + return fResponseText; + } + set + { + fResponseText = value; + } + } + private String fResponseText; + #endregion + + #region Methods + public void SendError(Int32 responseCode, String responseText, String message) + { + this.Code = responseCode; + this.ResponseText = responseText; + + String lMessageHtml = String.Format("

Error {0} {1}

{2}


{3}

", responseCode, responseText, message, DEFAULT_SERVER_NAME); + this.ContentBytes = Encoding.ASCII.GetBytes(lMessageHtml); + + this.Header.ContentType = "text/html"; + } + + public void SendError(Int32 responseCode, String message) + { + SendError(responseCode, "ERROR", message); + } + + public void SendError(Int32 responseCode, String responseText, Exception ex) + { + this.Code = responseCode; + this.ResponseText = responseText; +#if FULLFRAMEWORK + String lMessageHtml = String.Format("

Error {0} {1}

{2}: {3}

{4}


{3}

", + responseCode, ex.GetType().Name, ex.GetType().FullName, ex.Message, ex.StackTrace.ToString(), DEFAULT_SERVER_NAME); +#else + String lMessageHtml = String.Format("

Error {0} {1}

{2}: {3}

{4}


", + responseCode, ex.GetType().Name, ex.GetType().FullName, ex.Message, DEFAULT_SERVER_NAME); +#endif + this.ContentBytes = Encoding.ASCII.GetBytes(lMessageHtml); + this.Header.ContentType = "text/html"; + } + + public void SendErrorWithCustomBody(Int32 responseCode, String responseText, String message) + { + this.Code = responseCode; + this.ResponseText = responseText; + this.ContentBytes = Encoding.ASCII.GetBytes(message); + this.Header.ContentType = "text/html"; + } + + private const String DEFAULT_SERVER_NAME = "RemObjects Internet Pack for .NET HTTP Server"; + + public override void FinalizeHeader() + { + base.FinalizeHeader(); + Header.SetResponseHeader("1.1", Code, ResponseText); + } + #endregion + } + + public class HttpClientRequest : HttpOutgoingRequestResponse + { + public HttpClientRequest() + : base() + { + this.RequestType = RequestType.Get; + this.Url = new UrlParser(); + } + + protected override Boolean Client + { + get + { + return true; + } + } + + protected override Boolean Server + { + get + { + return false; + } + } + + #region Properties + public UrlParser Url + { + get + { + return this.fUrl; + } + set + { + this.fUrl = value; + } + } + private UrlParser fUrl; + + public Boolean UseProxy + { + get + { + return this.fUseProxy; + } + set + { + this.fUseProxy = value; + } + } + private Boolean fUseProxy; + + public RequestType RequestType + { + get + { + return this.fRequestType; + } + set + { + this.fRequestType = value; + } + } + private RequestType fRequestType; + #endregion + + #region Methods + public override void FinalizeHeader() + { + base.FinalizeHeader(); + + String lRequestTypeString; + switch (this.RequestType) + { + case RequestType.Get: + lRequestTypeString = "GET"; + break; + + case RequestType.Post: + lRequestTypeString = "POST"; + break; + + case RequestType.Put: + lRequestTypeString = "PUT"; + break; + + case RequestType.Delete: + lRequestTypeString = "DELETE"; + break; + + case RequestType.Head: + lRequestTypeString = "HEAD"; + break; + + default: + throw new HttpHeaderException("Invalid Request Type specified"); + } + + // If connection goes thru proxy we have to provide full target Url (it is used by proxy to forward request). + // Otherwise only path (fe '/bin') is needed + // Always providing full target Url will breack the backward compatibility + this.Header.SetRequestHeader("1.1", lRequestTypeString, this.UseProxy ? this.Url.ToString() : this.Url.PathAndParams); + + this.Header.SetHeaderValue("Host", this.Url.HostnameAndPort); + } + #endregion + } + + public class HttpClientResponse : HttpIncomingRequestResponse, IDisposable + { + protected internal HttpClientResponse(Connection connection, HttpHeaders headers) + : base(connection, headers) + { + this.fCode = Int32.Parse(Header.ResponseCode); + } + + protected override Boolean Client + { + get + { + return true; + } + } + + protected override Boolean Server + { + get + { + return false; + } + } + + #region Properties + public Int32 Code + { + get + { + return fCode; + } + } + private Int32 fCode; + #endregion + + #region IDisposable Members + public void Dispose() + { + if (KeepAlive) + { + if (FlushContent()) + { + if (DataConnection.Pool != null) + DataConnection.Pool.ReleaseConnection(DataConnection); + } + else + { + DataConnection.Dispose(); + } + } + else + { + DataConnection.Dispose(); + } + } + #endregion + } + + public class HttpIncomingStream : Stream + { + public HttpIncomingStream(HttpIncomingRequestResponse owner) + { + if (owner.Chunked) + throw new Exception("ContentStream is currently not supported for Chunked HTTP transfer."); + + this.fOwner = owner; + } + + private HttpIncomingRequestResponse fOwner; + + internal Boolean FlushContent() + { + Int32 lLeft = (Int32)Length - (Int32)fPosition; + + if (lLeft > 16 * 1024) + return false; + + fOwner.DataConnection.SkipBytes(lLeft); + + return true; + } + + #region System.IO.Stream Methods + public override void Flush() + { + /* no-op? */ + } + + public override void Close() + { + /* no-op? */ + } + + public override Int32 Read(Byte[] buffer, Int32 offset, Int32 size) + { + if (size > Length - fPosition) size = (Int32)Length - (Int32)fPosition; + + if (size <= 0) return 0; + Int32 lResult = fOwner.DataConnection.Receive(buffer, offset, size); + fPosition += lResult; + return lResult; + } + + public override Int64 Seek(Int64 offset, SeekOrigin origin) + { + throw new Exception(String.Format("{0} does not support seeking", this.GetType().FullName)); + } + + public override void SetLength(Int64 length) + { + throw new Exception(String.Format("{0} does not support SetLength", this.GetType().FullName)); + } + + public override void Write(Byte[] buffer, Int32 offset, Int32 size) + { + throw new Exception(String.Format("{0} is a read-only Stream", this.GetType().FullName)); + } + + public override Boolean CanRead + { + get + { + return true; + } + } + + public override Boolean CanSeek + { + get + { + return false; + } + } + + public override Boolean CanWrite + { + get + { + return false; + } + } + + public override Int64 Length + { + get + { + return fOwner.ContentLength; + } + } + + public override Int64 Position + { + get + { + return fPosition; + } + set + { + Seek(value, SeekOrigin.Begin); + fPosition = value; + } + } + private Int64 fPosition; + #endregion + } + + public enum ContentSource + { + ContentNone, + ContentBytes, + ContentStream, + ContentString + } + + public enum RequestType + { + Get, + Post, + Put, + Delete, + Head + } + + public class QueryString : Hashtable + { + public QueryString(String query) + : base() + { + fQuery = query; + if (fQuery != null && fQuery.Length > 0) + { + String[] lParams = fQuery.Split(new char[] { '&' }); + for (Int32 i = 0; i < lParams.Length; i++) + { + Int32 lEqual = lParams[i].IndexOf('='); + if (lEqual > -1) + { + String lName = lParams[i].Substring(0, lEqual).Trim().ToLower(); + String lValue = lParams[i].Substring(lEqual + 1); + if (ContainsKey(lName)) + base[lName] = this[lName] + "," + lValue; + else + base[lName] = lValue; + } + else + { + this.Add(lParams[i], null); + } + } + } + + } + + private String fQuery; + + public override String ToString() + { + if (fQuery == null) + return ""; + + return fQuery; + } + + public new String this[object key] + { + get + { + return (String)base[key]; + } + } + + } + + [Serializable] + public class HttpRequestInvalidException : System.ApplicationException + { + public HttpRequestInvalidException(Int32 errorCode, String errorMessage) + : this(errorCode, errorMessage, null) + { + } + + public HttpRequestInvalidException(Int32 errorCode, String errorMessage, Exception innerException) + : base(errorCode.ToString() + " " + errorMessage, innerException) + { + this.fResponse = new HttpServerResponse(); + this.fResponse.Code = errorCode; + this.fResponse.ResponseText = errorMessage; + this.fResponse.ContentString = errorMessage; + } + + public HttpServerResponse Response + { + get + { + return this.fResponse; + } + } + private readonly HttpServerResponse fResponse; + } +} diff --git a/Source/RemObjects.InternetPack/HttpServer.cs b/Source/RemObjects.InternetPack/HttpServer.cs new file mode 100644 index 0000000..3b5bcc1 --- /dev/null +++ b/Source/RemObjects.InternetPack/HttpServer.cs @@ -0,0 +1,228 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.Net.Sockets; + +namespace RemObjects.InternetPack.Http +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.HttpServer.bmp")] +#endif + public class HttpServer : Server + { + public HttpServer() + : base() + { + this.DefaultPort = 80; + this.KeepAlive = true; + this.ServerName = sServerName; + this.ValidateRequests = true; + } + +#if FULLFRAMEWORK + public HttpServer(IContainer container) + : this() + { + container.Add(this); + } +#endif + + public override Type GetWorkerClass() + { + return typeof(HttpWorker); + } + + #region Properties + [DefaultValue(true), Category("Server")] + public Boolean KeepAlive + { + get + { + return fKeepAlive; + } + set + { + fKeepAlive = value; + } + } + private Boolean fKeepAlive; + + [DefaultValue(true), Category("Server")] + public Boolean ValidateRequests + { + get + { + return fValidateRequests; + } + set + { + fValidateRequests = value; + } + } + private Boolean fValidateRequests; + + [DefaultValue(sServerName), Category("Server")] + public String ServerName + { + get + { + return fServerName; + } + set + { + fServerName = value; + } + } + private String fServerName; + + public const String sServerName = "Internet Pack HTTP Server"; + #endregion + + #region Events + public event OnHttpRequestHandler OnHttpRequest; + + protected virtual void TriggerOnHttpRequest(Connection connection, HttpServerRequest request, HttpServerResponse response) + { + if (OnHttpRequest != null) + { + OnHttpRequestArgs lEventArgs = new OnHttpRequestArgs(connection, request, response); + OnHttpRequest(this, lEventArgs); + } + } + #endregion + + #region Methods for implementation in descendand classes + internal protected virtual void HandleHttpRequest(Connection connection, HttpServerRequest request, HttpServerResponse response) + { + this.TriggerOnHttpRequest(connection, request, response); + } + #endregion + } + + public class HttpWorker : Worker + { + public HttpWorker() + { + } + + public new HttpServer Owner + { + get + { + return (HttpServer)base.Owner; + } + } + + protected override void DoWork() + { + try + { + ProcessRequests(); + } + catch (SocketException ex) + { + /* 10054 means the connection was closed by the client while reading from the socket. + * we'll just terminate the thread gracefully, as if this was expected. */ + /* ToDo: provide some sort of notification to the server object via event. */ + if (ex.ErrorCode != 10054) + throw ex; + } + } + + public virtual void ProcessRequests() + { + do + { + try + { + HttpHeaders lHeaders = HttpHeaders.Create(DataConnection); + if (lHeaders == null) + { + DataConnection.Close(); // disconnected + break; + } + HttpServerRequest lRequest = new HttpServerRequest(DataConnection, lHeaders); + + /* ToDo: make (some) validation optinal, for speed purposes */ + if (Owner.ValidateRequests) + lRequest.Validate(); + + HttpServerResponse lResponse = new HttpServerResponse(); + lResponse.KeepAlive = (lRequest.KeepAlive && Owner.KeepAlive); + lResponse.Header.SetHeaderValue("Server", Owner.ServerName); + + Owner.HandleHttpRequest(DataConnection, lRequest, lResponse); + + lRequest.FlushContent(); + + lResponse.WriteToConnection(DataConnection); + if (!lRequest.KeepAlive) + DataConnection.Close(false); + } + catch (HttpRequestInvalidException e) + { + e.Response.Header.SetHeaderValue("Server", Owner.ServerName); + e.Response.WriteToConnection(DataConnection); + DataConnection.Close(); + } + catch (ConnectionClosedException) + { + DataConnection.Close(false); + } + catch (SocketException) + { + DataConnection.Close(false); + } + catch (Exception e) + { + HttpServerResponse lResponse = new HttpServerResponse(); + lResponse.Header.SetHeaderValue("Server", Owner.ServerName); + lResponse.SendError(500, "Server Error while processing HTTP request.", e); + lResponse.WriteToConnection(DataConnection); + DataConnection.Close(); + } + + if (!Owner.KeepAlive) + DataConnection.Close(); + } + while (Owner.KeepAlive && DataConnection.Connected); + } + } + + public delegate void OnHttpRequestHandler(Object sender, OnHttpRequestArgs e); + + public class OnHttpRequestArgs : ConnectionEventArgs + { + public OnHttpRequestArgs(Connection connection, HttpServerRequest request, HttpServerResponse response) + : base(connection) + { + this.fRequest = request; + this.fResponse = response; + } + + public HttpServerRequest Request + { + get + { + return this.fRequest; + } + } + private readonly HttpServerRequest fRequest; + + public HttpServerResponse Response + { + get + { + return this.fResponse; + } + } + private readonly HttpServerResponse fResponse; + } +} diff --git a/Source/RemObjects.InternetPack/HttpsConnection.cs b/Source/RemObjects.InternetPack/HttpsConnection.cs new file mode 100644 index 0000000..bb0eac8 --- /dev/null +++ b/Source/RemObjects.InternetPack/HttpsConnection.cs @@ -0,0 +1,172 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.Net.Sockets; +using System.Text; + +namespace RemObjects.InternetPack.Http +{ + public class HttpsConnectionFactory : SslConnectionFactory + { + public HttpsConnectionFactory(HttpProxySettings proxySettings) + { + this.ProxySettings = proxySettings; + this.TargetPort = 8099; + } + + [DefaultValue(8099)] + [Category("Ssl Options")] + public Int32 TargetPort + { + get + { + return this.fTargetPort; + } + set + { + this.fTargetPort = value; + } + } + private Int32 fTargetPort; + + [Browsable(false)] + public HttpProxySettings ProxySettings + { + get + { + return this.fProxySettings; + } + protected set + { + this.fProxySettings = value; + } + } + private HttpProxySettings fProxySettings; + + public override Connection CreateClientConnection(Binding binding) + { + if (!this.Enabled) + return new Connection(binding); + + if (this.HasCertificate) + this.LoadCertificate(); + + return new HttpSslConnection(this, binding); + } + + public override Connection CreateClientConnection(Connection connection) + { + if (this.HasCertificate) + this.LoadCertificate(); + + return new HttpSslConnection(this, connection); + } + } + + public class HttpSslConnection : SslConnection + { + public HttpSslConnection(HttpsConnectionFactory factory, Binding binding) + : base(factory, binding) + { + this.fHttpsConnectionFactory = factory; + } + + public HttpSslConnection(HttpsConnectionFactory factory, Socket socket) + : base(factory, socket) + { + this.fHttpsConnectionFactory = factory; + } + + public HttpSslConnection(HttpsConnectionFactory factory, Connection connection) + : base(factory, connection) + { + this.fHttpsConnectionFactory = factory; + } + + private HttpsConnectionFactory fHttpsConnectionFactory; + + private String ComposeSslTunnelRequest() + { + StringBuilder lSslTunnelRequest = new StringBuilder(1024); + + String lFullHostName = this.fHttpsConnectionFactory.TargetHostName + ":" + this.fHttpsConnectionFactory.TargetPort; + + lSslTunnelRequest.Append("CONNECT "); + lSslTunnelRequest.Append(lFullHostName); + lSslTunnelRequest.AppendLine(" HTTP/1.1"); + + lSslTunnelRequest.Append("Host: "); + lSslTunnelRequest.AppendLine(lFullHostName); + + if (!String.IsNullOrEmpty(this.fHttpsConnectionFactory.ProxySettings.UserName)) + { + Byte[] lByteData = System.Text.Encoding.UTF8.GetBytes(this.fHttpsConnectionFactory.ProxySettings.UserName + ":" + this.fHttpsConnectionFactory.ProxySettings.Password); + + lSslTunnelRequest.Append("Proxy-Authorization: Basic "); + lSslTunnelRequest.Append(Convert.ToBase64String(lByteData, 0, lByteData.Length)); + lSslTunnelRequest.AppendLine(); + } + lSslTunnelRequest.AppendLine(); + + return lSslTunnelRequest.ToString(); + } + + private void ParseSslTunnelResponse(Byte[] rawResponseData) + { + String lResonse = Encoding.UTF8.GetString(rawResponseData); + if (String.IsNullOrEmpty(lResonse) || (lResonse.Length < 9)) + throw new System.IO.IOException("Proxy server didn't send an answer for SSL tunnel request"); + + // Parse result + + // Its first line should look like "HTTP/1.1 200 Blind-Connection Established" + // So we check does the response contain 200 or not + // We check only next 3 chars after the first " " + + Int32 lSpacePos = lResonse.IndexOf(' ') + 1; + if (lSpacePos > lResonse.Length - 3) + throw new SocketException(); + + String lHttpResultCode = lResonse.Substring(lSpacePos, 3); + if (String.Equals(lHttpResultCode, "407", StringComparison.Ordinal)) + throw new SocketException(); + + if (!String.Equals(lHttpResultCode, "200", StringComparison.Ordinal)) + throw new SocketException(); + } + + private void SendSslTunnelRequest() + { + if (!this.fHttpsConnectionFactory.ProxySettings.UseProxy) + return; + + this.DataSocket.Send(Encoding.UTF8.GetBytes(this.ComposeSslTunnelRequest())); + + Byte[] lRawResponse = new Byte[1024]; + this.DataSocket.Receive(lRawResponse); + + this.ParseSslTunnelResponse(lRawResponse); + } + + public override void InitializeClientConnection() + { + this.SendSslTunnelRequest(); + + base.InitializeClientConnection(); + } + + public override IAsyncResult BeginInitializeClientConnection(AsyncCallback callback, Object state) + { + this.SendSslTunnelRequest(); + + return base.BeginInitializeClientConnection(callback, state); + } + } +} diff --git a/Source/RemObjects.InternetPack/Interfaces.cs b/Source/RemObjects.InternetPack/Interfaces.cs new file mode 100644 index 0000000..3062ff5 --- /dev/null +++ b/Source/RemObjects.InternetPack/Interfaces.cs @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Net.Sockets; +using System.Threading; + +namespace RemObjects.InternetPack +{ + public interface IOwned + { + Server Owner { get; set; } + } + + public interface IListener : IOwned + { + void Listen(); + Socket ListeningSocket { get; set; } + } + + public interface IWorker : IOwned + { + void Work(); + Connection DataConnection { get; set; } + Thread Thread { get; set; } + event EventHandler Done; + } + + public interface IAsyncWorker : IOwned + { + Connection DataConnection { get; set; } + void Setup(); + } + + public interface IConnectionFactory + { + Connection CreateServerConnection(Socket socket); + Connection CreateClientConnection(Binding binding); + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/LdapClient.cs b/Source/RemObjects.InternetPack/LdapClient.cs new file mode 100644 index 0000000..8632eb7 --- /dev/null +++ b/Source/RemObjects.InternetPack/LdapClient.cs @@ -0,0 +1,1363 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; + +namespace RemObjects.InternetPack.Ldap +{ + // http://tools.ietf.org/html/rfc4511 << ldap + // http://tools.ietf.org/html/rfc3641 << asn1 (encoding of values) + + public class LdapException : Exception + { + public LdapException(String message) + : base(message) + { + } + + public LdapException(String message, Int32 code) + : base(message) + { + this.fCode = code; + } + + public LdapException(Int32 code) + : base(ErrorToString(code)) + { + this.fCode = code; + } + + public Int32 Code + { + get + { + return fCode; + } + } + private Int32 fCode; + + public static String ErrorToString(Int32 code) + { + switch (code) + { + case 0: return "Success"; + case 1: return "Operations Error"; + case 2: return "Protocol Error"; + case 3: return "Timelimit Exceeded"; + case 4: return "Sizelimit Exceeded"; + case 5: return "Compare False"; + case 6: return "Compare True"; + case 7: return "Authentication Method Not Supported"; + case 8: return "Strong Authentication Required"; + case 9: return "Partial Results"; + case 10: return "Referral"; + case 11: return "Administrative Limit Exceeded"; + case 12: return "Unavailable Critical Extension"; + case 13: return "Confidentiality Required"; + case 14: return "SASL Bind In Progress"; + case 16: return "No Such Attribute"; + case 17: return "Undefined Attribute Type"; + case 18: return "Inappropriate Matching"; + case 19: return "Constraint Violation"; + case 20: return "Attribute Or Value Exists"; + case 21: return "Invalid Attribute Syntax"; + case 32: return "No Such Object"; + case 33: return "Alias Problem"; + case 34: return "Invalid DN Syntax"; + case 35: return "Is Leaf"; + case 36: return "Alias Dereferencing Problem"; + case 48: return "Inappropriate Authentication"; + case 49: return "Invalid Credentials"; + case 50: return "Insufficient Access Rights"; + case 51: return "Busy"; + case 52: return "Unavailable"; + case 53: return "Unwilling To Perform"; + case 54: return "Loop Detect"; + case 64: return "Naming Violation"; + case 65: return "Object Class Violation"; + case 66: return "Not Allowed On Non-leaf"; + case 67: return "Not Allowed On RDN"; + case 68: return "Entry Already Exists"; + case 69: return "Object Class Modifications Prohibited"; + case 71: return "Affects Multiple DSAs"; + case 80: return "Other"; + case 81: return "Server Down"; + case 82: return "Local Error"; + case 83: return "Encoding Error"; + case 84: return "Decoding Error"; + case 85: return "Ldap Timeout"; + case 86: return "Authentication Unknown"; + case 87: return "Filter Error"; + case 88: return "User Cancelled"; + case 89: return "Parameter Error"; + case 90: return "No Memory"; + case 91: return "Connect Error"; + case 92: return "Ldap Not Supported"; + case 93: return "Control Not Found"; + case 94: return "No Results Returned"; + case 95: return "More Results To Return"; + case 96: return "Client Loop"; + case 97: return "Referral Limit Exceeded"; + case 112: return "TLS not supported"; + case 113: return "SSL handshake failed"; + case 114: return "SSL Provider not found"; + default: return "Unknown Error"; + } + } + } + +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.FtpClient.bmp")] +#endif + public class LdapClient : Client + { + public LdapClient() + { + this.Port = LdapPort; + this.BindDigest = DigestType.None; + this.LdapVersion = 3; + this.fCurrentConnection = null; + } + + public enum DigestType + { + None, + MD5 + } + + private Int32 fSequenceNumber; // sending requests to ldap uses a sequence id + private MemoryStream fMemoryStream; + private BinaryWriter fWriter; + + public const Int32 LdapSPort = 636; + public const Int32 LdapPort = 389; + + public Boolean LoggedIn + { + get + { + return fLoggedIn; + } + } + private Boolean fLoggedIn; +#if FULLFRAMEWORK + public Boolean UseStartTLS + { + get + { + return fUseStartTLS; + } + set + { + fUseStartTLS = value; + } + } + private Boolean fUseStartTLS; +#endif + + public DigestType BindDigest + { + get + { + return fBindDigest; + } + set + { + fBindDigest = value; + } + } + private DigestType fBindDigest; + + public Int32 LdapVersion + { + get + { + return fLdapVersion; + } + set + { + fLdapVersion = value; + } + } + private Int32 fLdapVersion; + + public String BindDN + { + get + { + return fBindDN; + } + set + { + fBindDN = value; + } + } + private String fBindDN; + + public String BindPassword + { + get + { + return fBindPassword; + } + set + { + fBindPassword = value; + } + } + private String fBindPassword; + + public Connection CurrentConnection + { + get + { + return fCurrentConnection; + } + } + private Connection fCurrentConnection; + + public Boolean Connected + { + get + { + return (fCurrentConnection != null) && (fCurrentConnection.Connected); + } + } + + public static String Escape(String s) + { + return s.Replace("\\", "\\5c") + .Replace("*", "\\2a") + .Replace("(", "\\28") + .Replace(")", "\\29") + .Replace("\0", "\\00") + .Replace("/", "\\2f"); + } + + + public virtual void Open() + { + +#if FULLFRAMEWORK + if ((SslOptions.Enabled || UseStartTLS) && SslOptions.TargetHostName == null && HostName != null) + SslOptions.TargetHostName = HostName; +#endif + if (fCurrentConnection != null) + { + + if (fCurrentConnection.Connected) { fCurrentConnection.Close(); } + fCurrentConnection = null; + } + fCurrentConnection = Connect(); +#if FULLFRAMEWORK + if (!SslOptions.Enabled && UseStartTLS) + { + if (SendStartTLS()) + { + fCurrentConnection = SslOptions.CreateClientConnection(fCurrentConnection); + ((SslConnection)fCurrentConnection).InitializeClientConnection(); + fCurrentConnection.EnableNagle = EnableNagle; + } + else + throw new LdapException("Could not initialize TLS"); + } +#endif + fLoggedIn = false; + } + + public virtual void Close() + { + if (fLoggedIn) + Unbind(); + if (fCurrentConnection != null) + { + fCurrentConnection.Close(); + fCurrentConnection = null; + } + } + +#if FULLFRAMEWORK + private const String StartTLSObjID = "1.3.6.1.4.1.1466.20037"; + // http://www.networksorcery.com/enp/rfc/rfc2830.txt + private Boolean SendStartTLS() + { + Int32 seqid = SendLdapRequest(Asn1.LDAPEXTREQ, new BerString(Asn1.LDAPOID, StartTLSObjID)); + Response res = ReadResponse(); + return (res.Code == 0 && res.SequenceId == seqid); + } +#endif + + private Int32 SendLdapRequest(Byte command, params BerValue[] args) + { + if (fMemoryStream == null) + { + fMemoryStream = new MemoryStream(); + fWriter = new BinaryWriter(fMemoryStream); + } + + fSequenceNumber++; + if (fSequenceNumber < 0) + fSequenceNumber = 1; + + BerSequence lCommandParameters = new BerSequence(); + lCommandParameters.TypeCodeTag = command; + for (Int32 i = 0; i < args.Length; i++) + lCommandParameters.Items.Add(args[i]); + + // Now we have a wrapped ldap command; we need to add an integer before it, in a regular sequence + BerSequence lSequence = new BerSequence(); + // first write the sequence number + lSequence.Items.Add(new BerInteger(fSequenceNumber)); + // then the comamnd data + lSequence.Items.Add(lCommandParameters); + + fMemoryStream.SetLength(0); + lSequence.Write(fWriter); + fWriter.Flush(); + + Byte[] lData = fMemoryStream.ToArray(); + fCurrentConnection.Write(lData, 0, lData.Length); + + return fSequenceNumber; + } + + private BerValue ReadBerValue() + { + Int32 lCode = fCurrentConnection.ReadByte(); + if (lCode < 0) + throw new ConnectionClosedException(); + + Int32 lLength = fCurrentConnection.ReadByte(); + if (lLength < 0) + throw new ConnectionClosedException(); + + if (lLength >= 0x80) + { + Byte[] lBuffer = new Byte[lLength & ~0x80]; + fCurrentConnection.Read(lBuffer, 0, lBuffer.Length); + lLength = 0; + for (Int32 i = 0; i < lBuffer.Length; i++) + lLength = lLength << 8 | lBuffer[i]; + } + + Byte[] lData = new Byte[lLength]; + fCurrentConnection.Read(lData, 0, lData.Length); + + return BerValue.Read(lData, lLength, (Byte)lCode); + } + + private class Response + { + public Int32 SequenceId; + public Int32 TypeCode; //type code tag + public Int32 Code; + public String DN; + public String Result; + public BerSequence Referers; // referers + public BerValue[] RestData; // rest data; only valid when it's not referers + } + + private Response ReadResponse() + { + BerValue lValue = ReadBerValue(); + + if (lValue != null && lValue.Type == BerType.Sequence && ((BerSequence)lValue).Items.Count >= 2 && ((BerSequence)lValue).Items[0].Type == BerType.Integer) + { + Response lResponse = new Response(); + lResponse.SequenceId = ((BerInteger)((BerSequence)lValue).Items[0]).Value; + + BerSequence lSubValue = ((BerSequence)lValue).Items[1] as BerSequence; + if (lSubValue == null) + return null; + + lResponse.TypeCode = lSubValue.TypeCodeTag; + if (lSubValue.Items.Count == 0) + return null; + + if (lResponse.TypeCode == Asn1.LDAPSEARCHENTRY) + { + lResponse.RestData = ((List)lSubValue.Items).ToArray(); + } + else + { + if (!(lSubValue.Items[0] is BerInteger)) + return null; // Int32 or sequence + + lResponse.Code = ((BerInteger)lSubValue.Items[0]).Value; + + if (lSubValue.Items.Count > 1 && lSubValue.Items[1].Type == BerType.String) + lResponse.DN = ((BerString)lSubValue.Items[1]).Value; + + if (lSubValue.Items.Count > 2 && lSubValue.Items[2].Type == BerType.String) + lResponse.Result = ((BerString)lSubValue.Items[2]).Value; + + if (lSubValue.Items.Count > 3 && lResponse.Code == 10) // 10 = referers + { + lResponse.Referers = lSubValue.Items[3] as BerSequence; + } + else if (lSubValue.Items.Count > 3) + { + lResponse.RestData = new BerValue[lSubValue.Items.Count - 3]; + for (Int32 i = 0; i < lResponse.RestData.Length; i++) + lResponse.RestData[i] = lSubValue.Items[i + 3]; + } + } + + return lResponse; + } + + return null; // crap on the line + } + + public void Bind() + { + this.Bind(fBindDN, fBindPassword, fBindDigest); + } + + public void Bind(String dn, String password, DigestType digestType) + { + if (fCurrentConnection == null) + Open(); + + if (String.IsNullOrEmpty(dn) || String.IsNullOrEmpty(password)) + digestType = DigestType.None; + + switch (digestType) + { + case DigestType.MD5: + { + Int32 lSequenceId = SendLdapRequest(Asn1.LDAPBINDREQ, + new BerInteger(fLdapVersion), + new BerString(Asn1.OCTETSTRING, ""), + new BerSequence(Asn1.LDAPBINDSASL, new BerString(Asn1.OCTETSTRING, "DIGEST-MD5"))); + + Response lResponse = ReadResponse(); + if (lResponse.SequenceId != lSequenceId) throw new LdapException("Invalid sequence id in bind response"); + if (lResponse.Code != 14 || lResponse.RestData == null || lResponse.RestData[0].Type != BerType.String) // 14 = Sasl bind in progress + throw new LdapException(lResponse.Code); + + + String lResult = ((BerString)lResponse.RestData[0]).Value; + String lEncodedResult = Sasl.MD5Login(lResult, dn, password, GetTargetHostName()); + + lSequenceId = SendLdapRequest(Asn1.LDAPBINDREQ, + new BerInteger(fLdapVersion), + new BerString(Asn1.OCTETSTRING, ""), + new BerSequence(Asn1.LDAPBINDSASL, new BerString(Asn1.OCTETSTRING, lEncodedResult))); + + lResponse = ReadResponse(); + if (lResponse.SequenceId != lSequenceId) + throw new LdapException("Invalid sequence id in bind response"); + + if (lResponse.Code != 14 || lResponse.RestData == null || lResponse.RestData[0].Type != BerType.String) // 14 = Sasl bind in progress + throw new LdapException(lResponse.Code); + + lSequenceId = SendLdapRequest(Asn1.LDAPBINDREQ, + new BerInteger(fLdapVersion), + new BerString(Asn1.OCTETSTRING, ""), + new BerSequence(Asn1.LDAPBINDSASL, new BerString(Asn1.OCTETSTRING, "DIGEST-MD5"))); + + lResponse = ReadResponse(); + if (lResponse.SequenceId != lSequenceId) + throw new LdapException("Invalid sequence id in bind response"); + + if (lResponse.Code != 0) + throw new LdapException(lResponse.Code); + break; + } + default: + { + Int32 lSequenceId = SendLdapRequest(Asn1.LDAPBINDREQ, + new BerInteger(fLdapVersion), + new BerString(Asn1.OCTETSTRING, dn), + new BerString(Asn1.LDAPBINDPASSWORD, password)); + Response lResponse = ReadResponse(); + + if (lResponse.SequenceId != lSequenceId) + throw new LdapException("Invalid sequence id in bind response"); + + if (lResponse.Code != 0) + throw new LdapException(lResponse.Code); + + break; + } + } + + fLoggedIn = true; + } + + private String GetTargetHostName() + { + IPAddress lAddress; + if (String.IsNullOrEmpty(HostName)) + { + lAddress = HostAddress; + } + else +#if COMPACTFRAMEWORK + try + { + lAddress = IPAddress.Parse(HostName); + } + catch + { + return HostName; + } +#else + if (!IPAddress.TryParse(HostName, out lAddress)) + return HostName; +#endif + try + { + return Dns.DnsLookup.ReverseResolve(lAddress); + } + catch (Exception) + { + return lAddress.ToString(); + } + } + + public void Unbind() + { + if (fCurrentConnection == null) + throw new LdapException("Not connected"); + + SendLdapRequest(Asn1.LDAPUNBINDREQ); + fLoggedIn = false; + Close(); + } + + public enum SearchScope + { + BaseObject = 0, + SingleLevel = 1, + FullSubtree = 2 + } + + public enum AliasDereferencing + { + Never = 0, + InSearching = 1, + FindingBase = 2, + Always = 3 + } + + public const String AllAttributes = "*"; + + public const String NoAttributes = "1.1"; + + + public LdapSearchResults Search(String baseObject, SearchScope scope, AliasDereferencing aliases, Int32 size, Int32 time, Boolean typesOnly, + String filter, String[] attributes) + { + if (attributes == null || attributes.Length == 0) + attributes = new String[] { AllAttributes }; + + BerValue[] attributevalues = new BerValue[attributes.Length]; + for (Int32 i = 0; i < attributes.Length; i++) + attributevalues[i] = new BerString(Asn1.OCTETSTRING, attributes[i]); + + Int32 lSequenceId = SendLdapRequest(Asn1.LDAPSEARCHREQ, + new BerString(Asn1.OCTETSTRING, baseObject), + new BerEnumerated((Int32)scope), + new BerEnumerated((Int32)aliases), + new BerInteger(size), + new BerInteger(time), + new BerBoolean(typesOnly), + Asn1.ParseFilter(String.IsNullOrEmpty(filter) ? "(objectclass=*)" : filter), + new BerSequence(Asn1.SEQUENCE, attributevalues)); + + LdapSearchResults lResults = new LdapSearchResults(); + while (true) + { + Response lResponse = ReadResponse(); + + if (lResponse.SequenceId != lSequenceId) + throw new LdapException("Invalid sequence id in bind response"); + + if (lResponse.Code != 0) + throw new LdapException(lResponse.Code); + + if (lResponse.TypeCode == Asn1.LDAPSEARCHENTRY) + { + if (lResponse.RestData != null && lResponse.RestData.Length > 0 && lResponse.RestData[0].Type == BerType.String) + { + LdapObject obj = new LdapObject(((BerString)lResponse.RestData[0]).Value); + lResults.Add(obj); + if (lResponse.RestData.Length > 1 && lResponse.RestData[1].Type == BerType.Sequence) + { + foreach (BerValue attribute in ((BerSequence)lResponse.RestData[1]).Items) + { + if (attribute.Type == BerType.Sequence && ((BerSequence)attribute).Items.Count > 0 && ((BerSequence)attribute).Items[0].Type == BerType.String) + { + LdapAttribute att = new LdapAttribute(((BerString)((BerSequence)attribute).Items[0]).Value); + obj.Attributes.Add(att); + if (((BerSequence)attribute).Items.Count > 1 && ((BerSequence)attribute).Items[1].Type == BerType.Sequence) + { + foreach (BerValue value in ((BerSequence)((BerSequence)attribute).Items[1]).Items) + { + switch (value.Type) + { + case BerType.BitString: + att.Binary = true; + att.Add(((BerBinary)value).Value); + break; + case BerType.Boolean: + att.Binary = false; + att.Add(((BerBoolean)value).Value.ToString()); + break; + case BerType.Enumerated: + case BerType.Integer: + att.Binary = false; + att.Add(((BerInteger)value).Value.ToString()); + break; + case BerType.IpAddress: + att.Binary = false; + att.Add(((BerIpAddress)value).Value.ToString()); + break; + case BerType.Other: + att.Binary = true; + att.Add(((BerOther)value).Value); + break; + case BerType.String: + att.Binary = false; + att.Add(((BerString)value).Value.ToString()); + break; + case BerType.UInteger: + att.Binary = false; + att.Add(((BerUInteger)value).Value.ToString()); + break; + default: + break; + } + } + } + } + } + } + } + } + else if (lResponse.TypeCode == Asn1.LDAPSEARCHREF) + { + if (lResponse.Referers != null) + { + foreach (BerValue val in lResponse.Referers.Items) + { + if (val is BerString) + lResults.Referals.Add(((BerString)val).Value); + } + } + else if (lResponse.RestData != null) + { + foreach (BerValue val in lResponse.RestData) + { + if (val is BerString) + lResults.Referals.Add(((BerString)val).Value); + if (val is BerSequence) + { + foreach (BerValue val2 in ((BerSequence)val).Items) + { + if (val2 is BerString) + lResults.Referals.Add(((BerString)val2).Value); + } + } + } + } + } + else if (lResponse.TypeCode == Asn1.LDAPSEARCHDONE) + break; + else + throw new LdapException("Unknown response from server"); + } + + return lResults; + } + } + + public class LdapAttribute + { + public LdapAttribute() + { + this.fValues = new List(); + } + + public LdapAttribute(String key) + : this() + { + this.fKey = key; + } + + private List fValues; + + public String Key + { + get + { + return fKey; + } + set + { + fKey = value; + } + } + private String fKey; + + public Boolean Binary + { + get + { + return fBinary; + } + set + { + if (fBinary != value) + { + fValues.Clear(); + fBinary = value; + } + } + } + private Boolean fBinary; + + public Int32 Count + { + get + { + return fValues.Count; + } + } + + public Byte[] SingleBinaryValue + { + get + { + if (!fBinary) + throw new ArgumentException("Values are not binary"); + + if (fValues.Count == 0) + return null; + + return (Byte[])fValues[0]; + } + } + + public String SingleStringValue + { + get + { + if (fBinary) + throw new ArgumentException("Values are not String values"); + + if (fValues.Count == 0) + return null; + + return (String)fValues[0]; + } + } + + public void RemoveAt(Int32 index) + { + fValues.RemoveAt(index); + } + + public void Remove(String item) + { + fValues.Remove(item); + } + + public void Remove(Byte[] item) + { + fValues.Remove(item); + } + + public void Add(String item) + { + if (fBinary) + throw new ArgumentException("Values are not String values"); + + fValues.Add(item); + } + + public void Add(Byte[] item) + { + if (!fBinary) + throw new ArgumentException("Values are not binary"); + + fValues.Add(item); + } + + public String GetString(Int32 index) + { + if (fBinary) + throw new ArgumentException("Values are not String values"); + + return (String)fValues[index]; + } + + public Byte[] GetBinary(Int32 index) + { + if (!fBinary) + throw new ArgumentException("Values are not binary"); + + return (Byte[])fValues[index]; + } + + public void SetString(Int32 index, String value) + { + if (fBinary) + throw new ArgumentException("Values are not String values"); + + fValues[index] = value; + } + + public void SetBinary(Int32 index, Byte[] value) + { + if (!fBinary) + throw new ArgumentException("Values are not binary"); + + fValues[index] = value; + } + } + + public class LdapAttributes : List + { + public new LdapAttribute this[Int32 index] + { + get + { + return base[index]; + } + set + { + base[index] = value; + } + } + + public LdapAttribute this[String key] + { + get + { + for (Int32 i = 0; i < Count; i++) + if (String.Equals(key, base[i].Key, StringComparison.InvariantCultureIgnoreCase)) + return base[i]; + + return null; + } + } + + public String GetStringAttribute(String key) + { + LdapAttribute lAttribute = this[key]; + if (lAttribute == null) + return null; + + if (lAttribute.Binary) + return Convert.ToString(lAttribute.SingleBinaryValue); + + return lAttribute.SingleStringValue; + } + } + + public class LdapObject + { + public LdapObject() + { + this.fAttributes = new LdapAttributes(); + } + + public LdapObject(String dn) + : this() + { + this.DN = dn; + } + + public String DN + { + get + { + return fDN; + } + set + { + fDN = value; + } + } + private String fDN; + + public LdapAttributes Attributes + { + get + { + return fAttributes; + } + } + private LdapAttributes fAttributes; + } + + public class LdapSearchResults : List + { + public LdapSearchResults() + { + this.fReferals = new List(); + } + + private List fReferals; + public IList Referals + { + get + { + return fReferals; + } + } + } + + public class LdapUserLookup : System.ComponentModel.Component + { + public String UserSearchBase + { + get + { + return fUserSearchBase; + } + set + { + fUserSearchBase = value; + } + } + private String fUserSearchBase; + + public String UserFilter + { + get + { + return fUserFilter; + } + set + { + fUserFilter = value; + } + } + private String fUserFilter = "(objectClass=inetOrgPerson)"; + + public Boolean SearchSubTree + { + get + { + return fSearchSubTree; + } + set + { + fSearchSubTree = value; + } + } + private Boolean fSearchSubTree = true; + + public String UserNameField + { + get + { + return fUserNameField; + } + set + { + fUserNameField = value; + } + } + private String fUserNameField = "uid"; + + public String GroupSearchBase + { + get + { + return fGroupSearchBase; + } + set + { + fGroupSearchBase = value; + } + } + private String fGroupSearchBase; + + public String GroupFilter + { + get + { + return fGroupFilter; + } + set + { + fGroupFilter = value; + } + } + private String fGroupFilter = "(objectClass=groupOfNames)"; + + public String GroupNameField + { + get + { + return fGroupNameField; + } + set + { + fGroupNameField = value; + } + } + private String fGroupNameField = "cn"; + + public String GroupMemberField + { + get + { + return fGroupMemberField; + } + set + { + fGroupMemberField = value; + } + } + private String fGroupMemberField = "member"; + + public Boolean SearchGroups + { + get + { + return fSearchGroups; + } + set + { + fSearchGroups = value; + } + } + private Boolean fSearchGroups = true; + + public String LookupDN + { + get + { + return fLookupDN; + } + set + { + fLookupDN = value; + } + } + private String fLookupDN; + + public String LookupPassword + { + get + { + return fLookupPassword; + } + set + { + fLookupPassword = value; + } + } + private String fLookupPassword; + + public String Hostname + { + get + { + return fHostname; + } + set + { + fHostname = value; + } + } + private String fHostname; + + public Int32 Port + { + get + { + return fPort; + } + set + { + fPort = value; + } + } + private Int32 fPort = LdapClient.LdapPort; + + public Boolean UseStartTLS + { + get + { + return fUseStartTLS; + } + set + { + fUseStartTLS = value; + } + } + private Boolean fUseStartTLS; + + /// + /// strip the dn of the group base; for example: + /// GroupBase: ou=groups,cn=company,cn=com + /// Item: cn=mygroup,ou=groups,cn=company,cn=com returns: "mygroup" + /// Item: cn=mygroup,ou=list,ou=groups,cn=company,cn=com returns: "list.mygroup" + /// + public Boolean StripGroupBaseDN + { + get + { + return fStripGroupBaseDN; + } + set + { + fStripGroupBaseDN = value; + } + } + private Boolean fStripGroupBaseDN; + +#if FULLFRAMEWORK + public SslConnectionFactory SslOptions + { + get + { + return fSslOptions; + } + set + { + fSslOptions = value; + } + } + private SslConnectionFactory fSslOptions = new SslConnectionFactory(); +#endif + public class LookupResults + { + public LookupResults() + { + this.fGroupMembership = new List(); + } + + public String Username + { + get + { + return fUsername; + } + set + { + fUsername = value; + } + } + private String fUsername; + + public String DN + { + get + { + return fDN; + } + set + { + fDN = value; + } + } + private String fDN; + + public LdapObject UserObject + { + get + { + return fUserObject; + } + set + { + fUserObject = value; + } + } + private LdapObject fUserObject; + + public List GroupMembership + { + get + { + return fGroupMembership; + } + } + private List fGroupMembership; + } + /// + /// Thread safe way to execute ldap requests + /// + /// + /// + /// + public LookupResults Login(String username, String password) + { + if (String.IsNullOrEmpty(fUserSearchBase)) + throw new LdapException("UserSearchBase is not set"); + + if (fSearchGroups && String.IsNullOrEmpty(fGroupSearchBase)) + throw new LdapException("GroupSearchBase is not set"); + + if (String.IsNullOrEmpty(fLookupDN)) + throw new LdapException("LookupDN is not set"); + + if (String.IsNullOrEmpty(fLookupPassword)) + throw new LdapException("LookupPassword is not set"); + + if (String.IsNullOrEmpty(fHostname)) + throw new LdapException("HostName is not set"); + + if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password)) + return null; + + using (LdapClient lClient = new LdapClient()) + { +#if FULLFRAMEWORK + lClient.SslOptions = fSslOptions; + lClient.UseStartTLS = fUseStartTLS; +#endif + lClient.HostName = fHostname; + lClient.Port = fPort; + + if (BeforeConnect != null) + BeforeConnect(this, new LdapEventArgs(lClient)); + + lClient.Open(); + + if (AfterConnect != null) + AfterConnect(this, new LdapEventArgs(lClient)); + + lClient.BindDN = fLookupDN; + lClient.BindPassword = fLookupPassword; + lClient.Bind(); + + LookupResults lResult = new LookupResults(); + + foreach (LdapObject obj in lClient.Search( + fUserSearchBase, + fSearchSubTree ? LdapClient.SearchScope.FullSubtree : LdapClient.SearchScope.SingleLevel, + LdapClient.AliasDereferencing.Always, + 0, 0, false, fUserFilter, + new String[] { fUserNameField })) + { + if (obj.Attributes.GetStringAttribute(fUserNameField) == username) + { + lResult.DN = obj.DN; + lResult.Username = obj.Attributes.GetStringAttribute(fUserNameField); + break; + } + } + + if (lResult.DN == null) + return null; + + foreach (LdapObject obj in lClient.Search(lResult.DN, LdapClient.SearchScope.BaseObject, LdapClient.AliasDereferencing.Always, 0, 0, false, null, null)) + { + lResult.UserObject = obj; + break; + } + + if (fSearchGroups) + { + String s = "(&" + fGroupFilter + "(" + fGroupMemberField + "=" + lResult.DN + "))"; + //(&(objectClass=groupOfNames)(member=uid=ck,ou=users,dc=remobjects,dc=com)) + //String s = "(objectClass=groupOfNames)"; + //String s = "(member=uid=ck,ou=users,dc=remobjects,dc=com)"; + foreach (LdapObject obj in + lClient.Search(fGroupSearchBase, fSearchSubTree ? LdapClient.SearchScope.FullSubtree : LdapClient.SearchScope.SingleLevel, + LdapClient.AliasDereferencing.Always, + 0, 0, false, s, new String[] { fGroupNameField, fGroupMemberField })) + { + if (fStripGroupBaseDN) + lResult.GroupMembership.Add(StripGroupBase(obj.DN)); + else + lResult.GroupMembership.Add(obj.DN); + } + } + + lClient.BindDN = lResult.DN; + lClient.BindPassword = password; + try + { + lClient.Bind(); + } + catch (LdapException) + { + lResult = null; + } + + lClient.Unbind(); + + if (Disconnected != null) + Disconnected(this, new LdapEventArgs(lClient)); + + return lResult; + } + } + + private String StripGroupBase(String dn) + { + String[] lItems = dn.Trim().Split(','); + String[] lGroupDN = fGroupSearchBase.Trim().Split(','); + + for (Int32 i = 0; i < lItems.Length; i++) + lItems[i] = lItems[i].Trim(); + + for (Int32 i = 0; i < lGroupDN.Length; i++) + lGroupDN[i] = lGroupDN[i].Trim(); + + if (lGroupDN.Length >= lItems.Length) + return dn; // makes no sense + + for (Int32 i = lGroupDN.Length - 1; i >= 0; i--) + if (0 != String.Compare(lItems[lItems.Length - lGroupDN.Length + i], lGroupDN[i], StringComparison.InvariantCultureIgnoreCase)) + return dn; // shouldn't happen + + String lResult = null; + for (Int32 i = lItems.Length - lGroupDN.Length - 1; i >= 0; i--) + { + String lValue = lItems[i]; + + if (lValue.IndexOf("=") != -1) + lValue = lValue.Substring(lValue.IndexOf('=') + 1); + + if (lResult == null) + lResult = lValue; + else + lResult = lResult + "." + lValue; + } + + return lResult; + } + + public event EventHandler BeforeConnect; + + public event EventHandler AfterConnect; + + public event EventHandler Disconnected; + } + + public class LdapEventArgs : EventArgs + { + public LdapEventArgs(LdapClient client) + { + this.fClient = client; + } + + public LdapClient Client + { + get + { + return this.fClient; + } + } + private readonly LdapClient fClient; + } +} diff --git a/Source/RemObjects.InternetPack/Message.cs b/Source/RemObjects.InternetPack/Message.cs new file mode 100644 index 0000000..87065c0 --- /dev/null +++ b/Source/RemObjects.InternetPack/Message.cs @@ -0,0 +1,459 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.IO; +using System.Text; + +namespace RemObjects.InternetPack.Messages +{ + public class MailMessage + { + private HeaderFields fFields = new HeaderFields(); + private MessageAttachments fAttachments = new MessageAttachments(); + + #region Properties + public MessageEncoder Encoder + { + get + { + return fEncoder; + } + set + { + fEncoder = value; + } + } + private MessageEncoder fEncoder; + + public MessageAddresses To + { + get + { + return fTo; + } + } + private MessageAddresses fTo = new MessageAddresses(); + + public MessageAddresses Cc + { + get + { + return fCc; + } + } + private MessageAddresses fCc = new MessageAddresses(); + + public MessageAddresses Bcc + { + get + { + return fBcc; + } + } + private MessageAddresses fBcc = new MessageAddresses(); + + public MessageAddress From + { + get + { + return fFrom; + } + } + private MessageAddress fFrom = new MessageAddress(); + + public MessageAddress Sender + { + get + { + return fSender; + } + } + private MessageAddress fSender = new MessageAddress(); + + public String MessageId + { + get + { + return fFields["Message-Id"].Value; + } + set + { + fFields["Message-Id"].Value = value; + } + } + + public String UserAgent + { + get + { + return fFields["User-Agent"].Value; + } + set + { + fFields["User-Agent"].Value = value; + } + } + + public String Subject + { + get + { + return fFields["Subject"].Value; + } + set + { + fFields["Subject"].Value = value; + } + } + + // Since date might be in a different timezone, we just give the original value too + public String Date + { + get + { + return fFields["Date"].Value; + } + set + { + fFields["Date"].Value = value; + } + } + + // Return the date in the timezone of the current computer + public DateTime DateHome + { + get + { + return GetDateHome(); + } + set + { + SetDateHome(value); + } + } + + // Newsgroup, for newsgroup articles + public String Newsgroups + { + get + { + return fFields["Newsgroups"].Value; + } + set + { + fFields["Newsgroups"].Value = value; + } + } + + public HeaderFields Fields + { + get + { + return fFields; + } + } + + public String Contents + { + get + { + return fContents; + } + set + { + fContents = value; + } + } + private String fContents; + #endregion + + private void SetDateHome(DateTime value) + { + Date = value.ToString("r"); + } + + private DateTime GetDateHome() + { + String lDate = Date.Trim(); + Int32 lIndex = lDate.IndexOf('('); + + if (lIndex >= 0) + lDate = lDate.Substring(0, lIndex).Trim(); + + try + { + return DateTime.Parse(lDate, new System.Globalization.DateTimeFormatInfo(), System.Globalization.DateTimeStyles.AllowWhiteSpaces); + } + catch + { + return DateTime.ParseExact(lDate, "ddd, d MMM yyyy HH':'mm':'ss zzz", new System.Globalization.DateTimeFormatInfo(), System.Globalization.DateTimeStyles.AllowWhiteSpaces); + } + } + + public void ValidateEncoder() + { + if (this.fEncoder == null) + this.fEncoder = new DefaultEncoder(); + } + + public void EncodeMessage(Stream destination) + { + this.ValidateEncoder(); + this.fEncoder.EncodeMessage(this, destination); + } + + public void DecodeMessage(Stream source) + { + this.ValidateEncoder(); + this.fEncoder.DecodeMessage(this, source); + } + + public void SaveToFile(String filename) + { + using (FileStream stream = new FileStream(filename, FileMode.Create)) + this.EncodeMessage(stream); + } + } + + public class DefaultEncoder : MessageEncoder + { + protected const String CRLF = "\r\n"; + + private static String SplitMultiLine(String source, Int32 length) + { + StringBuilder lRes = new StringBuilder(); + Int32 i; + + while (source.Length > length) + { + i = source.LastIndexOf(' ', length); + if (i < (length / 3)) + { + i = source.IndexOf(' ', length); + if (i == -1) break; + } + + lRes.Append(source.Substring(0, i)); + source = " " + source.Substring(i + 1); + if (source.Trim().Length == 0) break; + + lRes.Append("\r\n"); + } + + lRes.Append(source); + + return lRes.ToString(); + } + + public override void EncodeMessage(MailMessage source, Stream destination) + { + StreamWriter lWrter = new StreamWriter(destination); + + if (DefaultEncoder.NeedEncoding(source)) + lWrter.WriteLine("MIME-Version: 1.0"); + + for (Int32 i = 0; i < source.Fields.Count; i++) + { + String lKey = source.Fields.Keys[i]; + HeaderField lValue = source.Fields[lKey]; + + if (lKey.Equals("Subject")) + { + lValue.Value = DefaultEncoder.EncodeUtf8Base64(lValue.Value); + lWrter.WriteLine(lKey + ": " + lValue.ToString()); + continue; + } + + lWrter.WriteLine(SplitMultiLine(lKey + ": " + lValue.ToString(), 80)); + } + + String lFromEncoded = DefaultEncoder.EncodeUtf8Base64(source.From.Name); + String lFrom = String.Format("From: {0} <{1}>", lFromEncoded, source.From.Address); + + lWrter.WriteLine(lFrom); + + if (source.Sender.IsSet()) + lWrter.WriteLine(SplitMultiLine("Sender: " + source.Sender.ToString(), 80)); + + if (source.To.Count > 0) + lWrter.WriteLine(SplitMultiLine("To: " + source.To.ToString(), 80)); + + if (source.Cc.Count > 0) + lWrter.WriteLine(SplitMultiLine("Cc: " + source.Cc.ToString(), 80)); + + if (-1 != DefaultEncoder.ContainsUnicodeSymbols(source.Contents)) + { + lWrter.WriteLine("Content-Type: text/plain; charset=UTF-8; format=flowed"); + lWrter.WriteLine("Content-Transfer-Encoding: 8bit"); + } + + lWrter.WriteLine(); + lWrter.Write(source.Contents); + + if (!source.Contents.EndsWith("\r\n")) // always add an enter + lWrter.WriteLine(); + + lWrter.Flush(); + } + + private String RemoveQuotes(String value) + { + if (value.StartsWith("\"") && value.EndsWith("\"")) + return value.Substring(1, value.Length - 2); + + return value; + } + + private void ParseHeader(MailMessage message, String header) + { + StringReader lStringReader = new StringReader(header); + HeaderField lField = null; + + String lLine; + while (!String.IsNullOrEmpty((lLine = lStringReader.ReadLine()))) + { + if ((lLine[0] == ' ' || lLine[0] == '\t') && (lField != null)) + { // header continuation or property + lField.Value += lLine; + } + else + { // header + Int32 lPosition = lLine.IndexOf(':'); + String lName = lLine.Substring(0, lPosition); + String lValue; + + if ((lPosition + 2) < lLine.Length) + lValue = lLine.Substring(lPosition + 2); + else + lValue = String.Empty; + + lField = new HeaderField(RemoveQuotes(lValue)); + message.Fields.Add(lName, lField); + } + } + + lField = message.Fields["To"]; + if (lField != null) + { + message.Fields.Remove("To"); + SetAddresses(message.To, lField.Value); + } + + lField = message.Fields["From"]; + if (lField != null) + { + message.Fields.Remove("From"); + message.From.FromString(lField.Value); + } + + lField = message.Fields["Sender"]; + if (lField != null) + { + message.Fields.Remove("Sender"); + message.Sender.FromString(lField.Value); + } + + lField = message.Fields["CC"]; + if (lField != null) + { + message.Fields.Remove("CC"); + SetAddresses(message.Cc, lField.Value); + } + + lField = message.Fields["bcc"]; + if (lField != null) + { + message.Fields.Remove("bcc"); + SetAddresses(message.Bcc, lField.Value); + } + } + + private void SetAddresses(MessageAddresses address, String names) + { + String[] lAddresses = names.Split(';'); + + for (Int32 i = 0; i < lAddresses.Length; i++) + address.Add(lAddresses[i].Trim()); + } + + public override void DecodeMessage(MailMessage destination, Stream source) + { + TextReader lReader = new StreamReader(source); + String lMessage = lReader.ReadToEnd(); + String lBody; + + //Debug.Write(lMessage); + if (lMessage.StartsWith(CRLF)) + { + lBody = lMessage.Substring(2); + } + else + { + Int32 lHeaderEnd = lMessage.IndexOf(CRLF + CRLF); + if (lHeaderEnd < 0) + throw (new Exception("Invalid email message")); + + ParseHeader(destination, lMessage.Substring(0, lHeaderEnd)); + + lBody = lMessage.Substring(lHeaderEnd + 4); + } + + destination.Contents = lBody; + } + + public static String EncodeUtf8Base64(String value) + { + String lStartTag = "=?UTF-8?B?"; + String lEndTag = "?="; + + Int32 i = ContainsUnicodeSymbols(value); + + if (i != -1) + { + StringBuilder lResult = new StringBuilder(); + if (i > 0) + lResult.Append(value.Substring(0, i)); + + lResult.Append(lStartTag); + lResult.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(value.Substring(i)))); + lResult.Append(lEndTag); + + return lResult.ToString(); + } + + return value; + + } + + public static Boolean NeedEncoding(MailMessage message) + { + if (null == message) + throw new ArgumentNullException(); + + Boolean lIsUnicode = (-1 == ContainsUnicodeSymbols(message.Subject)); + if (!lIsUnicode) + return true; + + lIsUnicode = (-1 == ContainsUnicodeSymbols(message.Contents)); + if (!lIsUnicode) + return true; + + return true; + } + + public static Int32 ContainsUnicodeSymbols(String value) + { + for (Int32 i = 0; i < value.Length; i++) + if (value[i] < '\u0000' || value[i] > '\u007F') + return i; + + return -1; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/MessageAddress.cs b/Source/RemObjects.InternetPack/MessageAddress.cs new file mode 100644 index 0000000..219c943 --- /dev/null +++ b/Source/RemObjects.InternetPack/MessageAddress.cs @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace RemObjects.InternetPack.Messages +{ + public class MessageAddress + { + public String Name + { + get + { + return this.fName; + } + set + { + this.fName = value; + } + } + private String fName = String.Empty; + + public String Address + { + get + { + return this.fAddress; + } + set + { + this.fAddress = value; + } + } + private String fAddress = String.Empty; + + public void Clear() + { + this.Name = ""; + this.Address = ""; + } + + public Boolean IsSet() + { + return (this.Name.Length != 0) || (this.Address.Length != 0); + } + + public override String ToString() + { + return String.Format("{0} <{1}>", this.Name, this.Address); + } + + public void FromString(String input) + { + Int32 i = input.IndexOf('<'); + if (i == -1) + { + this.Address = input; + this.Name = ""; + } + else + { + this.Name = input.Substring(0, i).Trim(); + input = input.Substring(i + 1); + i = input.IndexOf('>'); + + if (i != -1) + input = input.Substring(0, i); + + this.Address = input; + } + } + + public static MessageAddress ParseAddress(String address) + { + MessageAddress lAddress = new MessageAddress(); + lAddress.FromString(address); + + return lAddress; + } + } + + public class MessageAddresses : List + { + public MessageAddress Add(String name, String address) + { + MessageAddress item = new MessageAddress(); + item.Name = name; + item.Address = address; + + this.Add(item); + + return item; + } + + public MessageAddress Add(String address) + { + MessageAddress item = new MessageAddress(); + item.FromString(address); + + this.Add(item); + + return item; + } + + public override String ToString() + { + StringBuilder lResult = new StringBuilder(); + Boolean lIsFirstItem = true; + + foreach (MessageAddress address in this) + { + if (!lIsFirstItem) + lResult.Append(", "); + else + lIsFirstItem = false; + + lResult.Append(address.ToString()); + } + + return lResult.ToString(); + } + + public static MessageAddresses ParseAddresses(IEnumerable addresses) + { + MessageAddresses lAddresses = new MessageAddresses(); + + foreach (String address in addresses) + lAddresses.Add(MessageAddress.ParseAddress(address)); + + return lAddresses; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/MessageAttachment.cs b/Source/RemObjects.InternetPack/MessageAttachment.cs new file mode 100644 index 0000000..03aa3b3 --- /dev/null +++ b/Source/RemObjects.InternetPack/MessageAttachment.cs @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; + +namespace RemObjects.InternetPack.Messages +{ + public class MessageAttachment : IDisposable + { + #region Properties + public Boolean OwnsData + { + get + { + return this.fOwnsData; + } + set + { + this.fOwnsData = value; + } + } + private Boolean fOwnsData; + + public Stream Data + { + get + { + return this.fData; + } + set + { + this.fData = value; + } + } + private Stream fData; + + public String Filename + { + get + { + return this.fFilename; + } + set + { + this.fFilename = value; + } + } + private String fFilename; + #endregion + + #region IDisposable Members + public void Dispose() + { + if (this.OwnsData && (this.Data != null)) + this.Data.Close(); + } + #endregion + } + + public class MessageAttachments + { + private IList fItems = new List(8); + + public Int32 Count + { + get + { + return this.fItems.Count; + } + } + + public MessageAttachment this[Int32 index] + { + get + { + return this.fItems[index]; + } + } + + public void Clear() + { + foreach (MessageAttachment item in this.fItems) + if (item is IDisposable) + ((IDisposable)item).Dispose(); + + this.fItems.Clear(); + } + + public MessageAttachment Add() + { + MessageAttachment item = new MessageAttachment(); + this.fItems.Add(item); + + return item; + } + + public MessageAttachment Add(String Name) + { + MessageAttachment item = new MessageAttachment(); + item.Filename = Name; + + fItems.Add(item); + + return item; + } + + public MessageAttachment Add(String name, Stream data, Boolean ownsData) + { + MessageAttachment item = new MessageAttachment(); + item.Filename = name; + item.Data = data; + item.OwnsData = ownsData; + + fItems.Add(item); + + return item; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/MessageEncoder.cs b/Source/RemObjects.InternetPack/MessageEncoder.cs new file mode 100644 index 0000000..8d018eb --- /dev/null +++ b/Source/RemObjects.InternetPack/MessageEncoder.cs @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System.IO; + +namespace RemObjects.InternetPack.Messages +{ + public abstract class MessageEncoder + { + public abstract void EncodeMessage(MailMessage source, Stream destination); + public abstract void DecodeMessage(MailMessage destination, Stream source); + } +} diff --git a/Source/RemObjects.InternetPack/MessageHeaderField.cs b/Source/RemObjects.InternetPack/MessageHeaderField.cs new file mode 100644 index 0000000..a034ee7 --- /dev/null +++ b/Source/RemObjects.InternetPack/MessageHeaderField.cs @@ -0,0 +1,199 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Text; + +namespace RemObjects.InternetPack.Messages +{ + public class HeaderField : Hashtable + { + public HeaderField(String value) + { + this.fUnnamedValue = String.Empty; + this.Value = value; + } + + #region Private fields + private String fUnnamedValue; + #endregion + + public String Value + { + get + { + return this.fValue; + } + set + { + this.fValue = value; + this.Parse(); + } + } + private String fValue; + + public void Parse() + { + //String lValue = ""; + String lRemainder = fValue; + String lProperty = null; + String lPropertyValue; + Int32 lEquals; + Int32 lSemi; + + while (lRemainder.Length > 0) + { + if (lRemainder.IndexOf('=') >= 0) + { + if (lRemainder[0] == '"') + { + lRemainder = lRemainder.Substring(1); + lSemi = lRemainder.IndexOf("\";"); + if (lSemi >= 0) + { + lPropertyValue = lRemainder.Substring(0, lSemi - 1); + lRemainder = lRemainder.Substring(lSemi + 1); + } + else + { + lPropertyValue = lRemainder.Substring(0, lRemainder.Length - 1); + lRemainder = ""; + } + } + else + { + lSemi = lRemainder.IndexOf(';'); + if (lSemi >= 0) + { + lPropertyValue = lRemainder.Substring(0, lSemi); + lRemainder = lRemainder.Substring(lSemi + 1); + } + else + { + if ((lRemainder[0] == '"') && (lRemainder[lRemainder.Length - 1] == '"')) + lPropertyValue = lRemainder.Substring(1, lRemainder.Length - 2); + else + lPropertyValue = lRemainder; + lRemainder = ""; + } + } + } + else + { + if ((lRemainder[0] == '"') && (lRemainder[lRemainder.Length - 1] == '"')) + lPropertyValue = lRemainder.Substring(1, lRemainder.Length - 2); + else + lPropertyValue = lRemainder; + lRemainder = ""; + } + + lPropertyValue = lPropertyValue.Trim(); + + if (String.IsNullOrEmpty(lProperty)) + this.fUnnamedValue = lPropertyValue; + else + { + if (this.ContainsKey(lProperty)) + this[lProperty] = this[lProperty] + lPropertyValue; + else + this.Add(lProperty, lPropertyValue); + } + + lRemainder = lRemainder.Trim(); + + lEquals = lRemainder.IndexOf('='); + if (lEquals >= 0) + { + lProperty = lRemainder.Substring(0, lEquals); + lRemainder = lRemainder.Substring(lEquals + 1); + } + } + } + + public override String ToString() + { + StringBuilder lResult = new StringBuilder(""); + Boolean lFirst = true; + + foreach (String key in Keys) + { + if (lFirst) + lFirst = false; + else + lResult.Append("; "); + + lResult.AppendFormat(System.Globalization.CultureInfo.InvariantCulture, "{0}={1}", key, this[key]); + } + + if (fUnnamedValue != "") + { + if (!lFirst) + lResult.Append("; "); + + lResult.Append(fUnnamedValue); + } + + return lResult.ToString(); + } + } + + public class HeaderFields : NameObjectCollectionBase + { + public HeaderFields() + : base() + { + } + + public void Add(String name, HeaderField field) + { + BaseAdd(name, field); + } + + public void Remove(String name) + { + BaseRemove(name); + } + + public void Clear() + { + BaseClear(); + } + + public HeaderField this[Int32 index] + { + get + { + return BaseGet(index) as HeaderField; + } + set + { + BaseSet(index, value); + } + } + + public HeaderField this[String index] + { + get + { + HeaderField lResult = BaseGet(index) as HeaderField; + if (lResult == null) + { + lResult = new HeaderField(""); + BaseSet(index, lResult); + } + return lResult; + } + set + { + BaseSet(index, value); + } + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Decode/Base64.cs b/Source/RemObjects.InternetPack/Mime/Decode/Base64.cs new file mode 100644 index 0000000..762cb70 --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Decode/Base64.cs @@ -0,0 +1,46 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Text; + +namespace RemObjects.InternetPack.Messages.Mime.Decode +{ + /// + /// Utility class for dealing with Base64 encoded strings + /// + static class Base64 + { + /// + /// Decodes a base64 encoded String into the bytes it describes + /// + /// The String to decode + /// A Byte array that the base64 String described + public static Byte[] Decode(String base64Encoded) + { + return Convert.FromBase64String(base64Encoded); + } + + /// + /// Decodes a Base64 encoded String using a specified + /// + /// Source String to decode + /// The encoding to use for the decoded Byte array that describes + /// A decoded String + /// If or is + /// If is not a valid base64 encoded String + public static String Decode(String base64Encoded, Encoding encoding) + { + if (base64Encoded == null) + throw new ArgumentNullException("base64Encoded"); + + if (encoding == null) + throw new ArgumentNullException("encoding"); + + return encoding.GetString(Decode(base64Encoded)); + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Decode/EncodedWord.cs b/Source/RemObjects.InternetPack/Mime/Decode/EncodedWord.cs new file mode 100644 index 0000000..fadec07 --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Decode/EncodedWord.cs @@ -0,0 +1,125 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Text; +using System.Text.RegularExpressions; + +namespace RemObjects.InternetPack.Messages.Mime.Decode +{ + /// + /// Utility class for dealing with encoded word strings
+ ///
+ /// EncodedWord encoded strings are only in ASCII, but can embed information + /// about characters in other character sets.
+ ///
+ /// It is done by specifying the character set, an encoding that maps from ASCII to + /// the correct bytes and the actual encoded String.
+ ///
+ /// It is specified in a format that is best summarized by a BNF:
+ /// "=?" character_set "?" encoding "?" encoded-text "?="
+ ///
+ /// + /// =?ISO-8859-1?Q?=2D?= + /// Here ISO-8859-1 is the character set.
+ /// Q is the encoding method (quoted-printable). B is also supported (Base 64).
+ /// The encoded text is the =2D part which is decoded to a space. + ///
+ static class EncodedWord + { + /// + /// Decode text that is encoded with the encoding.
+ ///
+ /// This method will decode any encoded-word found in the String.
+ /// All parts which is not encoded will not be touched.
+ ///
+ /// From RFC 2047:
+ /// + /// Generally, an "encoded-word" is a sequence of printable ASCII + /// characters that begins with "=?", ends with "?=", and has two "?"s in + /// between. It specifies a character set and an encoding method, and + /// also includes the original text encoded as graphic ASCII characters, + /// according to the rules for that encoding method. + /// + /// Example:
+ /// =?ISO-8859-1?q?this=20is=20some=20text?= other text here + ///
+ /// See RFC 2047 section 2 "Syntax of encoded-words" for more details + /// Source text. May be content which is not encoded. + /// Decoded text + /// If is + public static String Decode(String encodedWords) + { + if (encodedWords == null) + throw new ArgumentNullException("encodedWords"); + + String decodedWords = encodedWords; + + // Notice that RFC2231 redefines the BNF to + // encoded-word := "=?" charset ["*" language] "?" encoded-text "?=" + // but no usage of this BNF have been spotted yet. It is here to + // ease debugging if such a case is discovered. + + // This is the regex that should fit the BNF + // RFC Says that NO WHITESPACE is allowed in this encoding, but there are examples + // where whitespace is there, and therefore this regex allows for such. + const String strRegEx = @"\=\?(?\S+?)\?(?\w)\?(?.+?)\?\="; + // \w Matches any word character including underscore. Equivalent to "[A-Za-z0-9_]". + // \S Matches any nonwhite space character. Equivalent to "[^ \f\n\r\t\v]". + // +? non-gready equivalent to + + // (?REGEX) is a named group with name NAME and regular expression REGEX + + MatchCollection matches = Regex.Matches(encodedWords, strRegEx); + foreach (Match match in matches) + { + // If this match was not a success, we should not use it + if (!match.Success) continue; + + String fullMatchValue = match.Value; + + String encodedText = match.Groups["Content"].Value; + String encoding = match.Groups["Encoding"].Value; + String charset = match.Groups["Charset"].Value; + + // Get the encoding which corrosponds to the character set + Encoding charsetEncoding = Utility.ParseCharsetToEncoding(charset); + + // Store decoded text here when done + String decodedText; + + // Encoding may also be written in lowercase + switch (encoding.ToUpperInvariant()) + { + // RFC: + // The "B" encoding is identical to the "BASE64" + // encoding defined by RFC 2045. + // http://tools.ietf.org/html/rfc2045#section-6.8 + case "B": + decodedText = Base64.Decode(encodedText, charsetEncoding); + break; + + // RFC: + // The "Q" encoding is similar to the "Quoted-Printable" content- + // transfer-encoding defined in RFC 2045. + // There are more details to this. Please check + // http://tools.ietf.org/html/rfc2047#section-4.2 + // + case "Q": + decodedText = QuotedPrintable.DecodeEncodedWord(encodedText, charsetEncoding); + break; + + default: + throw new ArgumentException("The encoding " + encoding + " was not recognized"); + } + + // Repalce our encoded value with our decoded value + decodedWords = decodedWords.Replace(fullMatchValue, decodedText); + } + + return decodedWords; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Decode/QuotedPrintable.cs b/Source/RemObjects.InternetPack/Mime/Decode/QuotedPrintable.cs new file mode 100644 index 0000000..a0771ce --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Decode/QuotedPrintable.cs @@ -0,0 +1,331 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; + +namespace RemObjects.InternetPack.Messages.Mime.Decode +{ + /// + /// Used for decoding Quoted-Printable text.
+ /// This is a robust implementation of a Quoted-Printable decoder defined in RFC 2045 and RFC 2047.
+ /// Every measurement has been taken to conform to the RFC. + ///
+ static class QuotedPrintable + { + /// + /// Decodes a Quoted-Printable String according to RFC 2047.
+ /// RFC 2047 is used for decoding Encoded-Word encoded strings. + ///
+ /// Quoted-Printable encoded String + /// Specifies which encoding the returned String will be in + /// A decoded String in the correct encoding + /// If or is + public static String DecodeEncodedWord(String toDecode, Encoding encoding) + { + if (toDecode == null) + throw new ArgumentNullException("toDecode"); + + if (encoding == null) + throw new ArgumentNullException("encoding"); + + // Decode the QuotedPrintable String and return it + return encoding.GetString(Rfc2047QuotedPrintableDecode(toDecode, true)); + } + + /// + /// Decodes a Quoted-Printable String according to RFC 2045.
+ /// RFC 2045 specifies the decoding of a body encoded with Content-Transfer-Encoding of quoted-printable. + ///
+ /// Quoted-Printable encoded String + /// A decoded Byte array that the Quoted-Printable encoded String described + /// If is + public static Byte[] DecodeContentTransferEncoding(String toDecode) + { + if (toDecode == null) + throw new ArgumentNullException("toDecode"); + + // Decode the QuotedPrintable String and return it + return Rfc2047QuotedPrintableDecode(toDecode, false); + } + + /// + /// This is the actual decoder. + /// + /// The String to be decoded from Quoted-Printable + /// + /// If , specifies that RFC 2047 quoted printable decoding is used.
+ /// This is for quoted-printable encoded words
+ ///
+ /// If , specifies that RFC 2045 quoted printable decoding is used.
+ /// This is for quoted-printable Content-Transfer-Encoding + /// + /// A decoded Byte array that was described by + /// If is + /// See RFC 2047 section 4.2 for RFC details + private static Byte[] Rfc2047QuotedPrintableDecode(String toDecode, Boolean encodedWordVariant) + { + if (toDecode == null) + throw new ArgumentNullException("toDecode"); + + // Create a Byte array builder which is roughly equivalent to a StringBuilder + using (MemoryStream byteArrayBuilder = new MemoryStream()) + { + // Remove illegal control characters + toDecode = RemoveIllegalControlCharacters(toDecode); + + // Run through the whole String that needs to be decoded + for (Int32 i = 0; i < toDecode.Length; i++) + { + char currentChar = toDecode[i]; + if (currentChar == '=') + { + // Check that there is at least two characters behind the equal sign + if (toDecode.Length - i < 3) + { + // We are at the end of the toDecode String, but something is missing. Handle it the way RFC 2045 states + WriteAllBytesToStream(byteArrayBuilder, DecodeEqualSignNotLongEnough(toDecode.Substring(i))); + + // Since it was the last part, we should stop parsing anymore + break; + } + + // Decode the Quoted-Printable part + String quotedPrintablePart = toDecode.Substring(i, 3); + WriteAllBytesToStream(byteArrayBuilder, DecodeEqualSign(quotedPrintablePart)); + + // We now consumed two extra characters. Go forward two extra characters + i += 2; + } + else + { + // This character is not quoted printable hex encoded. + + // Could it be the _ character, which represents space + // and are we using the encoded word variant of QuotedPrintable + if (currentChar == '_' && encodedWordVariant) + { + // The RFC specifies that the "_" always represents hexadecimal 20 even if the + // SPACE character occupies a different code position in the character set in use. + byteArrayBuilder.WriteByte(0x20); + } + else + { + // This is not encoded at all. This is a literal which should just be included into the output. + byteArrayBuilder.WriteByte((Byte)currentChar); + } + } + } + + return byteArrayBuilder.ToArray(); + } + } + + /// + /// Writes all bytes in a Byte array to a stream + /// + /// The stream to write to + /// The bytes to write to the + private static void WriteAllBytesToStream(Stream stream, Byte[] toWrite) + { + stream.Write(toWrite, 0, toWrite.Length); + } + + /// + /// RFC 2045 states about robustness:
+ /// + /// Control characters other than TAB, or CR and LF as parts of CRLF pairs, + /// must not appear. The same is true for octets with decimal values greater + /// than 126. If found in incoming quoted-printable data by a decoder, a + /// robust implementation might exclude them from the decoded data and warn + /// the user that illegal characters were discovered. + /// + /// Control characters are defined in RFC 2396 as
+ /// control = US-ASCII coded characters 00-1F and 7F hexadecimal + ///
+ /// String to be stripped from illegal control characters + /// A String with no illegal control characters + /// If is + private static String RemoveIllegalControlCharacters(String input) + { + if (input == null) + throw new ArgumentNullException("input"); + + // First we remove any \r or \n which is not part of a \r\n pair + input = RemoveCarriageReturnAndNewLinewIfNotInPair(input); + + // Here only legal \r\n is left over + // We now simply keep them, and the \t which is also allowed + // \x0A = \n + // \x0D = \r + // \x09 = \t) + return Regex.Replace(input, "[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]", ""); + } + + /// + /// This method will remove any \r and \n which is not paired as \r\n + /// + /// String to remove lonely \r and \n's from + /// A String without lonely \r and \n's + /// If is + private static String RemoveCarriageReturnAndNewLinewIfNotInPair(String input) + { + if (input == null) + throw new ArgumentNullException("input"); + + // Use this for building up the new String. This is used for performance instead + // of altering the input String each time a illegal token is found + StringBuilder newString = new StringBuilder(input.Length); + + for (Int32 i = 0; i < input.Length; i++) + { + // There is a character after it + // Check for lonely \r + // There is a lonely \r if it is the last character in the input or if there + // is no \n following it + if (input[i] == '\r' && (i + 1 >= input.Length || input[i + 1] != '\n')) + { + // Illegal token \r found. Do not add it to the new String + + // Check for lonely \n + // There is a lonely \n if \n is the first character or if there + // is no \r in front of it + } + else if (input[i] == '\n' && (i - 1 < 0 || input[i - 1] != '\r')) + { + // Illegal token \n found. Do not add it to the new String + } + else + { + // No illegal tokens found. Simply insert the character we are at + // in our new String + newString.Append(input[i]); + } + } + + return newString.ToString(); + } + + /// + /// RFC 2045 says that a robust implementation should handle:
+ /// + /// An "=" cannot be the ultimate or penultimate character in an encoded + /// object. This could be handled as in case (2) above. + /// + /// Case (2) is:
+ /// + /// An "=" followed by a character that is neither a + /// hexadecimal digit (including "abcdef") nor the CR character of a CRLF pair + /// is illegal. This case can be the result of US-ASCII text having been + /// included in a quoted-printable part of a message without itself having + /// been subjected to quoted-printable encoding. A reasonable approach by a + /// robust implementation might be to include the "=" character and the + /// following character in the decoded data without any transformation and, if + /// possible, indicate to the user that proper decoding was not possible at + /// this point in the data. + /// + ///
+ /// + /// The String to decode which cannot have length above or equal to 3 + /// and must start with an equal sign. + /// + /// A decoded Byte array + /// If is + /// Thrown if a the parameter has length above 2 or does not start with an equal sign. + private static Byte[] DecodeEqualSignNotLongEnough(String decode) + { + if (decode == null) + throw new ArgumentNullException("decode"); + + // We can only decode wrong length equal signs + if (decode.Length >= 3) + throw new ArgumentException("decode must have length lower than 3", "decode"); + + // First char must be = + if (decode[0] != '=') + throw new ArgumentException("First part of decode must be an equal sign", "decode"); + + // We will now believe that the String sent to us, was actually not encoded + // Therefore it must be in US-ASCII and we will return the bytes it corrosponds to + return Encoding.ASCII.GetBytes(decode); + } + + /// + /// This helper method will decode a String of the form "=XX" where X is any character.
+ /// This method will never fail, unless an argument of length not equal to three is passed. + ///
+ /// The length 3 character that needs to be decoded + /// A decoded Byte array + /// If is + /// Thrown if a the parameter does not have length 3 or does not start with an equal sign. + private static Byte[] DecodeEqualSign(String decode) + { + if (decode == null) + throw new ArgumentNullException("decode"); + + // We can only decode the String if it has length 3 - other calls to this function is invalid + if (decode.Length != 3) + throw new ArgumentException("decode must have length 3", "decode"); + + // First char must be = + if (decode[0] != '=') + throw new ArgumentException("decode must start with an equal sign", "decode"); + + // There are two cases where an equal sign might appear + // It might be a + // - hex-String like =3D, denoting the character with hex value 3D + // - it might be the last character on the line before a CRLF + // pair, denoting a soft linebreak, which simply + // splits the text up, because of the 76 chars per line restriction + if (decode.Contains("\r\n")) + { + // Soft break detected + // We want to return String.Empty which is equivalent to a zero-length Byte array + return new Byte[0]; + } + + // Hex String detected. Convertion needed. + // It might be that the String located after the equal sign is not hex characters + // An example: =JU + // In that case we would like to catch the FormatException and do something else + try + { + // The number part of the String is the last two digits. Here we simply remove the equal sign + String numberString = decode.Substring(1); + + // Now we create a Byte array with the converted number encoded in the String as a hex value (base 16) + // This will also handle illegal encodings like =3d where the hex digits are not uppercase, + // which is a robustness requirement from RFC 2045. + Byte[] oneByte = new Byte[] { Convert.ToByte(numberString, 16) }; + + // Simply return our one Byte Byte array + return oneByte; + } + catch (FormatException) + { + // RFC 2045 says about robust implementation: + // An "=" followed by a character that is neither a + // hexadecimal digit (including "abcdef") nor the CR + // character of a CRLF pair is illegal. This case can be + // the result of US-ASCII text having been included in a + // quoted-printable part of a message without itself + // having been subjected to quoted-printable encoding. A + // reasonable approach by a robust implementation might be + // to include the "=" character and the following + // character in the decoded data without any + // transformation and, if possible, indicate to the user + // that proper decoding was not possible at this point in + // the data. + + // So we choose to believe this is actually an un-encoded String + // Therefore it must be in US-ASCII and we will return the bytes it corrosponds to + return Encoding.ASCII.GetBytes(decode); + } + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Decode/Rfc2231Decoder.cs b/Source/RemObjects.InternetPack/Mime/Decode/Rfc2231Decoder.cs new file mode 100644 index 0000000..22f2502 --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Decode/Rfc2231Decoder.cs @@ -0,0 +1,274 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace RemObjects.InternetPack.Messages.Mime.Decode +{ + /// + /// This class is responsible for decoding parameters that has been encoded with:
+ /// + /// + /// Continuation
+ /// This is where a single parameter has such a Int64 value that it could + /// be wrapped while in transit. Instead multiple parameters is used on each line.
+ ///
+ /// Example
+ /// From: Content-Type: text/html; boundary="someVeryLongStringHereWhichCouldBeWrappedInTransit"
+ /// To: Content-Type: text/html; boundary*0="someVeryLongStringHere" boundary*1="WhichCouldBeWrappedInTransit"
+ ///
+ /// + /// Encoding
+ /// Sometimes other characters then ASCII characters are needed in parameters.
+ /// The parameter is then given a different name to specify that it is encoded.
+ ///
+ /// Example
+ /// From: Content-Disposition attachment; filename="specialCharsÆØÅ"
+ /// To: Content-Disposition attachment; filename*="ISO-8859-1'en-us'specialCharsC6D8C0"
+ /// This encoding is almost the same as encoding, and is used to decode the value.
+ ///
+ /// + /// Continuation and Encoding
+ /// Both Continuation and Encoding can be used on the same time.
+ ///
+ /// Example
+ /// From: Content-Disposition attachment; filename="specialCharsÆØÅWhichIsSoLong"
+ /// To: Content-Disposition attachment; filename*0*="ISO-8859-1'en-us'specialCharsC6D8C0"; filename*1*="WhichIsSoLong"
+ /// This could also be encoded as:
+ /// To: Content-Disposition attachment; filename*0*="ISO-8859-1'en-us'specialCharsC6D8C0"; filename*1="WhichIsSoLong"
+ /// Notice that filename*1 does not have an * after it - denoting it IS NOT encoded.
+ /// There are some rules about this:
+ /// + /// The encoding must be mentioned in the first part (filename*0*), which has to be encoded. + /// No other part must specify an encoding, but if encoded it uses the encoding mentioned in the first part. + /// Parts may be encoded or not in any order. + /// + ///
+ ///
+ ///
+ /// More information and the specification is available in RFC 2231. + ///
+ static class Rfc2231Decoder + { + /// + /// Decodes a String of the form:
+ /// value0; key1=value1; key2=value2; key3=value3
+ /// The returned List of key value pairs will have the key as key and the decoded value as value.
+ /// The first value0 will have a key of .
+ ///
+ /// If continuation is used, then multiple keys will be merged into one key with the different values + /// decoded into on big value for that key.
+ /// Example:
+ /// + /// title*0=part1 + /// title*1=part2 + /// + /// will have key and value of:

+ /// title=decode(part1)decode(part2) + ///
+ /// The String to decode. + /// A list of decoded key value pairs. + public static List> Decode(String toDecode) + { + // Normalize the input to take account for missing semicolons after parameters. + // Example + // text/plain; charset=\"iso-8859-1\" name=\"somefile.txt\" + // is normalized to + // text/plain; charset=\"iso-8859-1\"; name=\"somefile.txt\" + // Only works for parameters inside quotes + // \s = matches whitespace + toDecode = Regex.Replace(toDecode, "=\\s*\"(?[^\"]*)\" ", "=\"${value}\"; "); + + // Split by semicolon, but only if not inside quotes + List splitted = Utility.SplitStringWithCharNotInsideQuotes(toDecode.Trim(), ';'); + + List> collection = new List>(splitted.Count); + + foreach (String part in splitted) + { + // Empty strings should not be processed + if (part.Length == 0) + continue; + + String[] keyValue = part.Split(new char[] { '=' }, 2); + if (keyValue.Length == 1) + { + collection.Add(new KeyValuePair("", keyValue[0])); + } + else if (keyValue.Length == 2) + { + collection.Add(new KeyValuePair(keyValue[0], keyValue[1])); + } + else + { + throw new ArgumentException("When splitting the part \"" + part + "\" by = there was " + keyValue.Length + " parts. Only 1 and 2 are supported"); + } + } + + return DecodePairs(collection); + } + + /// + /// Decodes the list of key value pairs into a decoded list of key value pairs.
+ /// There may be less keys in the decoded list, but then the values for the lost keys will have been appended + /// to the new key. + ///
+ /// The pairs to decode + /// A decoded list of pairs + private static List> DecodePairs(List> pairs) + { + if (pairs == null) + throw new ArgumentNullException("pairs"); + + List> resultPairs = new List>(pairs.Count); + + Int32 pairsCount = pairs.Count; + for (Int32 i = 0; i < pairsCount; i++) + { + KeyValuePair currentPair = pairs[i]; + String key = currentPair.Key; + String value = Utility.RemoveQuotesIfAny(currentPair.Value); + + // Is it a continuation parameter? (encoded or not) + if (key.EndsWith("*0", StringComparison.OrdinalIgnoreCase) || key.EndsWith("*0*", StringComparison.OrdinalIgnoreCase)) + { + // This encoding will not be used if we get into the if which tells us + // that the whole continuation is not encoded + + String encoding = "notEncoded - Value here is never used"; + + // Now lets find out if it is encoded too. + if (key.EndsWith("*0*", StringComparison.OrdinalIgnoreCase)) + { + // It is encoded. + + // Fetch out the encoding for later use and decode the value + value = DecodeSingleValue(value, out encoding); + + // Find the right key to use to store the full value + // Remove the start *0 which tells is it is a continuation, and the first one + // And remove the * afterwards which tells us it is encoded + key = key.Replace("*0*", ""); + } + else + { + // It is not encoded, and no parts of the continuation is encoded either + + // Find the right key to use to store the full value + // Remove the start *0 which tells is it is a continuation, and the first one + key = key.Replace("*0", ""); + } + + // The StringBuilder will hold the full decoded value from all continuation parts + StringBuilder builder = new StringBuilder(); + + // Append the decoded value + builder.Append(value); + + // Now go trough the next keys to see if they are part of the continuation + for (Int32 j = i + 1, continuationCount = 1; j < pairsCount; j++, continuationCount++) + { + String jKey = pairs[j].Key; + String valueJKey = Utility.RemoveQuotesIfAny(pairs[j].Value); + + if (jKey.Equals(key + "*" + continuationCount)) + { + // This value part of the continuation is not encoded + // Therefore remove qoutes if any and add to our stringbuilder + builder.Append(valueJKey); + + // Remember to increment i, as we have now treated one more KeyValuePair + i++; + } + else if (jKey.Equals(key + "*" + continuationCount + "*")) + { + // We will not get into this part if the first part was not encoded + // Therefore the encoding will only be used if and only if the + // first part was encoded, in which case we have remembered the encoding used + + // This value part of the continuation is encoded + // the encoding is not given in the current value, + // but was given in the first continuation, which we remembered for use here + valueJKey = DecodeSingleValue(valueJKey, encoding); + builder.Append(valueJKey); + + // Remember to increment i, as we have now treated one more KeyValuePair + i++; + } + else + { + // No more keys for this continuation + break; + } + } + + // Add the key and the full value as a pair + value = builder.ToString(); + resultPairs.Add(new KeyValuePair(key, value)); + } + else if (key.EndsWith("*", StringComparison.OrdinalIgnoreCase)) + { + // This parameter is only encoded - it is not part of a continuation + // We need to change the key from "*" to "" and decode the value + + // To get the key we want, we remove the last * that denotes + // that the value hold by the key was encoded + key = key.Replace("*", ""); + + // Decode the value + String throwAway; + value = DecodeSingleValue(value, out throwAway); + + // Now input the new value with the new key + resultPairs.Add(new KeyValuePair(key, value)); + } + else + { + // Fully normal key - the value is not encoded + // Therefore nothing to do, and we can simply pass the pair + // as being decoded now + resultPairs.Add(currentPair); + } + } + + return resultPairs; + } + + /// + /// This will decode a single value of the form: ISO-8859-1'en-us'%3D%3DIamHere
+ /// Which is basically a form just using % instead of =
+ /// Notice that 'en-us' part is not used for anything. + ///
+ /// The encoding used to decode with - it is given back for later use + /// The value to decode + /// The decoded value that corresponds to + private static String DecodeSingleValue(String toDecode, out String encodingUsed) + { + encodingUsed = toDecode.Substring(0, toDecode.IndexOf('\'')); + toDecode = toDecode.Substring(toDecode.LastIndexOf('\'') + 1); + return DecodeSingleValue(toDecode, encodingUsed); + } + + /// + /// This will decode a single value of the form: %3D%3DIamHere + /// Which is basically a form just using % instead of = + /// + /// The value to decode + /// The encoding used to decode with + /// The decoded value that corresponds to + private static String DecodeSingleValue(String valueToDecode, String encoding) + { + // The encoding used is the same as QuotedPrintable, we only + // need to change % to = + // And otherwise make it look like the correct EncodedWord encoding + valueToDecode = "=?" + encoding + "?Q?" + valueToDecode.Replace("%", "=") + "?="; + return EncodedWord.Decode(valueToDecode); + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Decode/Rfc2822DateTime.cs b/Source/RemObjects.InternetPack/Mime/Decode/Rfc2822DateTime.cs new file mode 100644 index 0000000..ab51ea0 --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Decode/Rfc2822DateTime.cs @@ -0,0 +1,236 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace RemObjects.InternetPack.Messages.Mime.Decode +{ + /// + /// Class used to decode RFC 2822 Date header fields. + /// + static class Rfc2822DateTime + { + /// + /// Converts a String in RFC 2822 format into a object + /// + /// The date to convert + /// A valid object, which represents the same time as the String that was converted + /// If is + /// If the could not be parsed into a object + public static DateTime StringToDate(String inputDate) + { + if (inputDate == null) + throw new ArgumentNullException("inputDate"); + + // Old date specification allows comments and a lot of whitespace + inputDate = StripCommentsAndExcessWhitespace(inputDate); + + try + { + // Extract the date + String date = ExtractDate(inputDate); + + // Convert the date String into a DateTime + DateTime dateTime = Convert.ToDateTime(date, CultureInfo.InvariantCulture); + + // If a day-name is specified in the inputDate String, check if it fits with the date + ValidateDayNameIfAny(dateTime, inputDate); + + // Convert the date into UTC + dateTime = new DateTime(dateTime.Ticks, DateTimeKind.Utc); + + // Adjust according to the time zone + dateTime = AdjustTimezone(dateTime, inputDate); + + // Return the parsed date + return dateTime; + } + catch (ArgumentException e) + { + throw new ArgumentException("Could not parse date: " + e.Message + ". Input was: \"" + inputDate + "\"", e); + } + } + + /// + /// Adjust the object given according to the timezone specified in the . + /// + /// The date to alter + /// The input date, in which the timezone can be found + /// An date altered according to the timezone + /// If no timezone was found in + private static DateTime AdjustTimezone(DateTime dateTime, String dateInput) + { + // We know that the timezones are always in the last part of the date input + String[] parts = dateInput.Split(' '); + String lastPart = parts[parts.Length - 1]; + + // Convert timezones in older formats to [+-]dddd format. + lastPart = Regex.Replace(lastPart, @"UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-I]|[K-Y]|Z", MatchEvaluator); + + // Find the timezone specification + // Example: Fri, 21 Nov 1997 09:55:06 -0600 + // finds -0600 + Match match = Regex.Match(lastPart, @"[\+-](?\d\d)(?\d\d)"); + if (match.Success) + { + // We have found that the timezone is in +dddd or -dddd format + // Add the number of hours and minutes to our found date + Int32 hours = Int32.Parse(match.Groups["hours"].Value); + Int32 minutes = Int32.Parse(match.Groups["minutes"].Value); + + Int32 factor = match.Value[0] == '+' ? -1 : 1; + + dateTime = dateTime.AddHours(factor * hours); + dateTime = dateTime.AddMinutes(factor * minutes); + + return dateTime; + } + + // A timezone of -0000 is the same as doing nothing + return dateTime; + } + + /// + /// Convert timezones in older formats to [+-]dddd format. + /// + /// The match that was found + /// The String to replace the matched String with + private static String MatchEvaluator(Match match) + { + if (!match.Success) + { + throw new ArgumentException("Match success are always true"); + } + + switch (match.Value) + { + // "A" through "I" + // are equivalent to "+0100" through "+0900" respectively + case "A": return "+0100"; + case "B": return "+0200"; + case "C": return "+0300"; + case "D": return "+0400"; + case "E": return "+0500"; + case "F": return "+0600"; + case "G": return "+0700"; + case "H": return "+0800"; + case "I": return "+0900"; + + // "K", "L", and "M" + // are equivalent to "+1000", "+1100", and "+1200" respectively + case "K": return "+1000"; + case "L": return "+1100"; + case "M": return "+1200"; + + // "N" through "Y" + // are equivalent to "-0100" through "-1200" respectively + case "N": return "-0100"; + case "O": return "-0200"; + case "P": return "-0300"; + case "Q": return "-0400"; + case "R": return "-0500"; + case "S": return "-0600"; + case "T": return "-0700"; + case "U": return "-0800"; + case "V": return "-0900"; + case "W": return "-1000"; + case "X": return "-1100"; + case "Y": return "-1200"; + + // "Z", "UT" and "GMT" + // is equivalent to "+0000" + case "Z": + case "UT": + case "GMT": + return "+0000"; + + // US time zones + case "EDT": return "-0400"; // EDT is semantically equivalent to -0400 + case "EST": return "-0500"; // EST is semantically equivalent to -0500 + case "CDT": return "-0500"; // CDT is semantically equivalent to -0500 + case "CST": return "-0600"; // CST is semantically equivalent to -0600 + case "MDT": return "-0600"; // MDT is semantically equivalent to -0600 + case "MST": return "-0700"; // MST is semantically equivalent to -0700 + case "PDT": return "-0700"; // PDT is semantically equivalent to -0700 + case "PST": return "-0800"; // PST is semantically equivalent to -0800 + + default: + throw new ArgumentException("Unexpected input"); + } + } + + /// + /// Extracts the date part from the + /// + /// The date input String, from which to extract the date part + /// The extracted date part + /// If a date part could not be extracted from + private static String ExtractDate(String dateInput) + { + // Matches the date and time part of a String + // Example: Fri, 21 Nov 1997 09:55:06 -0600 + // Finds: 21 Nov 1997 09:55:06 + // Seconds does not need to be specified + // Even though it is illigal, sometimes hours, minutes or seconds are only specified with one digit + Match match = Regex.Match(dateInput, @"\d\d? .+ (\d\d\d\d|\d\d) \d?\d:\d?\d(:\d?\d)?"); + if (match.Success) + { + return match.Value; + } + + throw new ArgumentException("No date part found"); + } + + /// + /// Validates that the given agrees with a day-name specified + /// in . + /// + /// The time to check + /// The date input to extract the day-name from + /// If and does not agree on the day + private static void ValidateDayNameIfAny(DateTime dateTime, String dateInput) + { + // Check if there is a day name in front of the date + // Example: Fri, 21 Nov 1997 09:55:06 -0600 + if (dateInput.Length >= 4 && dateInput[3] == ',') + { + String dayName = dateInput.Substring(0, 3); + + } + + // If no day name was found no checks can be made + } + + /// + /// Strips and removes all comments and excessive whitespace from the String + /// + /// The input to strip from + /// The stripped String + private static String StripCommentsAndExcessWhitespace(String input) + { + // Strip out comments + // Also strips out nested comments + input = Regex.Replace(input, @"(\((?>\((?)|\)(?<-C>)|.?)*(?(C)(?!))\))", ""); + + // Reduce any whitespace character to one space only + input = Regex.Replace(input, @"\s+", " "); + + // Remove all initial whitespace + input = Regex.Replace(input, @"^\s+", ""); + + // Remove all ending whitespace + input = Regex.Replace(input, @"\s+$", ""); + + // Remove spaces at colons + // Example: 22: 33 : 44 => 22:33:44 + input = Regex.Replace(input, @" ?: ?", ":"); + + return input; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Decode/Utility.cs b/Source/RemObjects.InternetPack/Mime/Decode/Utility.cs new file mode 100644 index 0000000..2bb505f --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Decode/Utility.cs @@ -0,0 +1,196 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +namespace RemObjects.InternetPack.Messages.Mime.Decode +{ + /// + /// Contains common operations needed while decoding. + /// + static class Utility + { + /// + /// Separate header name and header value. + /// + /// If is + public static String[] GetHeadersValue(String rawHeader) + { + if (rawHeader == null) + throw new ArgumentNullException("rawHeader"); + + String[] array = new String[] { String.Empty, String.Empty }; + Int32 indexOfColon = rawHeader.IndexOf(':'); + + // Check if it is allowed to make substring calls + if (indexOfColon >= 0 && rawHeader.Length >= indexOfColon + 1) + { + array[0] = rawHeader.Substring(0, indexOfColon).Trim(); + array[1] = rawHeader.Substring(indexOfColon + 1).Trim(); + } + + return array; + } + + /// + /// Remove quotes, if found, around the String. + /// + /// Text with quotes or without quotes + /// Text without quotes + /// If is + public static String RemoveQuotesIfAny(String text) + { + if (text == null) + throw new ArgumentNullException("text"); + + String returner = text; + + if (returner.StartsWith("\"", StringComparison.OrdinalIgnoreCase)) + returner = returner.Substring(1); + if (returner.EndsWith("\"", StringComparison.OrdinalIgnoreCase)) + returner = returner.Substring(0, returner.Length - 1); + + return returner; + } + + /// + /// Split a String into a list of strings using a specified character.
+ /// Everything inside quotes are ignored. + ///
+ /// A String to split + /// The character to use to split with + /// A List of strings that was delimited by the character + public static List SplitStringWithCharNotInsideQuotes(String input, char toSplitAt) + { + List elements = new List(); + + Int32 lastSplitLocation = 0; + Boolean insideQuote = false; + + char[] characters = input.ToCharArray(); + + for (Int32 i = 0; i < characters.Length; i++) + { + char character = characters[i]; + if (character == '\"') + insideQuote = !insideQuote; + + // Only split if we are not inside quotes + if (character == toSplitAt && !insideQuote) + { + // We need to split + Int32 length = i - lastSplitLocation; + elements.Add(input.Substring(lastSplitLocation, length)); + + // Update last split location + // + 1 so that we do not include the character used to split with next time + lastSplitLocation = i + 1; + } + } + + // Add the last part + elements.Add(input.Substring(lastSplitLocation, input.Length - lastSplitLocation)); + + return elements; + } + + /// + /// Parse a character set into an encoding. + /// + /// The character set to parse + /// An encoding which corresponds to the character set + /// If is + public static Encoding ParseCharsetToEncoding(String characterSet) + { + if (characterSet == null) + throw new ArgumentNullException("characterSet"); + + String charSetUpper = characterSet.ToUpperInvariant(); + if (charSetUpper.Contains("WINDOWS") || charSetUpper.Contains("CP")) + { + // It seems the character set contains an codepage value, which we should use to parse the encoding + charSetUpper = charSetUpper.Replace("CP", ""); // Remove cp + charSetUpper = charSetUpper.Replace("WINDOWS", ""); // Remove windows + charSetUpper = charSetUpper.Replace("-", ""); // Remove - which could be used as cp-1554 + + // Now we hope the only thing left in the characterSet is numbers. + Int32 codepageNumber = Int32.Parse(charSetUpper, CultureInfo.InvariantCulture); + + return Encoding.GetEncoding(codepageNumber); + } + + // Some emails incorrectly specify the encoding to be utf8 - but it has to be utf-8 + if (characterSet.Equals("utf8", StringComparison.InvariantCultureIgnoreCase)) + characterSet = "utf-8"; + + // It seems there is no codepage value in the characterSet. It must be a named encoding + return Encoding.GetEncoding(characterSet); + } + } + + /// + /// Utility to help reading bytes and strings of a + /// + static class StreamUtility + { + /// + /// Read a line from the stream. + /// A line is interpreted as all the bytes read until a CRLF or LF is encountered.
+ /// CRLF pair or LF is not included in the String. + ///
+ /// The stream from which the line is to be read + /// A line read from the stream returned as a Byte array or if no bytes were readable from the stream + /// If is + private static Byte[] ReadLineAsBytes(Stream stream) + { + if (stream == null) + throw new ArgumentNullException("stream"); + + using (MemoryStream memoryStream = new MemoryStream()) + { + while (true) + { + Int32 justRead = stream.ReadByte(); + if (justRead == -1 && memoryStream.Length > 0) + break; + + // Check if we started at the end of the stream we read from + // and we have not read anything from it yet + if (justRead == -1 && memoryStream.Length == 0) + return null; + + char readChar = (char)justRead; + + // Do not write \r or \n + if (readChar != '\r' && readChar != '\n') + memoryStream.WriteByte((Byte)justRead); + + // Last point in CRLF pair + if (readChar == '\n') + break; + } + + return memoryStream.ToArray(); + } + } + + /// + /// Read a line from the stream. for more documentation. + /// + /// The stream to read from + /// A line read from the stream or if nothing could be read from the stream + /// If is + public static String ReadLineAsAscii(Stream stream) + { + Byte[] readFromStream = ReadLineAsBytes(stream); + return readFromStream != null ? Encoding.ASCII.GetString(readFromStream) : null; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Header/HeaderExtractor.cs b/Source/RemObjects.InternetPack/Mime/Header/HeaderExtractor.cs new file mode 100644 index 0000000..4e84570 --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Header/HeaderExtractor.cs @@ -0,0 +1,172 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Collections.Specialized; +using System.IO; +using System.Text; +using RemObjects.InternetPack.Messages.Mime.Decode; + +namespace RemObjects.InternetPack.Messages.Mime.Header +{ + /// + /// Utility class that divides a message into a body and a header.
+ /// The header is then parsed to a strongly typed object. + ///
+ static class HeaderExtractor + { + /// + /// Find the end of the header section in a Byte array.
+ /// The headers have ended when a blank line is found + ///
+ /// The full message stored as a Byte array + /// The position of the line just after the header end blank line + private static Int32 FindHeaderEndPosition(Byte[] messageContent) + { + // Convert the Byte array into a stream + using (Stream stream = new MemoryStream(messageContent)) + { + while (true) + { + // Read a line from the stream. We know headers are in US-ASCII + // therefore it is not problem to read them as such + String line = StreamUtility.ReadLineAsAscii(stream); + + // The end of headers is signaled when a blank line is found + // or if the line is null - in which case the email is actually an email with + // only headers but no body + if (String.IsNullOrEmpty(line)) + return (Int32)stream.Position; + } + } + } + + /// + /// Extract the header part and body part of a message.
+ /// The headers are then parsed to a strongly typed object. + ///
+ /// The full message in bytes where header and body needs to be extracted from + /// The extracted header parts of the message + /// The body part of the message + /// If is + public static void ExtractHeadersAndBody(Byte[] fullRawMessage, out MessageHeader headers, out Byte[] body) + { + if (fullRawMessage == null) + throw new ArgumentNullException("fullRawMessage"); + + // Find the end location of the headers + Int32 endOfHeaderLocation = FindHeaderEndPosition(fullRawMessage); + + // The headers are always in ASCII - therefore we can convert the header part into a String + // using US-ASCII encoding + String headersString = Encoding.ASCII.GetString(fullRawMessage, 0, endOfHeaderLocation); + + // Now parse the headers to a NameValueCollection + NameValueCollection headersUnparsedCollection = ExtractHeaders(headersString); + + // Use the NameValueCollection to parse it into a strongly-typed MessageHeader header + headers = new MessageHeader(headersUnparsedCollection); + + // Since we know where the headers end, we also know where the body is + // Copy the body part into the body parameter + body = new Byte[fullRawMessage.Length - endOfHeaderLocation]; + Array.Copy(fullRawMessage, endOfHeaderLocation, body, 0, body.Length); + } + + // token = 1* + private static readonly char[] SEPARATORS = new char[] { '(', ')', '<', '>', '@', ',', ';', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ' }; + + /// + /// Method that takes a full message and extract the headers from it. + /// + /// The message to extract headers from. Does not need the body part. Needs the empty headers end line. + /// A collection of Name and Value pairs of headers + /// If is + private static NameValueCollection ExtractHeaders(String messageContent) + { + if (messageContent == null) + throw new ArgumentNullException("messageContent"); + + NameValueCollection headers = new NameValueCollection(); + + using (StringReader messageReader = new StringReader(messageContent)) + { + // Read until all headers have ended. + // The headers ends when an empty line is encountered + // An empty message might actually not have an empty line, in which + // case the headers end with null value. + String line; + Boolean lIsFirstLine = true; + while (!String.IsNullOrEmpty(line = messageReader.ReadLine())) + { + // Split into name and value + String[] splittedValue = Utility.GetHeadersValue(line); + + Int32 lSeparatorPos = line.IndexOfAny(SEPARATORS); + if (lIsFirstLine && lSeparatorPos != -1 && (line.IndexOf(':') == -1 || line.IndexOf(':') > line.IndexOf(' '))) + { + // not header field-name was found, mb Request-Line + headers.Add("_Request-Line_", line); + lIsFirstLine = false; + continue; + } + + // First index is header name + String headerName = splittedValue[0]; + + // Second index is the header value. + // Use a StringBuilder since the header value may be continued on the next line + StringBuilder headerValue = new StringBuilder(splittedValue[1]); + + // Keep reading until we would hit next header + // This if for handling multi line headers + while (IsMoreLinesInHeaderValue(messageReader)) + { + // Unfolding is accomplished by simply removing any CRLF + // that is immediately followed by WSP + // This was done using ReadLine (it discards CRLF) + // See http://tools.ietf.org/html/rfc822#section-3.1.1 for more information + String moreHeaderValue = messageReader.ReadLine(); + + // If this exception is ever raised, there is an serious algorithm failure + // IsMoreLinesInHeaderValue does not return true if the next line does not exist + // This check is only included to stop the nagging "possibly null" code analysis hint + if (moreHeaderValue == null) + throw new ArgumentException("This will never happen"); + + // Simply append the line just read to the header value + headerValue.Append(moreHeaderValue); + } + + // Now we have the name and full value. Add it + headers.Add(headerName, headerValue.ToString()); + } + } + + return headers; + } + + /// + /// Check if the next line is part of the current header value we are parsing by + /// peeking on the next character of the .
+ /// This should only be called while parsing headers. + ///
+ /// The reader from which the header is read from + /// if multi-line header. otherwise + private static Boolean IsMoreLinesInHeaderValue(TextReader reader) + { + Int32 peek = reader.Peek(); + if (peek == -1) + return false; + + char peekChar = (char)peek; + + // A multi line header must have a whitespace character + // on the next line if it is to be continued + return peekChar == ' ' || peekChar == '\t'; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Header/HeaderFieldParser.cs b/Source/RemObjects.InternetPack/Mime/Header/HeaderFieldParser.cs new file mode 100644 index 0000000..13cbcfe --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Header/HeaderFieldParser.cs @@ -0,0 +1,258 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net.Mail; +using System.Net.Mime; +using RemObjects.InternetPack.Messages.Mime.Decode; + +namespace RemObjects.InternetPack.Messages.Mime.Header +{ + /// + /// Class that can parse different fields in the header sections of a MIME message. + /// + static class HeaderFieldParser + { + /// + /// Parses the Content-Transfer-Encoding header. + /// + /// The value for the header to be parsed + /// A + /// If is + /// If the could not be parsed to a + public static ContentTransferEncoding ParseContentTransferEncoding(String headerValue) + { + if (headerValue == null) + throw new ArgumentNullException("headerValue"); + + switch (headerValue.Trim().ToUpperInvariant()) + { + case "7BIT": + return ContentTransferEncoding.SevenBit; + + case "8BIT": + return ContentTransferEncoding.EightBit; + + case "QUOTED-PRINTABLE": + return ContentTransferEncoding.QuotedPrintable; + + case "BASE64": + return ContentTransferEncoding.Base64; + + case "BINARY": + return ContentTransferEncoding.Binary; + + // If a wrong argument is passed to this parser method, then we assume + // default encoding, which is SevenBit. + // This is to ensure that we do not throw exceptions, even if the email not MIME valid. + default: + return ContentTransferEncoding.SevenBit; + } + } + + /// + /// Parses an ImportanceType from a given Importance header value. + /// + /// The value to be parsed + /// A . If the is not recognized, Normal is returned. + /// If is + public static MailPriority ParseImportance(String headerValue) + { + if (headerValue == null) + throw new ArgumentNullException("headerValue"); + + switch (headerValue.ToUpperInvariant()) + { + case "5": + case "HIGH": + return MailPriority.High; + + case "3": + case "NORMAL": + return MailPriority.Normal; + + case "1": + case "LOW": + return MailPriority.Low; + + default: + return MailPriority.Normal; + } + } + + /// + /// Parses a the value for the header Content-Type to + /// a object. + /// + /// The value to be parsed + /// A object + /// If is + public static ContentType ParseContentType(String headerValue) + { + if (headerValue == null) + throw new ArgumentNullException("headerValue"); + + // We create an empty Content-Type which we will fill in when we see the values + ContentType contentType = new ContentType(); + + // Now decode the parameters + List> parameters = Rfc2231Decoder.Decode(headerValue); + + foreach (KeyValuePair keyValuePair in parameters) + { + String key = keyValuePair.Key.ToUpperInvariant().Trim(); + String value = keyValuePair.Value.Trim(); + switch (key) + { + case "": + // This is the MediaType - it has no key since it is the first one mentioned in the + // headerValue and has no = in it. + contentType.MediaType = value; + break; + + case "BOUNDARY": + contentType.Boundary = Utility.RemoveQuotesIfAny(value); + break; + + case "CHARSET": + contentType.CharSet = Utility.RemoveQuotesIfAny(value); + break; + + case "NAME": + contentType.Name = EncodedWord.Decode(Utility.RemoveQuotesIfAny(value)); + break; + + default: + // This is to shut up the code help that is saying that contentType.Parameters + // can be null - which it cant! + if (contentType.Parameters == null) + throw new Exception("The ContentType parameters property is null. This will never be thrown."); + + // We add the unknown value to our parameters list + // "Known" unknown values are: + // - title + // - report-type + contentType.Parameters.Add(key, value); + break; + } + } + + return contentType; + } + + /// + /// Parses a the value for the header Content-Disposition to a object. + /// + /// The value to be parsed + /// A object + /// If is + public static ContentDisposition ParseContentDisposition(String headerValue) + { + if (headerValue == null) + throw new ArgumentNullException("headerValue"); + + // See http://www.ietf.org/rfc/rfc2183.txt for RFC definition + + // Create empty ContentDisposition - we will fill in details as we read them + ContentDisposition contentDisposition = new ContentDisposition(); + + // Now decode the parameters + List> parameters = Rfc2231Decoder.Decode(headerValue); + + foreach (KeyValuePair keyValuePair in parameters) + { + String key = keyValuePair.Key.ToUpperInvariant().Trim(); + String value = keyValuePair.Value; + switch (key) + { + case "": + // This is the DispisitionType - it has no key since it is the first one + // and has no = in it. + contentDisposition.DispositionType = value; + break; + + // The correct name of the parameter is filename, but some emails also contains the parameter + // name, which also holds the name of the file. Therefore we use both names for the same field. + case "NAME": + case "FILENAME": + // The filename might be in qoutes, and it might be encoded-word encoded + contentDisposition.FileName = EncodedWord.Decode(Utility.RemoveQuotesIfAny(value)); + break; + + case "CREATION-DATE": + // Notice that we need to create a new DateTime because of a failure in .NET 2.0. + // The failure is: you cannot give contentDisposition a DateTime with a Kind of UTC + // It will set the CreationDate correctly, but when trying to read it out it will throw an exception. + // It is the same with ModificationDate and ReadDate. + // This is fixed in 4.0 - maybe in 3.0 too. + // Therefore we create a new DateTime which have a DateTimeKind set to unspecified + DateTime creationDate = new DateTime(Rfc2822DateTime.StringToDate(Utility.RemoveQuotesIfAny(value)).Ticks); + contentDisposition.CreationDate = creationDate; + break; + + case "MODIFICATION-DATE": + DateTime midificationDate = new DateTime(Rfc2822DateTime.StringToDate(Utility.RemoveQuotesIfAny(value)).Ticks); + contentDisposition.ModificationDate = midificationDate; + break; + + case "READ-DATE": + DateTime readDate = new DateTime(Rfc2822DateTime.StringToDate(Utility.RemoveQuotesIfAny(value)).Ticks); + contentDisposition.ReadDate = readDate; + break; + + case "SIZE": + contentDisposition.Size = Int32.Parse(Utility.RemoveQuotesIfAny(value), CultureInfo.InvariantCulture); + break; + + default: + throw new ArgumentException("Unknown parameter in Content-Disposition. Ask developer to fix! Parameter: " + key); + } + } + + return contentDisposition; + } + + /// + /// Parses an ID like Message-Id and Content-Id.
+ /// Example:
+ /// <test@test.com>
+ /// into
+ /// test@test.com + ///
+ /// The id to parse + /// A parsed ID + public static String ParseId(String headerValue) + { + // Remove whitespace in front and behind since + // whitespace is allowed there + // Remove the last > and the first < + return headerValue.Trim().TrimEnd('>').TrimStart('<'); + } + + /// + /// Parses multiple IDs from a single String like In-Reply-To. + /// + /// The value to parse + /// A list of IDs + public static List ParseMultipleIDs(String headerValue) + { + List returner = new List(); + + // Split the String by > + // We cannot use ' ' (space) here since this is a possible value: + // + String[] ids = headerValue.Trim().Split(new char[] { '>' }, StringSplitOptions.RemoveEmptyEntries); + foreach (String id in ids) + { + returner.Add(ParseId(id)); + } + + return returner; + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/Header/MessageHeader.cs b/Source/RemObjects.InternetPack/Mime/Header/MessageHeader.cs new file mode 100644 index 0000000..386f880 --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/Header/MessageHeader.cs @@ -0,0 +1,839 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Net.Mail; +using System.Net.Mime; +using System.Text; +using RemObjects.InternetPack.Messages.Mime.Decode; + +namespace RemObjects.InternetPack.Messages.Mime.Header +{ + /// + /// Class that holds all headers for a message
+ /// Headers which are unknown the the parser will be held in the collection.
+ ///
+ /// This class cannot be instantiated from outside the library. + ///
+ /// + /// See RFC 4021 for a large list of headers.
+ ///
+ public sealed class MessageHeader + { + #region Properties + /// + /// All headers which were not recognized and explicitly dealt with.
+ /// This should mostly be custom headers, which are marked as X-[name].
+ ///
+ /// This list will be empty if all headers were recognized and parsed. + ///
+ /// + /// If you as a user, feels that a header in this collection should + /// be parsed, feel free to notify the developers. + /// + public NameValueCollection UnknownHeaders + { + get + { + return fUnknownHeaders; + } + private set + { + fUnknownHeaders = value; + } + } + private NameValueCollection fUnknownHeaders; + + /// + /// A human readable description of the body
+ ///
+ /// if no Content-Description header was present in the message. + ///
+ public String ContentDescription + { + get + { + return fContentDescription; + } + private set + { + fContentDescription = value; + } + } + private String fContentDescription; + + /// + /// ID of the content part (like an attached image). Used with MultiPart messages.
+ ///
+ /// if no Content-ID header field was present in the message. + ///
+ /// For an ID of the message + public String ContentId + { + get + { + return fContentId; + } + private set + { + fContentId = value; + } + } + private String fContentId; + + /// + /// Message keywords
+ ///
+ /// The list will be empty if no Keywords header was present in the message + ///
+ public List Keywords + { + get + { + return fKeywords; + } + private set + { + fKeywords = value; + } + } + private List fKeywords; + + /// + /// A List of emails to people who wishes to be notified when some event happens.
+ /// These events could be email: + /// + /// deletion + /// printing + /// received + /// ... + /// + /// The list will be empty if no Disposition-Notification-To header was present in the message + ///
+ /// See RFC 3798 for details + public MessageAddresses DispositionNotificationTo + { + get + { + return fDispositionNotificationTo; + } + private set + { + fDispositionNotificationTo = value; + } + } + private MessageAddresses fDispositionNotificationTo; + + /// + /// This is the Received headers. This tells the path that the email went.
+ ///
+ /// The list will be empty if no Received header was present in the message + ///
+ public List Received + { + get + { + return fReceived; + } + private set + { + fReceived = value; + } + } + private List fReceived; + + /// + /// Importance of this email.
+ ///
+ /// The importance level is set to normal, if no Importance header field was mentioned or it contained + /// unknown information. This is the expected behavior according to the RFC. + ///
+ public MailPriority Importance + { + get + { + return fImportance; + } + private set + { + fImportance = value; + } + } + private MailPriority fImportance; + + /// + /// This header describes the Content encoding during transfer.
+ ///
+ /// If no Content-Transfer-Encoding header was present in the message, it is set + /// to the default of SevenBit in accordance to the RFC. + ///
+ /// See RFC 2045 section 6 for details + public ContentTransferEncoding ContentTransferEncoding + { + get + { + return fContentTransferEncoding; + } + private set + { + fContentTransferEncoding = value; + } + } + private ContentTransferEncoding fContentTransferEncoding; + + /// + /// Carbon Copy. This specifies who got a copy of the message.
+ ///
+ /// The list will be empty if no Cc header was present in the message + ///
+ public MessageAddresses Cc + { + get + { + return fCc; + } + private set + { + fCc = value; + } + } + private MessageAddresses fCc; + + /// + /// Blind Carbon Copy. This specifies who got a copy of the message, but others + /// cannot see who these persons are.
+ ///
+ /// The list will be empty if no Received Bcc was present in the message + ///
+ public MessageAddresses Bcc + { + get + { + return fBcc; + } + private set + { + fBcc = value; + } + } + private MessageAddresses fBcc; + + /// + /// Specifies who this mail was for
+ ///
+ /// The list will be empty if no To header was present in the message + ///
+ public MessageAddresses To + { + get + { + return fTo; + } + private set + { + fTo = value; + } + } + private MessageAddresses fTo; + + /// + /// Specifies who sent the email
+ ///
+ /// if no From header field was present in the message + ///
+ public MessageAddress From + { + get + { + return fFrom; + } + private set + { + fFrom = value; + } + } + private MessageAddress fFrom; + + /// + /// Specifies who a reply to the message should be sent to
+ ///
+ /// if no Reply-To header field was present in the message + ///
+ public MessageAddress ReplyTo + { + get + { + return fReplyTo; + } + private set + { + fReplyTo = value; + } + } + private MessageAddress fReplyTo; + + /// + /// The message identifier(s) of the original message(s) to which the + /// current message is a reply.
+ ///
+ /// The list will be empty if no In-Reply-To header was present in the message + ///
+ public List InReplyTo + { + get + { + return fInReplyTo; + } + private set + { + fInReplyTo = value; + } + } + private List fInReplyTo; + + /// + /// The message identifier(s) of other message(s) to which the current + /// message is related to.
+ ///
+ /// The list will be empty if no References header was present in the message + ///
+ public List References + { + get + { + return fReferences; + } + private set + { + fReferences = value; + } + } + private List fReferences; + + /// + /// This is the sender of the email address.
+ ///
+ /// if no Sender header field was present in the message + ///
+ /// + /// The RFC states that this field can be used if a secretary + /// is sending an email for someone she is working for. + /// The email here will then be the secretary's email, and + /// the Reply-To field would hold the address of the person she works for.
+ /// RFC states that if the Sender is the same as the From field, + /// sender should not be included in the message. + ///
+ public MessageAddress Sender + { + get + { + return fSender; + } + private set + { + fSender = value; + } + } + private MessageAddress fSender; + + /// + /// The Content-Type header field.
+ ///
+ /// If not set, the ContentType is created by the default "text/plain; charset=us-ascii" which is + /// defined in RFC 2045 section 5.2.
+ /// If set, the default is overridden. + ///
+ public ContentType ContentType + { + get + { + return fContentType; + } + private set + { + fContentType = value; + } + } + private ContentType fContentType; + + /// + /// Used to describe if a is to be displayed or to be though of as an attachment.
+ /// Also contains information about filename if such was sent.
+ ///
+ /// if no Content-Disposition header field was present in the message + ///
+ public ContentDisposition ContentDisposition + { + get + { + return fContentDisposition; + } + private set + { + fContentDisposition = value; + } + } + private ContentDisposition fContentDisposition; + + /// + /// The Date when the email was sent.
+ /// This is the raw value. for a parsed up value of this field.
+ ///
+ /// if no Date header field was present in the message + ///
+ /// See RFC 5322 section 3.6.1 for more details + public String Date + { + get + { + return fDate; + } + private set + { + fDate = value; + } + } + private String fDate; + + /// + /// The Date when the email was sent.
+ /// This is the parsed equivalent of .
+ /// Notice that the of the object is in UTC and has NOT been converted + /// to local . + ///
+ /// See RFC 5322 section 3.6.1 for more details + public DateTime DateSent + { + get + { + return fDateSent; + } + private set + { + fDateSent = value; + } + } + private DateTime fDateSent; + + /// + /// An ID of the message that is SUPPOSED to be in every message according to the RFC.
+ /// The ID is unique.
+ ///
+ /// if no Message-ID header field was present in the message + ///
+ public String MessageId + { + get + { + return fMessageId; + } + private set + { + fMessageId = value; + } + } + private String fMessageId; + + /// + /// The Mime Version.
+ /// This field will almost always show 1.0
+ ///
+ /// if no Mime-Version header field was present in the message + ///
+ public String MimeVersion + { + get + { + return fMimeVersion; + } + private set + { + fMimeVersion = value; + } + } + private String fMimeVersion; + + /// + /// A single Mail Address with no username inside.
+ /// This is a trace header field, that should be in all messages.
+ /// Replies should be sent to this address.
+ ///
+ /// if no Return-Path header field was present in the message + ///
+ public MessageAddress ReturnPath + { + get + { + return fReturnPath; + } + private set + { + fReturnPath = value; + } + } + private MessageAddress fReturnPath; + + /// + /// The subject line of the message in decoded, one line state.
+ /// This should be in all messages.
+ ///
+ /// if no Subject header field was present in the message + ///
+ public String Subject + { + get + { + return fSubject; + } + private set + { + fSubject = value; + } + } + private String fSubject; + + /// + /// Represents RequestLine of http response. + /// Not reqiured cat can appear in some messages. + /// Example : POST /odata/$batch HTTP/1.1 + /// + public String RequestLine + { + get + { + return fRequestLine; + } + private set + { + fRequestLine = value; + } + } + private String fRequestLine; + #endregion + + private readonly Dictionary fAllHeaders = new Dictionary(); + + public MessageHeader() + { + // Create empty lists as defaults. We do not like null values + // List with an initial capacity set to zero will be replaced + // when a corrosponding header is found + this.To = new MessageAddresses(); + this.Cc = new MessageAddresses(); + this.Bcc = new MessageAddresses(); + this.Received = new List(); + this.Keywords = new List(); + this.InReplyTo = new List(); + this.References = new List(); + this.DispositionNotificationTo = new MessageAddresses(); + this.UnknownHeaders = new NameValueCollection(); + } + + /// + /// Parses a to a MessageHeader + /// + /// The collection that should be traversed and parsed + /// A valid MessageHeader object + /// If is + public MessageHeader(NameValueCollection headers) + : this() + { + if (headers == null) + throw new ArgumentNullException("headers"); + + // Default importancetype is Normal (assumed if not set) + Importance = MailPriority.Normal; + + // 7BIT is the default ContentTransferEncoding (assumed if not set) + ContentTransferEncoding = ContentTransferEncoding.SevenBit; + + // text/plain; charset=us-ascii is the default ContentType + //ContentType = new ContentType("text/plain; charset=us-ascii"); + ContentType = new ContentType("application/atom+xml"); + + // Now parse the actual headers + ParseHeaders(headers); + } + + /// + /// Parses a to a + /// + /// The collection that should be traversed and parsed + /// A valid object + /// If is + private void ParseHeaders(NameValueCollection headers) + { + if (headers == null) + throw new ArgumentNullException("headers"); + + // Now begin to parse the header values + foreach (String headerName in headers.Keys) + { + String[] headerValues = headers.GetValues(headerName); + if (headerValues != null) + { + foreach (String headerValue in headerValues) + { + ParseHeader(headerName, headerValue); + } + } + } + } + + private static MessageAddresses ParseAddresses(String input) + { + return MessageAddresses.ParseAddresses(Utility.SplitStringWithCharNotInsideQuotes(input, ',')); + } + + private void HeaderValueChanged(String headerName, String headerValue) + { + if (fAllHeaders.ContainsKey(headerName)) + fAllHeaders[headerName] = headerValue; + else + fAllHeaders.Add(headerName, headerValue); + } + + #region Header fields parsing + /// + /// Parses a single header and sets member variables according to it. + /// + /// The name of the header + /// The value of the header in unfolded state (only one line) + /// If or is + public void ParseHeader(String headerName, String headerValue) + { + if (headerName == null) + throw new ArgumentNullException("headerName"); + + if (headerValue == null) + throw new ArgumentNullException("headerValue"); + + HeaderValueChanged(headerName, headerValue); + + switch (headerName.ToUpperInvariant()) + { + // See http://tools.ietf.org/html/rfc5322#section-3.6.3 + case "TO": + this.To = MessageHeader.ParseAddresses(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.3 + case "CC": + this.Cc = MessageHeader.ParseAddresses(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.3 + case "BCC": + this.Bcc = MessageHeader.ParseAddresses(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.2 + case "FROM": + // There is only one MailAddress in the from field + this.From = MessageAddress.ParseAddress(headerValue); + break; + + // http://tools.ietf.org/html/rfc5322#section-3.6.2 + // The implementation here might be wrong + case "REPLY-TO": + // This field may actually be a list of addresses, but no + // such case has been encountered + this.ReplyTo = MessageAddress.ParseAddress(headerValue); + break; + + // http://tools.ietf.org/html/rfc5322#section-3.6.2 + case "SENDER": + this.Sender = MessageAddress.ParseAddress(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.5 + // RFC 5322: + // The "Keywords:" field contains a comma-separated list of one or more + // words or quoted-strings. + // The field are intended to have only human-readable content + // with information about the message + case "KEYWORDS": + String[] keywordsTemp = headerValue.Split(','); + foreach (String keyword in keywordsTemp) + { + // Remove the quotes if there is any + Keywords.Add(Utility.RemoveQuotesIfAny(keyword.Trim())); + } + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.7 + case "RECEIVED": + // Simply add the value to the list + Received.Add(headerValue.Trim()); + break; + + case "IMPORTANCE": + Importance = HeaderFieldParser.ParseImportance(headerValue.Trim()); + break; + + // See http://tools.ietf.org/html/rfc3798#section-2.1 + case "DISPOSITION-NOTIFICATION-TO": + this.DispositionNotificationTo = MessageHeader.ParseAddresses(headerValue); + break; + + case "MIME-VERSION": + MimeVersion = headerValue.Trim(); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.5 + case "SUBJECT": + case "THREAD-TOPIC": + Subject = EncodedWord.Decode(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.7 + case "RETURN-PATH": + // Return-paths does not include a username, but we + // may still use the address parser + this.ReturnPath = MessageAddress.ParseAddress(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.4 + // Example Message-ID + // <33cdd74d6b89ab2250ecd75b40a41405@nfs.eksperten.dk> + case "MESSAGE-ID": + MessageId = HeaderFieldParser.ParseId(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.4 + case "IN-REPLY-TO": + InReplyTo = HeaderFieldParser.ParseMultipleIDs(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.4 + case "REFERENCES": + References = HeaderFieldParser.ParseMultipleIDs(headerValue); + break; + + // See http://tools.ietf.org/html/rfc5322#section-3.6.1)) + case "DATE": + Date = headerValue.Trim(); + DateSent = Rfc2822DateTime.StringToDate(headerValue); + break; + + // See http://tools.ietf.org/html/rfc2045#section-6 + // See ContentTransferEncoding class for more details + case "CONTENT-TRANSFER-ENCODING": + ContentTransferEncoding = HeaderFieldParser.ParseContentTransferEncoding(headerValue.Trim()); + break; + + // See http://tools.ietf.org/html/rfc2045#section-8 + case "CONTENT-DESCRIPTION": + // Human description of for example a file. Can be encoded + ContentDescription = EncodedWord.Decode(headerValue.Trim()); + break; + + // See http://tools.ietf.org/html/rfc2045#section-5.1 + // Example: Content-type: text/plain; charset="us-ascii" + case "CONTENT-TYPE": + ContentType = HeaderFieldParser.ParseContentType(headerValue); + break; + + // See http://tools.ietf.org/html/rfc2183 + case "CONTENT-DISPOSITION": + ContentDisposition = HeaderFieldParser.ParseContentDisposition(headerValue); + break; + + // See http://tools.ietf.org/html/rfc2045#section-7 + // Example: + case "CONTENT-ID": + ContentId = HeaderFieldParser.ParseId(headerValue); + break; + + case "_REQUEST-LINE_": + // header could contain request-line that could not be parsed as simple key value pair, so store entire line + // Example: POST /odata/$batch HTTP/1.1 + RequestLine = headerValue; + break; + + default: + // This is an unknown header + + // Custom headers are allowed. That means headers + // that are not mentionen in the RFC. + // Such headers start with the letter "X" + // We do not have any special parsing of such + + // Add it to unknown headers + UnknownHeaders.Add(headerName, headerValue); + break; + } + } + #endregion + + public String this[String name] + { + get + { + if (this.fAllHeaders.ContainsKey(name)) + return fAllHeaders[name]; + + return null; + } + set + { + this.ParseHeader(name, value); + } + } + + public override String ToString() + { + StringBuilder lResult = new StringBuilder(); + Store(lResult); + return lResult.ToString(); + } + + public void Store(StringBuilder sb) + { + foreach (KeyValuePair aHeader in fAllHeaders) + { + if (!aHeader.Key.StartsWith("_")) + { + sb.Append(String.Format("{0}:{1}\r\n", aHeader.Key, aHeader.Value)); + } + } + } + } + + /// + /// that describes the ContentTransferEncoding header field + /// + /// See RFC 2045 section 6 for more details + public enum ContentTransferEncoding + { + /// + /// 7 bit Encoding + /// + SevenBit, + + /// + /// 8 bit Encoding + /// + EightBit, + + /// + /// Quoted Printable Encoding + /// + QuotedPrintable, + + /// + /// Base64 Encoding + /// + Base64, + + /// + /// Binary Encoding + /// + Binary + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/MessagePart.cs b/Source/RemObjects.InternetPack/Mime/MessagePart.cs new file mode 100644 index 0000000..9a17949 --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/MessagePart.cs @@ -0,0 +1,641 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Mime; +using System.Text; +using RemObjects.InternetPack.Messages.Mime.Decode; +using RemObjects.InternetPack.Messages.Mime.Header; + +namespace RemObjects.InternetPack.Messages.Mime +{ + /// + /// A MessagePart is a part of an email message used to describe the whole email parse tree.
+ ///
+ /// Email messages are tree structures:
+ /// Email messages may contain large tree structures, and the MessagePart are the nodes of the this structure.
+ /// A MessagePart may either be a leaf in the structure or a internal node with links to other MessageParts.
+ /// The root of the message tree is the class.
+ ///
+ /// Leafs:
+ /// If a MessagePart is a leaf, the part is not a MultiPart message.
+ /// Leafs are where the contents of an email are placed.
+ /// This includes, but is not limited to: attachments, text or images referenced from HTML.
+ /// The content of an attachment can be fetched by using the property.
+ /// If you want to have the text version of a MessagePart, use the method which will
+ /// convert the into a String using the encoding the message was sent with.
+ ///
+ /// Internal nodes:
+ /// If a MessagePart is an internal node in the email tree structure, then the part is a MultiPart message.
+ /// The property will then contain links to the parts it contain.
+ /// The property of the MessagePart will not be set.
+ ///
+ /// See the example for a parsing example.
+ /// This class cannot be instantiated from outside the library. + ///
+ /// + /// This example illustrates how the message parse tree looks like given a specific message
+ ///
+ /// The message source in this example is:
+ /// + /// MIME-Version: 1.0 + /// Content-Type: multipart/mixed; boundary="frontier" + /// + /// This is a message with multiple parts in MIME format. + /// --frontier + /// Content-Type: text/plain + /// + /// This is the body of the message. + /// --frontier + /// Content-Type: application/octet-stream + /// Content-Transfer-Encoding: base64 + /// + /// PGh0bWw+CiAgPGHLYWQ+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPHA+VGhpcyBpcyB0aGUg + /// Ym9keSBvZiB0aGUgbWVzc2FnZS48L3A+CiAgPC9ib2R5Pgo8L2h0bWw+Cg== + /// --frontier-- + /// + /// The tree will look as follows, where the content-type media type of the message is listed
+ /// + /// - Message root + /// - multipart/mixed MessagePart + /// - text/plain MessagePart + /// - application/octet-stream MessagePart + /// + /// It is possible to have more complex message trees like the following:
+ /// + /// - Message root + /// - multipart/mixed MessagePart + /// - text/plain MessagePart + /// - text/plain MessagePart + /// - multipart/parallel + /// - audio/basic + /// - image/tiff + /// - text/enriched + /// - message/rfc822 + /// + /// But it is also possible to have very simple message trees like:
+ /// + /// - Message root + /// - text/plain + /// + ///
+ public class MessagePart + { + #region Public properties + /// + /// The Content-Type header field.
+ ///
+ /// If not set, the ContentType is created by the default "text/plain; charset=us-ascii" which is + /// defined in RFC 2045 section 5.2.
+ ///
+ /// If set, the default is overridden. + ///
+ public ContentType ContentType + { + get + { + return fContentType; + } + private set + { + fContentType = value; + } + } + private ContentType fContentType; + + /// + /// A human readable description of the body
+ ///
+ /// if no Content-Description header was present in the message.
+ ///
+ public String ContentDescription + { + get + { + return fContentDescription; + } + private set + { + fContentDescription = value; + } + } + private String fContentDescription; + + /// + /// This header describes the Content encoding during transfer.
+ ///
+ /// If no Content-Transfer-Encoding header was present in the message, it is set + /// to the default of SevenBit in accordance to the RFC. + ///
+ /// See RFC 2045 section 6 for details + public ContentTransferEncoding ContentTransferEncoding + { + get + { + return fContentTransferEncoding; + } + private set + { + fContentTransferEncoding = value; + } + } + private ContentTransferEncoding fContentTransferEncoding; + + /// + /// ID of the content part (like an attached image). Used with MultiPart messages.
+ ///
+ /// if no Content-ID header field was present in the message. + ///
+ public String ContentId + { + get + { + return fContentId; + } + private set + { + fContentId = value; + } + } + private String fContentId; + + /// + /// Used to describe if a is to be displayed or to be though of as an attachment.
+ /// Also contains information about filename if such was sent.
+ ///
+ /// if no Content-Disposition header field was present in the message + ///
+ public ContentDisposition ContentDisposition + { + get + { + return fContentDisposition; + } + private set + { + fContentDisposition = value; + } + } + private ContentDisposition fContentDisposition; + + /// + /// This is the encoding used to parse the message body if the
+ /// is not a MultiPart message. It is derived from the character set property. + ///
+ public Encoding BodyEncoding + { + get + { + return fBodyEncoding; + } + private set + { + fBodyEncoding = value; + } + } + private Encoding fBodyEncoding; + + /// + /// This is the parsed body of this .
+ /// It is parsed in that way, if the body was ContentTransferEncoded, it has been decoded to the + /// correct bytes.
+ ///
+ /// It will be if this is a MultiPart message.
+ /// Use to check if this is a MultiPart message. + ///
+ public Byte[] Body + { + get + { + return fBody; + } + private set + { + fBody = value; + } + } + private Byte[] fBody; + + /// + /// Describes if this is a MultiPart message
+ ///
+ /// The is a MultiPart message if the media type property starts with "multipart/" + ///
+ public Boolean IsMultiPart + { + get + { + return ContentType.MediaType.StartsWith("multipart/", StringComparison.OrdinalIgnoreCase); + } + } + + /// + /// A is considered to be holding text in it's body if the MediaType + /// starts either "text/" or is equal to "message/rfc822" + /// + public Boolean IsText + { + get + { + String mediaType = ContentType.MediaType; + return mediaType.StartsWith("text/", StringComparison.OrdinalIgnoreCase) || mediaType.Equals("message/rfc822", StringComparison.OrdinalIgnoreCase); + } + } + + /// + /// A is considered to be an attachment, if
+ /// - it is not holding text and is not a MultiPart message
+ /// or
+ /// - it has a Content-Disposition header that says it is an attachment + ///
+ public Boolean IsAttachment + { + get + { + // Inline is the opposite of attachment + return (!IsText && !IsMultiPart) || (ContentDisposition != null && !ContentDisposition.Inline); + } + } + + /// + /// This is a convenient-property for figuring out a FileName for this .
+ /// If the is a MultiPart message, then it makes no sense to try to find a FileName.
+ ///
+ /// The FileName can be specified in the or in the properties.
+ /// If none of these places two places tells about the FileName, a default "(no name)" is returned. + ///
+ public String FileName + { + get + { + return fFileName; + } + private set + { + fFileName = value; + } + } + private String fFileName; + + /// + /// If this is a MultiPart message, then this property + /// has a list of each of the Multiple parts that the message consists of.
+ ///
+ /// It is if it is not a MultiPart message.
+ /// Use to check if this is a MultiPart message. + ///
+ public List MessageParts + { + get + { + return fMessageParts; + } + private set + { + fMessageParts = value; + } + } + private List fMessageParts; + + public MessageHeader Header + { + get + { + return fHeader; + } + private set + { + fHeader = value; + } + } + private MessageHeader fHeader; + #endregion + + #region Constructors + /// + /// Used to construct the topmost message part + /// + /// The body that needs to be parsed + /// The headers that should be used from the message + /// If or is + public MessagePart(Byte[] rawBody, MessageHeader headers) + { + if (rawBody == null) + throw new ArgumentNullException("rawBody"); + + if (headers == null) + throw new ArgumentNullException("headers"); + + this.Header = headers; + + if (headers.ContentType != null) + ContentType = headers.ContentType; + else + ContentType = new ContentType("text/plain; charset=us-ascii"); + ContentDescription = headers.ContentDescription; + ContentTransferEncoding = headers.ContentTransferEncoding; + ContentId = headers.ContentId; + ContentDisposition = headers.ContentDisposition; + + FileName = FindFileName(ContentType, ContentDisposition, "(no name)"); + BodyEncoding = ParseBodyEncoding(ContentType.CharSet); + + ParseBody(rawBody); + } + #endregion + + #region Parsing + /// + /// Parses a character set into an encoding + /// + /// The character set that needs to be parsed. is allowed. + /// The encoding specified by the parameter, or ASCII if the character set was or empty + private static Encoding ParseBodyEncoding(String characterSet) + { + // Default encoding in Mime messages is US-ASCII + Encoding encoding = Encoding.ASCII; + + // If the character set was specified, find the encoding that the character + // set describes, and use that one instead + if (!String.IsNullOrEmpty(characterSet)) + encoding = Utility.ParseCharsetToEncoding(characterSet); + + return encoding; + } + + /// + /// Figures out the filename of this message part from some headers. + /// property. + /// + /// The Content-Type header + /// The Content-Disposition header + /// The default filename to use, if no other could be found + /// The filename found, or the default one if not such filename could be found in the headers + /// if is + private static String FindFileName(ContentType contentType, ContentDisposition contentDisposition, String defaultName) + { + if (contentType == null) + throw new ArgumentNullException("contentType"); + + if (contentDisposition != null && contentDisposition.FileName != null) + return contentDisposition.FileName; + + if (contentType.Name != null) + return contentType.Name; + + return defaultName; + } + + /// + /// Parses a Byte array as a body of an email message. + /// + /// The Byte array to parse as body of an email message. This array may not contain headers. + private void ParseBody(Byte[] rawBody) + { + if (IsMultiPart) + { + // Parses a MultiPart message + ParseMultiPartBody(rawBody); + } + else + { + // Parses a non MultiPart message + // Decode the body accodingly and set the Body property + Body = DecodeBody(rawBody, ContentTransferEncoding); + } + } + + /// + /// Parses the Byte array as a MultiPart message.
+ /// It is not valid to call this method if returned .
+ /// Fills the property of this . + ///
+ /// The Byte array which is to be parsed as a MultiPart message + private void ParseMultiPartBody(Byte[] rawBody) + { + // Fetch out the boundary used to delimit the messages within the body + String multipartBoundary = ContentType.Boundary; + + // Fetch the individual MultiPart message parts using the MultiPart boundary + List bodyParts = GetMultiPartParts(rawBody, multipartBoundary); + + // Initialize the MessageParts property, with room to as many bodies as we have found + MessageParts = new List(bodyParts.Count); + + // Now parse each Byte array as a message body and add it the the MessageParts property + foreach (Byte[] bodyPart in bodyParts) + { + MessagePart messagePart = GetMessagePart(bodyPart); + MessageParts.Add(messagePart); + } + } + + /// + /// Given a Byte array describing a full message.
+ /// Parses the Byte array into a . + ///
+ /// The Byte array containing both headers and body of a message + /// A which was described by the Byte array + private static MessagePart GetMessagePart(Byte[] rawMessageContent) + { + // Find the headers and the body parts of the Byte array + MessageHeader headers; + Byte[] body; + HeaderExtractor.ExtractHeadersAndBody(rawMessageContent, out headers, out body); + + // Create a new MessagePart from the headers and the body + return new MessagePart(body, headers); + } + + /// + /// Gets a list of Byte arrays where each entry in the list is a full message of a message part + /// + /// The raw Byte array describing the body of a message which is a MultiPart message + /// The delimiter that splits the different MultiPart bodies from each other + /// A list of Byte arrays, each a full message of a + private static List GetMultiPartParts(Byte[] rawBody, String multipPartBoundary) + { + // This is the list we want to return + List messageBodies = new List(); + + // Create a stream from which we can find MultiPart boundaries + using (MemoryStream stream = new MemoryStream(rawBody)) + { + Boolean lastMultipartBoundaryEncountered; + + // Find the start of the first message in this multipart + // Since the method returns the first character on a the line containing the MultiPart boundary, we + // need to add the MultiPart boundary with prepended "--" and appended CRLF pair to the position returned. + Int32 startLocation = FindPositionOfNextMultiPartBoundary(stream, multipPartBoundary, out lastMultipartBoundaryEncountered) + ("--" + multipPartBoundary + "\r\n").Length; + while (true) + { + // When we have just parsed the last multipart entry, stop parsing on + if (lastMultipartBoundaryEncountered) + break; + + // Find the end location of the current multipart + // Since the method returns the first character on a the line containing the MultiPart boundary, we + // need to go a CRLF pair back, so that we do not get that into the body of the message part + Int32 stopLocation = FindPositionOfNextMultiPartBoundary(stream, multipPartBoundary, out lastMultipartBoundaryEncountered) - "\r\n".Length; + + // If we could not find the next multipart boundary, but we had not yet discovered the last boundary, then + // we will consider the rest of the bytes as contained in a last message part. + if (stopLocation <= -1) + { + // Include everything except the last CRLF. + stopLocation = (Int32)stream.Length - "\r\n".Length; + + // We consider this as the last part + lastMultipartBoundaryEncountered = true; + + // Special case: when the last multipart delimiter is not ending with "--", but is indeed the last + // one, then the next multipart would contain nothing, and we should not include such one. + if (startLocation >= stopLocation) + break; + } + + // We have now found the start and end of a message part + // Now we create a Byte array with the correct length and put the message part's bytes into + // it and add it to our list we want to return + Int32 length = stopLocation - startLocation; + Byte[] messageBody = new Byte[length]; + Array.Copy(rawBody, startLocation, messageBody, 0, length); + messageBodies.Add(messageBody); + + // We want to advance to the next message parts start. + // We can find this by jumping forward the MultiPart boundary from the last + // message parts end position + startLocation = stopLocation + ("\r\n" + "--" + multipPartBoundary + "\r\n").Length; + } + } + + // We are done + return messageBodies; + } + + /// + /// Method that is able to find a specific MultiPart boundary in a Stream.
+ /// The Stream passed should not be used for anything else then for looking for MultiPart boundaries + /// The stream to find the next MultiPart boundary in. Do not use it for anything else then with this method. + /// The MultiPart boundary to look for. This should be found in the header + /// Is set to if the next MultiPart boundary was indicated to be the last one, by having -- appended to it. Otherwise set to + ///
+ /// The position of the first character of the line that contained MultiPartBoundary or -1 if no (more) MultiPart boundaries was found + private static Int32 FindPositionOfNextMultiPartBoundary(Stream stream, String multiPartBoundary, out Boolean lastMultipartBoundaryFound) + { + lastMultipartBoundaryFound = false; + while (true) + { + // Get the current position. This is the first position on the line - no characters of the line will + // have been read yet + Int32 currentPos = (Int32)stream.Position; + + // Read the line + String line = StreamUtility.ReadLineAsAscii(stream); + + // If we kept reading until there was no more lines, we did not meet + // the MultiPart boundary. -1 is then returned to describe this. + if (line == null) + return -1; + + // The MultiPart boundary is the MultiPartBoundary with "--" in front of it + // which is to be at the very start of a line + if (line.StartsWith("--" + multiPartBoundary, StringComparison.Ordinal)) + { + // Check if the found boundary was also the last one + lastMultipartBoundaryFound = line.StartsWith("--" + multiPartBoundary + "--", StringComparison.OrdinalIgnoreCase); + return currentPos; + } + } + } + + /// + /// Decodes a Byte array into another Byte array based upon the Content Transfer encoding + /// + /// The Byte array to decode into another Byte array + /// The of the Byte array + /// A Byte array which comes from the being used on the + /// If is + /// Thrown if the is unsupported + private static Byte[] DecodeBody(Byte[] messageBody, ContentTransferEncoding contentTransferEncoding) + { + if (messageBody == null) + throw new ArgumentNullException("messageBody"); + + switch (contentTransferEncoding) + { + case ContentTransferEncoding.QuotedPrintable: + // If encoded in QuotedPrintable, everything in the body is in US-ASCII + return QuotedPrintable.DecodeContentTransferEncoding(Encoding.ASCII.GetString(messageBody)); + + case ContentTransferEncoding.Base64: + // If encoded in Base64, everything in the body is in US-ASCII + return Base64.Decode(Encoding.ASCII.GetString(messageBody)); + + case ContentTransferEncoding.SevenBit: + case ContentTransferEncoding.Binary: + case ContentTransferEncoding.EightBit: + // We do not have to do anything + return messageBody; + + default: + throw new ArgumentOutOfRangeException("contentTransferEncoding"); + } + } + #endregion + + #region Public methods + /// + /// Gets this MessagePart's as text.
+ /// This is simply the being used on the raw bytes of the property.
+ /// This method is only valid to call if it is not a MultiPart message and therefore contains a body.
+ ///
+ /// The property as a String + public String GetBodyAsText() + { + return BodyEncoding.GetString(Body); + } + + public void ToHttpResponseBody(StringBuilder builder) + { + if (builder == null) + throw new ArgumentNullException(); + + + if (this.IsMultiPart) + { + StringBuilder lInnerBuilder = new StringBuilder(); + foreach (MessagePart part in MessageParts) + { + lInnerBuilder.AppendFormat("--{0}\r\n", this.Header.ContentType.Boundary); + part.ToHttpResponseBody(lInnerBuilder); + } + lInnerBuilder.AppendFormat("--{0}--\r\n", this.Header.ContentType.Boundary); + + String lInnerPart = lInnerBuilder.ToString(); + this.Header["Content-Length"] = Encoding.UTF8.GetByteCount(lInnerPart).ToString(); + + this.Header.Store(builder); + builder.Append("\r\n"); + builder.Append(lInnerPart); + + } + else + { + this.Header.Store(builder); + builder.Append("\r\n"); + String body = this.GetBodyAsText(); + if (!String.IsNullOrEmpty(body)) + { + builder.AppendFormat("{0}\r\n", body); + } + } + } + #endregion + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Mime/MimeMessage.cs b/Source/RemObjects.InternetPack/Mime/MimeMessage.cs new file mode 100644 index 0000000..fdb1fce --- /dev/null +++ b/Source/RemObjects.InternetPack/Mime/MimeMessage.cs @@ -0,0 +1,142 @@ +// Public Domain OpenPOP.NET < http://hpop.sourceforge.net > library MIME decoder code portions +// +// Author of OpenPOP.NET library is Kasper Foens ( http://foens.users.sourceforge.net ) +// Full copy of OpenPOP.NET can be obtained from http://hpop.sourceforge.net +// + +using System; +using System.Text; +using RemObjects.InternetPack.Messages.Mime.Header; + +namespace RemObjects.InternetPack.Messages.Mime +{ + /// + /// This is the root of the email tree structure.
+ /// for a description about the structure.
+ ///
+ /// A Message (this class) contains the headers of an email message such as: + /// + /// - To + /// - From + /// - Subject + /// - Content-Type + /// - Message-ID + /// + /// which are located in the property.
+ ///
+ /// Use the property to find the actual content of the email message. + ///
+ /// + /// Examples are available on the project homepage. + /// + public class MimeMessage + { + #region Public properties + /// + /// Headers of the Message. + /// + public MessageHeader Headers + { + get + { + return fHeaders; + } + private set + { + fHeaders = value; + } + } + private MessageHeader fHeaders; + + /// + /// This is the body of the email Message.
+ ///
+ /// If the body was parsed for this Message, this property will never be . + ///
+ public MessagePart MessagePart + { + get + { + return fMessagePart; + } + private set + { + fMessagePart = value; + } + } + private MessagePart fMessagePart; + + /// + /// The raw content from which this message has been constructed.
+ /// These bytes can be persisted and later used to recreate the Message. + ///
+ public Byte[] RawMessage + { + get + { + return fRawMessage; + } + private set + { + fRawMessage = value; + } + } + private Byte[] fRawMessage; + #endregion + + #region Constructors + // todo check if can remove this + public MimeMessage() + { + } + + /// + /// Convenience constructor for .
+ ///
+ /// Creates a message from a Byte array. The full message including its body is parsed. + ///
+ /// The Byte array which is the message contents to parse + public MimeMessage(Byte[] rawMessageContent) + : this(rawMessageContent, true) + { + } + + /// + /// Constructs a message from a Byte array.
+ ///
+ /// The headers are always parsed, but if is , the body is not parsed. + ///
+ /// The Byte array which is the message contents to parse + /// if the body should be parsed, if only headers should be parsed out of the Byte array + public MimeMessage(Byte[] rawMessageContent, Boolean parseBody) + { + RawMessage = rawMessageContent; + + // Find the headers and the body parts of the Byte array + MessageHeader headersTemp; + Byte[] body; + HeaderExtractor.ExtractHeadersAndBody(rawMessageContent, out headersTemp, out body); + + // Set the Headers property + Headers = headersTemp; + + // Should we also parse the body? + if (parseBody) + { + // Parse the body into a MessagePart + MessagePart = new MessagePart(body, Headers); + } + } + #endregion + + public void ToHttpResponseBody(StringBuilder sb) + { + foreach (MessagePart part in this.MessagePart.MessageParts) + { + sb.AppendFormat("--{0}\r\n", this.Headers.ContentType.Boundary); + part.ToHttpResponseBody(sb); + } + sb.AppendFormat("--{0}--\r\n", this.Headers.ContentType.Boundary); + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Pop3Client.cs b/Source/RemObjects.InternetPack/Pop3Client.cs new file mode 100644 index 0000000..7e1fc98 --- /dev/null +++ b/Source/RemObjects.InternetPack/Pop3Client.cs @@ -0,0 +1,230 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using RemObjects.InternetPack.CommandBased; +using RemObjects.InternetPack.Messages; + +namespace RemObjects.InternetPack.Email +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.Pop3Client.bmp")] +#endif + public class Pop3Client : CommandBasedClient + { + public Pop3Client() + { + this.Port = 110; + this.fMessages = new List(32); + } + + #region Private fields + private List fMessages; + #endregion + + class MailStatus + { + public Boolean Retrieved = false; + public MailMessage Message = null; + } + + public String User + { + get + { + return this.fUser; + } + set + { + this.fUser = value; + } + } + private String fUser; + + public String Password + { + get + { + return this.fPassword; + } + set + { + this.fPassword = value; + } + } + private String fPassword; + + public override void Open() + { + base.Open(); + + String lResponse = this.CurrentConnection.ReadLine(); + this.SendLog(LogDirection.Receive, lResponse); + + if (lResponse.StartsWith("+ERR")) + { + this.Close(); + + throw new Exception(String.Format("Invalid connection reply: {0}", lResponse)); + } + } + + public override void Close() + { + if (this.Connected) + this.SendAndReceive("QUIT"); // we don't care about it's result value, we are gonna quit anyway + + base.Close(); + } + + public String SendAndReceive(String command) + { + this.SendLog(LogDirection.Send, command); + this.CurrentConnection.WriteLine(command); + + String lResponse = this.CurrentConnection.ReadLine(); + this.SendLog(LogDirection.Receive, lResponse); + + return lResponse; + } + + public Boolean SendAndCheck(String command) + { + String lResponse = this.SendAndReceive(command); + + return lResponse.StartsWith("+OK"); + } + + public void DeleteMessage(Int32 messageIndex, Int32 messageNumber) + { + String lCommand = "DELE "; + + if (messageNumber != -1) + lCommand += messageNumber.ToString(); + else + lCommand += messageIndex.ToString(); + + String lResponse = this.SendAndReceive(lCommand); + if (lResponse.StartsWith("-ERR")) + throw new Exception(String.Format("Could not delete message at index {0}: {1}", messageIndex, lResponse)); + } + + public Int32 MessageCount + { + get + { + return this.fMessages.Count; + } + } + + public void ClearMessages() + { + this.fMessages.Clear(); + } + + public MailMessage GetHeaders(Int32 messageIndex, Int32 messageNumber) + { + String lCommand = "TOP "; + + if (messageNumber == -1) + lCommand += messageIndex.ToString(); + else + lCommand += messageNumber.ToString(); + + lCommand += " 1"; + + String lResponse = SendAndReceive(lCommand); + + if (lResponse.StartsWith("-ERR")) + throw new Exception(String.Format("Could not retrieve message at index {0}: {1}", messageIndex, lResponse)); + + MailMessage lMessage = new MailMessage(); + MemoryStream lStream = new MemoryStream(); + + using (StreamWriter writer = new StreamWriter(lStream)) + { + while (lResponse != ".") + { + lResponse = this.CurrentConnection.ReadLine(); + writer.WriteLine(lResponse); + } + + writer.Flush(); + + lStream.Position = 0; + lMessage.DecodeMessage(lStream); + } + + return lMessage; + } + + public MailMessage GetMessage(Int32 messageIndex, Int32 messageNumber) + { + if (this.fMessages[messageIndex - 1].Retrieved) + return this.fMessages[messageIndex - 1].Message; + + String lCommand = "RETR "; + + if (messageNumber == -1) + lCommand += messageIndex.ToString(); + else + lCommand += messageNumber.ToString(); + + String lResponse = this.SendAndReceive(lCommand); + + if (lResponse.StartsWith("-ERR")) + throw new Exception(String.Format("Could not retrieve message at index {0}: {1}", messageIndex, lResponse)); + + MemoryStream lStream = new MemoryStream(); + using (StreamWriter writer = new StreamWriter(lStream)) + { + this.fMessages[messageIndex - 1].Retrieved = true; + + while (lResponse != ".") + { + lResponse = CurrentConnection.ReadLine(); + writer.WriteLine(lResponse); + } + + writer.Flush(); + + this.fMessages[messageIndex - 1].Message = new MailMessage(); + lStream.Position = 0; + this.fMessages[messageIndex - 1].Message.DecodeMessage(lStream); + } + + return this.fMessages[messageIndex - 1].Message; + } + + public void Login() + { + if (!this.SendAndCheck("USER " + this.User)) + throw new Exception("Invalid user or password"); + + if (!this.SendAndCheck("PASS " + this.Password)) + throw new Exception("Invalid user or password"); + } + + public void Stat() + { + this.fMessages.Clear(); + + String lResponse = SendAndReceive("STAT"); + if (lResponse.StartsWith("-ERR")) + throw new Exception(String.Format("Could not retrieve mailbox status: {1}", lResponse)); + + String[] lResponses = lResponse.Split(new Char[] { ' ' }); + Int32 lCount = Int32.Parse(lResponses[1]); + + for (Int32 i = 0; i < lCount; i++) + this.fMessages.Add(new MailStatus()); + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/Properties/AndroidManifest.xml b/Source/RemObjects.InternetPack/Properties/AndroidManifest.xml new file mode 100644 index 0000000..a7f9117 --- /dev/null +++ b/Source/RemObjects.InternetPack/Properties/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.2003.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2003.csproj new file mode 100644 index 0000000..044d431 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2003.csproj @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.2005.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2005.csproj new file mode 100644 index 0000000..d775d82 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2005.csproj @@ -0,0 +1,305 @@ + + + Local + 8.0.50727 + 2.0 + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E} + Debug + AnyCPU + + + + + RemObjects.InternetPack + + + JScript + Grid + IE50 + false + Library + RemObjects.InternetPack + OnBuildSuccess + + + + + + + false + + + bin\debug\ + false + 285212672 + false + + + TRACE;DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + + + true + 4096 + false + 1699 + false + false + false + false + 4 + full + + + ..\..\Bin\ + false + 285212672 + false + + + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + + + true + 4096 + false + 1699 + true + false + false + false + 4 + pdbonly + + + ..\..\Bin\Trial\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;TRIAL + + + false + 4096 + false + + + true + false + false + false + 4 + + + ..\..\Bin\Mono\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONO + + + false + 4096 + false + + + true + false + false + false + 4 + + + ..\..\Bin\Trial\Mono\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;TRIAL;MONO + + + false + 4096 + false + + + true + false + false + false + 4 + + + true + bin\Licensed\ + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + 285212672 + true + 1699 + pdbonly + AnyCPU + true + GlobalSuppressions.cs + + + + System + + + System.Data + + + System.Drawing + + + System.XML + + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + + + Component + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + + + + + + + + + + + + + + + + Component + + + + Component + + + Component + + + Code + + + Component + + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.2008.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2008.csproj new file mode 100644 index 0000000..601ccbd --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2008.csproj @@ -0,0 +1,305 @@ + + + Local + 9.0.30729 + 2.0 + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E} + Debug + AnyCPU + + + + + RemObjects.InternetPack + + + JScript + Grid + IE50 + false + Library + RemObjects.InternetPack + OnBuildSuccess + + + + + + + false + + + ..\..\bin\ + false + 285212672 + false + + + TRACE;DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + + + true + 4096 + false + 1699 + false + false + false + false + 4 + full + + + ..\..\Bin\ + false + 285212672 + false + + + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + + + true + 4096 + false + 1699 + true + false + false + false + 4 + pdbonly + + + ..\..\Bin\Trial\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;TRIAL + + + false + 4096 + false + + + true + false + false + false + 4 + + + ..\..\Bin\Mono\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONO + + + false + 4096 + false + + + true + false + false + false + 4 + + + ..\..\Bin\Trial\Mono\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;TRIAL;MONO + + + false + 4096 + false + + + true + false + false + false + 4 + + + true + bin\Licensed\ + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + 285212672 + true + 1699 + pdbonly + AnyCPU + true + GlobalSuppressions.cs + + + + System + + + System.Data + + + System.Drawing + + + System.XML + + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + + + + + + + + + + + + + + + + + + Component + + + + Component + + + Component + + + Code + + + Component + + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.2008.csproj.user b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2008.csproj.user new file mode 100644 index 0000000..32f6213 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2008.csproj.user @@ -0,0 +1,17 @@ + + + ProjectFiles + + + + + + + + + + + en-US + false + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.2010.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2010.csproj new file mode 100644 index 0000000..3a0147b --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.2010.csproj @@ -0,0 +1,307 @@ + + + + Local + 9.0.30729 + 2.0 + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E} + Debug + AnyCPU + + + + + RemObjects.InternetPack + + + JScript + Grid + IE50 + false + Library + RemObjects.InternetPack + OnBuildSuccess + + + + + + + false + v2.0 + + + ..\..\bin\ + false + 285212672 + false + + + TRACE;DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + + + true + 4096 + false + 1699 + false + false + false + false + 4 + full + + + ..\..\Bin\ + false + 285212672 + false + + + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + + + true + 4096 + false + 1699 + true + false + false + false + 4 + pdbonly + + + ..\..\Bin\Trial\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;TRIAL + + + false + 4096 + false + + + true + false + false + false + 4 + + + ..\..\Bin\Mono\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONO + + + false + 4096 + false + + + true + false + false + false + 4 + + + ..\..\Bin\Trial\Mono\ + false + 285212672 + false + + + FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;TRIAL;MONO + + + false + 4096 + false + + + true + false + false + false + 4 + + + true + bin\Licensed\ + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;DESIGN + 285212672 + true + 1699 + pdbonly + AnyCPU + true + GlobalSuppressions.cs + + + + System + + + System.Data + + + System.Drawing + + + System.XML + + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + Component + + + Code + + + + Code + + + + Component + + + Code + + + Component + + + Code + + + + + + + + + + + + + + + + + + Component + + + + Component + + + Component + + + Code + + + Component + + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2003.csdproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2003.csdproj new file mode 100644 index 0000000..d4f4cb6 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2003.csdproj @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2003.csdproj.user b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2003.csdproj.user new file mode 100644 index 0000000..30bed12 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2003.csdproj.user @@ -0,0 +1,55 @@ + + + + + + + + + + + + + diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2005.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2005.csproj new file mode 100644 index 0000000..ec923e4 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2005.csproj @@ -0,0 +1,233 @@ + + + Local + 8.0.50727 + 2.0 + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB} + Debug + AnyCPU + {4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + + + RemObjects.InternetPack + + + false + Library + \Program Files\RemObjects.InternetPack.CF + RemObjects.InternetPack.Core + + + + + + + + + + + 3C41C503-53EF-4c2a-8DD4-A8217CAD115E + PocketPC + $(AssemblyName) + v2.0 + + + bin\Debug\ + false + 0 + false + + + DEBUG;TRACE;COMPACTFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY + + + true + 4096 + false + false + false + false + 4 + full + true + true + off + 1699 + + + ..\..\Bin\CF\ + false + 0 + false + + + TRACE;COMPACTFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY + + + true + 4096 + true + false + false + false + 4 + full + true + true + off + + + + MSCorLib + False + + + System + False + + + System.Data + False + + + System.XML + False + + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Code + + + Code + + + Code + + + Code + + + Component + + + + Component + + + Component + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2005.csproj.user b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2005.csproj.user new file mode 100644 index 0000000..296ea5d --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2005.csproj.user @@ -0,0 +1,7 @@ + + + E282E6BE-C7C3-4ece-916A-88FB1CF8AF3C + ..\..\..\DebugServer\Bin\CF\ + ShowAllFiles + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2008.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2008.csproj new file mode 100644 index 0000000..d34afd3 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.CF.2008.csproj @@ -0,0 +1,236 @@ + + + Local + 8.0.50727 + 2.0 + {67DA6880-E6C5-4C3A-B84F-80FBCC2FD5DB} + Debug + AnyCPU + {4D628B5B-2FBC-4AA6-8C16-197242AEB884};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + + + RemObjects.InternetPack + + + false + Library + \Program Files\RemObjects.InternetPack.CF + RemObjects.InternetPack.Core + + + + + + + + + 2.0 + Pocket PC 2003 + + + 3C41C503-53EF-4c2a-8DD4-A8217CAD115E + PocketPC + $(AssemblyName) + v2.0 + + + bin\Debug\ + false + 0 + false + + + DEBUG;TRACE;COMPACTFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY + + + true + 4096 + false + false + false + false + 4 + full + true + true + off + 1699 + + + ..\..\Bin\CF\ + false + 0 + false + + + TRACE;COMPACTFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY + + + true + 4096 + true + false + false + false + 4 + full + true + true + off + + + + MSCorLib + False + + + System + False + + + System.Data + False + + + System.XML + False + + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + Component + + + Code + + + Code + + + + Component + + + Code + + + Component + + + Code + + + Code + + + Code + + + Code + + + Code + + + Component + + + + Component + + + Component + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoAndroid.2010.Deploy.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoAndroid.2010.Deploy.csproj new file mode 100644 index 0000000..59d447e --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoAndroid.2010.Deploy.csproj @@ -0,0 +1,296 @@ + + + + Local + 9.0.30729 + 2.0 + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Debug + AnyCPU + + + + + RemObjects.InternetPack.MonoAndroid + + + JScript + Grid + IE50 + false + Library + RemObjects.InternetPack + OnBuildSuccess + + + + + + + false + v2.2 + Properties\AndroidManifest.xml + True + armeabi%3barmeabi-v7a + true + None + + + ..\..\bin\MonoAndroid\ + false + 285212672 + false + + + TRACE;DEBUG;MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;FULLFRAMEWORK + + + true + 4096 + false + 1699 + false + false + false + false + 4 + full + + + ..\..\Bin\MonoAndroid\ + false + 285212672 + false + + + DEBUG;MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;FULLFRAMEWORK + + + true + 4096 + false + 1699 + true + false + false + false + 4 + pdbonly + + + ..\..\Bin\Trial\MonoAndroid\ + false + 285212672 + false + + + MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;TRIAL;FULLFRAMEWORK + + + false + 4096 + false + + + true + false + false + false + 4 + + + ..\..\Bin\Mono\MonoAndroid\ + false + 285212672 + false + + + MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;MONO;FULLFRAMEWORK + + + false + 4096 + false + + + true + false + false + false + 4 + + + ..\..\Bin\Trial\Mono\MonoAndroid\ + false + 285212672 + false + + + MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;TRIAL;MONO;FULLFRAMEWORK + + + false + 4096 + false + + + true + false + false + false + 4 + + + true + bin\Licensed\MonoAndroid\ + DEBUG;MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;FULLFRAMEWORK + 285212672 + true + 1699 + pdbonly + AnyCPU + true + GlobalSuppressions.cs + + + + + + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + + + + + + Component + + + + Component + + + Component + + + Code + + + Component + + + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoAndroid.2010.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoAndroid.2010.csproj new file mode 100644 index 0000000..0d1bef6 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoAndroid.2010.csproj @@ -0,0 +1,306 @@ + + + + Local + 9.0.30729 + 2.0 + {0EB0BB6D-4E63-456B-88DD-B3F782D4A10E} + {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Debug + AnyCPU + + + + + RemObjects.InternetPack.MonoAndroid + + + JScript + Grid + IE50 + false + Library + RemObjects.InternetPack + OnBuildSuccess + + + + + + + false + v2.2 + Properties\AndroidManifest.xml + True + armeabi%3barmeabi-v7a + true + None + + + ..\..\bin\MonoAndroid\ + false + 285212672 + false + + + TRACE;DEBUG;MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;FULLFRAMEWORK + true + + + true + 4096 + 1699 + false + false + false + false + 4 + full + + + ..\..\Bin\MonoAndroid\ + false + 285212672 + false + + + DEBUG;MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;FULLFRAMEWORK + true + + + true + 4096 + 1699 + true + false + false + false + 4 + pdbonly + + + ..\..\Bin\Trial\MonoAndroid\ + false + 285212672 + false + + + MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;TRIAL;FULLFRAMEWORK + true + + + false + 4096 + + + true + false + false + false + 4 + + + ..\..\Bin\Mono\MonoAndroid\ + false + 285212672 + false + + + MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;MONO;FULLFRAMEWORK + true + + + false + 4096 + + + true + false + false + false + 4 + + + ..\..\Bin\Trial\Mono\MonoAndroid\ + false + 285212672 + false + + + MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;TRIAL;MONO;FULLFRAMEWORK + true + + + false + 4096 + + + true + false + false + false + 4 + + + true + bin\Licensed\MonoAndroid\ + DEBUG;MONOANDROID;REMOBJECTS_SIGN_ASSEMBLY;FULLFRAMEWORK + true + 285212672 + true + 1699 + pdbonly + AnyCPU + true + GlobalSuppressions.cs + + + + ..\..\..\Data Abstract for .NET\Bin\MonoAndroid\mscorlib.dll + + + ..\..\..\Data Abstract for .NET\Bin\MonoAndroid\System.dll + + + ..\..\..\Data Abstract for .NET\Bin\MonoAndroid\System.Core.dll + + + ..\..\..\Data Abstract for .NET\Bin\MonoAndroid\System.Xml.dll + + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + + + + + + Component + + + + Component + + + Component + + + Code + + + Component + + + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoTouch.Deploy.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoTouch.Deploy.csproj new file mode 100644 index 0000000..9964057 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.MonoTouch.Deploy.csproj @@ -0,0 +1,242 @@ + + + + Release + iPhone + 9.0.21022 + 2.0 + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A} + {E613F3A2-FE9C-494F-B74E-F63BCB86FEA6};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + RemObjects.InternetPack + v3.5 + 3.0 + RemObjects.InternetPack.MonoTouch + 3.0 + + + true + full + false + ..\..\Bin\MonoTouch + TRACE;DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + 1699 + prompt + 4 + None + True + + + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + 1699 + prompt + 4 + False + + + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + 1699 + prompt + 4 + False + + + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH;TRIAL + 1699 + prompt + 4 + False + + + true + full + false + ..\..\Bin\MonoTouch + TRACE;DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + prompt + 4 + True + + + pdbonly + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + prompt + 4 + iPhone Developer + False + + + pdbonly + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + prompt + 4 + iPhone Developer + False + + + pdbonly + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH;;TRIAL + prompt + 4 + iPhone Developer + False + + + + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Code + + + Code + + + + + + + + Component + + + Component + + + Component + + + Code + + + Component + + + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.Monotouch.csproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.Monotouch.csproj new file mode 100644 index 0000000..93c8c7c --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.Monotouch.csproj @@ -0,0 +1,260 @@ + + + + Release + iPhone + 9.0.21022 + 2.0 + {4FC8874C-B3B4-40F1-BB3A-411CE2C1712A} + {E613F3A2-FE9C-494F-B74E-F63BCB86FEA6};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + RemObjects.InternetPack + v3.5 + 3.0 + RemObjects.InternetPack.MonoTouch + 3.0 + + + true + full + false + ..\..\Bin\MonoTouch + TRACE;DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + true + 1699 + prompt + 4 + None + True + + + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + true + 1699 + prompt + 4 + False + + + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + true + 1699 + prompt + 4 + False + + + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH;TRIAL + true + 1699 + prompt + 4 + False + + + true + true + full + false + ..\..\Bin\MonoTouch + TRACE;DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + prompt + 4 + True + + + pdbonly + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + true + prompt + 4 + iPhone Developer + False + + + pdbonly + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH + true + prompt + 4 + iPhone Developer + False + + + pdbonly + true + pdbonly + true + ..\..\Bin\MonoTouch + DEBUG;FULLFRAMEWORK;REMOBJECTS_SIGN_ASSEMBLY;MONOTOUCH;TRIAL + true + prompt + 4 + iPhone Developer + False + + + + ..\..\..\Data Abstract for .NET\Bin\MonoTouch\mscorlib.dll + + + ..\..\..\Data Abstract for .NET\Bin\MonoTouch\System.dll + + + ..\..\..\Data Abstract for .NET\Bin\MonoTouch\System.Core.dll + + + ..\..\..\Data Abstract for .NET\Bin\MonoTouch\System.Xml.dll + + + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Component + + + Component + + + Code + + + Code + + + Code + + + Component + + + Code + + + Component + + + Code + + + Component + + + Component + + + Code + + + Code + + + Component + + + Code + + + Code + + + + + + + + Component + + + Component + + + Component + + + Code + + + Component + + + + + Component + + + Component + + + Code + + + Code + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/RemObjects.InternetPack.bdsproj b/Source/RemObjects.InternetPack/RemObjects.InternetPack.bdsproj new file mode 100644 index 0000000..fbc3001 --- /dev/null +++ b/Source/RemObjects.InternetPack/RemObjects.InternetPack.bdsproj @@ -0,0 +1,125 @@ + + + + + + + + + + + + + Debug + + + + 4 + False + + RemObjects.InternetPack.Core + False + True + False + False + False + TRACE + + Assembly + + 285212672 + False + bin\Release + + + + False + + + + + False + + False + False + + IIS + + + + + + + + 4 + True + + RemObjects.InternetPack.Core + True + False + False + False + False + TRACE;DEBUG + + Assembly + + 285212672 + False + ..\..\Bin + + + + False + + + + + False + + False + False + + IIS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Source/RemObjects.InternetPack/Resources/PoweredByButton.bmp b/Source/RemObjects.InternetPack/Resources/PoweredByButton.bmp new file mode 100644 index 0000000..ef71b09 Binary files /dev/null and b/Source/RemObjects.InternetPack/Resources/PoweredByButton.bmp differ diff --git a/Source/RemObjects.InternetPack/Sasl.cs b/Source/RemObjects.InternetPack/Sasl.cs new file mode 100644 index 0000000..3dac16b --- /dev/null +++ b/Source/RemObjects.InternetPack/Sasl.cs @@ -0,0 +1,352 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace RemObjects.InternetPack +{ + static class Sasl + { + public static String MD5Login(String saslChallenge, String username, String password, String hostname) + { + throw new NotImplementedException(); + } + + public class SaslString + { + public SaslString() + { + this.fValues = new Dictionary(16, StringComparer.OrdinalIgnoreCase); + } + + public SaslString(String source) + : this() + { + this.Parse(source); + } + + private IDictionary fValues; + + public Boolean IsMisformed + { + get + { + return fIsMisformed; + } + } + private Boolean fIsMisformed; + + public IDictionary Values + { + get + { + return fValues; + } + } + + public String this[String name] + { + get + { + if (fValues.ContainsKey(name)) + return fValues[name]; + + return null; + } + set + { + if (value == null) + fValues.Remove(name); + else + fValues[name] = value; + } + } + + private void AddKeyValuePair(String key, String value) + { + if (String.IsNullOrEmpty(key)) + { + this.fIsMisformed = true; + return; + } + + this.fValues[key] = value; + } + + private void DecodeQuotedPair(String source, Int32 startIndex, Int32 endIndex) + { + if ((endIndex - startIndex) < 5) + { + this.fIsMisformed = true; + return; + } + + String lDequotedString = source.Substring(startIndex, (endIndex - startIndex) - 1); + Int32 lSeparatorIndex = lDequotedString.IndexOfAny(new char[] { ':', '=' }); + if ((lSeparatorIndex >= 1) ? ((lSeparatorIndex + 1) == lDequotedString.Length) : true) + { + this.fIsMisformed = true; + } + else + { + String lKey = lDequotedString.Substring(0, lSeparatorIndex); + String lValue = lDequotedString.Substring(lSeparatorIndex + 1, (lDequotedString.Length - lSeparatorIndex) - 1); + this.AddKeyValuePair(lKey, lValue); + } + } + + private String DequoteString(String source, Int32 startIndex, Int32 endIndex) + { + if (endIndex > (startIndex + 1)) + return source.Substring(startIndex, (endIndex - startIndex) - 1).Replace("\"\"", "\""); + + return String.Empty; + } + + public void Clear() + { + fValues.Clear(); + } + + public void Remove(String name) + { + fValues.Remove(name); + } + + public void Parse(String loginString) + { + if (String.IsNullOrEmpty(loginString)) + { + fIsMisformed = true; + return; + } + + Char[] lLoginString = loginString.ToCharArray(); + + ParserState lParserState = ParserState.EntryStart; + Int32 lParserPosition = 0; + Int32 lLoginStringLength = lLoginString.Length; + String lKey = String.Empty; + + Int32 lTokenStart = 0; + while (true) + { + if (lParserPosition >= lLoginStringLength) + { + switch (lParserState) + { + case ParserState.Key: + case ParserState.KeyQuoted: + case ParserState.KeyQuotedStart: + case ParserState.KeyValueSeparator: + case ParserState.ValueQuotedStart: + case ParserState.ValueQuoted: + case ParserState.Recovering: + fIsMisformed = true; + break; + + case ParserState.KeyQuotedEnd: + this.DecodeQuotedPair(loginString, lTokenStart, lParserPosition); + break; + + case ParserState.Value: + AddKeyValuePair(lKey, loginString.Substring(lTokenStart, lParserPosition - lTokenStart)); + break; + + case ParserState.ValueQuotedEnd: + this.AddKeyValuePair(lKey, this.DequoteString(loginString, lTokenStart, lParserPosition)); + break; + } + lParserState = ParserState.LoginStringEnd; + + return; + } + + Char lChar = lLoginString[lParserPosition]; + switch (lParserState) + { + case ParserState.EntryStart: + if ((lChar == ':') || (lChar == '=')) + { + lParserState = ParserState.Recovering; + fIsMisformed = true; + } + else if (lChar == '"') + { + lParserState = ParserState.KeyQuotedStart; + } + else if (lChar != ',') + { + lParserState = ParserState.Key; + lTokenStart = lParserPosition; + lKey = String.Empty; + } + break; + + case ParserState.Key: + if ((lChar == ':') || (lChar == '=')) + { + lParserState = ParserState.KeyValueSeparator; + lKey = loginString.Substring(lTokenStart, lParserPosition - lTokenStart); + } + else if (lChar == ',') + { + lParserState = ParserState.EntryStart; + fIsMisformed = true; + } + break; + + case ParserState.KeyQuotedStart: + lParserState = (lChar != '"') ? ParserState.KeyQuoted : ParserState.KeyQuotedEnd; + lTokenStart = lParserPosition; + lKey = String.Empty; + break; + + case ParserState.KeyQuoted: + if (lChar == '"') + lParserState = ParserState.KeyQuotedEnd; + break; + + case ParserState.KeyQuotedEnd: + if ((lChar == ':') || (lChar == '=')) + { + lParserState = ParserState.KeyValueSeparator; + lKey = DequoteString(loginString, lTokenStart, lParserPosition); + } + else if (lChar == '"') + lParserState = ParserState.KeyQuoted; + else if (lChar == ',') + { + lParserState = ParserState.EntryStart; + this.DecodeQuotedPair(loginString, lTokenStart, lParserPosition); + } + break; + + case ParserState.KeyValueSeparator: + switch (lChar) + { + case ':': + case '=': + lParserState = ParserState.Recovering; + fIsMisformed = true; + break; + case ',': + lParserState = ParserState.EntryStart; + AddKeyValuePair(lKey, String.Empty); + break; + case '"': + lParserState = ParserState.ValueQuotedStart; + break; + default: + + lParserState = ParserState.Value; + lTokenStart = lParserPosition; + break; + } + break; + + case ParserState.Value: + if (lChar == ',') + { + lParserState = ParserState.EntryStart; + AddKeyValuePair(lKey, loginString.Substring(lTokenStart, lParserPosition - lTokenStart)); + } + break; + + case ParserState.ValueQuotedStart: + lParserState = (lChar != '"') ? ParserState.ValueQuoted : ParserState.ValueQuotedEnd; + lTokenStart = lParserPosition; + break; + + case ParserState.ValueQuoted: + if (lChar == '"') + lParserState = ParserState.ValueQuotedEnd; + break; + + case ParserState.ValueQuotedEnd: + switch (lChar) + { + case '"': + lParserState = ParserState.ValueQuoted; + break; + case ':': + case '=': + lParserState = ParserState.Recovering; + fIsMisformed = true; + break; + case ',': + lParserState = ParserState.EntryStart; + AddKeyValuePair(lKey, this.DequoteString(loginString, lTokenStart, lParserPosition)); + break; + } + break; + + case ParserState.Recovering: + if (lChar == ',') + lParserState = ParserState.EntryStart; + break; + } + lParserPosition++; + } + } + + public override String ToString() + { + StringBuilder lResult = new StringBuilder(); + Boolean lFirst = true; + + foreach (KeyValuePair entry in fValues) + { + if (lFirst) + lFirst = false; + else + lResult.Append(','); + + if (entry.Key == null) + continue; + + lResult.Append(entry.Key); + lResult.Append("="); + if (entry.Value != null) + { + if (entry.Value.IndexOfAny(new char[] { ',', '"', '\'' }) != -1) + { + lResult.Append('"'); + lResult.Append(entry.Value.Replace("\"", "\"\"")); + lResult.Append('"'); + } + else + { + lResult.Append(entry.Value); + } + } + } + + return lResult.ToString(); + } + + private enum ParserState + { + EntryStart, + Key, + KeyQuotedStart, + KeyQuoted, + KeyQuotedEnd, + KeyValueSeparator, + Value, + ValueQuotedStart, + ValueQuoted, + ValueQuotedEnd, + LoginStringEnd, + Recovering + } + } + } +} diff --git a/Source/RemObjects.InternetPack/Server.cs b/Source/RemObjects.InternetPack/Server.cs new file mode 100644 index 0000000..0dcdc45 --- /dev/null +++ b/Source/RemObjects.InternetPack/Server.cs @@ -0,0 +1,553 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.Net; +using System.Net.Sockets; +using System.Threading; + +namespace RemObjects.InternetPack +{ + public abstract class Server : System.ComponentModel.Component + { + protected Server() + { + fBindings = new ServerBindings(); + +#if FULLFRAMEWORK + if (Socket.SupportsIPv4) + { + fBindingV4 = new ServerBinding(); + fBindingV4.Address = IPAddress.Any; + fBindingV4.AddressFamily = AddressFamily.InterNetwork; + } + + // This is a workaround for Mono 2.10 / Mac + // Socket.OSSupportsIPv6 there fails with ArgumentException + Boolean lIsIPv6Supported = false; + try + { + lIsIPv6Supported = Socket.OSSupportsIPv6; + } + catch (ArgumentException) + { + lIsIPv6Supported = (Environment.OSVersion.Platform == PlatformID.Unix); // Mono reports Unix platform for MacOSX + } + + if (lIsIPv6Supported) + { + fBindingV6 = new ServerBinding(); + fBindingV6.Address = IPAddress.IPv6Any; + fBindingV6.AddressFamily = AddressFamily.InterNetworkV6; + } +#else + fBindingV4 = new ServerBinding(); + fBindingV4.Address = IPAddress.Any; + fBindingV4.AddressFamily = AddressFamily.InterNetwork; +#endif + + if (fBindingV4 == null && fBindingV6 == null) + throw new Exception("This host's network stack supports neither IPv4 nor IPv6 Internet Protocol"); + + Timeout = Connection.DEFAULT_TIMEOUT; + TimeoutEnabled = true; + MaxLineLength = Connection.DEFAULT_MAX_LINE_LENGTH; + MaxLineLengthEnabled = true; + } + + protected override void Dispose(Boolean disposing) + { + if (disposing) + Close(); + + base.Dispose(disposing); + } + + #region Properties + [Category("Server"), Browsable(false)] + public ServerBindings Bindings + { + get + { + return fBindings; + } + } + private ServerBindings fBindings; + + [Category("Server"), Browsable(false), Obsolete("Please use BindingV4 and BindingV6 instead", false)] + public ServerBinding Binding + { + get + { + if (fBindingV4 != null) + return fBindingV4; + + return fBindingV6; + } + } + + [Category("Server"), Browsable(false)] + public ServerBinding BindingV4 + { + get + { + return fBindingV4; + } + } + private ServerBinding fBindingV4; + + [Category("Server"), Browsable(false)] + public ServerBinding BindingV6 + { + get + { + return fBindingV6; + } + } + private ServerBinding fBindingV6 = null; + + [Category("Server"), Browsable(true), DefaultValue(true)] + public Boolean BindV6 + { + get + { + return fBindV6; + } + set + { + fBindV6 = value; + } + } + private Boolean fBindV6 = true; + + [Category("Server"), Browsable(true), DefaultValue(true)] + public Boolean BindV4 + { + get + { + return fBindV4; + } + set + { + fBindV4 = value; + } + } + private Boolean fBindV4 = true; + + [Category("Server")] + public Int32 Port + { + get + { + if (fBindingV4 != null) + return fBindingV4.Port; + + return fBindingV6.Port; + } + set + { + if (fBindingV4 != null) + fBindingV4.Port = value; + + if (fBindingV6 != null) + fBindingV6.Port = value; + } + } + + [Category("Server"), DefaultValue(true)] + public Boolean CloseConnectionsOnShutdown + { + get + { + return fCloseConnectionsOnShutdown; + } + set + { + fCloseConnectionsOnShutdown = value; + } + } + private Boolean fCloseConnectionsOnShutdown = true; + + [Category("Server"), DefaultValue(false)] + public Boolean EnableNagle + { + get + { + return fEnableNagle; + } + set + { + fEnableNagle = value; + } + } + private Boolean fEnableNagle = false; + + protected Int32 DefaultPort + { + get + { + if (fBindingV4 != null) + return fBindingV4.DefaultPort; + + return fBindingV6.DefaultPort; + } + set + { + if (fBindingV4 != null) + { + fBindingV4.DefaultPort = value; + fBindingV4.Port = value; + } + + if (fBindingV6 != null) + { + fBindingV6.DefaultPort = value; + fBindingV6.Port = value; + } + } + } + + public Boolean ShouldSerializePort() + { + return (Port != DefaultPort); + } + + [Browsable(false), DefaultValue(null)] + public Type ConnectionClass + { + get + { + return fConnectionClass; + } + set + { + + if (value != null && !value.IsSubclassOf(typeof(Connection))) + throw new Exception(String.Format("The assigned Type '{0}' is not a descendant of Connection", value.FullName)); + fConnectionClass = value; + } + } + private Type fConnectionClass; + + [Browsable(false), DefaultValue(null)] + public IConnectionFactory ConnectionFactory + { + get + { + return fConnectionFactory; + } + set + { + fConnectionFactory = value; + } + } + private IConnectionFactory fConnectionFactory; + +#if FULLFRAMEWORK + [Category("Server")] + public SslConnectionFactory SslOptions + { + get + { + return fSslOptions; + } + } + private SslConnectionFactory fSslOptions = new SslConnectionFactory(); +#endif + +#if FULLFRAMEWORK + [Category("Server"), Browsable(false), DefaultValue(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] +#endif + public Boolean Active + { + get + { + return fActive; + } + set + { + if (value != Active) + { + if (value) + Open(); + else + Close(); + } + } + } + protected Boolean fActive; + #endregion + + #region Properties: Security + [Category("Security"), DefaultValue(true)] + public Boolean TimeoutEnabled + { + get + { + return fTimeoutEnabled; + } + set + { + fTimeoutEnabled = value; + } + } + private Boolean fTimeoutEnabled; + + [Category("Security"), DefaultValue(Connection.DEFAULT_TIMEOUT)] + public Int32 Timeout + { + get + { + return fTimeout; + } + set + { + fTimeout = value; + } + } + private Int32 fTimeout; + + [Category("Security"), DefaultValue(true)] + public Boolean MaxLineLengthEnabled + { + get + { + return fMaxLineLengthEnabled; + } + set + { + fMaxLineLengthEnabled = value; + } + } + private Boolean fMaxLineLengthEnabled; + + [Category("Security"), DefaultValue(Connection.DEFAULT_MAX_LINE_LENGTH)] + public Int32 MaxLineLength + { + get + { + return fMaxLineLength; + } + set + { + fMaxLineLength = value; + } + } + private Int32 fMaxLineLength; + #endregion + + #region Methods + private static Boolean IsRunningOnMono() + { + return Type.GetType("Mono.Runtime") != null; + } + + public virtual void Open() + { + try + { + Int32 lActualPort = this.Port; + + Boolean lBindV6 = (this.fBindingV6 != null) && this.fBindV6; + if (lBindV6) + { + this.fBindingV6.EnableNagle = EnableNagle; + this.fBindingV6.Bind(new Listener(this, this.GetWorkerClass())); + lActualPort = ((System.Net.IPEndPoint)this.fBindingV6.ListeningSocket.LocalEndPoint).Port; + } + + if ((this.fBindV4 && (this.fBindingV4 != null)) && + !(lBindV6 && Server.IsRunningOnMono())) + { + if (this.Port == 0) + this.fBindingV4.Port = lActualPort; + this.fBindingV4.EnableNagle = EnableNagle; + this.fBindingV4.Bind(new Listener(this, this.GetWorkerClass())); + lActualPort = ((System.Net.IPEndPoint)this.fBindingV4.ListeningSocket.LocalEndPoint).Port; + } + + if (this.Port != lActualPort) + this.Port = lActualPort; + + this.fActive = true; + } + catch + { + this.Close(); + throw; + } + } + + public virtual void Close() + { + fActive = false; + if (fBindingV4 != null && fBindV4) + fBindingV4.Unbind(true); + + if (fBindingV6 != null && fBindV6) + fBindingV6.Unbind(true); + } + + public virtual Type GetWorkerClass() + { + return typeof(Worker); + } + #endregion + } + + public class Listener : IListener + { + public Listener(Server owner, Type workerClass) + { + fOwner = owner; + fWorkerClass = workerClass; + } + + #region Properties + public Socket ListeningSocket + { + get + { + return fListeningSocket; + } + set + { + fListeningSocket = value; + } + } + private Socket fListeningSocket; + + public Type WorkerClass + { + get + { + return fWorkerClass; + } + } + private Type fWorkerClass; + + public Server Owner + { + get + { + return fOwner; + } + set + { + fOwner = value; + } + } + private Server fOwner; + #endregion + + public virtual void Listen() + { + Socket lSocket; + + WorkerCollection lWorkers = null; + if (Owner.CloseConnectionsOnShutdown) lWorkers = new WorkerCollection(); + + try + { + do + { + try + { + lSocket = fListeningSocket.Accept(); + } + catch (ObjectDisposedException) + { + return; + } + catch (SocketException) + { + /* If Accept fails with a SocketException, the socket was ListeningSocket was probably + * closed, so we'll just exit and terminate the thread. */ + return; + } + + if (lSocket != null) + { + Object lObject = Activator.CreateInstance(WorkerClass); + IWorker lWorker = lObject as IWorker; + lWorker.Owner = Owner; + + if (Owner.ConnectionFactory != null) + { + lWorker.DataConnection = Owner.ConnectionFactory.CreateServerConnection(lSocket); + } + else if (Owner.ConnectionClass != null) + { + lWorker.DataConnection = (Connection)Activator.CreateInstance(Owner.ConnectionClass); + lWorker.DataConnection.Init(lSocket); + } +#if FULLFRAMEWORK + else if (Owner.SslOptions.Enabled) + { + lWorker.DataConnection = Owner.SslOptions.CreateServerConnection(lSocket); + } +#endif + else + { + lWorker.DataConnection = new Connection(lSocket); + } +#if FULLFRAMEWORK + if (Owner.TimeoutEnabled) + { + lWorker.DataConnection.TimeoutEnabled = true; + lWorker.DataConnection.Timeout = Owner.Timeout; + } +#endif + if (Owner.MaxLineLengthEnabled) + { + lWorker.DataConnection.MaxLineLengthEnabled = true; + lWorker.DataConnection.MaxLineLength = Owner.MaxLineLength; + } + + try + { + lWorker.DataConnection.InitializeServerConnection(); + } + catch (Exception) // nothing should escape this loop. + { + lWorker.DataConnection.Dispose(); + continue; + } + + lWorker.Thread = new Thread(new ThreadStart(lWorker.Work)); + try + { +#if FULLFRAMEWORK + lWorker.Thread.Name = String.Format("Internet Pack {0} for {1}", WorkerClass.Name, lSocket.RemoteEndPoint.ToString()); +#endif + + } + catch (SocketException) + { + // mono can fail in that code if the remote side has been disconnected. Since we already create a thread we have to run it (leak otherwise) + } + + if (lWorkers != null) + lWorkers.Add(lWorker); + + lWorker.Thread.Start(); + } + } + while (lSocket != null); + + } + finally + { + if (lWorkers != null) + lWorkers.Close(); + } + + } + } +} diff --git a/Source/RemObjects.InternetPack/Server.resx b/Source/RemObjects.InternetPack/Server.resx new file mode 100644 index 0000000..dd0ea4d --- /dev/null +++ b/Source/RemObjects.InternetPack/Server.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/Source/RemObjects.InternetPack/SimpleHttpServer.cs b/Source/RemObjects.InternetPack/SimpleHttpServer.cs new file mode 100644 index 0000000..570b9a0 --- /dev/null +++ b/Source/RemObjects.InternetPack/SimpleHttpServer.cs @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.IO; + +namespace RemObjects.InternetPack.Http +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.SimpleHttpServer.bmp")] +#endif + public class SimpleHttpServer : HttpServer + { + #region Propeties + [Category("Server")] + public String RootPath + { + get + { + return this.fRootPath; + } + set + { + this.fRootPath = value; + } + } + private String fRootPath; + #endregion + + #region Overriden Methods + protected internal override void HandleHttpRequest(Connection connection, HttpServerRequest request, HttpServerResponse response) + { + base.HandleHttpRequest(connection, request, response); + + if (response.ContentSource == ContentSource.ContentNone) + { + if (request.Header.RequestType == "GET") + { + String lPath = RootPath + request.Header.RequestPath.Replace('/', Path.DirectorySeparatorChar); + if (lPath.IndexOf("..") == -1) + { + if (File.Exists(lPath)) + { + response.Header.ContentType = "text/html"; + response.ContentStream = new FileStream(lPath, FileMode.Open, FileAccess.Read, FileShare.Read); + response.CloseStream = true; /* Response will close stream once it's been sent */ + } + else + { + response.SendError(404, String.Format("File '{0}' not found.", lPath)); + } + } + else + { + response.SendError(403, String.Format("Bad Request: Path '{0}' contains '..' which is invalid.", lPath)); + } + } + else + { + response.SendError(500, String.Format("Request Type '{0}' not supported.", request.Header.RequestType)); + } + } + } + #endregion + } +} diff --git a/Source/RemObjects.InternetPack/SimpleHttpServer.resx b/Source/RemObjects.InternetPack/SimpleHttpServer.resx new file mode 100644 index 0000000..dd0ea4d --- /dev/null +++ b/Source/RemObjects.InternetPack/SimpleHttpServer.resx @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.0.0.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/Source/RemObjects.InternetPack/SimpleServer.cs b/Source/RemObjects.InternetPack/SimpleServer.cs new file mode 100644 index 0000000..1702769 --- /dev/null +++ b/Source/RemObjects.InternetPack/SimpleServer.cs @@ -0,0 +1,47 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System.Net; + +namespace RemObjects.InternetPack +{ + public class SimpleServer + { + public SimpleServer() + { + this.fBinding = new ServerBinding(); + this.fBinding.Address = IPAddress.Any; + } + + #region Properties + public ServerBinding Binding + { + get + { + return this.fBinding; + } + } + private ServerBinding fBinding; + #endregion + + public void Open() + { + this.Binding.BindUnthreaded(); + } + + public void Close() + { + this.Binding.Unbind(); + } + + public Connection WaitForConnection() + { + return this.Binding.Accept(); + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/SmtpClient.cs b/Source/RemObjects.InternetPack/SmtpClient.cs new file mode 100644 index 0000000..945b8de --- /dev/null +++ b/Source/RemObjects.InternetPack/SmtpClient.cs @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.IO; +using RemObjects.InternetPack.CommandBased; +using RemObjects.InternetPack.Messages; + +// http://www.ietf.org/rfc/rfc0821.txt +// http://www.faqs.org/rfcs/rfc2554.html + +namespace RemObjects.InternetPack.Email +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Client), "Glyphs.SmtpClient.bmp")] +#endif + public class SmtpClient : CommandBasedClient + { + public SmtpClient() + { + this.Port = 25; + } + + public String HeloDomain + { + get + { + return this.fHeloDomain; + } + set + { + this.fHeloDomain = value; + } + } + private String fHeloDomain; + + public Boolean UseAuth + { + get + { + return this.fUseAuth; + } + set + { + this.fUseAuth = value; + } + } + private Boolean fUseAuth; + + public String AuthUser + { + get + { + return this.fAuthUser; + } + set + { + this.fAuthUser = value; + } + } + private String fAuthUser; + + public String AuthPassword + { + get + { + return this.fAuthPassword; + } + set + { + this.fAuthPassword = value; + } + } + private String fAuthPassword; + + public override void Open() + { + base.Open(); + + if (!this.WaitForResponse(220)) + { + this.Close(); + + throw new Exception(String.Format("Invalid connection reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + } + + if (this.UseAuth) + { + if (!this.SendAndWaitForResponse("EHLO " + this.HeloDomain, 250)) + throw new Exception(String.Format("Invalid ehlo reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + + if (!this.SendAndWaitForResponse("AUTH LOGIN", 334)) + throw new Exception(String.Format("Invalid AUTH reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + + Byte[] lBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(this.AuthUser); + String lEncodedString = System.Convert.ToBase64String(lBytes, 0, lBytes.Length); + if (!SendAndWaitForResponse(lEncodedString, 334)) + throw new Exception(String.Format("Invalid AUTH username reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + + lBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(this.AuthPassword); + lEncodedString = System.Convert.ToBase64String(lBytes, 0, lBytes.Length); + if (!this.SendAndWaitForResponse(lEncodedString, 235)) + throw new Exception(String.Format("Invalid AUTH password reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + } + else + { + // HELO moved to "Open" as it must only be sent once during session + if (!this.SendAndWaitForResponse("HELO " + this.HeloDomain, 250)) + throw new Exception(String.Format("Invalid helo reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + } + } + + public override void Close() + { + if (this.Connected) + this.SendAndWaitForResponse("QUIT", 221); // we don't care about it's result value, we are gonna quit anyway + + base.Close(); + } + + public void SendMessage(MailMessage message, String origin, String[] destination) + { + if (origin.Length == 0) throw + new ArgumentException("No origin provided", "origin"); + + if (destination.Length == 0) + throw new ArgumentException("No destination provided", "destination"); + + if (!this.SendAndWaitForResponse(String.Format("MAIL FROM: <{0}>", origin), 250)) + throw new Exception(String.Format("Invalid mail from reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + + for (Int32 i = 0; i < destination.Length; i++) + if (!this.SendAndWaitForResponse(String.Format("RCPT TO: <{0}>", destination[i]), 250)) + throw new Exception(String.Format("Invalid rcpt to reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + + if (!this.SendAndWaitForResponse("DATA", 354)) + throw new Exception(String.Format("Invalid data reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + + MemoryStream lStr = new MemoryStream(); + + message.EncodeMessage(lStr); + lStr.Position = 0; + lStr.WriteTo(this.CurrentConnection); + if (!this.SendAndWaitForResponse(".", 250)) + throw new Exception(String.Format("Invalid data reply: {0} {1}", this.LastResponseNo, this.LastResponseText)); + } + + private static void EnlistMailAddresses(IList destination, MessageAddresses addresses) + { + for (Int32 i = 0; i < addresses.Count; i++) + { + String lAddress = addresses[i].Address; + + if (String.IsNullOrEmpty(lAddress) || destination.Contains(lAddress)) + continue; + + destination.Add(lAddress); + } + } + + public void SendMessage(MailMessage message) + { + List lAddresses = new List(); + + SmtpClient.EnlistMailAddresses(lAddresses, message.To); + SmtpClient.EnlistMailAddresses(lAddresses, message.Cc); + SmtpClient.EnlistMailAddresses(lAddresses, message.Bcc); + + this.SendMessage(message, message.From.Address, lAddresses.ToArray()); + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/SslConnection.cs b/Source/RemObjects.InternetPack/SslConnection.cs new file mode 100644 index 0000000..2ffec5b --- /dev/null +++ b/Source/RemObjects.InternetPack/SslConnection.cs @@ -0,0 +1,724 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.ComponentModel; +using System.IO; +using System.Net.Security; +using System.Net.Sockets; +using System.Reflection; +using System.Security; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace RemObjects.InternetPack +{ + public class SslValidateCertificateArgs : EventArgs + { + public SslValidateCertificateArgs(X509Certificate certificate) + { + this.fCertificate = certificate; + this.Cancel = false; + } + + public X509Certificate Certificate + { + get + { + return this.fCertificate; + } + } + private X509Certificate fCertificate; + + public Boolean Cancel + { + get + { + return this.fCancel; + } + set + { + this.fCancel = value; + } + } + private Boolean fCancel; + } + + public class SslNeedPasswordArgs : EventArgs + { + public SecureString Password + { + get + { + return this.fPassword; + } + set + { + this.fPassword = value; + } + } + private SecureString fPassword; + + public String PasswordString + { + get + { + return null; + } + set + { + if (value == null) + { + this.fPassword = null; + return; + } + + this.fPassword = new SecureString(); + for (Int32 i = 0; i < value.Length; i++) + this.fPassword.AppendChar(value[i]); + } + } + } + + public class SslConnectionFactory : IConnectionFactory + { + [DefaultValue(""), Category("Ssl Options")] + public String TargetHostName + { + get + { + return this.fTargetHostName; + } + set + { + this.fTargetHostName = value; + } + } + private String fTargetHostName; + + [DefaultValue(false), Category("Ssl Options")] + public Boolean UseMono + { + get + { + return this.fUseMono; + } + set + { + this.fUseMono = value; + } + } + private Boolean fUseMono; + + [DefaultValue(false), Category("Ssl Options")] + public Boolean Enabled + { + get + { + return this.fEnabled; + } + set + { + fEnabled = value; + } + } + private Boolean fEnabled; + + [DefaultValue(null), Category("Ssl Options")] + public String CertificateFileName + { + get + { + return this.fCertificateFileName; + } + set + { + this.fCertificateFileName = value; + } + } + private String fCertificateFileName; + + [Category("Ssl Options")] + public event EventHandler NeedPassword; + + protected virtual void OnNeedPassword(SslNeedPasswordArgs e) + { + if (this.NeedPassword != null) + this.NeedPassword(this, e); + } + + [Browsable(false)] + public X509Certificate2 Certificate + { + get + { + return this.fCertificate; + } + set + { + this.fCertificate = value; + } + } + private X509Certificate2 fCertificate; + + [Browsable(false)] + public Boolean HasCertificate + { + get + { + return this.fCertificate != null || !String.IsNullOrEmpty(this.fCertificateFileName); + } + } + + protected void LoadCertificate() + { + lock (this) + { + if (this.Certificate != null) + return; + + if (String.IsNullOrEmpty(this.CertificateFileName)) + throw new Exception("Certificate not set and CertificateFileName is empty"); + + SslNeedPasswordArgs lArgs = new SslNeedPasswordArgs(); + this.OnNeedPassword(lArgs); + + this.Certificate = new X509Certificate2(this.CertificateFileName, lArgs.Password, X509KeyStorageFlags.Exportable); + } + } + + private Assembly fMonoAssembly; + + public Assembly GetMonoAssembly() + { + if (!this.UseMono) + return null; + + if (this.fMonoAssembly != null) + return this.fMonoAssembly; + + try + { + this.fMonoAssembly = Assembly.Load("Mono.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756"); + } + catch (Exception) + { + this.fUseMono = false; + } + + return this.fMonoAssembly; + } + + [Category("Ssl Options")] + public event EventHandler ValidateRemoteCertificate; + + protected virtual void OnValidateRemoteCertificate(SslValidateCertificateArgs e) + { + if (this.ValidateRemoteCertificate != null) + this.ValidateRemoteCertificate(this, e); + } + + public Boolean OnValidateRemoteCertificate(X509Certificate certificate) + { + SslValidateCertificateArgs lEventArgs = new SslValidateCertificateArgs(certificate); + this.OnValidateRemoteCertificate(lEventArgs); + + return (!lEventArgs.Cancel); + } + + #region IConnectionFactory Members + public virtual Connection CreateServerConnection(Socket socket) + { + if (!this.Enabled) + return new Connection(socket); + + if (this.Certificate == null) + this.LoadCertificate(); + + return new SslConnection(this, socket); + } + + public virtual Connection CreateClientConnection(Binding binding) + { + if (!this.Enabled) + return new Connection(binding); + + if (this.HasCertificate) + this.LoadCertificate(); + + return new SslConnection(this, binding); + } + + public virtual Connection CreateClientConnection(Connection connection) + { + if (this.HasCertificate) + this.LoadCertificate(); + + return new SslConnection(this, connection); + } + #endregion + } + + public class SslConnection : Connection + { + protected class InnerConnection : Connection + { + public InnerConnection(Socket socket) + : base(socket) + { + } + + public InnerConnection(Binding binding) + : base(binding) + { + } + + public override Int32 Read(Byte[] buffer, Int32 offset, Int32 size) + { + return this.ReceiveWhatsAvailable(buffer, offset, size); + } + } + + private SslConnectionFactory fFactory; + private Connection fInnerConnection; + private Stream fSsl; + + public SslConnection(SslConnectionFactory factory, Binding binding) + : base((Socket)null) + { + fFactory = factory; + fInnerConnection = new InnerConnection(binding); + fInnerConnection.BufferedAsync = false; + fInnerConnection.AsyncDisconnect += new EventHandler(InnerConnection_AsyncDisconnect); + fInnerConnection.AsyncHaveIncompleteData += new EventHandler(InnerConnection_AsyncHaveIncompleteData); + } + + public SslConnection(SslConnectionFactory factory, Socket socket) + : base((Socket)null) + { + fFactory = factory; + fInnerConnection = new InnerConnection(socket); + fInnerConnection.BufferedAsync = false; + fInnerConnection.AsyncDisconnect += new EventHandler(InnerConnection_AsyncDisconnect); + fInnerConnection.AsyncHaveIncompleteData += new EventHandler(InnerConnection_AsyncHaveIncompleteData); + } + + public SslConnection(SslConnectionFactory factory, Connection connection) + : base((Socket)null) + { + fFactory = factory; + fInnerConnection = connection; + fInnerConnection.BufferedAsync = false; + fInnerConnection.AsyncDisconnect += new EventHandler(InnerConnection_AsyncDisconnect); + fInnerConnection.AsyncHaveIncompleteData += new EventHandler(InnerConnection_AsyncHaveIncompleteData); + } + + public override Int32 DataSocketAvailable + { + get + { + return fInnerConnection.DataSocketAvailable; + } + } + + public override Socket DataSocket + { + get + { + return this.fInnerConnection.DataSocket; + } + } + + public override Boolean DataSocketConnected + { + get + { + return fInnerConnection.Connected; + } + } + + public override Boolean EnableNagle + { + get + { + return fInnerConnection.EnableNagle; + } + set + { + fInnerConnection.EnableNagle = value; + } + } + + public override Boolean Secure + { + get + { + return true; + } + } + + private Boolean Ssl_RemoteCertificateValidation(Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) + { + return this.fFactory.OnValidateRemoteCertificate(certificate); + } + + private AsymmetricAlgorithm MonoSsl_GetPrivateKey(X509Certificate certificate, String targetHost) + { + return this.fFactory.Certificate.PrivateKey; + } + + private Boolean MonoSsl_RemoteCertificateValidation(X509Certificate certificate, Int32[] errors) + { + return fFactory.OnValidateRemoteCertificate(certificate); + } + + private void CreateMonoServerStream() + { + Type lType = this.fFactory.GetMonoAssembly().GetType("Mono.Security.Protocol.Tls.SslServerStream", true); + this.fSsl = (Stream)Activator.CreateInstance(lType, fInnerConnection, this.fFactory.Certificate, false, false); + + Object o = Delegate.CreateDelegate(fFactory.GetMonoAssembly().GetType("Mono.Security.Protocol.Tls.PrivateKeySelectionCallback", true), this, "MonoSsl_GetPrivateKey", false, true); + lType.InvokeMember("set_PrivateKeyCertSelectionDelegate", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, this.fSsl, new Object[] { o }); + + o = Delegate.CreateDelegate(fFactory.GetMonoAssembly().GetType("Mono.Security.Protocol.Tls.CertificateValidationCallback", true), this, "MonoSsl_RemoteCertificateValidation", false, true); + lType.InvokeMember("set_ClientCertValidationDelegate", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, this.fSsl, new Object[] { o }); + } + + protected internal override void InitializeServerConnection() + { + if (this.fFactory.GetMonoAssembly() != null) + { + this.CreateMonoServerStream(); + } + else + { + this.fSsl = new SslStream(this.fInnerConnection, true, new RemoteCertificateValidationCallback(Ssl_RemoteCertificateValidation)); + ((SslStream)this.fSsl).AuthenticateAsServer(this.fFactory.Certificate); + } + } + + internal protected override IAsyncResult BeginInitializeServerConnection(AsyncCallback callback, Object state) + { + if (this.fFactory.UseMono) + { + this.CreateMonoServerStream(); + return null; + } + + this.fSsl = new SslStream(this.fInnerConnection, true, new RemoteCertificateValidationCallback(Ssl_RemoteCertificateValidation)); + return ((SslStream)this.fSsl).BeginAuthenticateAsServer(this.fFactory.Certificate, callback, state); + } + + protected internal override void EndInitializeServerConnection(IAsyncResult ar) + { + if (!this.fFactory.UseMono) + ((SslStream)this.fSsl).EndAuthenticateAsServer(ar); + } + + private void CreateMonoClientStream() + { + Type lType = fFactory.GetMonoAssembly().GetType("Mono.Security.Protocol.Tls.SslClientStream", true); + this.fSsl = (Stream)Activator.CreateInstance(lType, this.fInnerConnection, this.fFactory.TargetHostName, false); + + Object o = Delegate.CreateDelegate(fFactory.GetMonoAssembly().GetType("Mono.Security.Protocol.Tls.PrivateKeySelectionCallback", true), this, "MonoSsl_GetPrivateKey", false, true); + lType.InvokeMember("set_PrivateKeyCertSelectionDelegate", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, this.fSsl, new Object[] { o }); + + o = Delegate.CreateDelegate(fFactory.GetMonoAssembly().GetType("Mono.Security.Protocol.Tls.CertificateValidationCallback", true), this, "MonoSsl_RemoteCertificateValidation", false, true); + lType.InvokeMember("set_ServerCertValidationDelegate", BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, this.fSsl, new Object[] { o }); + } + + public override void Connect(System.Net.EndPoint endPoint) + { + this.fInnerConnection.Connect(endPoint); + + this.InitializeClientConnection(); + } + + public virtual void InitializeClientConnection() + { + if (this.fFactory.GetMonoAssembly() != null) + { + this.CreateMonoClientStream(); + } + else + { + this.fSsl = new SslStream(this.fInnerConnection, true, new RemoteCertificateValidationCallback(this.Ssl_RemoteCertificateValidation)); + ((SslStream)this.fSsl).AuthenticateAsClient(this.fFactory.TargetHostName); + } + } + + public virtual IAsyncResult BeginInitializeClientConnection(AsyncCallback callback, Object state) + { + if (this.fFactory.GetMonoAssembly() != null) + { + this.CreateMonoClientStream(); + return null; + } + + this.fSsl = new SslStream(this.fInnerConnection, true, new RemoteCertificateValidationCallback(Ssl_RemoteCertificateValidation)); + return ((SslStream)this.fSsl).BeginAuthenticateAsClient(this.fFactory.TargetHostName, callback, state); + } + + public void EndInitializeClientConnection(IAsyncResult ar) + { + if (this.fFactory.GetMonoAssembly() == null) + ((SslStream)this.fSsl).EndAuthenticateAsClient(ar); + } + + public override void Connect(System.Net.IPAddress address, Int32 port) + { + this.Connect(new System.Net.IPEndPoint(address, port)); + } + + private class ConnectWrapper : IAsyncResult + { + public ConnectWrapper(AsyncCallback callback, Object state) + { + this.fAsyncState = state; + this.fCallback = callback; + } + + private AsyncCallback fCallback; + private volatile Boolean fComplete; + + public Object AsyncState + { + get + { + return this.fAsyncState; + } + set + { + this.fAsyncState = value; + } + } + private Object fAsyncState; + + public System.Threading.WaitHandle AsyncWaitHandle + { + get + { + if (this.fWaitHandle != null) + return this.fWaitHandle; + + lock (this) + { + if (this.fWaitHandle == null) + this.fWaitHandle = new System.Threading.ManualResetEvent(fComplete); + } + + return this.fWaitHandle; + } + } + private volatile System.Threading.ManualResetEvent fWaitHandle; + + public Boolean CompletedSynchronously + { + get + { + return false; + } + } + + public Boolean IsCompleted + { + get + { + return this.fComplete; + } + } + + public void Dispose() + { + if (this.fWaitHandle != null) + this.fWaitHandle.Close(); + + this.fWaitHandle = null; + } + + public void ConnectionConnect(IAsyncResult ar) + { + SslConnection lOwner = (SslConnection)ar.AsyncState; + try + { + lOwner.fInnerConnection.EndConnect(ar); + } + catch (Exception ex) + { + this.fFailure = ex; + this.fComplete = true; + lock (this) + { + if (this.fWaitHandle != null) + this.fWaitHandle.Set(); + } + this.fCallback(ar); + + return; + } + + if (lOwner.fFactory.GetMonoAssembly() != null) + { + lOwner.CreateMonoClientStream(); + fComplete = true; + lock (this) + { + if (this.fWaitHandle != null) + this.fWaitHandle.Set(); + } + this.fCallback(ar); + + return; + } + + lOwner.fSsl = new SslStream(lOwner.fInnerConnection, true, new RemoteCertificateValidationCallback(lOwner.Ssl_RemoteCertificateValidation)); + ((SslStream)lOwner.fSsl).BeginAuthenticateAsClient(lOwner.fFactory.TargetHostName, new AsyncCallback(SslAuthenticateAsClient), lOwner); + } + + private void SslAuthenticateAsClient(IAsyncResult ar) + { + SslConnection lOwner = (SslConnection)ar.AsyncState; + try + { + ((SslStream)lOwner.fSsl).EndAuthenticateAsClient(ar); + } + catch (Exception ex) + { + this.fFailure = ex; + this.fComplete = true; + lock (this) + { + if (this.fWaitHandle != null) + this.fWaitHandle.Set(); + } + this.fCallback(ar); + return; + } + + lOwner.CreateMonoClientStream(); + this.fComplete = true; + lock (this) + { + if (this.fWaitHandle != null) + this.fWaitHandle.Set(); + } + this.fCallback(ar); + return; + } + + private Exception fFailure; + public void EndConnect() + { + if (!fComplete) + AsyncWaitHandle.WaitOne(); + + this.Dispose(); + if (this.fFailure != null) + throw this.fFailure; + } + } + + public override IAsyncResult BeginConnect(System.Net.EndPoint endPoint, AsyncCallback callback, Object state) + { + ConnectWrapper lWrapper = new ConnectWrapper(callback, state); + this.fInnerConnection.BeginConnect(endPoint, new AsyncCallback(lWrapper.ConnectionConnect), this); + + return lWrapper; + } + + public override IAsyncResult BeginConnect(System.Net.IPAddress address, Int32 port, AsyncCallback callback, Object state) + { + return this.BeginConnect(new System.Net.IPEndPoint(address, port), callback, state); + } + + public override void EndConnect(IAsyncResult ar) + { + ((ConnectWrapper)ar).EndConnect(); + } + + protected override void DataSocketClose() + { + this.fInnerConnection.Close(); + } + + protected override void DataSocketClose(Boolean dispose) + { + this.fInnerConnection.Close(dispose); + } + + void InnerConnection_AsyncHaveIncompleteData(Object sender, EventArgs e) + { + this.TriggerAsyncHaveIncompleteData(); + } + + void InnerConnection_AsyncDisconnect(Object sender, EventArgs e) + { + this.TriggerAsyncDisconnect(); + } + + protected override Int32 DataSocketReceiveWhatsAvaiable(Byte[] buffer, Int32 offset, Int32 size) + { + try + { + return this.fSsl.Read(buffer, offset, size); + } + catch (IOException) + { + throw new SocketException(); + } + } + + protected override Int32 DataSocketSendAsMuchAsPossible(Byte[] buffer, Int32 offset, Int32 size) + { + try + { + this.fSsl.Write(buffer, offset, size); + return size; + } + catch (IOException) + { + throw new SocketException(); + } + } + + protected override IAsyncResult IntBeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) + { + return this.fSsl.BeginRead(buffer, offset, count, callback, state); + } + + protected override Int32 IntEndRead(IAsyncResult ar) + { + try + { + return this.fSsl.EndRead(ar); + } + catch (IOException) + { + throw new SocketException(); + } + } + + protected override IAsyncResult IntBeginWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state) + { + return this.fSsl.BeginWrite(buffer, offset, count, callback, state); + } + + protected override void IntEndWrite(IAsyncResult ar) + { + try + { + this.fSsl.EndWrite(ar); + } + catch (IOException) + { + // SocketException is expected in all code that deals with Connection, not IOException + throw new SocketException(); + } + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/TcpClient.cs b/Source/RemObjects.InternetPack/TcpClient.cs new file mode 100644 index 0000000..60d4dc1 --- /dev/null +++ b/Source/RemObjects.InternetPack/TcpClient.cs @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +namespace RemObjects.InternetPack +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.TcpClient.bmp")] +#endif + public class TcpClient : Client + { + public TcpClient() + : base() + { + } + } +} diff --git a/Source/RemObjects.InternetPack/TcpServer.cs b/Source/RemObjects.InternetPack/TcpServer.cs new file mode 100644 index 0000000..9063f6d --- /dev/null +++ b/Source/RemObjects.InternetPack/TcpServer.cs @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Net.Sockets; +using System.ComponentModel; + +namespace RemObjects.InternetPack +{ +#if DESIGN + [System.Drawing.ToolboxBitmap(typeof(RemObjects.InternetPack.Server), "Glyphs.TcpServer.bmp")] +#endif + public class TcpServer : Server + { + public TcpServer() + : base() + { + } + +#if FULLFRAMEWORK + public TcpServer(IContainer container) + : this() + { + container.Add(this); + } +#endif + + public override Type GetWorkerClass() + { + return typeof(TcpWorker); + } + + #region Events + public event OnTcpConnectionHandler OnTcpConnection; + + protected virtual void TriggerOnTcpConnection(Connection connection) + { + if (this.OnTcpConnection != null) + { + OnTcpConnectionArgs lEventArgs = new OnTcpConnectionArgs(connection); + this.OnTcpConnection(this, lEventArgs); + } + } + #endregion + + #region Methods for implementation in descendand classes + internal protected virtual void HandleTcpConnection(Connection connection) + { + this.TriggerOnTcpConnection(connection); + } + #endregion + } + + public class TcpWorker : Worker + { + public TcpWorker() + { + } + + public new TcpServer Owner + { + get + { + return (TcpServer)base.Owner; + } + } + + protected override void DoWork() + { + try + { + Owner.HandleTcpConnection(DataConnection); + } + catch (SocketException ex) + { + /* 10054 means the connection was closed by the client while reading from the socket. + * we'll just terminate the thread gracefully, as if this was expected. */ + /* ToDo: provide some sort of notification to the server object via event. */ + if (ex.ErrorCode != 10054) + throw ex; + } + catch (Exception) + { + } + finally + { + DataConnection.Close(); + } + } + } + + #region Events + public delegate void OnTcpConnectionHandler(Object sender, OnTcpConnectionArgs e); + + public class OnTcpConnectionArgs : ConnectionEventArgs + { + public OnTcpConnectionArgs(Connection connection) + : base(connection) + { + } + } + #endregion +} diff --git a/Source/RemObjects.InternetPack/UrlParser.cs b/Source/RemObjects.InternetPack/UrlParser.cs new file mode 100644 index 0000000..443d9cf --- /dev/null +++ b/Source/RemObjects.InternetPack/UrlParser.cs @@ -0,0 +1,333 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; + +namespace RemObjects.InternetPack.Http +{ + public static class KnownProtocols + { + static KnownProtocols() + { + fProtocols = new Dictionary(8); + fProtocols.Add("ftp", 21); + fProtocols.Add("ssh", 22); + fProtocols.Add("telnet", 23); + fProtocols.Add("smtp", 25); + fProtocols.Add("http", 80); + fProtocols.Add("pop3", 110); + fProtocols.Add("https", 443); + } + + private static IDictionary fProtocols; + + public static void RegisterProtocol(String name, Int32 port) + { + if (fProtocols.ContainsKey(name)) + throw new Exception(String.Format("Protocol '{0}' alraedy registered.", name)); + fProtocols.Add(name, port); + } + + public static Int32 GetProtocolDefaultPort(String protocol) + { + if (fProtocols.ContainsKey(protocol)) + return (Int32)fProtocols[protocol]; + + return -1; + } + } + +#if FULLFRAMEWORK + [TypeConverter(typeof(UrlConverter))] +#endif + [Serializable] + public class UrlParser : MarshalByRefObject + { + public UrlParser() + { + this.fProtocol = DEFAULT_PROTOCOL; + this.fDefaultPort = KnownProtocols.GetProtocolDefaultPort(fProtocol); + this.fPort = fDefaultPort; + } + + public UrlParser(String url) + { + this.Parse(url); + } + + #region Properties + public String Url + { + get + { + return String.Format("{0}://{1}{2}", this.Protocol, this.HostnameAndPort, this.PathAndParams); + } + set + { + this.Parse(value); + } + } + + [Browsable(false)] + public String PathAndParams + { + get + { + if (this.Params != null && this.Params.Length != 0) + return this.Path + "?" + this.Params; + + return this.Path; + } + } + + [Browsable(false)] + public String HostnameAndPort + { + get + { + if (this.Port != this.fDefaultPort) + return this.Hostname + ":" + this.Port.ToString(); + + return this.Hostname; + } + } + + public String Protocol + { + get + { + return this.fProtocol; + } + set + { + this.fProtocol = value; + + if (this.fPort == this.fDefaultPort) + this.fPort = KnownProtocols.GetProtocolDefaultPort(this.fProtocol); + + this.fDefaultPort = KnownProtocols.GetProtocolDefaultPort(this.fProtocol); + } + } + private String fProtocol; + + public String Hostname + { + get + { + return this.fHostname; + } + set + { + this.fHostname = value; + } + } + private String fHostname; + + public Int32 Port + { + get + { + return this.fPort; + } + set + { + this.fPort = value; + } + } + private Int32 fPort; + private Int32 fDefaultPort; + + public String Path + { + get + { + return this.fPath; + } + set + { + if (String.IsNullOrEmpty(value)) + this.fPath = "/"; + else + this.fPath = value; + } + } + private String fPath; + + public String Params + { + get + { + return this.fParams; + } + set + { + this.fParams = value; + } + } + private String fParams; + #endregion + + public override String ToString() + { + return this.Url; + } + + const String DEFAULT_PROTOCOL = "http"; + + public void Parse(String url) + { + Int32 lProtocolPosition = url.IndexOf("://"); + if (lProtocolPosition >= 0) + { + this.Protocol = url.Substring(0, lProtocolPosition); + url = url.Substring(lProtocolPosition + 3); /* skip over :// */ + } + else + { + this.Protocol = DEFAULT_PROTOCOL; + } + + String lHostAndPort; + lProtocolPosition = url.IndexOf('/'); + if (lProtocolPosition == -1) + lProtocolPosition = url.IndexOf('?'); + + if (lProtocolPosition >= 0) + { + lHostAndPort = url.Substring(0, lProtocolPosition); + url = url.Substring(lProtocolPosition); + } + else + { + lHostAndPort = url; + url = ""; + } + + if (lHostAndPort.StartsWith("[")) + { + lProtocolPosition = lHostAndPort.IndexOf(']'); + + if (lProtocolPosition > 0) + { + this.Hostname = lHostAndPort.Substring(1, lProtocolPosition - 1); + String lRest = lHostAndPort.Substring(lProtocolPosition + 1).Trim(); + if (lRest.StartsWith(":")) + { + String lPort = lRest.Substring(1); + try + { + this.Port = Int32.Parse(lPort); + } + catch (FormatException ex) + { + throw new UrlParserException(String.Format("Invalid Port specification '{0}'", lPort), ex); + } + } + } + else + { + throw new UrlParserException(String.Format("Invalid IPv6 host name specification '{0}'", lHostAndPort)); + } + } + else + { + lProtocolPosition = lHostAndPort.IndexOf(':'); + + if (lProtocolPosition >= 0) + { + this.Hostname = lHostAndPort.Substring(0, lProtocolPosition); + String lPort = lHostAndPort.Substring(lProtocolPosition + 1); + try + { + this.Port = Int32.Parse(lPort); + } + catch (FormatException ex) + { + throw new UrlParserException(String.Format("Invalid Port specification '{0}'", lPort), ex); + } + } + else + { + this.Hostname = lHostAndPort; + this.Port = fDefaultPort; + } + } + + lProtocolPosition = url.IndexOf('?'); + if (lProtocolPosition >= 0) + { + this.Path = url.Substring(0, lProtocolPosition); + this.Params = url.Substring(lProtocolPosition + 1); + } + else + { + if (url.Length == 0) + url = "/"; + this.Path = url; + this.Params = null; + } + } + } + + #region UrlConverter class +#if FULLFRAMEWORK + class UrlConverter : ExpandableObjectConverter + { + public UrlConverter() + { + } + + public override Boolean CanConvertFrom(ITypeDescriptorContext context, Type type) + { + if (type == typeof(String)) + return true; + + return base.CanConvertFrom(context, type); + } + + public override Object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value) + { + if (value is String) + return new UrlParser(value as String); + + return base.ConvertFrom(context, culture, value); + } + + public override Object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, Object value, Type destinationType) + { + if (destinationType == typeof(String) && value is UrlParser) + return ((UrlParser)value).ToString(); + + return base.ConvertTo(context, culture, value, destinationType); + } + } +#endif + #endregion + + [Serializable] + public class UrlParserException : Exception + { + public UrlParserException() + : base() + { + } + + public UrlParserException(String message) + : base(message) + { + } + + public UrlParserException(String message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/Source/RemObjects.InternetPack/Worker.cs b/Source/RemObjects.InternetPack/Worker.cs new file mode 100644 index 0000000..dcacbce --- /dev/null +++ b/Source/RemObjects.InternetPack/Worker.cs @@ -0,0 +1,137 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, LLC. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Threading; + +namespace RemObjects.InternetPack +{ + public abstract class AsyncWorker : IAsyncWorker + { + public AsyncServer AsyncOwner + { + get + { + return this.fOwner; + } + set + { + this.fOwner = value; + } + } + private AsyncServer fOwner; + + public Server Owner + { + get + { + return this.fOwner; + } + set + { + this.fOwner = (AsyncServer)value; + } + } + + public Connection DataConnection + { + get + { + return this.fDataConnection; + } + set + { + this.fDataConnection = value; + } + } + private Connection fDataConnection; + + public void Setup() + { + this.DoSetup(); + } + + public virtual void Done() + { + if (this.fOwner != null) + this.fOwner.ClientClosed(this); + + if (this.DataConnection != null) + this.DataConnection.Dispose(); + } + + protected abstract void DoSetup(); + } + + public abstract class Worker : IWorker + { + protected Worker() + { + } + + public Connection DataConnection + { + get + { + return this.fDataConnection; + } + set + { + this.fDataConnection = value; + } + } + private Connection fDataConnection; + + public Thread Thread + { + get + { + return this.fThread; + } + set + { + this.fThread = value; + } + } + public Thread fThread; + + public Server Owner + { + get + { + return this.fOwner; + } + set + { + this.fOwner = value; + } + } + private Server fOwner; + + protected abstract void DoWork(); + + public event EventHandler Done; + + public void Work() + { + try + { + this.DoWork(); + this.DataConnection.Close(false); + } + catch + { + } + finally + { + if (this.Done != null) + this.Done(this, EventArgs.Empty); + } + } + } +} \ No newline at end of file diff --git a/Source/RemObjects.InternetPack/WorkerCollection.cs b/Source/RemObjects.InternetPack/WorkerCollection.cs new file mode 100644 index 0000000..92c619e --- /dev/null +++ b/Source/RemObjects.InternetPack/WorkerCollection.cs @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------- + RemObjects Internet Pack for .NET - Core Library + (c)opyright RemObjects Software, Inc. 2003-2012. All rights reserved. + + Using this code requires a valid license of the RemObjects Internet Pack + which can be obtained at http://www.remobjects.com?ip. +---------------------------------------------------------------------------*/ + +using System; +using System.Collections; + +namespace RemObjects.InternetPack +{ + public class WorkerCollection : ArrayList + { + private void OnDone(Object sender, EventArgs e) + { + lock (this) + { + base.Remove(sender); + } + } + + public void Add(IWorker worker) + { + worker.Done += new EventHandler(OnDone); + base.Add(worker); + } + + public void Remove(IWorker worker) + { + worker.Done -= new EventHandler(OnDone); + base.Remove(worker); + } + + public void Close() + { +#if FULLFRAMEWORK + Object[] lWaitArray; +#endif + lock (this) + { + foreach (IWorker worker in this) + worker.DataConnection.Close(); + +#if FULLFRAMEWORK + lWaitArray = new Object[Count]; + this.CopyTo(lWaitArray); +#endif + } + +#if FULLFRAMEWORK + foreach (IWorker worker in lWaitArray) + worker.Thread.Join(); +#endif + } + } +}