From ba02d10db750eba9dfc99c742b7e704dad0a0899 Mon Sep 17 00:00:00 2001 From: Bert Johnson Date: Wed, 14 Aug 2013 16:50:57 -0500 Subject: [PATCH] 1.5.0 release, featuring revamped settings wizard and various minor enhancements. --- .gitattributes | 63 + .gitignore | 156 + .../Properties/AssemblyInfo.cs | 72 +- OpaqueMail.Net.Proxy/EnumsAndStructs.cs | 580 +- OpaqueMail.Net.Proxy/IMAP/ImapProxy.cs | 1437 ++-- OpaqueMail.Net.Proxy/POP3/Pop3Proxy.cs | 1271 ++-- .../Properties/AssemblyInfo.cs | 72 +- OpaqueMail.Net.Proxy/ProxyFunctions.cs | 626 +- OpaqueMail.Net.Proxy/SMTP/SmtpProxy.cs | 1807 ++--- .../OpaqueMail.Net.ProxyInstaller.isl | 5889 +++++++++++++++++ .../OpaqueMail.Net.ProxyInstaller.isproj | 30 + OpaqueMail.Net.ProxyService/Program.cs | 50 +- .../Properties/AssemblyInfo.cs | 72 +- OpaqueMail.Net.ProxyService/ProxyService.cs | 498 +- .../Form1.Designer.cs | 754 ++- OpaqueMail.Net.ProxySettings/Form1.cs | 3178 +++++---- OpaqueMail.Net.ProxySettings/Form1.resx | 546 +- .../OpaqueMail.Net.ProxySettings.csproj | 254 +- .../OpaqueMail.Proxy.Sample.xml | 250 +- OpaqueMail.Net.ProxySettings/Program.cs | 44 +- .../Properties/AssemblyInfo.cs | 72 +- .../Signature.Resources/OpaqueMail.htm | 745 +++ .../Signature.Resources/OpaqueMail.rtf | 195 + .../Signature.Resources/OpaqueMail.txt | Bin 0 -> 268 bytes .../colorschememapping.xml | 2 + .../Signature.Resources/filelist.xml | 6 + .../Signature.Resources/themedata.thmx | Bin 0 -> 3134 bytes OpaqueMail.Net.ProxySettings/Windows8Mail.cs | 268 + OpaqueMail.Net.TestClient/Form1.cs | 2151 +++--- OpaqueMail.Net.TestClient/Form1.resx | 430 +- .../Properties/AssemblyInfo.cs | 72 +- OpaqueMail.Net/Functions.cs | 2786 ++++---- OpaqueMail.Net/MailMessage.cs | 437 +- OpaqueMail.Net/Properties/AssemblyInfo.cs | 72 +- OpaqueMail.Net/ReadOnlyMailMessage.cs | 1718 ++--- OpaqueMail.Net/SMTP/SmtpClient.cs | 1312 ++-- 36 files changed, 17944 insertions(+), 9971 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 OpaqueMail.Net.ProxyInstaller/OpaqueMail.Net.ProxyInstaller.isl create mode 100644 OpaqueMail.Net.ProxyInstaller/OpaqueMail.Net.ProxyInstaller.isproj create mode 100644 OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.htm create mode 100644 OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.rtf create mode 100644 OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.txt create mode 100644 OpaqueMail.Net.ProxySettings/Signature.Resources/colorschememapping.xml create mode 100644 OpaqueMail.Net.ProxySettings/Signature.Resources/filelist.xml create mode 100644 OpaqueMail.Net.ProxySettings/Signature.Resources/themedata.thmx create mode 100644 OpaqueMail.Net.ProxySettings/Windows8Mail.cs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bc915c --- /dev/null +++ b/.gitignore @@ -0,0 +1,156 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store diff --git a/OpaqueMail.Net.CertHelper/Properties/AssemblyInfo.cs b/OpaqueMail.Net.CertHelper/Properties/AssemblyInfo.cs index 9f101b7..f0caa67 100644 --- a/OpaqueMail.Net.CertHelper/Properties/AssemblyInfo.cs +++ b/OpaqueMail.Net.CertHelper/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpaqueMail.Net.CertHelper")] -[assembly: AssemblyDescription(".NET helper library for interacting with X509 certificates.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Bert Johnson")] -[assembly: AssemblyProduct("OpaqueMail")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("41516709-605d-41da-9e9e-7e1e36ad944b")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.2")] -[assembly: AssemblyFileVersion("1.4.2.2")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpaqueMail.Net.CertHelper")] +[assembly: AssemblyDescription(".NET helper library for interacting with X509 certificates.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Bert Johnson")] +[assembly: AssemblyProduct("OpaqueMail")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("41516709-605d-41da-9e9e-7e1e36ad944b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.5.0")] +[assembly: AssemblyFileVersion("1.5.0.0")] diff --git a/OpaqueMail.Net.Proxy/EnumsAndStructs.cs b/OpaqueMail.Net.Proxy/EnumsAndStructs.cs index 60765d5..ee91cde 100644 --- a/OpaqueMail.Net.Proxy/EnumsAndStructs.cs +++ b/OpaqueMail.Net.Proxy/EnumsAndStructs.cs @@ -1,288 +1,292 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; - -namespace OpaqueMail.Net.Proxy -{ - /// - /// Arguments passed in when instantiating a new IMAP proxy instance. - /// - public struct ImapProxyArguments - { - /// IP addresses to accept connections from. - public string AcceptedIPs; - /// Certificate to authenticate the server. - public X509Certificate Certificate; - /// Local IP address to bind to. - public IPAddress LocalIpAddress; - /// Local IP address to listen on. - public int LocalPort; - /// Whether the local server supports TLS/SSL. - public bool LocalEnableSsl; - /// Remote server hostname to forward all IMAP messages to. - public string RemoteServerHostName; - /// Remote server port to connect to. - public int RemoteServerPort; - /// Whether the remote IMAP server requires TLS/SSL. - public bool RemoteServerEnableSsl; - /// (Optional) Credentials to be used for all connections to the remote IMAP server. When set, this overrides any credentials passed locally. - public NetworkCredential RemoteServerCredential; - - /// The file where events and exception information should be logged. - public string LogFile; - /// Proxy logging level, determining how much information is logged. - public LogLevel LogLevel; - - /// The instance number of the proxy. - public int InstanceId; - - /// IMAP Proxy to start. - public ImapProxy Proxy; - } - - /// - /// Arguments passed in when instantiating a new IMAP proxy connection instance. - /// - public struct ImapProxyConnectionArguments - { - /// IP addresses to accept connections from. - public string AcceptedIPs; - /// TCP connection to the client. - public TcpClient TcpClient; - /// Certificate to authenticate the server. - public X509Certificate Certificate; - /// Local IP address to bind to. - public IPAddress LocalIpAddress; - /// Local IP address to listen on. - public int LocalPort; - /// Whether the local server supports TLS/SSL. - public bool LocalEnableSsl; - /// Remote server hostname to forward all IMAP messages to. - public string RemoteServerHostName; - /// Remote server port to connect to. - public int RemoteServerPort; - /// Whether the remote IMAP server requires TLS/SSL. - public bool RemoteServerEnableSsl; - /// (Optional) Credentials to be used for all connections to the remote IMAP server. When set, this overrides any credentials passed locally. - public NetworkCredential RemoteServerCredential; - - /// A unique connection identifier for logging. - public string ConnectionId; - } - - /// - /// Proxy logging level, determining how much information is logged. - /// - public enum LogLevel - { - None = 0, // No logging. - Critical = 1, // Only critical errors, such as the inability to start a proxy. - Error = 2, // All errors, including unexpected disconnections. - Warning = 4, // Errors and warnings, such as rejected IPs. - Information = 8, // Errors, warnings, and other information, such as starting and stopping messages. - Verbose = 16, // All of the above, plus individual commands. - Raw = 32 // All of the above, plus the raw client and server bytes. - // Warning: sensitive information such as credentials will be saved with Raw logging. - } - - /// - /// Arguments passed in when instantiating a new POP3 proxy instance. - /// - public struct Pop3ProxyArguments - { - /// IP addresses to accept connections from. - public string AcceptedIPs; - /// Certificate to authenticate the server. - public X509Certificate Certificate; - /// Local IP address to bind to. - public IPAddress LocalIpAddress; - /// Local IP address to listen on. - public int LocalPort; - /// Whether the local server supports TLS/SSL. - public bool LocalEnableSsl; - /// Remote server hostname to forward all POP3 messages to. - public string RemoteServerHostName; - /// Remote server port to connect to. - public int RemoteServerPort; - /// Whether the remote POP3 server requires TLS/SSL. - public bool RemoteServerEnableSsl; - /// (Optional) Credentials to be used for all connections to the remote POP3 server. When set, this overrides any credentials passed locally. - public NetworkCredential RemoteServerCredential; - - /// The file where events and exception information should be logged. - public string LogFile; - /// Proxy logging level, determining how much information is logged. - public LogLevel LogLevel; - - /// The instance number of the proxy. - public int InstanceId; - - /// POP3 Proxy to start. - public Pop3Proxy Proxy; - } - - /// - /// Arguments passed in when instantiating a new POP3 proxy connection instance. - /// - public struct Pop3ProxyConnectionArguments - { - /// IP addresses to accept connections from. - public string AcceptedIPs; - /// TCP connection to the client. - public TcpClient TcpClient; - /// Certificate to authenticate the server. - public X509Certificate Certificate; - /// Local IP address to bind to. - public IPAddress LocalIpAddress; - /// Local IP address to listen on. - public int LocalPort; - /// Whether the local server supports TLS/SSL. - public bool LocalEnableSsl; - /// Remote server hostname to forward all POP3 messages to. - public string RemoteServerHostName; - /// Remote server port to connect to. - public int RemoteServerPort; - /// Whether the remote POP3 server requires TLS/SSL. - public bool RemoteServerEnableSsl; - /// (Optional) Credentials to be used for all connections to the remote POP3 server. When set, this overrides any credentials passed locally. - public NetworkCredential RemoteServerCredential; - - /// A unique connection identifier for logging. - public string ConnectionId; - } - - /// - /// Arguments passed when processing a message. - /// - public struct ProcessMessageArguments - { - /// The text of the message to process. - public string MessageText; - - /// A unique connection identifier for logging. - public string ConnectionId; - } - - /// - /// Arguments passed in when instantiating a new SMTP proxy instance. - /// - public struct SmtpProxyArguments - { - /// IP addresses to accept connections from. - public string AcceptedIPs; - /// Certificate to authenticate the server. - public X509Certificate Certificate; - /// Local IP address to bind to. - public IPAddress LocalIpAddress; - /// Local IP address to listen on. - public int LocalPort; - /// Whether the local server supports TLS/SSL. - public bool LocalEnableSsl; - /// Remote server hostname to forward all SMTP messages to. - public string RemoteServerHostName; - /// Remote server port to connect to. - public int RemoteServerPort; - /// Whether the remote SMTP server requires TLS/SSL. - public bool RemoteServerEnableSsl; - /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. - public NetworkCredential RemoteServerCredential; - - /// Encrypt the e-mail's envelope. When SmimeSign is true, encryption is the second S/MIME operation. - public bool SmimeEncryptedEnvelope; - /// Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied. - public bool SmimeRemovePreviousOperations; - /// Whether S/MIME settings for encryption and signing are explicitly required or only preferred. - public SmimeSettingsMode SmimeSettingsMode; - /// Sign the e-mail. When true, signing is the first S/MIME operation. - public bool SmimeSigned; - /// Triple-wrap the e-mail by signing, then encrypting the envelope, then signing the encrypted envelope. - public bool SmimeTripleWrapped; - - /// The file where events and exception information should be logged. - public string LogFile; - /// Proxy logging level, determining how much information is logged. - public LogLevel LogLevel; - - /// The instance number of the proxy. - public int InstanceId; - - /// Send e-mail reminders when a signing certificate is due to expire within 30 days. - public bool SendCertificateReminders; - - /// SMTP Proxy to start. - public SmtpProxy Proxy; - } - - /// - /// Arguments passed in when instantiating a new SMTP proxy connection instance. - /// - public struct SmtpProxyConnectionArguments - { - /// IP addresses to accept connections from. - public string AcceptedIPs; - /// TCP connection to the client. - public TcpClient TcpClient; - /// Certificate to authenticate the server. - public X509Certificate Certificate; - /// Local IP address to bind to. - public IPAddress LocalIpAddress; - /// Local IP address to listen on. - public int LocalPort; - /// Whether the local server supports TLS/SSL. - public bool LocalEnableSsl; - /// Remote server hostname to forward all SMTP messages to. - public string RemoteServerHostName; - /// Remote server port to connect to. - public int RemoteServerPort; - /// Whether the remote SMTP server requires TLS/SSL. - public bool RemoteServerEnableSsl; - /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. - public NetworkCredential RemoteServerCredential; - - /// Encrypt the e-mail's envelope. When SmimeSign is true, encryption is the second S/MIME operation. - public bool SmimeEncryptedEnvelope; - /// Whether S/MIME settings for encryption and signing are explicitly required or only preferred. - public SmimeSettingsMode SmimeSettingsMode; - /// Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied. - public bool SmimeRemovePreviousOperations; - /// Sign the e-mail. When true, signing is the first S/MIME operation. - public bool SmimeSigned; - /// Triple-wrap the e-mail by signing, then encrypting the envelope, then signing the encrypted envelope. - public bool SmimeTripleWrapped; - - /// Send e-mail reminders when a signing certificate is due to expire within 30 days. - public bool SendCertificateReminders; - - /// A unique connection identifier for logging. - public string ConnectionId; - } - - /// - /// Arguments passed when relaying commands between two connections. - /// - public struct TransmitArguments - { - /// Stream to read commands from. - public Stream ClientStream; - /// Stream to rebroadcast commands to. - public Stream RemoteServerStream; - - /// Whether the target of this invocation is the client. - public bool IsClient; - - /// A unique connection identifier for logging. - public string ConnectionId; - - /// IP address of client. - public string IPAddress; - - /// (Optional) Credentials to be used for all connections to the remote server. When set, this overrides any credentials passed locally. - public NetworkCredential Credential; - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; + +namespace OpaqueMail.Net.Proxy +{ + /// + /// Arguments passed in when instantiating a new IMAP proxy instance. + /// + public struct ImapProxyArguments + { + /// IP addresses to accept connections from. + public string AcceptedIPs; + /// Certificate to authenticate the server. + public X509Certificate Certificate; + /// Local IP address to bind to. + public IPAddress LocalIpAddress; + /// Local IP address to listen on. + public int LocalPort; + /// Whether the local server supports TLS/SSL. + public bool LocalEnableSsl; + /// Remote server hostname to forward all IMAP messages to. + public string RemoteServerHostName; + /// Remote server port to connect to. + public int RemoteServerPort; + /// Whether the remote IMAP server requires TLS/SSL. + public bool RemoteServerEnableSsl; + /// (Optional) Credentials to be used for all connections to the remote IMAP server. When set, this overrides any credentials passed locally. + public NetworkCredential RemoteServerCredential; + + /// The file where events and exception information should be logged. + public string LogFile; + /// Proxy logging level, determining how much information is logged. + public LogLevel LogLevel; + + /// The instance number of the proxy. + public int InstanceId; + + /// IMAP Proxy to start. + public ImapProxy Proxy; + } + + /// + /// Arguments passed in when instantiating a new IMAP proxy connection instance. + /// + public struct ImapProxyConnectionArguments + { + /// IP addresses to accept connections from. + public string AcceptedIPs; + /// TCP connection to the client. + public TcpClient TcpClient; + /// Certificate to authenticate the server. + public X509Certificate Certificate; + /// Local IP address to bind to. + public IPAddress LocalIpAddress; + /// Local IP address to listen on. + public int LocalPort; + /// Whether the local server supports TLS/SSL. + public bool LocalEnableSsl; + /// Remote server hostname to forward all IMAP messages to. + public string RemoteServerHostName; + /// Remote server port to connect to. + public int RemoteServerPort; + /// Whether the remote IMAP server requires TLS/SSL. + public bool RemoteServerEnableSsl; + /// (Optional) Credentials to be used for all connections to the remote IMAP server. When set, this overrides any credentials passed locally. + public NetworkCredential RemoteServerCredential; + + /// A unique connection identifier for logging. + public string ConnectionId; + } + + /// + /// Proxy logging level, determining how much information is logged. + /// + public enum LogLevel + { + None = 0, // No logging. + Critical = 1, // Only critical errors, such as the inability to start a proxy. + Error = 2, // All errors, including unexpected disconnections. + Warning = 4, // Errors and warnings, such as rejected IPs. + Information = 8, // Errors, warnings, and other information, such as starting and stopping messages. + Verbose = 16, // All of the above, plus individual commands. + Raw = 32 // All of the above, plus the raw client and server bytes. + // Warning: sensitive information such as credentials will be saved with Raw logging. + } + + /// + /// Arguments passed in when instantiating a new POP3 proxy instance. + /// + public struct Pop3ProxyArguments + { + /// IP addresses to accept connections from. + public string AcceptedIPs; + /// Certificate to authenticate the server. + public X509Certificate Certificate; + /// Local IP address to bind to. + public IPAddress LocalIpAddress; + /// Local IP address to listen on. + public int LocalPort; + /// Whether the local server supports TLS/SSL. + public bool LocalEnableSsl; + /// Remote server hostname to forward all POP3 messages to. + public string RemoteServerHostName; + /// Remote server port to connect to. + public int RemoteServerPort; + /// Whether the remote POP3 server requires TLS/SSL. + public bool RemoteServerEnableSsl; + /// (Optional) Credentials to be used for all connections to the remote POP3 server. When set, this overrides any credentials passed locally. + public NetworkCredential RemoteServerCredential; + + /// The file where events and exception information should be logged. + public string LogFile; + /// Proxy logging level, determining how much information is logged. + public LogLevel LogLevel; + + /// The instance number of the proxy. + public int InstanceId; + + /// POP3 Proxy to start. + public Pop3Proxy Proxy; + } + + /// + /// Arguments passed in when instantiating a new POP3 proxy connection instance. + /// + public struct Pop3ProxyConnectionArguments + { + /// IP addresses to accept connections from. + public string AcceptedIPs; + /// TCP connection to the client. + public TcpClient TcpClient; + /// Certificate to authenticate the server. + public X509Certificate Certificate; + /// Local IP address to bind to. + public IPAddress LocalIpAddress; + /// Local IP address to listen on. + public int LocalPort; + /// Whether the local server supports TLS/SSL. + public bool LocalEnableSsl; + /// Remote server hostname to forward all POP3 messages to. + public string RemoteServerHostName; + /// Remote server port to connect to. + public int RemoteServerPort; + /// Whether the remote POP3 server requires TLS/SSL. + public bool RemoteServerEnableSsl; + /// (Optional) Credentials to be used for all connections to the remote POP3 server. When set, this overrides any credentials passed locally. + public NetworkCredential RemoteServerCredential; + + /// A unique connection identifier for logging. + public string ConnectionId; + } + + /// + /// Arguments passed when processing a message. + /// + public struct ProcessMessageArguments + { + /// The text of the message to process. + public string MessageText; + + /// A unique connection identifier for logging. + public string ConnectionId; + } + + /// + /// Arguments passed in when instantiating a new SMTP proxy instance. + /// + public struct SmtpProxyArguments + { + /// IP addresses to accept connections from. + public string AcceptedIPs; + /// Certificate to authenticate the server. + public X509Certificate Certificate; + /// Local IP address to bind to. + public IPAddress LocalIpAddress; + /// Local IP address to listen on. + public int LocalPort; + /// Whether the local server supports TLS/SSL. + public bool LocalEnableSsl; + /// Remote server hostname to forward all SMTP messages to. + public string RemoteServerHostName; + /// Remote server port to connect to. + public int RemoteServerPort; + /// Whether the remote SMTP server requires TLS/SSL. + public bool RemoteServerEnableSsl; + /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. + public NetworkCredential RemoteServerCredential; + /// (Optional) "From" address for all sent messages. When supplied, it will override any values sent from the client. + public string RemoteServerFrom; + + /// Encrypt the e-mail's envelope. When SmimeSign is true, encryption is the second S/MIME operation. + public bool SmimeEncryptedEnvelope; + /// Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied. + public bool SmimeRemovePreviousOperations; + /// Whether S/MIME settings for encryption and signing are explicitly required or only preferred. + public SmimeSettingsMode SmimeSettingsMode; + /// Sign the e-mail. When true, signing is the first S/MIME operation. + public bool SmimeSigned; + /// Triple-wrap the e-mail by signing, then encrypting the envelope, then signing the encrypted envelope. + public bool SmimeTripleWrapped; + + /// The file where events and exception information should be logged. + public string LogFile; + /// Proxy logging level, determining how much information is logged. + public LogLevel LogLevel; + + /// The instance number of the proxy. + public int InstanceId; + + /// Send e-mail reminders when a signing certificate is due to expire within 30 days. + public bool SendCertificateReminders; + + /// SMTP Proxy to start. + public SmtpProxy Proxy; + } + + /// + /// Arguments passed in when instantiating a new SMTP proxy connection instance. + /// + public struct SmtpProxyConnectionArguments + { + /// IP addresses to accept connections from. + public string AcceptedIPs; + /// TCP connection to the client. + public TcpClient TcpClient; + /// Certificate to authenticate the server. + public X509Certificate Certificate; + /// Local IP address to bind to. + public IPAddress LocalIpAddress; + /// Local IP address to listen on. + public int LocalPort; + /// Whether the local server supports TLS/SSL. + public bool LocalEnableSsl; + /// Remote server hostname to forward all SMTP messages to. + public string RemoteServerHostName; + /// Remote server port to connect to. + public int RemoteServerPort; + /// Whether the remote SMTP server requires TLS/SSL. + public bool RemoteServerEnableSsl; + /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. + public NetworkCredential RemoteServerCredential; + /// (Optional) "From" address for all sent messages. When supplied, it will override any values sent from the client. + public string RemoteServerFrom; + + /// Encrypt the e-mail's envelope. When SmimeSign is true, encryption is the second S/MIME operation. + public bool SmimeEncryptedEnvelope; + /// Whether S/MIME settings for encryption and signing are explicitly required or only preferred. + public SmimeSettingsMode SmimeSettingsMode; + /// Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied. + public bool SmimeRemovePreviousOperations; + /// Sign the e-mail. When true, signing is the first S/MIME operation. + public bool SmimeSigned; + /// Triple-wrap the e-mail by signing, then encrypting the envelope, then signing the encrypted envelope. + public bool SmimeTripleWrapped; + + /// Send e-mail reminders when a signing certificate is due to expire within 30 days. + public bool SendCertificateReminders; + + /// A unique connection identifier for logging. + public string ConnectionId; + } + + /// + /// Arguments passed when relaying commands between two connections. + /// + public struct TransmitArguments + { + /// Stream to read commands from. + public Stream ClientStream; + /// Stream to rebroadcast commands to. + public Stream RemoteServerStream; + + /// Whether the target of this invocation is the client. + public bool IsClient; + + /// A unique connection identifier for logging. + public string ConnectionId; + + /// IP address of client. + public string IPAddress; + + /// (Optional) Credentials to be used for all connections to the remote server. When set, this overrides any credentials passed locally. + public NetworkCredential Credential; + } +} diff --git a/OpaqueMail.Net.Proxy/IMAP/ImapProxy.cs b/OpaqueMail.Net.Proxy/IMAP/ImapProxy.cs index bfb762a..48533b8 100644 --- a/OpaqueMail.Net.Proxy/IMAP/ImapProxy.cs +++ b/OpaqueMail.Net.Proxy/IMAP/ImapProxy.cs @@ -1,717 +1,720 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml.XPath; - -namespace OpaqueMail.Net.Proxy -{ - public class ImapProxy : ProxyBase - { - #region Public Methods - /// - /// Start a IMAP proxy instance. - /// - /// IP addresses to accept connections from. - /// Local IP address to bind to. - /// Local port to listen on. - /// Whether the local server supports TLS/SSL. - /// Remote server hostname to forward all IMAP messages to. - /// Remote server port to connect to. - /// Whether the remote IMAP server requires TLS/SSL. - public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl) - { - Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, null, "", LogLevel.None, 0); - } - - /// - /// Start a IMAP proxy instance. - /// - /// IP addresses to accept connections from. - /// Local IP address to bind to. - /// Local port to listen on. - /// Whether the local server supports TLS/SSL. - /// Remote server hostname to forward all IMAP messages to. - /// Remote server port to connect to. - /// Whether the remote IMAP server requires TLS/SSL. - /// (Optional) Credentials to be used for all connections to the remote IMAP server. When set, this overrides any credentials passed locally. - /// File where event logs and exception information will be written. - /// Proxy logging level, determining how much information is logged. - /// The instance number of the proxy. - public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string logFile, LogLevel logLevel, int instanceId) - { - // Create the log writer. - string logFileName = ""; - if (!string.IsNullOrEmpty(logFile)) - { - logFileName = ProxyFunctions.GetLogFileName(logFile, instanceId); - LogWriter = new StreamWriter(logFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); - LogLevel = logLevel; - } - - // Make sure the remote server isn't an infinite loop back to this server. - string fqdn = Functions.GetLocalFQDN(); - if (remoteServerHostName.ToUpper() == fqdn.ToUpper() && remoteServerPort == localPort) - { - ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server host name {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); - return; - } - IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); - foreach (IPAddress hostIP in hostEntry.AddressList) - { - if (remoteServerHostName == hostIP.ToString() && remoteServerPort == localPort) - { - ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server hostname {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); - return; - } - } - - ProxyFunctions.Log(LogWriter, SessionId, "Starting service.", Proxy.LogLevel.Information, LogLevel); - - // Attempt to start up to 3 times in case another service using the port is shutting down. - int startAttempts = 0; - while (startAttempts < 3) - { - startAttempts++; - - // If we've failed to start once, wait an extra 10 seconds. - if (startAttempts > 1) - { - ProxyFunctions.Log(LogWriter, SessionId, "Attempting to start for the " + (startAttempts == 2 ? "2nd" : "3rd") + " time.", Proxy.LogLevel.Information, LogLevel); - Thread.Sleep(10000 * startAttempts); - } - - try - { - X509Certificate serverCertificate = null; - - // Generate a unique session ID for logging. - SessionId = Guid.NewGuid().ToString(); - ConnectionId = 0; - - // If local SSL is supported via STARTTLS, ensure we have a valid server certificate. - if (localEnableSsl) - { - serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, fqdn); - // In case the service as running as the current user, check the Current User certificate store as well. - if (serverCertificate == null) - serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, fqdn); - - // If no certificate was found, generate a self-signed certificate. - if (serverCertificate == null) - { - ProxyFunctions.Log(LogWriter, SessionId, "No signing certificate found, so generating new certificate.", Proxy.LogLevel.Warning, LogLevel); - - List oids = new List(); - oids.Add("1.3.6.1.5.5.7.3.1"); // Server Authentication. - - // Generate the certificate with a duration of 10 years, 4096-bits, and a key usage of server authentication. - serverCertificate = CertHelper.CreateSelfSignedCertificate(fqdn, fqdn, true, 4096, 10, oids); - - ProxyFunctions.Log(LogWriter, SessionId, "Certificate generated with Serial Number {" + serverCertificate.GetSerialNumberString() + "}.", Proxy.LogLevel.Information, LogLevel); - } - } - - Listener = new TcpListener(localIPAddress, localPort); - Listener.Start(); - - ProxyFunctions.Log(LogWriter, SessionId, "Service started.", Proxy.LogLevel.Information, LogLevel); - ProxyFunctions.Log(LogWriter, SessionId, "Listening on address {" + localIPAddress.ToString() + "}, port {" + localPort + "}.", Proxy.LogLevel.Information, LogLevel); - - Started = true; - - // Accept client requests, forking each into its own thread. - while (Started) - { - TcpClient client = Listener.AcceptTcpClient(); - - string newLogFileName = ProxyFunctions.GetLogFileName(logFile, instanceId); - if (newLogFileName != logFileName) - { - LogWriter.Close(); - LogWriter = new StreamWriter(newLogFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); - } - - // Prepare the arguments for our new thread. - ImapProxyConnectionArguments arguments = new ImapProxyConnectionArguments(); - arguments.AcceptedIPs = acceptedIPs; - arguments.TcpClient = client; - arguments.Certificate = serverCertificate; - arguments.LocalIpAddress = localIPAddress; - arguments.LocalPort = localPort; - arguments.LocalEnableSsl = localEnableSsl; - arguments.RemoteServerHostName = remoteServerHostName; - arguments.RemoteServerPort = remoteServerPort; - arguments.RemoteServerEnableSsl = remoteServerEnableSsl; - arguments.RemoteServerCredential = remoteServerCredential; - - // Increment the connection counter; - arguments.ConnectionId = (unchecked(++ConnectionId)).ToString(); - - // Fork the thread and continue listening for new connections. - Thread processThread = new Thread(new ParameterizedThreadStart(ProcessConnection)); - processThread.Name = "OpaqueMail IMAP Proxy Connection"; - processThread.Start(arguments); - } - return; - } - catch (Exception ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception when starting proxy: " + ex.Message, Proxy.LogLevel.Critical, LogLevel); - } - } - } - - /// - /// Stop the IMAP proxy and close all existing connections. - /// - public void Stop() - { - ProxyFunctions.Log(LogWriter, SessionId, "Stopping service.", Proxy.LogLevel.Information, LogLevel); - - Started = false; - - if (Listener != null) - Listener.Stop(); - - ProxyFunctions.Log(LogWriter, SessionId, "Service stopped.", Proxy.LogLevel.Information, LogLevel); - } - - /// - /// Start all IMAP proxy instances from the specified settings file. - /// - /// File containing the IMAP proxy settings. - public static List StartProxiesFromSettingsFile(string fileName) - { - List imapProxies = new List(); - - try - { - if (File.Exists(fileName)) - { - XPathDocument document = new XPathDocument(fileName); - XPathNavigator navigator = document.CreateNavigator(); - - int imapServiceCount = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/IMAP/ServiceCount"); - for (int i = 1; i <= imapServiceCount; i++) - { - ImapProxyArguments arguments = new ImapProxyArguments(); - arguments.AcceptedIPs = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/AcceptedIPs"); - - string localIpAddress = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/LocalIPAddress").ToUpper(); - switch (localIpAddress) - { - // Treat blank values as "Any". - case "": - case "ANY": - arguments.LocalIpAddress = IPAddress.Any; - break; - case "BROADCAST": - arguments.LocalIpAddress = IPAddress.Broadcast; - break; - case "IPV6ANY": - arguments.LocalIpAddress = IPAddress.IPv6Any; - break; - case "IPV6LOOPBACK": - arguments.LocalIpAddress = IPAddress.IPv6Loopback; - break; - case "LOOPBACK": - arguments.LocalIpAddress = IPAddress.Loopback; - break; - default: - // Try to parse the local IP address. If unable to, proceed to the next service instance. - if (!IPAddress.TryParse(localIpAddress, out arguments.LocalIpAddress)) - continue; - break; - } - - arguments.LocalPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/LocalPort"); - // If the port is invalid, proceed to the next service instance. - if (arguments.LocalPort < 1) - continue; - - arguments.LocalEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/IMAP/Service" + i + "/LocalEnableSSL"); - - arguments.RemoteServerHostName = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerHostName"); - // If the host name is invalid, proceed to the next service instance. - if (string.IsNullOrEmpty(arguments.RemoteServerHostName)) - continue; - - arguments.RemoteServerPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerPort"); - // If the port is invalid, proceed to the next service instance. - if (arguments.RemoteServerPort < 1) - continue; - - arguments.RemoteServerEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerEnableSSL"); - - string remoteServerUsername = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerUsername"); - if (!string.IsNullOrEmpty(remoteServerUsername)) - { - arguments.RemoteServerCredential = new NetworkCredential(); - arguments.RemoteServerCredential.UserName = remoteServerUsername; - arguments.RemoteServerCredential.Password = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerPassword"); - } - - string certificateLocationValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/Location"); - StoreLocation certificateLocation = StoreLocation.LocalMachine; - if (certificateLocationValue.ToUpper() == "CURRENTUSER") - certificateLocation = StoreLocation.CurrentUser; - - // Try to load the signing certificate based on its serial number first, then fallback to its subject name. - string certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/SerialNumber"); - if (!string.IsNullOrEmpty(certificateValue)) - arguments.Certificate = CertHelper.GetCertificateBySerialNumber(certificateLocation, certificateValue); - else - { - certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/SubjectName"); - if (!string.IsNullOrEmpty(certificateValue)) - arguments.Certificate = CertHelper.GetCertificateBySubjectName(certificateLocation, certificateValue); - } - - arguments.LogFile = ProxyFunctions.GetXmlStringValue(navigator, "Settings/IMAP/Service" + i + "/LogFile"); - - string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/IMAP/Service" + i + "/LogLevel"); - switch (logLevel.ToUpper()) - { - case "NONE": - arguments.LogLevel = LogLevel.None; - break; - case "CRITICAL": - arguments.LogLevel = LogLevel.Critical; - break; - case "ERROR": - arguments.LogLevel = LogLevel.Error; - break; - case "RAW": - arguments.LogLevel = LogLevel.Raw; - break; - case "VERBOSE": - arguments.LogLevel = LogLevel.Verbose; - break; - case "WARNING": - arguments.LogLevel = LogLevel.Warning; - break; - case "INFORMATION": - default: - arguments.LogLevel = LogLevel.Information; - break; - } - - arguments.InstanceId = i; - - // Remember the proxy in order to close it when the service stops. - arguments.Proxy = new ImapProxy(); - imapProxies.Add(arguments.Proxy); - - Thread proxyThread = new Thread(new ParameterizedThreadStart(StartProxy)); - proxyThread.Name = "OpaqueMail IMAP Proxy"; - proxyThread.Start(arguments); - } - } - } - catch - { - // Ignore errors if the XML settings file is malformed. - } - - return imapProxies; - } - #endregion Public Methods - - #region Private Methods - /// - /// Handle an incoming IMAP connection, from connection to completion. - /// - /// ImapProxyConnectionArguments object containing all parameters for this connection. - private void ProcessConnection(object parameters) - { - // Cast the passed-in parameters back to their original objects. - ImapProxyConnectionArguments arguments = (ImapProxyConnectionArguments)parameters; - - try - { - TcpClient client = arguments.TcpClient; - Stream clientStream = client.GetStream(); - - // Capture the client's IP information. - PropertyInfo pi = clientStream.GetType().GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance); - string ip = ((Socket)pi.GetValue(clientStream, null)).RemoteEndPoint.ToString(); - if (ip.IndexOf(":") > -1) - ip = ip.Substring(0, ip.IndexOf(":")); - - // If the IP address range filter contains the localhost entry 0.0.0.0, check if the client IP is a local address and update it to 0.0.0.0 if so. - if (arguments.AcceptedIPs.IndexOf("0.0.0.0") > -1) - { - if (ip == "127.0.0.1") - ip = "0.0.0.0"; - else - { - IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); - foreach (IPAddress hostIP in hostEntry.AddressList) - { - if (hostIP.ToString() == ip) - { - ip = "0.0.0.0"; - break; - } - } - } - } - - // Validate that the IP address is within an accepted range. - if (!ProxyFunctions.ValidateIP(arguments.AcceptedIPs, ip)) - { - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection rejected from {" + ip + "} due to its IP address.", Proxy.LogLevel.Warning, LogLevel); - - Functions.SendStreamString(clientStream, new byte[Constants.SMALLBUFFERSIZE], "500 IP address [" + ip + "] rejected.\r\n"); - - if (clientStream != null) - clientStream.Dispose(); - if (client != null) - client.Close(); - - return; - } - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "New connection established from {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); - - // If supported, upgrade the session's security through a TLS handshake. - if (arguments.LocalEnableSsl) - { - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Starting local TLS/SSL protection for {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); - clientStream = new SslStream(clientStream); - ((SslStream)clientStream).AuthenticateAsServer(arguments.Certificate); - } - - // Connect to the remote server. - TcpClient remoteServerClient = new TcpClient(arguments.RemoteServerHostName, arguments.RemoteServerPort); - Stream remoteServerStream = remoteServerClient.GetStream(); - - // If supported, upgrade the session's security through a TLS handshake. - if (arguments.RemoteServerEnableSsl) - { - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Starting remote TLS/SSL protection with {" + arguments.RemoteServerHostName + "}.", Proxy.LogLevel.Information, LogLevel); - remoteServerStream = new SslStream(remoteServerStream); - ((SslStream)remoteServerStream).AuthenticateAsClient(arguments.RemoteServerHostName); - } - - // Relay server data to the client. - TransmitArguments remoteServerToClientArguments = new TransmitArguments(); - remoteServerToClientArguments.ClientStream = remoteServerStream; - remoteServerToClientArguments.RemoteServerStream = clientStream; - remoteServerToClientArguments.IsClient = false; - remoteServerToClientArguments.ConnectionId = ConnectionId.ToString(); - remoteServerToClientArguments.IPAddress = ip; - Thread remoteServerToClientThread = new Thread(new ParameterizedThreadStart(RelayData)); - remoteServerToClientThread.Name = "OpaqueMail IMAP Proxy Server to Client"; - remoteServerToClientThread.Start(remoteServerToClientArguments); - - // Relay client data to the remote server. - TransmitArguments clientToRemoteServerArguments = new TransmitArguments(); - clientToRemoteServerArguments.ClientStream = clientStream; - clientToRemoteServerArguments.RemoteServerStream = remoteServerStream; - clientToRemoteServerArguments.IsClient = true; - clientToRemoteServerArguments.ConnectionId = ConnectionId.ToString(); - clientToRemoteServerArguments.IPAddress = ip; - clientToRemoteServerArguments.Credential = arguments.RemoteServerCredential; - Thread clientToRemoteServerThread = new Thread(new ParameterizedThreadStart(RelayData)); - clientToRemoteServerThread.Name = "OpaqueMail IMAP Proxy Client to Server"; - clientToRemoteServerThread.Start(clientToRemoteServerArguments); - } - catch (SocketException ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception communicating with {" + arguments.RemoteServerHostName + "} on port {" + arguments.RemoteServerPort + "}: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - catch (Exception ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - } - - /// - /// Relay data read from one connection to another. - /// - /// A TransmitArguments object containing local and remote server parameters. - private async void RelayData(object o) - { - // Cast the passed-in parameters back to their original objects. - TransmitArguments arguments = (TransmitArguments)o; - Stream clientStream = arguments.ClientStream; - Stream remoteServerStream = arguments.RemoteServerStream; - - // A byte array to streamline bit shuffling. - char[] buffer = new char[Constants.SMALLBUFFERSIZE]; - - // Placeholder variables to track the current message being transmitted. - bool inMessage = false; - int messageLength = 0; - StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE); - - // The overall number of bytes transmitted on this connection. - ulong bytesTransmitted = 0; - int appendLength = 0, appendBytesTransmitted = 0; - - // When a "[THROTTLED]" notice was last received. - DateTime lastThrottleTime = new DateTime(1900, 1, 1); - - bool stillReceiving = true; - try - { - using (StreamReader clientStreamReader = new StreamReader(clientStream)) - { - using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream)) - { - remoteServerStreamWriter.AutoFlush = true; - - while (Started && stillReceiving) - { - // Read data from the source and send it to its destination. - int bytesRead = await clientStreamReader.ReadAsync(buffer, 0, Constants.SMALLBUFFERSIZE); - - if (bytesRead > 0) - { - // Cast the bytes received to a string. - string stringRead = new string(buffer, 0, bytesRead); - bytesTransmitted += (ulong)bytesRead; - - // If this data comes from the client, log it. Otherwise, process it. - if (arguments.IsClient) - { - bool messageRelayed = false; - - string[] commandParts = stringRead.Split(new char[] { ' ' }, 4); - if (commandParts.Length > 2) - { - // If we're in an APPEND command, wait until we've completed the append before logging another command. - if (appendLength > 0) - { - appendBytesTransmitted += stringRead.Length; - if (appendBytesTransmitted >= appendLength) - LastCommandReceived = ""; - } - else - { - // Optionally replace credentials with those from our settings file. - if (arguments.Credential != null && commandParts[1] == "LOGIN" && commandParts.Length == 4) - { - await remoteServerStreamWriter.WriteAsync(commandParts[0] + " " + commandParts[1] + " " + arguments.Credential.UserName + " " + arguments.Credential.Password + "\r\n"); - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + commandParts[0] + " " + commandParts[1] + " " + arguments.Credential.UserName + " " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel); - - messageRelayed = true; - } - - if (commandParts[1] == "UID") - LastCommandReceived = commandParts[1] + " " + commandParts[2]; - else - LastCommandReceived = commandParts[1]; - - if (LogLevel == Proxy.LogLevel.Verbose) - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + LastCommandReceived + "} processed.", Proxy.LogLevel.Verbose, LogLevel); - - // If we're in an APPEND command, remember how many bytes we should receive. - if (LastCommandReceived == "APPEND") - { - appendLength = 0; - int.TryParse(Functions.ReturnBetween(stringRead, "{", "}"), out appendLength); - appendBytesTransmitted = stringRead.Length - stringRead.IndexOf("\r\n") - 2; - } - } - } - - if (!messageRelayed) - { - await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel); - } - } - else - { - await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel); - - // If we're currently receiving a message, check to see if it's completed. - if (inMessage) - { - messageBuilder.Append(stringRead); - if (messageBuilder.Length >= messageLength) - { - // If the message has been completed and it contains a signature, process it. - string message = messageBuilder.ToString(0, messageLength); - if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1) - { - Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); - processThread.Name = "OpaqueMail IMAP Proxy Signature Processor"; - ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); - processMessageArguments.MessageText = message; - processMessageArguments.ConnectionId = ConnectionId.ToString(); - processThread.Start(processMessageArguments); - } - - // We're no longer receiving a message, so continue. - inMessage = false; - stringRead = messageBuilder.ToString(messageLength, messageBuilder.Length - messageLength); - messageBuilder.Clear(); - } - } - - if (!inMessage) - { - if (stringRead.IndexOf("[THROTTLED]\r\n", StringComparison.Ordinal) > -1) - { - if (DateTime.Now - lastThrottleTime >= new TimeSpan(0, 20, 0)) - { - ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), "Connection speed throttled by the remote server.", Proxy.LogLevel.Warning, LogLevel); - lastThrottleTime = DateTime.Now; - } - } - - int pos = 0; - while (pos > -1) - { - // Messages are denoted by FETCH headers with their lengths in curly braces. - pos = stringRead.IndexOf(" FETCH ", pos); - if (pos > -1) - { - int openBrace = stringRead.IndexOf("{", pos); - int lineBreak = stringRead.IndexOf("\r", pos); - if (lineBreak < -1) - lineBreak = stringRead.Length + 1; - - if (openBrace > -1 && openBrace < lineBreak) - { - int closeBrace = stringRead.IndexOf("}", openBrace); - if (closeBrace > -1) - { - // Only proceed if we can parse the size of the message. - if (int.TryParse(stringRead.Substring(openBrace + 1, closeBrace - openBrace - 1), out messageLength)) - { - int messageBytesRead = stringRead.Length - closeBrace - 3; - - if (messageBytesRead > messageLength) - { - string message = stringRead.Substring(closeBrace + 3, messageLength); - if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1) - { - Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); - processThread.Name = "OpaqueMail IMAP Proxy Signature Processor"; - ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); - processMessageArguments.MessageText = message; - processMessageArguments.ConnectionId = ConnectionId.ToString(); - processThread.Start(processMessageArguments); - } - pos = closeBrace + 3 + messageLength; - } - else - { - inMessage = true; - messageBuilder.Clear(); - if (stringRead.Length > closeBrace + 3) - messageBuilder.Append(stringRead.Substring(closeBrace + 3)); - pos = -1; - } - } - else - pos = -1; - } - else - pos = -1; - } - else - pos = -1; - } - } - } - } - } - else - stillReceiving = false; - } - } - } - } -/* catch (IOException) - { - // Ignore either stream being closed. - }*/ - catch (ObjectDisposedException) - { - // Ignore either stream being closed. - } - catch (Exception ex) - { - // Log other exceptions. - ProxyFunctions.Log(LogWriter, SessionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - finally - { - // If sending to the local client, log the connection being closed. - if (!arguments.IsClient) - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); - - if (clientStream != null) - clientStream.Dispose(); - if (remoteServerStream != null) - remoteServerStream.Dispose(); - } - } - - /// - /// Process a transmitted message to import any signing certificates for subsequent S/MIME encryption. - /// - /// A ProcessMessageArguments object containing message parameters. - private void ProcessMessage(object o) - { - ProcessMessageArguments arguments = (ProcessMessageArguments)o; - - // Only parse the message if it contains a known S/MIME content type. - string canonicalMessageText = arguments.MessageText.ToLower(); - if (canonicalMessageText.IndexOf("application/x-pkcs7-signature") > -1 || canonicalMessageText.IndexOf("application/pkcs7-mime") > -1) - { - try - { - // Parse the message. - ReadOnlyMailMessage message = new ReadOnlyMailMessage(arguments.MessageText); - - // If the message contains a signing certificate that we haven't processed on this session, import it. - if (message.SmimeSigningCertificate != null && !SmimeCertificatesReceived.Contains(message.SmimeSigningCertificate)) - { - // Import the certificate to the Local Machine store. - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Importing certificate with Serial Number {" + message.SmimeSigningCertificate.SerialNumber + "}.", Proxy.LogLevel.Information, LogLevel); - CertHelper.InstallWindowsCertificate(message.SmimeSigningCertificate, StoreLocation.LocalMachine); - - // Remember this ceriticate to avoid importing it again this session. - SmimeCertificatesReceived.Add(message.SmimeSigningCertificate); - } - } - catch (Exception ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - } - } - - /// - /// Start an individual IMAP proxy on its own thread. - /// - /// ImapProxyArguments object containing all parameters for this connection. - private static void StartProxy(object parameters) - { - ImapProxyArguments arguments = (ImapProxyArguments)parameters; - - // Start the proxy using passed-in settings. - arguments.Proxy.Start(arguments.AcceptedIPs, arguments.LocalIpAddress, arguments.LocalPort, arguments.LocalEnableSsl, arguments.RemoteServerHostName, arguments.RemoteServerPort, arguments.RemoteServerEnableSsl, arguments.RemoteServerCredential, arguments.LogFile, arguments.LogLevel, arguments.InstanceId); - } - #endregion Private Methods - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.XPath; + +namespace OpaqueMail.Net.Proxy +{ + public class ImapProxy : ProxyBase + { + #region Public Methods + /// + /// Start a IMAP proxy instance. + /// + /// IP addresses to accept connections from. + /// Local IP address to bind to. + /// Local port to listen on. + /// Whether the local server supports TLS/SSL. + /// Remote server hostname to forward all IMAP messages to. + /// Remote server port to connect to. + /// Whether the remote IMAP server requires TLS/SSL. + public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl) + { + Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, null, "", LogLevel.None, 0); + } + + /// + /// Start a IMAP proxy instance. + /// + /// IP addresses to accept connections from. + /// Local IP address to bind to. + /// Local port to listen on. + /// Whether the local server supports TLS/SSL. + /// Remote server hostname to forward all IMAP messages to. + /// Remote server port to connect to. + /// Whether the remote IMAP server requires TLS/SSL. + /// (Optional) Credentials to be used for all connections to the remote IMAP server. When set, this overrides any credentials passed locally. + /// File where event logs and exception information will be written. + /// Proxy logging level, determining how much information is logged. + /// The instance number of the proxy. + public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string logFile, LogLevel logLevel, int instanceId) + { + // Create the log writer. + string logFileName = ""; + if (!string.IsNullOrEmpty(logFile)) + { + logFileName = ProxyFunctions.GetLogFileName(logFile, instanceId, localIPAddress.ToString(), remoteServerHostName, localPort, remoteServerPort); + LogWriter = new StreamWriter(logFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); + LogWriter.AutoFlush = true; + + LogLevel = logLevel; + } + + // Make sure the remote server isn't an infinite loop back to this server. + string fqdn = Functions.GetLocalFQDN(); + if (remoteServerHostName.ToUpper() == fqdn.ToUpper() && remoteServerPort == localPort) + { + ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server host name {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); + return; + } + IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress hostIP in hostEntry.AddressList) + { + if (remoteServerHostName == hostIP.ToString() && remoteServerPort == localPort) + { + ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server hostname {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); + return; + } + } + + ProxyFunctions.Log(LogWriter, SessionId, "Starting service.", Proxy.LogLevel.Information, LogLevel); + + // Attempt to start up to 3 times in case another service using the port is shutting down. + int startAttempts = 0; + while (startAttempts < 3) + { + startAttempts++; + + // If we've failed to start once, wait an extra 10 seconds. + if (startAttempts > 1) + { + ProxyFunctions.Log(LogWriter, SessionId, "Attempting to start for the " + (startAttempts == 2 ? "2nd" : "3rd") + " time.", Proxy.LogLevel.Information, LogLevel); + Thread.Sleep(10000 * startAttempts); + } + + try + { + X509Certificate serverCertificate = null; + + // Generate a unique session ID for logging. + SessionId = Guid.NewGuid().ToString(); + ConnectionId = 0; + + // If local SSL is supported via STARTTLS, ensure we have a valid server certificate. + if (localEnableSsl) + { + serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, fqdn); + // In case the service as running as the current user, check the Current User certificate store as well. + if (serverCertificate == null) + serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, fqdn); + + // If no certificate was found, generate a self-signed certificate. + if (serverCertificate == null) + { + ProxyFunctions.Log(LogWriter, SessionId, "No signing certificate found, so generating new certificate.", Proxy.LogLevel.Warning, LogLevel); + + List oids = new List(); + oids.Add("1.3.6.1.5.5.7.3.1"); // Server Authentication. + + // Generate the certificate with a duration of 10 years, 4096-bits, and a key usage of server authentication. + serverCertificate = CertHelper.CreateSelfSignedCertificate(fqdn, fqdn, true, 4096, 10, oids); + + ProxyFunctions.Log(LogWriter, SessionId, "Certificate generated with Serial Number {" + serverCertificate.GetSerialNumberString() + "}.", Proxy.LogLevel.Information, LogLevel); + } + } + + Listener = new TcpListener(localIPAddress, localPort); + Listener.Start(); + + ProxyFunctions.Log(LogWriter, SessionId, "Service started.", Proxy.LogLevel.Information, LogLevel); + ProxyFunctions.Log(LogWriter, SessionId, "Listening on address {" + localIPAddress.ToString() + "}, port {" + localPort + "}.", Proxy.LogLevel.Information, LogLevel); + + Started = true; + + // Accept client requests, forking each into its own thread. + while (Started) + { + TcpClient client = Listener.AcceptTcpClient(); + + string newLogFileName = ProxyFunctions.GetLogFileName(logFile, instanceId, localIPAddress.ToString(), remoteServerHostName, localPort, remoteServerPort); + if (newLogFileName != logFileName) + { + LogWriter.Close(); + LogWriter = new StreamWriter(newLogFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); + LogWriter.AutoFlush = true; + } + + // Prepare the arguments for our new thread. + ImapProxyConnectionArguments arguments = new ImapProxyConnectionArguments(); + arguments.AcceptedIPs = acceptedIPs; + arguments.TcpClient = client; + arguments.Certificate = serverCertificate; + arguments.LocalIpAddress = localIPAddress; + arguments.LocalPort = localPort; + arguments.LocalEnableSsl = localEnableSsl; + arguments.RemoteServerHostName = remoteServerHostName; + arguments.RemoteServerPort = remoteServerPort; + arguments.RemoteServerEnableSsl = remoteServerEnableSsl; + arguments.RemoteServerCredential = remoteServerCredential; + + // Increment the connection counter; + arguments.ConnectionId = (unchecked(++ConnectionId)).ToString(); + + // Fork the thread and continue listening for new connections. + Thread processThread = new Thread(new ParameterizedThreadStart(ProcessConnection)); + processThread.Name = "OpaqueMail IMAP Proxy Connection"; + processThread.Start(arguments); + } + return; + } + catch (Exception ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception when starting proxy: " + ex.Message, Proxy.LogLevel.Critical, LogLevel); + } + } + } + + /// + /// Stop the IMAP proxy and close all existing connections. + /// + public void Stop() + { + ProxyFunctions.Log(LogWriter, SessionId, "Stopping service.", Proxy.LogLevel.Information, LogLevel); + + Started = false; + + if (Listener != null) + Listener.Stop(); + + ProxyFunctions.Log(LogWriter, SessionId, "Service stopped.", Proxy.LogLevel.Information, LogLevel); + } + + /// + /// Start all IMAP proxy instances from the specified settings file. + /// + /// File containing the IMAP proxy settings. + public static List StartProxiesFromSettingsFile(string fileName) + { + List imapProxies = new List(); + + try + { + if (File.Exists(fileName)) + { + XPathDocument document = new XPathDocument(fileName); + XPathNavigator navigator = document.CreateNavigator(); + + int imapServiceCount = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/IMAP/ServiceCount"); + for (int i = 1; i <= imapServiceCount; i++) + { + ImapProxyArguments arguments = new ImapProxyArguments(); + arguments.AcceptedIPs = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/AcceptedIPs"); + + string localIpAddress = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/LocalIPAddress").ToUpper(); + switch (localIpAddress) + { + // Treat blank values as "Any". + case "": + case "ANY": + arguments.LocalIpAddress = IPAddress.Any; + break; + case "BROADCAST": + arguments.LocalIpAddress = IPAddress.Broadcast; + break; + case "IPV6ANY": + arguments.LocalIpAddress = IPAddress.IPv6Any; + break; + case "IPV6LOOPBACK": + arguments.LocalIpAddress = IPAddress.IPv6Loopback; + break; + case "LOOPBACK": + arguments.LocalIpAddress = IPAddress.Loopback; + break; + default: + // Try to parse the local IP address. If unable to, proceed to the next service instance. + if (!IPAddress.TryParse(localIpAddress, out arguments.LocalIpAddress)) + continue; + break; + } + + arguments.LocalPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/LocalPort"); + // If the port is invalid, proceed to the next service instance. + if (arguments.LocalPort < 1) + continue; + + arguments.LocalEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/IMAP/Service" + i + "/LocalEnableSSL"); + + arguments.RemoteServerHostName = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerHostName"); + // If the host name is invalid, proceed to the next service instance. + if (string.IsNullOrEmpty(arguments.RemoteServerHostName)) + continue; + + arguments.RemoteServerPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerPort"); + // If the port is invalid, proceed to the next service instance. + if (arguments.RemoteServerPort < 1) + continue; + + arguments.RemoteServerEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerEnableSSL"); + + string remoteServerUsername = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerUsername"); + if (!string.IsNullOrEmpty(remoteServerUsername)) + { + arguments.RemoteServerCredential = new NetworkCredential(); + arguments.RemoteServerCredential.UserName = remoteServerUsername; + arguments.RemoteServerCredential.Password = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerPassword"); + } + + string certificateLocationValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/Location"); + StoreLocation certificateLocation = StoreLocation.LocalMachine; + if (certificateLocationValue.ToUpper() == "CURRENTUSER") + certificateLocation = StoreLocation.CurrentUser; + + // Try to load the signing certificate based on its serial number first, then fallback to its subject name. + string certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/SerialNumber"); + if (!string.IsNullOrEmpty(certificateValue)) + arguments.Certificate = CertHelper.GetCertificateBySerialNumber(certificateLocation, certificateValue); + else + { + certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/SubjectName"); + if (!string.IsNullOrEmpty(certificateValue)) + arguments.Certificate = CertHelper.GetCertificateBySubjectName(certificateLocation, certificateValue); + } + + arguments.LogFile = ProxyFunctions.GetXmlStringValue(navigator, "Settings/IMAP/Service" + i + "/LogFile"); + + string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/IMAP/Service" + i + "/LogLevel"); + switch (logLevel.ToUpper()) + { + case "NONE": + arguments.LogLevel = LogLevel.None; + break; + case "CRITICAL": + arguments.LogLevel = LogLevel.Critical; + break; + case "ERROR": + arguments.LogLevel = LogLevel.Error; + break; + case "RAW": + arguments.LogLevel = LogLevel.Raw; + break; + case "VERBOSE": + arguments.LogLevel = LogLevel.Verbose; + break; + case "WARNING": + arguments.LogLevel = LogLevel.Warning; + break; + case "INFORMATION": + default: + arguments.LogLevel = LogLevel.Information; + break; + } + + arguments.InstanceId = i; + + // Remember the proxy in order to close it when the service stops. + arguments.Proxy = new ImapProxy(); + imapProxies.Add(arguments.Proxy); + + Thread proxyThread = new Thread(new ParameterizedThreadStart(StartProxy)); + proxyThread.Name = "OpaqueMail IMAP Proxy"; + proxyThread.Start(arguments); + } + } + } + catch + { + // Ignore errors if the XML settings file is malformed. + } + + return imapProxies; + } + #endregion Public Methods + + #region Private Methods + /// + /// Handle an incoming IMAP connection, from connection to completion. + /// + /// ImapProxyConnectionArguments object containing all parameters for this connection. + private void ProcessConnection(object parameters) + { + // Cast the passed-in parameters back to their original objects. + ImapProxyConnectionArguments arguments = (ImapProxyConnectionArguments)parameters; + + try + { + TcpClient client = arguments.TcpClient; + Stream clientStream = client.GetStream(); + + // Capture the client's IP information. + PropertyInfo pi = clientStream.GetType().GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance); + string ip = ((Socket)pi.GetValue(clientStream, null)).RemoteEndPoint.ToString(); + if (ip.IndexOf(":") > -1) + ip = ip.Substring(0, ip.IndexOf(":")); + + // If the IP address range filter contains the localhost entry 0.0.0.0, check if the client IP is a local address and update it to 0.0.0.0 if so. + if (arguments.AcceptedIPs.IndexOf("0.0.0.0") > -1) + { + if (ip == "127.0.0.1") + ip = "0.0.0.0"; + else + { + IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress hostIP in hostEntry.AddressList) + { + if (hostIP.ToString() == ip) + { + ip = "0.0.0.0"; + break; + } + } + } + } + + // Validate that the IP address is within an accepted range. + if (!ProxyFunctions.ValidateIP(arguments.AcceptedIPs, ip)) + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection rejected from {" + ip + "} due to its IP address.", Proxy.LogLevel.Warning, LogLevel); + + Functions.SendStreamString(clientStream, new byte[Constants.SMALLBUFFERSIZE], "500 IP address [" + ip + "] rejected.\r\n"); + + if (clientStream != null) + clientStream.Dispose(); + if (client != null) + client.Close(); + + return; + } + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "New connection established from {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); + + // If supported, upgrade the session's security through a TLS handshake. + if (arguments.LocalEnableSsl) + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Starting local TLS/SSL protection for {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); + clientStream = new SslStream(clientStream); + ((SslStream)clientStream).AuthenticateAsServer(arguments.Certificate); + } + + // Connect to the remote server. + TcpClient remoteServerClient = new TcpClient(arguments.RemoteServerHostName, arguments.RemoteServerPort); + Stream remoteServerStream = remoteServerClient.GetStream(); + + // If supported, upgrade the session's security through a TLS handshake. + if (arguments.RemoteServerEnableSsl) + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Starting remote TLS/SSL protection with {" + arguments.RemoteServerHostName + "}.", Proxy.LogLevel.Information, LogLevel); + remoteServerStream = new SslStream(remoteServerStream); + ((SslStream)remoteServerStream).AuthenticateAsClient(arguments.RemoteServerHostName); + } + + // Relay server data to the client. + TransmitArguments remoteServerToClientArguments = new TransmitArguments(); + remoteServerToClientArguments.ClientStream = remoteServerStream; + remoteServerToClientArguments.RemoteServerStream = clientStream; + remoteServerToClientArguments.IsClient = false; + remoteServerToClientArguments.ConnectionId = ConnectionId.ToString(); + remoteServerToClientArguments.IPAddress = ip; + Thread remoteServerToClientThread = new Thread(new ParameterizedThreadStart(RelayData)); + remoteServerToClientThread.Name = "OpaqueMail IMAP Proxy Server to Client"; + remoteServerToClientThread.Start(remoteServerToClientArguments); + + // Relay client data to the remote server. + TransmitArguments clientToRemoteServerArguments = new TransmitArguments(); + clientToRemoteServerArguments.ClientStream = clientStream; + clientToRemoteServerArguments.RemoteServerStream = remoteServerStream; + clientToRemoteServerArguments.IsClient = true; + clientToRemoteServerArguments.ConnectionId = ConnectionId.ToString(); + clientToRemoteServerArguments.IPAddress = ip; + clientToRemoteServerArguments.Credential = arguments.RemoteServerCredential; + Thread clientToRemoteServerThread = new Thread(new ParameterizedThreadStart(RelayData)); + clientToRemoteServerThread.Name = "OpaqueMail IMAP Proxy Client to Server"; + clientToRemoteServerThread.Start(clientToRemoteServerArguments); + } + catch (SocketException ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception communicating with {" + arguments.RemoteServerHostName + "} on port {" + arguments.RemoteServerPort + "}: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + catch (Exception ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + } + + /// + /// Relay data read from one connection to another. + /// + /// A TransmitArguments object containing local and remote server parameters. + private async void RelayData(object o) + { + // Cast the passed-in parameters back to their original objects. + TransmitArguments arguments = (TransmitArguments)o; + Stream clientStream = arguments.ClientStream; + Stream remoteServerStream = arguments.RemoteServerStream; + + // A byte array to streamline bit shuffling. + char[] buffer = new char[Constants.SMALLBUFFERSIZE]; + + // Placeholder variables to track the current message being transmitted. + bool inMessage = false; + int messageLength = 0; + StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE); + + // The overall number of bytes transmitted on this connection. + ulong bytesTransmitted = 0; + int appendLength = 0, appendBytesTransmitted = 0; + + // When a "[THROTTLED]" notice was last received. + DateTime lastThrottleTime = new DateTime(1900, 1, 1); + + bool stillReceiving = true; + try + { + using (StreamReader clientStreamReader = new StreamReader(clientStream)) + { + using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream)) + { + remoteServerStreamWriter.AutoFlush = true; + + while (Started && stillReceiving) + { + // Read data from the source and send it to its destination. + int bytesRead = await clientStreamReader.ReadAsync(buffer, 0, Constants.SMALLBUFFERSIZE); + + if (bytesRead > 0) + { + // Cast the bytes received to a string. + string stringRead = new string(buffer, 0, bytesRead); + bytesTransmitted += (ulong)bytesRead; + + // If this data comes from the client, log it. Otherwise, process it. + if (arguments.IsClient) + { + bool messageRelayed = false; + + string[] commandParts = stringRead.Split(new char[] { ' ' }, 4); + if (commandParts.Length > 2) + { + // If we're in an APPEND command, wait until we've completed the append before logging another command. + if (appendLength > 0) + { + appendBytesTransmitted += stringRead.Length; + if (appendBytesTransmitted >= appendLength) + LastCommandReceived = ""; + } + else + { + // Optionally replace credentials with those from our settings file. + if (arguments.Credential != null && commandParts[1] == "LOGIN" && commandParts.Length == 4) + { + await remoteServerStreamWriter.WriteAsync(commandParts[0] + " " + commandParts[1] + " " + arguments.Credential.UserName + " " + arguments.Credential.Password + "\r\n"); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + commandParts[0] + " " + commandParts[1] + " " + arguments.Credential.UserName + " " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel); + + messageRelayed = true; + } + + if (commandParts[1] == "UID") + LastCommandReceived = commandParts[1] + " " + commandParts[2]; + else + LastCommandReceived = commandParts[1]; + + if (LogLevel == Proxy.LogLevel.Verbose) + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + LastCommandReceived + "} processed.", Proxy.LogLevel.Verbose, LogLevel); + + // If we're in an APPEND command, remember how many bytes we should receive. + if (LastCommandReceived == "APPEND") + { + appendLength = 0; + int.TryParse(Functions.ReturnBetween(stringRead, "{", "}"), out appendLength); + appendBytesTransmitted = stringRead.Length - stringRead.IndexOf("\r\n") - 2; + } + } + } + + if (!messageRelayed) + { + await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel); + } + } + else + { + await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel); + + // If we're currently receiving a message, check to see if it's completed. + if (inMessage) + { + messageBuilder.Append(stringRead); + if (messageBuilder.Length >= messageLength) + { + // If the message has been completed and it contains a signature, process it. + string message = messageBuilder.ToString(0, messageLength); + if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1) + { + Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); + processThread.Name = "OpaqueMail IMAP Proxy Signature Processor"; + ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); + processMessageArguments.MessageText = message; + processMessageArguments.ConnectionId = ConnectionId.ToString(); + processThread.Start(processMessageArguments); + } + + // We're no longer receiving a message, so continue. + inMessage = false; + stringRead = messageBuilder.ToString(messageLength, messageBuilder.Length - messageLength); + messageBuilder.Clear(); + } + } + + if (!inMessage) + { + if (stringRead.IndexOf("[THROTTLED]\r\n", StringComparison.Ordinal) > -1) + { + if (DateTime.Now - lastThrottleTime >= new TimeSpan(0, 20, 0)) + { + ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), "Connection speed throttled by the remote server.", Proxy.LogLevel.Warning, LogLevel); + lastThrottleTime = DateTime.Now; + } + } + + int pos = 0; + while (pos > -1) + { + // Messages are denoted by FETCH headers with their lengths in curly braces. + pos = stringRead.IndexOf(" FETCH ", pos); + if (pos > -1) + { + int openBrace = stringRead.IndexOf("{", pos); + int lineBreak = stringRead.IndexOf("\r", pos); + if (lineBreak < -1) + lineBreak = stringRead.Length + 1; + + if (openBrace > -1 && openBrace < lineBreak) + { + int closeBrace = stringRead.IndexOf("}", openBrace); + if (closeBrace > -1) + { + // Only proceed if we can parse the size of the message. + if (int.TryParse(stringRead.Substring(openBrace + 1, closeBrace - openBrace - 1), out messageLength)) + { + int messageBytesRead = stringRead.Length - closeBrace - 3; + + if (messageBytesRead > messageLength) + { + string message = stringRead.Substring(closeBrace + 3, messageLength); + if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1) + { + Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); + processThread.Name = "OpaqueMail IMAP Proxy Signature Processor"; + ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); + processMessageArguments.MessageText = message; + processMessageArguments.ConnectionId = ConnectionId.ToString(); + processThread.Start(processMessageArguments); + } + pos = closeBrace + 3 + messageLength; + } + else + { + inMessage = true; + messageBuilder.Clear(); + if (stringRead.Length > closeBrace + 3) + messageBuilder.Append(stringRead.Substring(closeBrace + 3)); + pos = -1; + } + } + else + pos = -1; + } + else + pos = -1; + } + else + pos = -1; + } + } + } + } + } + else + stillReceiving = false; + } + } + } + } +/* catch (IOException) + { + // Ignore either stream being closed. + }*/ + catch (ObjectDisposedException) + { + // Ignore either stream being closed. + } + catch (Exception ex) + { + // Log other exceptions. + ProxyFunctions.Log(LogWriter, SessionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + finally + { + // If sending to the local client, log the connection being closed. + if (!arguments.IsClient) + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); + + if (clientStream != null) + clientStream.Dispose(); + if (remoteServerStream != null) + remoteServerStream.Dispose(); + } + } + + /// + /// Process a transmitted message to import any signing certificates for subsequent S/MIME encryption. + /// + /// A ProcessMessageArguments object containing message parameters. + private void ProcessMessage(object o) + { + ProcessMessageArguments arguments = (ProcessMessageArguments)o; + + // Only parse the message if it contains a known S/MIME content type. + string canonicalMessageText = arguments.MessageText.ToLower(); + if (canonicalMessageText.IndexOf("application/x-pkcs7-signature") > -1 || canonicalMessageText.IndexOf("application/pkcs7-mime") > -1) + { + try + { + // Parse the message. + ReadOnlyMailMessage message = new ReadOnlyMailMessage(arguments.MessageText); + + // If the message contains a signing certificate that we haven't processed on this session, import it. + if (message.SmimeSigningCertificate != null && !SmimeCertificatesReceived.Contains(message.SmimeSigningCertificate)) + { + // Import the certificate to the Local Machine store. + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Importing certificate with Serial Number {" + message.SmimeSigningCertificate.SerialNumber + "}.", Proxy.LogLevel.Information, LogLevel); + CertHelper.InstallWindowsCertificate(message.SmimeSigningCertificate, StoreLocation.LocalMachine); + + // Remember this ceriticate to avoid importing it again this session. + SmimeCertificatesReceived.Add(message.SmimeSigningCertificate); + } + } + catch (Exception ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + } + } + + /// + /// Start an individual IMAP proxy on its own thread. + /// + /// ImapProxyArguments object containing all parameters for this connection. + private static void StartProxy(object parameters) + { + ImapProxyArguments arguments = (ImapProxyArguments)parameters; + + // Start the proxy using passed-in settings. + arguments.Proxy.Start(arguments.AcceptedIPs, arguments.LocalIpAddress, arguments.LocalPort, arguments.LocalEnableSsl, arguments.RemoteServerHostName, arguments.RemoteServerPort, arguments.RemoteServerEnableSsl, arguments.RemoteServerCredential, arguments.LogFile, arguments.LogLevel, arguments.InstanceId); + } + #endregion Private Methods + } +} diff --git a/OpaqueMail.Net.Proxy/POP3/Pop3Proxy.cs b/OpaqueMail.Net.Proxy/POP3/Pop3Proxy.cs index d27c4f6..d066d3d 100644 --- a/OpaqueMail.Net.Proxy/POP3/Pop3Proxy.cs +++ b/OpaqueMail.Net.Proxy/POP3/Pop3Proxy.cs @@ -1,635 +1,636 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml.XPath; - -namespace OpaqueMail.Net.Proxy -{ - public class Pop3Proxy : ProxyBase - { - #region Public Methods - /// - /// Start a POP3 proxy instance. - /// - /// IP addresses to accept connections from. - /// Local IP address to bind to. - /// Local port to listen on. - /// Whether the local server supports TLS/SSL. - /// Remote server hostname to forward all POP3 messages to. - /// Remote server port to connect to. - /// Whether the remote POP3 server requires TLS/SSL. - public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl) - { - Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, null, "", LogLevel.None, 0); - } - - /// - /// Start a POP3 proxy instance. - /// - /// IP addresses to accept connections from. - /// Local IP address to bind to. - /// Local port to listen on. - /// Whether the local server supports TLS/SSL. - /// Remote server hostname to forward all POP3 messages to. - /// Remote server port to connect to. - /// Whether the remote POP3 server requires TLS/SSL. - /// (Optional) Credentials to be used for all connections to the remote POP3 server. When set, this overrides any credentials passed locally. - /// File where event logs and exception information will be written. - /// Proxy logging level, determining how much information is logged. - /// The instance number of the proxy. - public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string logFile, LogLevel logLevel, int instanceId) - { - // Create the log writer. - string logFileName = ""; - if (!string.IsNullOrEmpty(logFile)) - { - logFileName = ProxyFunctions.GetLogFileName(logFile, instanceId); - LogWriter = new StreamWriter(logFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); - LogLevel = logLevel; - } - - // Make sure the remote server isn't an infinite loop back to this server. - string fqdn = Functions.GetLocalFQDN(); - if (remoteServerHostName.ToUpper() == fqdn.ToUpper() && remoteServerPort == localPort) - { - ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server host name {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); - return; - } - IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); - foreach (IPAddress hostIP in hostEntry.AddressList) - { - if (remoteServerHostName == hostIP.ToString() && remoteServerPort == localPort) - { - ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server hostname {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); - return; - } - } - - ProxyFunctions.Log(LogWriter, SessionId, "Starting service.", Proxy.LogLevel.Information, LogLevel); - - // Attempt to start up to 3 times in case another service using the port is shutting down. - int startAttempts = 0; - while (startAttempts < 3) - { - startAttempts++; - - // If we've failed to start once, wait an extra 10 seconds. - if (startAttempts > 1) - { - ProxyFunctions.Log(LogWriter, SessionId, "Attempting to start for the " + (startAttempts == 2 ? "2nd" : "3rd") + " time.", Proxy.LogLevel.Information, LogLevel); - Thread.Sleep(10000 * startAttempts); - } - - try - { - X509Certificate serverCertificate = null; - - // Generate a unique session ID for logging. - SessionId = Guid.NewGuid().ToString(); - ConnectionId = 0; - - // If local SSL is supported via STARTTLS, ensure we have a valid server certificate. - if (localEnableSsl) - { - serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, fqdn); - // In case the service as running as the current user, check the Current User certificate store as well. - if (serverCertificate == null) - serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, fqdn); - - // If no certificate was found, generate a self-signed certificate. - if (serverCertificate == null) - { - ProxyFunctions.Log(LogWriter, SessionId, "No signing certificate found, so generating new certificate.", Proxy.LogLevel.Warning, LogLevel); - - List oids = new List(); - oids.Add("1.3.6.1.5.5.7.3.1"); // Server Authentication. - - // Generate the certificate with a duration of 10 years, 4096-bits, and a key usage of server authentication. - serverCertificate = CertHelper.CreateSelfSignedCertificate(fqdn, fqdn, true, 4096, 10, oids); - - ProxyFunctions.Log(LogWriter, SessionId, "Certificate generated with Serial Number {" + serverCertificate.GetSerialNumberString() + "}.", Proxy.LogLevel.Information, LogLevel); - } - } - - // Start listening on the specified port and IP address. - Listener = new TcpListener(localIPAddress, localPort); - Listener.Start(); - - ProxyFunctions.Log(LogWriter, SessionId, "Service started.", Proxy.LogLevel.Information, LogLevel); - ProxyFunctions.Log(LogWriter, SessionId, "Listening on address {" + localIPAddress.ToString() + "}, port {" + localPort + "}.", Proxy.LogLevel.Information, LogLevel); - - Started = true; - - // Accept client requests, forking each into its own thread. - while (Started) - { - TcpClient client = Listener.AcceptTcpClient(); - - string newLogFileName = ProxyFunctions.GetLogFileName(logFile, instanceId); - if (newLogFileName != logFileName) - { - LogWriter.Close(); - LogWriter = new StreamWriter(newLogFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); - LogWriter.AutoFlush = true; - } - - // Prepare the arguments for our new thread. - Pop3ProxyConnectionArguments arguments = new Pop3ProxyConnectionArguments(); - arguments.AcceptedIPs = acceptedIPs; - arguments.TcpClient = client; - arguments.Certificate = serverCertificate; - arguments.LocalIpAddress = localIPAddress; - arguments.LocalPort = localPort; - arguments.LocalEnableSsl = localEnableSsl; - arguments.RemoteServerHostName = remoteServerHostName; - arguments.RemoteServerPort = remoteServerPort; - arguments.RemoteServerEnableSsl = remoteServerEnableSsl; - arguments.RemoteServerCredential = remoteServerCredential; - - // Increment the connection counter; - arguments.ConnectionId = (unchecked(++ConnectionId)).ToString(); - - // Fork the thread and continue listening for new connections. - Thread processThread = new Thread(new ParameterizedThreadStart(ProcessConnection)); - processThread.Name = "OpaqueMail POP3 Proxy Connection"; - processThread.Start(arguments); - } - return; - } - catch (Exception ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception when starting proxy: " + ex.Message, Proxy.LogLevel.Critical, LogLevel); - } - } - } - - /// - /// Stop the POP3 proxy and close all existing connections. - /// - public void Stop() - { - ProxyFunctions.Log(LogWriter, SessionId, "Stopping service.", Proxy.LogLevel.Information, LogLevel); - - Started = false; - - if (Listener != null) - Listener.Stop(); - - ProxyFunctions.Log(LogWriter, SessionId, "Service stopped.", Proxy.LogLevel.Information, LogLevel); - } - - /// - /// Start all POP3 proxy instances from the specified settings file. - /// - /// File containing the POP3 proxy settings. - public static List StartProxiesFromSettingsFile(string fileName) - { - List pop3Proxies = new List(); - - try - { - if (File.Exists(fileName)) - { - XPathDocument document = new XPathDocument(fileName); - XPathNavigator navigator = document.CreateNavigator(); - - int pop3ServiceCount = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/IMAP/ServiceCount"); - for (int i = 1; i <= pop3ServiceCount; i++) - { - Pop3ProxyArguments arguments = new Pop3ProxyArguments(); - arguments.AcceptedIPs = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/AcceptedIPs"); - - string localIpAddress = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/LocalIPAddress").ToUpper(); - switch (localIpAddress) - { - // Treat blank values as "Any". - case "": - case "ANY": - arguments.LocalIpAddress = IPAddress.Any; - break; - case "BROADCAST": - arguments.LocalIpAddress = IPAddress.Broadcast; - break; - case "IPV6ANY": - arguments.LocalIpAddress = IPAddress.IPv6Any; - break; - case "IPV6LOOPBACK": - arguments.LocalIpAddress = IPAddress.IPv6Loopback; - break; - case "LOOPBACK": - arguments.LocalIpAddress = IPAddress.Loopback; - break; - default: - // Try to parse the local IP address. If unable to, proceed to the next service instance. - if (!IPAddress.TryParse(localIpAddress, out arguments.LocalIpAddress)) - continue; - break; - } - - arguments.LocalPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/LocalPort"); - // If the port is invalid, proceed to the next service instance. - if (arguments.LocalPort < 1) - continue; - - arguments.LocalEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/POP3/Service" + i + "/LocalEnableSSL"); - - arguments.RemoteServerHostName = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerHostName"); - // If the host name is invalid, proceed to the next service instance. - if (string.IsNullOrEmpty(arguments.RemoteServerHostName)) - continue; - - arguments.RemoteServerPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerPort"); - // If the port is invalid, proceed to the next service instance. - if (arguments.RemoteServerPort < 1) - continue; - - arguments.RemoteServerEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerEnableSSL"); - - string remoteServerUsername = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerUsername"); - if (!string.IsNullOrEmpty(remoteServerUsername)) - { - arguments.RemoteServerCredential = new NetworkCredential(); - arguments.RemoteServerCredential.UserName = remoteServerUsername; - arguments.RemoteServerCredential.Password = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerPassword"); - } - - string certificateLocationValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/Location"); - StoreLocation certificateLocation = StoreLocation.LocalMachine; - if (certificateLocationValue.ToUpper() == "CURRENTUSER") - certificateLocation = StoreLocation.CurrentUser; - - // Try to load the signing certificate based on its serial number first, then fallback to its subject name. - string certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/SerialNumber"); - if (!string.IsNullOrEmpty(certificateValue)) - arguments.Certificate = CertHelper.GetCertificateBySerialNumber(certificateLocation, certificateValue); - else - { - certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/SubjectName"); - if (!string.IsNullOrEmpty(certificateValue)) - arguments.Certificate = CertHelper.GetCertificateBySubjectName(certificateLocation, certificateValue); - } - - arguments.LogFile = ProxyFunctions.GetXmlStringValue(navigator, "Settings/POP3/Service" + i + "/LogFile"); - - string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/POP3/Service" + i + "/LogLevel"); - switch (logLevel.ToUpper()) - { - case "NONE": - arguments.LogLevel = LogLevel.None; - break; - case "CRITICAL": - arguments.LogLevel = LogLevel.Critical; - break; - case "ERROR": - arguments.LogLevel = LogLevel.Error; - break; - case "RAW": - arguments.LogLevel = LogLevel.Raw; - break; - case "VERBOSE": - arguments.LogLevel = LogLevel.Verbose; - break; - case "WARNING": - arguments.LogLevel = LogLevel.Warning; - break; - case "INFORMATION": - default: - arguments.LogLevel = LogLevel.Information; - break; - } - - arguments.InstanceId = i; - - // Remember the proxy in order to close it when the service stops. - arguments.Proxy = new Pop3Proxy(); - pop3Proxies.Add(arguments.Proxy); - - Thread proxyThread = new Thread(new ParameterizedThreadStart(StartProxy)); - proxyThread.Name = "OpaqueMail POP3 Proxy"; - proxyThread.Start(arguments); - } - } - } - catch - { - // Ignore errors if the XML settings file is malformed. - } - - return pop3Proxies; - } - #endregion Public Methods - - #region Private Methods - /// - /// Handle an incoming POP3 connection, from connection to completion. - /// - /// Pop3ProxyConnectionArguments object containing all parameters for this connection. - private void ProcessConnection(object parameters) - { - // Cast the passed-in parameters back to their original objects. - Pop3ProxyConnectionArguments arguments = (Pop3ProxyConnectionArguments)parameters; - - try - { - TcpClient client = arguments.TcpClient; - Stream clientStream = client.GetStream(); - - // Capture the client's IP information. - PropertyInfo pi = clientStream.GetType().GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance); - string ip = ((Socket)pi.GetValue(clientStream, null)).RemoteEndPoint.ToString(); - if (ip.IndexOf(":") > -1) - ip = ip.Substring(0, ip.IndexOf(":")); - - // If the IP address range filter contains the localhost entry 0.0.0.0, check if the client IP is a local address and update it to 0.0.0.0 if so. - if (arguments.AcceptedIPs.IndexOf("0.0.0.0") > -1) - { - if (ip == "127.0.0.1") - ip = "0.0.0.0"; - else - { - IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); - foreach (IPAddress hostIP in hostEntry.AddressList) - { - if (hostIP.ToString() == ip) - { - ip = "0.0.0.0"; - break; - } - } - } - } - - // Validate that the IP address is within an accepted range. - if (!ProxyFunctions.ValidateIP(arguments.AcceptedIPs, ip)) - { - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection rejected from {" + ip + "} due to its IP address.", Proxy.LogLevel.Warning, LogLevel); - - Functions.SendStreamString(clientStream, new byte[Constants.SMALLBUFFERSIZE], "500 IP address [" + ip + "] rejected.\r\n"); - - if (clientStream != null) - clientStream.Dispose(); - if (client != null) - client.Close(); - - return; - } - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "New connection established from {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); - - // If supported, upgrade the session's security through a TLS handshake. - if (arguments.LocalEnableSsl) - { - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Starting local TLS/SSL protection for {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); - clientStream = new SslStream(clientStream); - ((SslStream)clientStream).AuthenticateAsServer(arguments.Certificate); - } - - // Connect to the remote server. - TcpClient remoteServerClient = new TcpClient(arguments.RemoteServerHostName, arguments.RemoteServerPort); - Stream remoteServerStream = remoteServerClient.GetStream(); - - // If supported, upgrade the session's security through a TLS handshake. - if (arguments.RemoteServerEnableSsl) - { - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Starting remote TLS/SSL protection with {" + arguments.RemoteServerHostName + "}.", Proxy.LogLevel.Information, LogLevel); - remoteServerStream = new SslStream(remoteServerStream); - ((SslStream)remoteServerStream).AuthenticateAsClient(arguments.RemoteServerHostName); - } - - // Relay server data to the client. - TransmitArguments remoteServerToClientArguments = new TransmitArguments(); - remoteServerToClientArguments.ClientStream = remoteServerStream; - remoteServerToClientArguments.RemoteServerStream = clientStream; - remoteServerToClientArguments.IsClient = false; - remoteServerToClientArguments.ConnectionId = ConnectionId.ToString(); - remoteServerToClientArguments.IPAddress = ip; - Thread remoteServerToClientThread = new Thread(new ParameterizedThreadStart(RelayData)); - remoteServerToClientThread.Name = "OpaqueMail POP3 Proxy Server to Client"; - remoteServerToClientThread.Start(remoteServerToClientArguments); - - // Relay client data to the remote server. - TransmitArguments clientToRemoteServerArguments = new TransmitArguments(); - clientToRemoteServerArguments.ClientStream = clientStream; - clientToRemoteServerArguments.RemoteServerStream = remoteServerStream; - clientToRemoteServerArguments.IsClient = true; - clientToRemoteServerArguments.ConnectionId = ConnectionId.ToString(); - clientToRemoteServerArguments.IPAddress = ip; - clientToRemoteServerArguments.Credential = arguments.RemoteServerCredential; - Thread clientToRemoteServerThread = new Thread(new ParameterizedThreadStart(RelayData)); - clientToRemoteServerThread.Name = "OpaqueMail POP3 Proxy Client to Server"; - clientToRemoteServerThread.Start(clientToRemoteServerArguments); - } - catch (SocketException ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception communicating with {" + arguments.RemoteServerHostName + "} on port {" + arguments.RemoteServerPort + "}: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - catch (Exception ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - } - - /// - /// Relay data read from one connection to another. - /// - /// A TransmitArguments object containing local and remote server parameters. - private async void RelayData(object o) - { - // Cast the passed-in parameters back to their original objects. - TransmitArguments arguments = (TransmitArguments)o; - Stream clientStream = arguments.ClientStream; - Stream remoteServerStream = arguments.RemoteServerStream; - - // A byte array to streamline bit shuffling. - char[] buffer = new char[Constants.SMALLBUFFERSIZE]; - - // Placeholder variables to track the current message being transmitted. - StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE); - - // The overall number of bytes transmitted on this connection. - ulong bytesTransmitted = 0; - - bool stillReceiving = true; - try - { - using (StreamReader clientStreamReader = new StreamReader(clientStream)) - { - using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream)) - { - remoteServerStreamWriter.AutoFlush = true; - - while (Started && stillReceiving) - { - // Read data from the source and send it to its destination. - int bytesRead = await clientStreamReader.ReadAsync(buffer, 0, Constants.SMALLBUFFERSIZE); - - if (bytesRead > 0) - { - await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); - bytesTransmitted += (ulong)bytesRead; - - // Cast the bytes received to a string. - string stringRead = new string(buffer, 0, bytesRead); - - messageBuilder.Append(stringRead); - - // If this data comes from the client, log it. Otherwise, process it. - if (arguments.IsClient) - { - bool messageRelayed = false; - - string[] commandParts = stringRead.Substring(0, stringRead.Length - 2).Split(new char[] { ' ' }, 2); - - // Optionally replace credentials with those from our settings file. - if (arguments.Credential != null && commandParts.Length == 2) - { - if (commandParts[0] == "USER") - { - await remoteServerStreamWriter.WriteAsync("USER " + arguments.Credential.UserName + "\r\n"); - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: USER " + arguments.Credential.UserName, Proxy.LogLevel.Raw, LogLevel); - - messageRelayed = true; - } - else if (commandParts[0] == "PASS") - { - await remoteServerStreamWriter.WriteAsync("PASS" + arguments.Credential.Password + "\r\n"); - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: PASS " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel); - - messageRelayed = true; - } - } - - if (LogLevel == Proxy.LogLevel.Verbose) - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + commandParts[0] + "} processed.", Proxy.LogLevel.Verbose, LogLevel); - - if (!messageRelayed) - { - await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel); - } - } - else - { - await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel); - - // If we see a period between two linebreaks, treat it as the end of a message. - int endPos = stringRead.IndexOf("\r\n.\r\n"); - if (endPos > -1) - { - string message = messageBuilder.ToString(); - endPos = message.IndexOf("\r\n.\r\n"); - - int lastOkPos = message.LastIndexOf("+OK", endPos); - if (lastOkPos > -1) - { - if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1) - { - Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); - processThread.Name = "OpaqueMail POP3 Proxy Signature Processor"; - ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); - processMessageArguments.MessageText = message; - processMessageArguments.ConnectionId = ConnectionId.ToString(); - processThread.Start(processMessageArguments); - } - messageBuilder.Remove(0, endPos + 5); - } - else - messageBuilder.Clear(); - } - } - } - else - stillReceiving = false; - } - } - } - } -/* catch (IOException) - { - // Ignore either stream being closed. - }*/ - catch (ObjectDisposedException) - { - // Ignore either stream being closed. - } - catch (Exception ex) - { - // Log other exceptions. - ProxyFunctions.Log(LogWriter, SessionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - finally - { - // If sending to the local client, log the connection being closed. - if (!arguments.IsClient) - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); - - if (clientStream != null) - clientStream.Dispose(); - if (remoteServerStream != null) - remoteServerStream.Dispose(); - } - } - - /// - /// Process a transmitted message to import any signing certificates for subsequent S/MIME encryption. - /// - /// A ProcessMessageArguments object containing message parameters. - private void ProcessMessage(object o) - { - ProcessMessageArguments arguments = (ProcessMessageArguments)o; - - // Only parse the message if it contains a known S/MIME content type. - string canonicalMessageText = arguments.MessageText.ToLower(); - if (canonicalMessageText.IndexOf("application/x-pkcs7-signature") > -1 || canonicalMessageText.IndexOf("application/pkcs7-mime") > -1) - { - try - { - // Parse the message. - ReadOnlyMailMessage message = new ReadOnlyMailMessage(arguments.MessageText); - - // If the message contains a signing certificate that we haven't processed on this session, import it. - if (message.SmimeSigningCertificate != null && !SmimeCertificatesReceived.Contains(message.SmimeSigningCertificate)) - { - // Import the certificate to the Local Machine store. - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Importing certificate with Serial Number {" + message.SmimeSigningCertificate.SerialNumber + "}.", Proxy.LogLevel.Information, LogLevel); - CertHelper.InstallWindowsCertificate(message.SmimeSigningCertificate, StoreLocation.LocalMachine); - - // Remember this ceriticate to avoid importing it again this session. - SmimeCertificatesReceived.Add(message.SmimeSigningCertificate); - } - } - catch (Exception ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - } - } - - /// - /// Start an individual POP3 proxy on its own thread. - /// - /// Pop3ProxyArguments object containing all parameters for this connection. - private static void StartProxy(object parameters) - { - Pop3ProxyArguments arguments = (Pop3ProxyArguments)parameters; - - // Start the proxy using passed-in settings. - arguments.Proxy.Start(arguments.AcceptedIPs, arguments.LocalIpAddress, arguments.LocalPort, arguments.LocalEnableSsl, arguments.RemoteServerHostName, arguments.RemoteServerPort, arguments.RemoteServerEnableSsl, arguments.RemoteServerCredential, arguments.LogFile, arguments.LogLevel, arguments.InstanceId); - } - #endregion Private Methods - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.XPath; + +namespace OpaqueMail.Net.Proxy +{ + public class Pop3Proxy : ProxyBase + { + #region Public Methods + /// + /// Start a POP3 proxy instance. + /// + /// IP addresses to accept connections from. + /// Local IP address to bind to. + /// Local port to listen on. + /// Whether the local server supports TLS/SSL. + /// Remote server hostname to forward all POP3 messages to. + /// Remote server port to connect to. + /// Whether the remote POP3 server requires TLS/SSL. + public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl) + { + Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, null, "", LogLevel.None, 0); + } + + /// + /// Start a POP3 proxy instance. + /// + /// IP addresses to accept connections from. + /// Local IP address to bind to. + /// Local port to listen on. + /// Whether the local server supports TLS/SSL. + /// Remote server hostname to forward all POP3 messages to. + /// Remote server port to connect to. + /// Whether the remote POP3 server requires TLS/SSL. + /// (Optional) Credentials to be used for all connections to the remote POP3 server. When set, this overrides any credentials passed locally. + /// File where event logs and exception information will be written. + /// Proxy logging level, determining how much information is logged. + /// The instance number of the proxy. + public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string logFile, LogLevel logLevel, int instanceId) + { + // Create the log writer. + string logFileName = ""; + if (!string.IsNullOrEmpty(logFile)) + { + logFileName = ProxyFunctions.GetLogFileName(logFile, instanceId, localIPAddress.ToString(), remoteServerHostName, localPort, remoteServerPort); + LogWriter = new StreamWriter(logFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); + LogWriter.AutoFlush = true; + + LogLevel = logLevel; + } + + // Make sure the remote server isn't an infinite loop back to this server. + string fqdn = Functions.GetLocalFQDN(); + if (remoteServerHostName.ToUpper() == fqdn.ToUpper() && remoteServerPort == localPort) + { + ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server host name {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); + return; + } + IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress hostIP in hostEntry.AddressList) + { + if (remoteServerHostName == hostIP.ToString() && remoteServerPort == localPort) + { + ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server hostname {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); + return; + } + } + + ProxyFunctions.Log(LogWriter, SessionId, "Starting service.", Proxy.LogLevel.Information, LogLevel); + + // Attempt to start up to 3 times in case another service using the port is shutting down. + int startAttempts = 0; + while (startAttempts < 3) + { + startAttempts++; + + // If we've failed to start once, wait an extra 10 seconds. + if (startAttempts > 1) + { + ProxyFunctions.Log(LogWriter, SessionId, "Attempting to start for the " + (startAttempts == 2 ? "2nd" : "3rd") + " time.", Proxy.LogLevel.Information, LogLevel); + Thread.Sleep(10000 * startAttempts); + } + + try + { + X509Certificate serverCertificate = null; + + // Generate a unique session ID for logging. + SessionId = Guid.NewGuid().ToString(); + ConnectionId = 0; + + // If local SSL is supported via STARTTLS, ensure we have a valid server certificate. + if (localEnableSsl) + { + serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, fqdn); + // In case the service as running as the current user, check the Current User certificate store as well. + if (serverCertificate == null) + serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, fqdn); + + // If no certificate was found, generate a self-signed certificate. + if (serverCertificate == null) + { + ProxyFunctions.Log(LogWriter, SessionId, "No signing certificate found, so generating new certificate.", Proxy.LogLevel.Warning, LogLevel); + + List oids = new List(); + oids.Add("1.3.6.1.5.5.7.3.1"); // Server Authentication. + + // Generate the certificate with a duration of 10 years, 4096-bits, and a key usage of server authentication. + serverCertificate = CertHelper.CreateSelfSignedCertificate(fqdn, fqdn, true, 4096, 10, oids); + + ProxyFunctions.Log(LogWriter, SessionId, "Certificate generated with Serial Number {" + serverCertificate.GetSerialNumberString() + "}.", Proxy.LogLevel.Information, LogLevel); + } + } + + // Start listening on the specified port and IP address. + Listener = new TcpListener(localIPAddress, localPort); + Listener.Start(); + + ProxyFunctions.Log(LogWriter, SessionId, "Service started.", Proxy.LogLevel.Information, LogLevel); + ProxyFunctions.Log(LogWriter, SessionId, "Listening on address {" + localIPAddress.ToString() + "}, port {" + localPort + "}.", Proxy.LogLevel.Information, LogLevel); + + Started = true; + + // Accept client requests, forking each into its own thread. + while (Started) + { + TcpClient client = Listener.AcceptTcpClient(); + + string newLogFileName = ProxyFunctions.GetLogFileName(logFile, instanceId, localIPAddress.ToString(), remoteServerHostName, localPort, remoteServerPort); + if (newLogFileName != logFileName) + { + LogWriter.Close(); + LogWriter = new StreamWriter(newLogFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); + LogWriter.AutoFlush = true; + } + + // Prepare the arguments for our new thread. + Pop3ProxyConnectionArguments arguments = new Pop3ProxyConnectionArguments(); + arguments.AcceptedIPs = acceptedIPs; + arguments.TcpClient = client; + arguments.Certificate = serverCertificate; + arguments.LocalIpAddress = localIPAddress; + arguments.LocalPort = localPort; + arguments.LocalEnableSsl = localEnableSsl; + arguments.RemoteServerHostName = remoteServerHostName; + arguments.RemoteServerPort = remoteServerPort; + arguments.RemoteServerEnableSsl = remoteServerEnableSsl; + arguments.RemoteServerCredential = remoteServerCredential; + + // Increment the connection counter; + arguments.ConnectionId = (unchecked(++ConnectionId)).ToString(); + + // Fork the thread and continue listening for new connections. + Thread processThread = new Thread(new ParameterizedThreadStart(ProcessConnection)); + processThread.Name = "OpaqueMail POP3 Proxy Connection"; + processThread.Start(arguments); + } + return; + } + catch (Exception ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception when starting proxy: " + ex.Message, Proxy.LogLevel.Critical, LogLevel); + } + } + } + + /// + /// Stop the POP3 proxy and close all existing connections. + /// + public void Stop() + { + ProxyFunctions.Log(LogWriter, SessionId, "Stopping service.", Proxy.LogLevel.Information, LogLevel); + + Started = false; + + if (Listener != null) + Listener.Stop(); + + ProxyFunctions.Log(LogWriter, SessionId, "Service stopped.", Proxy.LogLevel.Information, LogLevel); + } + + /// + /// Start all POP3 proxy instances from the specified settings file. + /// + /// File containing the POP3 proxy settings. + public static List StartProxiesFromSettingsFile(string fileName) + { + List pop3Proxies = new List(); + + try + { + if (File.Exists(fileName)) + { + XPathDocument document = new XPathDocument(fileName); + XPathNavigator navigator = document.CreateNavigator(); + + int pop3ServiceCount = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/IMAP/ServiceCount"); + for (int i = 1; i <= pop3ServiceCount; i++) + { + Pop3ProxyArguments arguments = new Pop3ProxyArguments(); + arguments.AcceptedIPs = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/AcceptedIPs"); + + string localIpAddress = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/LocalIPAddress").ToUpper(); + switch (localIpAddress) + { + // Treat blank values as "Any". + case "": + case "ANY": + arguments.LocalIpAddress = IPAddress.Any; + break; + case "BROADCAST": + arguments.LocalIpAddress = IPAddress.Broadcast; + break; + case "IPV6ANY": + arguments.LocalIpAddress = IPAddress.IPv6Any; + break; + case "IPV6LOOPBACK": + arguments.LocalIpAddress = IPAddress.IPv6Loopback; + break; + case "LOOPBACK": + arguments.LocalIpAddress = IPAddress.Loopback; + break; + default: + // Try to parse the local IP address. If unable to, proceed to the next service instance. + if (!IPAddress.TryParse(localIpAddress, out arguments.LocalIpAddress)) + continue; + break; + } + + arguments.LocalPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/LocalPort"); + // If the port is invalid, proceed to the next service instance. + if (arguments.LocalPort < 1) + continue; + + arguments.LocalEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/POP3/Service" + i + "/LocalEnableSSL"); + + arguments.RemoteServerHostName = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerHostName"); + // If the host name is invalid, proceed to the next service instance. + if (string.IsNullOrEmpty(arguments.RemoteServerHostName)) + continue; + + arguments.RemoteServerPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerPort"); + // If the port is invalid, proceed to the next service instance. + if (arguments.RemoteServerPort < 1) + continue; + + arguments.RemoteServerEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerEnableSSL"); + + string remoteServerUsername = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerUsername"); + if (!string.IsNullOrEmpty(remoteServerUsername)) + { + arguments.RemoteServerCredential = new NetworkCredential(); + arguments.RemoteServerCredential.UserName = remoteServerUsername; + arguments.RemoteServerCredential.Password = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerPassword"); + } + + string certificateLocationValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/Location"); + StoreLocation certificateLocation = StoreLocation.LocalMachine; + if (certificateLocationValue.ToUpper() == "CURRENTUSER") + certificateLocation = StoreLocation.CurrentUser; + + // Try to load the signing certificate based on its serial number first, then fallback to its subject name. + string certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/SerialNumber"); + if (!string.IsNullOrEmpty(certificateValue)) + arguments.Certificate = CertHelper.GetCertificateBySerialNumber(certificateLocation, certificateValue); + else + { + certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/SubjectName"); + if (!string.IsNullOrEmpty(certificateValue)) + arguments.Certificate = CertHelper.GetCertificateBySubjectName(certificateLocation, certificateValue); + } + + arguments.LogFile = ProxyFunctions.GetXmlStringValue(navigator, "Settings/POP3/Service" + i + "/LogFile"); + + string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/POP3/Service" + i + "/LogLevel"); + switch (logLevel.ToUpper()) + { + case "NONE": + arguments.LogLevel = LogLevel.None; + break; + case "CRITICAL": + arguments.LogLevel = LogLevel.Critical; + break; + case "ERROR": + arguments.LogLevel = LogLevel.Error; + break; + case "RAW": + arguments.LogLevel = LogLevel.Raw; + break; + case "VERBOSE": + arguments.LogLevel = LogLevel.Verbose; + break; + case "WARNING": + arguments.LogLevel = LogLevel.Warning; + break; + case "INFORMATION": + default: + arguments.LogLevel = LogLevel.Information; + break; + } + + arguments.InstanceId = i; + + // Remember the proxy in order to close it when the service stops. + arguments.Proxy = new Pop3Proxy(); + pop3Proxies.Add(arguments.Proxy); + + Thread proxyThread = new Thread(new ParameterizedThreadStart(StartProxy)); + proxyThread.Name = "OpaqueMail POP3 Proxy"; + proxyThread.Start(arguments); + } + } + } + catch + { + // Ignore errors if the XML settings file is malformed. + } + + return pop3Proxies; + } + #endregion Public Methods + + #region Private Methods + /// + /// Handle an incoming POP3 connection, from connection to completion. + /// + /// Pop3ProxyConnectionArguments object containing all parameters for this connection. + private void ProcessConnection(object parameters) + { + // Cast the passed-in parameters back to their original objects. + Pop3ProxyConnectionArguments arguments = (Pop3ProxyConnectionArguments)parameters; + + try + { + TcpClient client = arguments.TcpClient; + Stream clientStream = client.GetStream(); + + // Capture the client's IP information. + PropertyInfo pi = clientStream.GetType().GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance); + string ip = ((Socket)pi.GetValue(clientStream, null)).RemoteEndPoint.ToString(); + if (ip.IndexOf(":") > -1) + ip = ip.Substring(0, ip.IndexOf(":")); + + // If the IP address range filter contains the localhost entry 0.0.0.0, check if the client IP is a local address and update it to 0.0.0.0 if so. + if (arguments.AcceptedIPs.IndexOf("0.0.0.0") > -1) + { + if (ip == "127.0.0.1") + ip = "0.0.0.0"; + else + { + IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress hostIP in hostEntry.AddressList) + { + if (hostIP.ToString() == ip) + { + ip = "0.0.0.0"; + break; + } + } + } + } + + // Validate that the IP address is within an accepted range. + if (!ProxyFunctions.ValidateIP(arguments.AcceptedIPs, ip)) + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection rejected from {" + ip + "} due to its IP address.", Proxy.LogLevel.Warning, LogLevel); + + Functions.SendStreamString(clientStream, new byte[Constants.SMALLBUFFERSIZE], "500 IP address [" + ip + "] rejected.\r\n"); + + if (clientStream != null) + clientStream.Dispose(); + if (client != null) + client.Close(); + + return; + } + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "New connection established from {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); + + // If supported, upgrade the session's security through a TLS handshake. + if (arguments.LocalEnableSsl) + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Starting local TLS/SSL protection for {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); + clientStream = new SslStream(clientStream); + ((SslStream)clientStream).AuthenticateAsServer(arguments.Certificate); + } + + // Connect to the remote server. + TcpClient remoteServerClient = new TcpClient(arguments.RemoteServerHostName, arguments.RemoteServerPort); + Stream remoteServerStream = remoteServerClient.GetStream(); + + // If supported, upgrade the session's security through a TLS handshake. + if (arguments.RemoteServerEnableSsl) + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Starting remote TLS/SSL protection with {" + arguments.RemoteServerHostName + "}.", Proxy.LogLevel.Information, LogLevel); + remoteServerStream = new SslStream(remoteServerStream); + ((SslStream)remoteServerStream).AuthenticateAsClient(arguments.RemoteServerHostName); + } + + // Relay server data to the client. + TransmitArguments remoteServerToClientArguments = new TransmitArguments(); + remoteServerToClientArguments.ClientStream = remoteServerStream; + remoteServerToClientArguments.RemoteServerStream = clientStream; + remoteServerToClientArguments.IsClient = false; + remoteServerToClientArguments.ConnectionId = ConnectionId.ToString(); + remoteServerToClientArguments.IPAddress = ip; + Thread remoteServerToClientThread = new Thread(new ParameterizedThreadStart(RelayData)); + remoteServerToClientThread.Name = "OpaqueMail POP3 Proxy Server to Client"; + remoteServerToClientThread.Start(remoteServerToClientArguments); + + // Relay client data to the remote server. + TransmitArguments clientToRemoteServerArguments = new TransmitArguments(); + clientToRemoteServerArguments.ClientStream = clientStream; + clientToRemoteServerArguments.RemoteServerStream = remoteServerStream; + clientToRemoteServerArguments.IsClient = true; + clientToRemoteServerArguments.ConnectionId = ConnectionId.ToString(); + clientToRemoteServerArguments.IPAddress = ip; + clientToRemoteServerArguments.Credential = arguments.RemoteServerCredential; + Thread clientToRemoteServerThread = new Thread(new ParameterizedThreadStart(RelayData)); + clientToRemoteServerThread.Name = "OpaqueMail POP3 Proxy Client to Server"; + clientToRemoteServerThread.Start(clientToRemoteServerArguments); + } + catch (SocketException ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception communicating with {" + arguments.RemoteServerHostName + "} on port {" + arguments.RemoteServerPort + "}: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + catch (Exception ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + } + + /// + /// Relay data read from one connection to another. + /// + /// A TransmitArguments object containing local and remote server parameters. + private async void RelayData(object o) + { + // Cast the passed-in parameters back to their original objects. + TransmitArguments arguments = (TransmitArguments)o; + Stream clientStream = arguments.ClientStream; + Stream remoteServerStream = arguments.RemoteServerStream; + + // A byte array to streamline bit shuffling. + char[] buffer = new char[Constants.SMALLBUFFERSIZE]; + + // Placeholder variables to track the current message being transmitted. + StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE); + + // The overall number of bytes transmitted on this connection. + ulong bytesTransmitted = 0; + + bool stillReceiving = true; + try + { + using (StreamReader clientStreamReader = new StreamReader(clientStream)) + { + using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream)) + { + remoteServerStreamWriter.AutoFlush = true; + + while (Started && stillReceiving) + { + // Read data from the source and send it to its destination. + int bytesRead = await clientStreamReader.ReadAsync(buffer, 0, Constants.SMALLBUFFERSIZE); + + if (bytesRead > 0) + { + bytesTransmitted += (ulong)bytesRead; + + // Cast the bytes received to a string. + string stringRead = new string(buffer, 0, bytesRead); + + messageBuilder.Append(stringRead); + + // If this data comes from the client, log it. Otherwise, process it. + if (arguments.IsClient) + { + bool messageRelayed = false; + + string[] commandParts = stringRead.Substring(0, stringRead.Length - 2).Split(new char[] { ' ' }, 2); + + // Optionally replace credentials with those from our settings file. + if (arguments.Credential != null && commandParts.Length == 2) + { + if (commandParts[0] == "USER") + { + await remoteServerStreamWriter.WriteAsync("USER " + arguments.Credential.UserName + "\r\n"); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: USER " + arguments.Credential.UserName, Proxy.LogLevel.Raw, LogLevel); + + messageRelayed = true; + } + else if (commandParts[0] == "PASS") + { + await remoteServerStreamWriter.WriteAsync("PASS" + arguments.Credential.Password + "\r\n"); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: PASS " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel); + + messageRelayed = true; + } + } + + if (LogLevel == Proxy.LogLevel.Verbose) + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + commandParts[0] + "} processed.", Proxy.LogLevel.Verbose, LogLevel); + + if (!messageRelayed) + { + await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel); + } + } + else + { + await remoteServerStreamWriter.WriteAsync(buffer, 0, bytesRead); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel); + + // If we see a period between two linebreaks, treat it as the end of a message. + int endPos = stringRead.IndexOf("\r\n.\r\n"); + if (endPos > -1) + { + string message = messageBuilder.ToString(); + endPos = message.IndexOf("\r\n.\r\n"); + + int lastOkPos = message.LastIndexOf("+OK", endPos); + if (lastOkPos > -1) + { + if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1) + { + Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); + processThread.Name = "OpaqueMail POP3 Proxy Signature Processor"; + ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); + processMessageArguments.MessageText = message; + processMessageArguments.ConnectionId = ConnectionId.ToString(); + processThread.Start(processMessageArguments); + } + messageBuilder.Remove(0, endPos + 5); + } + else + messageBuilder.Clear(); + } + } + } + else + stillReceiving = false; + } + } + } + } +/* catch (IOException) + { + // Ignore either stream being closed. + }*/ + catch (ObjectDisposedException) + { + // Ignore either stream being closed. + } + catch (Exception ex) + { + // Log other exceptions. + ProxyFunctions.Log(LogWriter, SessionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + finally + { + // If sending to the local client, log the connection being closed. + if (!arguments.IsClient) + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); + + if (clientStream != null) + clientStream.Dispose(); + if (remoteServerStream != null) + remoteServerStream.Dispose(); + } + } + + /// + /// Process a transmitted message to import any signing certificates for subsequent S/MIME encryption. + /// + /// A ProcessMessageArguments object containing message parameters. + private void ProcessMessage(object o) + { + ProcessMessageArguments arguments = (ProcessMessageArguments)o; + + // Only parse the message if it contains a known S/MIME content type. + string canonicalMessageText = arguments.MessageText.ToLower(); + if (canonicalMessageText.IndexOf("application/x-pkcs7-signature") > -1 || canonicalMessageText.IndexOf("application/pkcs7-mime") > -1) + { + try + { + // Parse the message. + ReadOnlyMailMessage message = new ReadOnlyMailMessage(arguments.MessageText); + + // If the message contains a signing certificate that we haven't processed on this session, import it. + if (message.SmimeSigningCertificate != null && !SmimeCertificatesReceived.Contains(message.SmimeSigningCertificate)) + { + // Import the certificate to the Local Machine store. + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Importing certificate with Serial Number {" + message.SmimeSigningCertificate.SerialNumber + "}.", Proxy.LogLevel.Information, LogLevel); + CertHelper.InstallWindowsCertificate(message.SmimeSigningCertificate, StoreLocation.LocalMachine); + + // Remember this ceriticate to avoid importing it again this session. + SmimeCertificatesReceived.Add(message.SmimeSigningCertificate); + } + } + catch (Exception ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + } + } + + /// + /// Start an individual POP3 proxy on its own thread. + /// + /// Pop3ProxyArguments object containing all parameters for this connection. + private static void StartProxy(object parameters) + { + Pop3ProxyArguments arguments = (Pop3ProxyArguments)parameters; + + // Start the proxy using passed-in settings. + arguments.Proxy.Start(arguments.AcceptedIPs, arguments.LocalIpAddress, arguments.LocalPort, arguments.LocalEnableSsl, arguments.RemoteServerHostName, arguments.RemoteServerPort, arguments.RemoteServerEnableSsl, arguments.RemoteServerCredential, arguments.LogFile, arguments.LogLevel, arguments.InstanceId); + } + #endregion Private Methods + } +} diff --git a/OpaqueMail.Net.Proxy/Properties/AssemblyInfo.cs b/OpaqueMail.Net.Proxy/Properties/AssemblyInfo.cs index 7106b90..59365df 100644 --- a/OpaqueMail.Net.Proxy/Properties/AssemblyInfo.cs +++ b/OpaqueMail.Net.Proxy/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpaqueMail.Net.Proxy")] -[assembly: AssemblyDescription("SMTP proxy to add or remove S/MIME message signing, encryption, and authentication for outbound messages.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Bert Johnson")] -[assembly: AssemblyProduct("OpaqueMail")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("0041886a-2f82-4a5d-a8e1-ade1788bcd2e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.2")] -[assembly: AssemblyFileVersion("1.4.2.2")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpaqueMail.Net.Proxy")] +[assembly: AssemblyDescription("SMTP proxy to add or remove S/MIME message signing, encryption, and authentication for outbound messages.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Bert Johnson")] +[assembly: AssemblyProduct("OpaqueMail")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0041886a-2f82-4a5d-a8e1-ade1788bcd2e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.5.0")] +[assembly: AssemblyFileVersion("1.5.0.0")] diff --git a/OpaqueMail.Net.Proxy/ProxyFunctions.cs b/OpaqueMail.Net.Proxy/ProxyFunctions.cs index cac54a2..3455e7e 100644 --- a/OpaqueMail.Net.Proxy/ProxyFunctions.cs +++ b/OpaqueMail.Net.Proxy/ProxyFunctions.cs @@ -1,310 +1,316 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.XPath; - -namespace OpaqueMail.Net.Proxy -{ - public class ProxyFunctions - { - #region Public Methods - /// - /// Process special characters in a log file name. - /// - /// Name of the log file. - /// The instance number of the proxy. - /// The final log file name, with full path, to be used. - public static string GetLogFileName(string fileName, int instanceId) - { - // Replace the {#} token with the proxy instance number. - if (instanceId == 1) - fileName = fileName.Replace("{#}", ""); - else - fileName = fileName.Replace("{#}", instanceId.ToString()); - - // If the log file location doesn't contain a directory, make it relative to where the service lives. - if (fileName.Length < 2 || (fileName[1] != ':' && !fileName.StartsWith("\\"))) - fileName = AppDomain.CurrentDomain.BaseDirectory + fileName; - - // Unless this is a UNC path, make sure the specified directory exists. - if (!fileName.StartsWith("\\")) - { - string[] pathParts = fileName.Split('\\'); - string path = pathParts[0]; - - for (int i = 1; i < pathParts.Length - 1; i++) - { - path += "\\" + pathParts[i]; - if (!Directory.Exists(path)) - Directory.CreateDirectory(path); - } - } - - StringBuilder logFileNameBuilder = new StringBuilder(Constants.TINYSBSIZE); - - DateTime now = DateTime.Now; - - int pos = 0, lastPos = 0; - while (pos > -1) - { - lastPos = pos; - pos = fileName.IndexOf("{", pos); - if (pos > -1) - { - logFileNameBuilder.Append(fileName.Substring(lastPos, pos - lastPos)); - - int endPos = fileName.IndexOf("}", pos); - if (endPos > -1) - { - try - { - // Attempt to append the .NET DateTime.ToString() representation of the variable. - logFileNameBuilder.Append(DateTime.Now.ToString(fileName.Substring(pos + 1, endPos - pos - 1))); - } - catch - { - } - - pos = endPos + 1; - } - else - pos = -1; - } - } - - if (lastPos > -1) - logFileNameBuilder.Append(fileName.Substring(lastPos)); - - return logFileNameBuilder.ToString(); - } - - /// - /// Return a boolean value from an XML document. - /// - /// An XPathNavigator within the current XmlDocument. - /// The XPath expression to evaluate. - /// A boolean representation of the setting, or false if none was found. - public static bool GetXmlBoolValue(XPathNavigator navigator, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - { - bool value = false; - bool.TryParse(valueNavigator.Value, out value); - return value; - } - else - return false; - } - - /// - /// Return an integer value from an XML document. - /// - /// An XPathNavigator within the current XmlDocument. - /// The XPath expression to evaluate. - /// An integer representation of the setting, or 0 if none was found. - public static int GetXmlIntValue(XPathNavigator navigator, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - { - int value = 0; - int.TryParse(valueNavigator.Value, out value); - return value; - } - else - return 0; - } - - /// - /// Return a string value from an XML document. - /// - /// An XPathNavigator within the current XmlDocument. - /// The XPath expression to evaluate. - /// A string representation of the setting, or an empty string if none was found. - public static string GetXmlStringValue(XPathNavigator navigator, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - return valueNavigator.Value; - else - return ""; - } - - /// - /// Log an event or exception. - /// - /// The current session's unique ID. - /// The message to log. - public static void Log(StreamWriter LogWriter, string sessionId, string message, LogLevel minimalLogLevel, LogLevel currentLogLevel) - { - if ((int)currentLogLevel >= (int)minimalLogLevel) - { - if (LogWriter != null) - { - lock (LogWriter) - { - LogWriter.Write("[" + DateTime.Now + "]\t" + sessionId + "\t\t" + minimalLogLevel.ToString().ToUpper() + "\t" + message + (message.EndsWith("\r\n") ? "" : "\r\n")); - LogWriter.Flush(); - } - } - } - } - - /// - /// Log an event or exception. - /// - /// The current session's unique ID. - /// The current connection's unique ID. - /// The message to log. - public static void Log(StreamWriter LogWriter, string sessionId, string connectionId, string message, LogLevel minimalLogLevel, LogLevel currentLogLevel) - { - if ((int)currentLogLevel >= (int)minimalLogLevel) - { - if (LogWriter != null) - { - lock (LogWriter) - { - LogWriter.Write("[" + DateTime.Now + "]\t" + sessionId + "\t" + connectionId + "\t" + minimalLogLevel.ToString().ToUpper() + "\t" + message + (message.EndsWith("\r\n") ? "" : "\r\n")); - LogWriter.Flush(); - } - } - } - } - - /// - /// Validate that an IP address is within an acceptable range. - /// - /// Range of accepted IPs. - /// IP address to check. - /// True if the IP provided falls within the accepted IP range.. - public static bool ValidateIP(string acceptedIPs, string ipAddress) - { - // Remove leading or trailing whitespace. - acceptedIPs = acceptedIPs.Trim(); - - // If there's no accepted IP range string or if all are accepted, return true. - if (string.IsNullOrEmpty(acceptedIPs) || acceptedIPs == "*" || acceptedIPs.ToUpper() == "ANY") - return true; - - int[] ipAddressOctets = ExplodeIPAddress(ipAddress); - - string[] acceptedIPparts = acceptedIPs.Split(','); - foreach (string acceptedIPpart in acceptedIPparts) - { - string canonicalAcceptedIPpart = acceptedIPpart.Trim().ToUpper(); - - // If all are accepted, return true; - if (canonicalAcceptedIPpart == "*" || canonicalAcceptedIPpart == "ANY") - return true; - - // Don't process blank strings. - if (!string.IsNullOrEmpty(canonicalAcceptedIPpart)) - { - bool matchedPart = true; - - // If this is an IP range, check that the address falls between them. - if (canonicalAcceptedIPpart.IndexOf("-") > -1) - { - string[] ipRangeParts = canonicalAcceptedIPpart.Split(new char[] { '-' }, 2); - - int[] ipRangeMinOctets = ExplodeIPAddress(ipRangeParts[0]); - int[] ipRangeMaxOctets = ExplodeIPAddress(ipRangeParts[1]); - - // Make sure we're above the range's minimum value. - int octetsToCompare = ipAddressOctets.Length <= ipRangeMinOctets.Length ? ipAddressOctets.Length : ipRangeMinOctets.Length; - for (int i = 0; i < ipAddressOctets.Length; i++) - { - // If the octet is a wildcard, we've successfully matched. - if (ipRangeMinOctets[i] == -1) - break; - - // If the octet is below the minimum value, we haven't matched. - if (ipAddressOctets[i] < ipRangeMinOctets[i]) - { - matchedPart = false; - break; - } - } - - // Only check the range's maximum value if we're above the range's minimum value. - if (matchedPart) - { - octetsToCompare = ipAddressOctets.Length <= ipRangeMaxOctets.Length ? ipAddressOctets.Length : ipRangeMaxOctets.Length; - for (int i = 0; i < ipAddressOctets.Length; i++) - { - // If the octet is a wildcard, we've successfully matched. - if (ipRangeMaxOctets[i] == -1) - break; - - // If the octet is above the maximum value, we haven't matched. - if (ipAddressOctets[i] > ipRangeMaxOctets[i]) - { - matchedPart = false; - break; - } - } - - // If we've made it this far, we've successfully matched the IP. - if (matchedPart) - return true; - } - } - else - { - // At this point, we're matching against a specific IP. - int[] ipTargetOctets = ExplodeIPAddress(canonicalAcceptedIPpart); - - // Compare to the target value. - int octetsToCompare = ipAddressOctets.Length <= ipTargetOctets.Length ? ipAddressOctets.Length : ipTargetOctets.Length; - for (int i = 0; i < ipAddressOctets.Length; i++) - { - // If the octet is a wildcard, we've successfully matched. - if (ipTargetOctets[i] == -1) - break; - - // If the octet isn't equal to the target value, we haven't matched. - if (ipAddressOctets[i] != ipTargetOctets[i]) - { - matchedPart = false; - break; - } - } - - // If we've made it this far, we've successfully matched the IP. - if (matchedPart) - return true; - } - } - } - - return false; - } - #endregion Public Methods - - #region Private Methods - /// - /// Divide an IP address string into its discrete components, representing wildcards with -1. - /// - /// The IP address string to process. - /// An array of integers representing the IP octets, with a -1 specifying a wildcard character. - private static int[] ExplodeIPAddress(string ipAddress) - { - string[] ipAddressParts = ipAddress.Split('.'); - - int[] ipAddressOctets = new int[ipAddressParts.Length]; - - for (int i = 0; i < ipAddressParts.Length; i++) - { - if (!int.TryParse(ipAddressParts[i], out ipAddressOctets[i])) - ipAddressOctets[i] = -1; - } - - return ipAddressOctets; - } - #endregion Private Methods - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.XPath; + +namespace OpaqueMail.Net.Proxy +{ + public class ProxyFunctions + { + #region Public Methods + /// + /// Process special characters in a log file name. + /// + /// Name of the log file. + /// The instance number of the proxy. + /// The final log file name, with full path, to be used. + public static string GetLogFileName(string fileName, int instanceId, string localIpAddress, string remoteServerHostName, int localPort, int remotePort) + { + // Replace the {#} token with the proxy instance number. + if (instanceId == 1) + fileName = fileName.Replace("{#}", ""); + else + fileName = fileName.Replace("{#}", instanceId.ToString()); + + // Replace server and port {} tokens. + fileName = fileName.Replace("{localIpAddress}", localIpAddress); + fileName = fileName.Replace("{remoteServerHostName}", remoteServerHostName); + fileName = fileName.Replace("{localPort}", localPort.ToString()); + fileName = fileName.Replace("{remotePort}", remotePort.ToString()); + + // If the log file location doesn't contain a directory, make it relative to where the service lives. + if (fileName.Length < 2 || (fileName[1] != ':' && !fileName.StartsWith("\\"))) + fileName = AppDomain.CurrentDomain.BaseDirectory + fileName; + + // Unless this is a UNC path, make sure the specified directory exists. + if (!fileName.StartsWith("\\")) + { + string[] pathParts = fileName.Split('\\'); + string path = pathParts[0]; + + for (int i = 1; i < pathParts.Length - 1; i++) + { + path += "\\" + pathParts[i]; + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + } + } + + StringBuilder logFileNameBuilder = new StringBuilder(Constants.TINYSBSIZE); + + DateTime now = DateTime.Now; + + int pos = 0, lastPos = 0; + while (pos > -1) + { + lastPos = pos; + pos = fileName.IndexOf("{", pos); + if (pos > -1) + { + logFileNameBuilder.Append(fileName.Substring(lastPos, pos - lastPos)); + + int endPos = fileName.IndexOf("}", pos); + if (endPos > -1) + { + try + { + // Attempt to append the .NET DateTime.ToString() representation of the variable. + logFileNameBuilder.Append(DateTime.Now.ToString(fileName.Substring(pos + 1, endPos - pos - 1))); + } + catch + { + } + + pos = endPos + 1; + } + else + pos = -1; + } + } + + if (lastPos > -1) + logFileNameBuilder.Append(fileName.Substring(lastPos)); + + return logFileNameBuilder.ToString(); + } + + /// + /// Return a boolean value from an XML document. + /// + /// An XPathNavigator within the current XmlDocument. + /// The XPath expression to evaluate. + /// A boolean representation of the setting, or false if none was found. + public static bool GetXmlBoolValue(XPathNavigator navigator, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + { + bool value = false; + bool.TryParse(valueNavigator.Value, out value); + return value; + } + else + return false; + } + + /// + /// Return an integer value from an XML document. + /// + /// An XPathNavigator within the current XmlDocument. + /// The XPath expression to evaluate. + /// An integer representation of the setting, or 0 if none was found. + public static int GetXmlIntValue(XPathNavigator navigator, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + { + int value = 0; + int.TryParse(valueNavigator.Value, out value); + return value; + } + else + return 0; + } + + /// + /// Return a string value from an XML document. + /// + /// An XPathNavigator within the current XmlDocument. + /// The XPath expression to evaluate. + /// A string representation of the setting, or an empty string if none was found. + public static string GetXmlStringValue(XPathNavigator navigator, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + return valueNavigator.Value; + else + return ""; + } + + /// + /// Log an event or exception. + /// + /// The current session's unique ID. + /// The message to log. + public static void Log(StreamWriter LogWriter, string sessionId, string message, LogLevel minimalLogLevel, LogLevel currentLogLevel) + { + if ((int)currentLogLevel >= (int)minimalLogLevel) + { + if (LogWriter != null) + { + lock (LogWriter) + { + LogWriter.Write("[" + DateTime.Now + "]\t" + sessionId + "\t\t" + minimalLogLevel.ToString().ToUpper() + "\t" + message + (message.EndsWith("\r\n") ? "" : "\r\n")); + LogWriter.Flush(); + } + } + } + } + + /// + /// Log an event or exception. + /// + /// The current session's unique ID. + /// The current connection's unique ID. + /// The message to log. + public static void Log(StreamWriter LogWriter, string sessionId, string connectionId, string message, LogLevel minimalLogLevel, LogLevel currentLogLevel) + { + if ((int)currentLogLevel >= (int)minimalLogLevel) + { + if (LogWriter != null) + { + lock (LogWriter) + { + LogWriter.Write("[" + DateTime.Now + "]\t" + sessionId + "\t" + connectionId + "\t" + minimalLogLevel.ToString().ToUpper() + "\t" + message + (message.EndsWith("\r\n") ? "" : "\r\n")); + LogWriter.Flush(); + } + } + } + } + + /// + /// Validate that an IP address is within an acceptable range. + /// + /// Range of accepted IPs. + /// IP address to check. + /// True if the IP provided falls within the accepted IP range.. + public static bool ValidateIP(string acceptedIPs, string ipAddress) + { + // Remove leading or trailing whitespace. + acceptedIPs = acceptedIPs.Trim(); + + // If there's no accepted IP range string or if all are accepted, return true. + if (string.IsNullOrEmpty(acceptedIPs) || acceptedIPs == "*" || acceptedIPs.ToUpper() == "ANY") + return true; + + int[] ipAddressOctets = ExplodeIPAddress(ipAddress); + + string[] acceptedIPparts = acceptedIPs.Split(','); + foreach (string acceptedIPpart in acceptedIPparts) + { + string canonicalAcceptedIPpart = acceptedIPpart.Trim().ToUpper(); + + // If all are accepted, return true; + if (canonicalAcceptedIPpart == "*" || canonicalAcceptedIPpart == "ANY") + return true; + + // Don't process blank strings. + if (!string.IsNullOrEmpty(canonicalAcceptedIPpart)) + { + bool matchedPart = true; + + // If this is an IP range, check that the address falls between them. + if (canonicalAcceptedIPpart.IndexOf("-") > -1) + { + string[] ipRangeParts = canonicalAcceptedIPpart.Split(new char[] { '-' }, 2); + + int[] ipRangeMinOctets = ExplodeIPAddress(ipRangeParts[0]); + int[] ipRangeMaxOctets = ExplodeIPAddress(ipRangeParts[1]); + + // Make sure we're above the range's minimum value. + int octetsToCompare = ipAddressOctets.Length <= ipRangeMinOctets.Length ? ipAddressOctets.Length : ipRangeMinOctets.Length; + for (int i = 0; i < ipAddressOctets.Length; i++) + { + // If the octet is a wildcard, we've successfully matched. + if (ipRangeMinOctets[i] == -1) + break; + + // If the octet is below the minimum value, we haven't matched. + if (ipAddressOctets[i] < ipRangeMinOctets[i]) + { + matchedPart = false; + break; + } + } + + // Only check the range's maximum value if we're above the range's minimum value. + if (matchedPart) + { + octetsToCompare = ipAddressOctets.Length <= ipRangeMaxOctets.Length ? ipAddressOctets.Length : ipRangeMaxOctets.Length; + for (int i = 0; i < ipAddressOctets.Length; i++) + { + // If the octet is a wildcard, we've successfully matched. + if (ipRangeMaxOctets[i] == -1) + break; + + // If the octet is above the maximum value, we haven't matched. + if (ipAddressOctets[i] > ipRangeMaxOctets[i]) + { + matchedPart = false; + break; + } + } + + // If we've made it this far, we've successfully matched the IP. + if (matchedPart) + return true; + } + } + else + { + // At this point, we're matching against a specific IP. + int[] ipTargetOctets = ExplodeIPAddress(canonicalAcceptedIPpart); + + // Compare to the target value. + int octetsToCompare = ipAddressOctets.Length <= ipTargetOctets.Length ? ipAddressOctets.Length : ipTargetOctets.Length; + for (int i = 0; i < ipAddressOctets.Length; i++) + { + // If the octet is a wildcard, we've successfully matched. + if (ipTargetOctets[i] == -1) + break; + + // If the octet isn't equal to the target value, we haven't matched. + if (ipAddressOctets[i] != ipTargetOctets[i]) + { + matchedPart = false; + break; + } + } + + // If we've made it this far, we've successfully matched the IP. + if (matchedPart) + return true; + } + } + } + + return false; + } + #endregion Public Methods + + #region Private Methods + /// + /// Divide an IP address string into its discrete components, representing wildcards with -1. + /// + /// The IP address string to process. + /// An array of integers representing the IP octets, with a -1 specifying a wildcard character. + private static int[] ExplodeIPAddress(string ipAddress) + { + string[] ipAddressParts = ipAddress.Split('.'); + + int[] ipAddressOctets = new int[ipAddressParts.Length]; + + for (int i = 0; i < ipAddressParts.Length; i++) + { + if (!int.TryParse(ipAddressParts[i], out ipAddressOctets[i])) + ipAddressOctets[i] = -1; + } + + return ipAddressOctets; + } + #endregion Private Methods + } +} diff --git a/OpaqueMail.Net.Proxy/SMTP/SmtpProxy.cs b/OpaqueMail.Net.Proxy/SMTP/SmtpProxy.cs index 2c3c4d3..941685f 100644 --- a/OpaqueMail.Net.Proxy/SMTP/SmtpProxy.cs +++ b/OpaqueMail.Net.Proxy/SMTP/SmtpProxy.cs @@ -1,894 +1,913 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml.XPath; - -namespace OpaqueMail.Net.Proxy -{ - public class SmtpProxy : ProxyBase - { - #region Private Members - private Dictionary CertificateReminders = new Dictionary(); - #endregion Private Members - - #region Public Methods - /// - /// Start an SMTP proxy instance. - /// - /// IP addresses to accept connections from. - /// Local IP address to bind to. - /// Local port to listen on. - /// Whether the local server supports TLS/SSL. - /// Remote server hostname to forward all SMTP messages to. - /// Remote server port to connect to. - /// Whether the remote SMTP server requires TLS/SSL. - public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl) - { - Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, null, SmimeSettingsMode.BestEffort, true, true, true, true, true, "", LogLevel.None, 0); - } - - /// - /// Start an SMTP proxy instance. - /// - /// IP addresses to accept connections from. - /// Local IP address to bind to. - /// Local port to listen on. - /// Whether the local server supports TLS/SSL. - /// Remote server hostname to forward all SMTP messages to. - /// Remote server port to connect to. - /// Whether the remote SMTP server requires TLS/SSL. - /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. - /// File where event logs and exception information will be written. - /// Proxy logging level, determining how much information is logged. - /// Proxy logging level, determining how much information is logged. - /// The instance number of the proxy. - public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string logFile, LogLevel logLevel, int instanceId) - { - Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, remoteServerCredential, SmimeSettingsMode.BestEffort, true, true, true, true, true, logFile, logLevel, instanceId); - } - - /// - /// Start an SMTP proxy instance. - /// - /// IP addresses to accept connections from. - /// Local IP address to bind to. - /// Local port to listen on. - /// Whether the local server supports TLS/SSL. - /// Remote server hostname to forward all SMTP messages to. - /// Remote server port to connect to. - /// Whether the remote SMTP server requires TLS/SSL. - /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. - /// Whether S/MIME settings for encryption and signing are explicitly required or only preferred. - /// Whether the e-mail's envelope should be encrypted. When SmimeSign is true, encryption is the second S/MIME operation. - /// Whether the e-mail should be signed. When true, signing is the first S/MIME operation - /// Whether the e-mail should be triple-wrapped by signing, then encrypting the envelope, then signing the encrypted envelope. - /// Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied. - /// Send e-mail reminders when a signing certificate is due to expire within 30 days. - public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, SmimeSettingsMode smimeSettingsMode, bool smimeSigned, bool smimeEncryptedEnvelope, bool smimeTripleWrapped, bool smimeRemovePreviousOperations, bool sendCertificateReminders) - { - Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, remoteServerCredential, smimeSettingsMode, smimeSigned, smimeEncryptedEnvelope, smimeTripleWrapped, smimeRemovePreviousOperations, sendCertificateReminders, "", LogLevel.None, 0); - } - - /// - /// Start an SMTP proxy instance. - /// - /// IP addresses to accept connections from. - /// Local IP address to bind to. - /// Local port to listen on. - /// Whether the local server supports TLS/SSL. - /// Remote server hostname to forward all SMTP messages to. - /// Remote server port to connect to. - /// Whether the remote SMTP server requires TLS/SSL. - /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. - /// Whether S/MIME settings for encryption and signing are explicitly required or only preferred. - /// Whether the e-mail's envelope should be encrypted. When SmimeSign is true, encryption is the second S/MIME operation. - /// Whether the e-mail should be signed. When true, signing is the first S/MIME operation - /// Whether the e-mail should be triple-wrapped by signing, then encrypting the envelope, then signing the encrypted envelope. - /// Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied. - /// Send e-mail reminders when a signing certificate is due to expire within 30 days. - /// File where event logs and exception information will be written. - /// Proxy logging level, determining how much information is logged. - /// The instance number of the proxy. - public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, SmimeSettingsMode smimeSettingsMode, bool smimeSigned, bool smimeEncryptedEnvelope, bool smimeTripleWrapped, bool smimeRemovePreviousOperations, bool sendCertificateReminders, string logFile, LogLevel logLevel, int instanceId) - { - // Create the log writer. - string logFileName = ""; - if (!string.IsNullOrEmpty(logFile)) - { - logFileName = ProxyFunctions.GetLogFileName(logFile, instanceId); - LogWriter = new StreamWriter(logFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); - LogLevel = logLevel; - } - - // Make sure the remote server isn't an infinite loop back to this server. - string fqdn = Functions.GetLocalFQDN(); - if (remoteServerHostName.ToUpper() == fqdn.ToUpper() && remoteServerPort == localPort) - { - ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server host name {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); - return; - } - IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); - foreach (IPAddress hostIP in hostEntry.AddressList) - { - if (remoteServerHostName == hostIP.ToString() && remoteServerPort == localPort) - { - ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server hostname {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); - return; - } - } - - ProxyFunctions.Log(LogWriter, SessionId, "Starting service.", Proxy.LogLevel.Information, LogLevel); - - // Attempt to start up to 3 times in case another service using the port is shutting down. - int startAttempts = 0; - while (startAttempts < 3) - { - startAttempts++; - - // If we've failed to start once, wait an extra 10 seconds. - if (startAttempts > 1) - { - ProxyFunctions.Log(LogWriter, SessionId, "Attempting to start for the " + (startAttempts == 2 ? "2nd" : "3rd") + " time.", Proxy.LogLevel.Information, LogLevel); - Thread.Sleep(10000 * startAttempts); - } - - try - { - X509Certificate serverCertificate = null; - - // Generate a unique session ID for logging. - SessionId = Guid.NewGuid().ToString(); - ConnectionId = 0; - - // If local SSL is supported via STARTTLS, ensure we have a valid server certificate. - if (localEnableSsl) - { - serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, fqdn); - // In case the service as running as the current user, check the Current User certificate store as well. - if (serverCertificate == null) - serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, fqdn); - - // If no certificate was found, generate a self-signed certificate. - if (serverCertificate == null) - { - ProxyFunctions.Log(LogWriter, SessionId, "No signing certificate found, so generating new certificate.", Proxy.LogLevel.Warning, LogLevel); - - List oids = new List(); - oids.Add("1.3.6.1.5.5.7.3.1"); // Server Authentication. - - // Generate the certificate with a duration of 10 years, 4096-bits, and a key usage of server authentication. - serverCertificate = CertHelper.CreateSelfSignedCertificate(fqdn, fqdn, true, 4096, 10, oids); - - ProxyFunctions.Log(LogWriter, SessionId, "New certificate generated with Serial Number {" + Encoding.UTF8.GetString(serverCertificate.GetSerialNumber()) + "}.", Proxy.LogLevel.Information, LogLevel); - } - } - - // Start listening on the specified port and IP address. - Listener = new TcpListener(localIPAddress, localPort); - Listener.Start(); - - ProxyFunctions.Log(LogWriter, SessionId, "Service started.", Proxy.LogLevel.Information, LogLevel); - ProxyFunctions.Log(LogWriter, SessionId, "Listening on address {" + localIPAddress.ToString() + "}, port {" + localPort + "}.", Proxy.LogLevel.Information, LogLevel); - - Started = true; - - // Accept client requests, forking each into its own thread. - while (Started) - { - TcpClient client = Listener.AcceptTcpClient(); - - string newLogFileName = ProxyFunctions.GetLogFileName(logFile, instanceId); - if (newLogFileName != logFileName) - { - LogWriter.Close(); - LogWriter = new StreamWriter(newLogFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); - LogWriter.AutoFlush = true; - } - - // Prepare the arguments for our new thread. - SmtpProxyConnectionArguments arguments = new SmtpProxyConnectionArguments(); - arguments.AcceptedIPs = acceptedIPs; - arguments.TcpClient = client; - arguments.Certificate = serverCertificate; - arguments.LocalIpAddress = localIPAddress; - arguments.LocalPort = localPort; - arguments.LocalEnableSsl = localEnableSsl; - arguments.RemoteServerHostName = remoteServerHostName; - arguments.RemoteServerPort = remoteServerPort; - arguments.RemoteServerEnableSsl = remoteServerEnableSsl; - arguments.RemoteServerCredential = remoteServerCredential; - - arguments.SmimeSettingsMode = smimeSettingsMode; - arguments.SmimeSigned = smimeSigned; - arguments.SmimeEncryptedEnvelope = smimeEncryptedEnvelope; - arguments.SmimeTripleWrapped = smimeTripleWrapped; - arguments.SmimeRemovePreviousOperations = smimeRemovePreviousOperations; - - arguments.SendCertificateReminders = sendCertificateReminders; - - // Increment the connection counter; - arguments.ConnectionId = (unchecked(++ConnectionId)).ToString(); - - // Fork the thread and continue listening for new connections. - Thread processThread = new Thread(new ParameterizedThreadStart(ProcessConnection)); - processThread.Name = "OpaqueMail SMTP Proxy Connection"; - processThread.Start(arguments); - } - return; - } - catch (Exception ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception when starting proxy: " + ex.Message, Proxy.LogLevel.Critical, LogLevel); - } - } - } - - /// - /// Stop the SMTP proxy and close all existing connections. - /// - public void Stop() - { - ProxyFunctions.Log(LogWriter, SessionId, "Stopping service.", Proxy.LogLevel.Information, LogLevel); - - Started = false; - - if (Listener != null) - Listener.Stop(); - - ProxyFunctions.Log(LogWriter, SessionId, "Service stopped.", Proxy.LogLevel.Information, LogLevel); - } - - /// - /// Start all SMTP proxy instances from the specified settings file. - /// - /// File containing the SMTP proxy settings. - public static List StartProxiesFromSettingsFile(string fileName) - { - List smtpProxies = new List(); - - try - { - if (File.Exists(fileName)) - { - XPathDocument document = new XPathDocument(fileName); - XPathNavigator navigator = document.CreateNavigator(); - - int smtpServiceCount = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/SMTP/ServiceCount"); - for (int i = 1; i <= smtpServiceCount; i++) - { - SmtpProxyArguments arguments = new SmtpProxyArguments(); - arguments.AcceptedIPs = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/AcceptedIPs"); - - string localIpAddress = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/LocalIPAddress").ToUpper(); - switch (localIpAddress) - { - // Treat blank values as "Any". - case "": - case "ANY": - arguments.LocalIpAddress = IPAddress.Any; - break; - case "BROADCAST": - arguments.LocalIpAddress = IPAddress.Broadcast; - break; - case "IPV6ANY": - arguments.LocalIpAddress = IPAddress.IPv6Any; - break; - case "IPV6LOOPBACK": - arguments.LocalIpAddress = IPAddress.IPv6Loopback; - break; - case "LOOPBACK": - arguments.LocalIpAddress = IPAddress.Loopback; - break; - default: - // Try to parse the local IP address. If unable to, proceed to the next service instance. - if (!IPAddress.TryParse(localIpAddress, out arguments.LocalIpAddress)) - continue; - break; - } - - arguments.LocalPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/LocalPort"); - // If the port is invalid, proceed to the next service instance. - if (arguments.LocalPort < 1) - continue; - - arguments.LocalEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/LocalEnableSSL"); - - arguments.RemoteServerHostName = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerHostName"); - // If the host name is invalid, proceed to the next service instance. - if (string.IsNullOrEmpty(arguments.RemoteServerHostName)) - continue; - - arguments.RemoteServerPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerPort"); - // If the port is invalid, proceed to the next service instance. - if (arguments.RemoteServerPort < 1) - continue; - - arguments.RemoteServerEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerEnableSSL"); - - string remoteServerUsername = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerUsername"); - if (!string.IsNullOrEmpty(remoteServerUsername)) - { - arguments.RemoteServerCredential = new NetworkCredential(); - arguments.RemoteServerCredential.UserName = remoteServerUsername; - arguments.RemoteServerCredential.Password = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerPassword"); - } - - string certificateLocationValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/Location"); - StoreLocation certificateLocation = StoreLocation.LocalMachine; - if (certificateLocationValue.ToUpper() == "CURRENTUSER") - certificateLocation = StoreLocation.CurrentUser; - - // Try to load the signing certificate based on its serial number first, then fallback to its subject name. - string certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/SerialNumber"); - if (!string.IsNullOrEmpty(certificateValue)) - arguments.Certificate = CertHelper.GetCertificateBySerialNumber(certificateLocation, certificateValue); - else - { - certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/SubjectName"); - if (!string.IsNullOrEmpty(certificateValue)) - arguments.Certificate = CertHelper.GetCertificateBySubjectName(certificateLocation, certificateValue); - } - - arguments.SendCertificateReminders = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SendCertificateReminders"); - - arguments.SmimeEncryptedEnvelope = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SMIMEEncrypt"); - arguments.SmimeRemovePreviousOperations = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SMIMERemovePreviousOperations"); - arguments.SmimeSigned = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SMIMESign"); - arguments.SmimeTripleWrapped = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SMIMETripleWrap"); - - // Look up the S/MIME settings mode, defaulting to requiring exact settings. - string smimeSettingsMode = ProxyFunctions.GetXmlStringValue(navigator, "Settings/SMTP/Service" + i + "/SMIMESettingsMode"); - if (smimeSettingsMode.ToUpper() == "BESTEFFORT") - arguments.SmimeSettingsMode = SmimeSettingsMode.BestEffort; - else - arguments.SmimeSettingsMode = SmimeSettingsMode.RequireExactSettings; - - arguments.LogFile = ProxyFunctions.GetXmlStringValue(navigator, "Settings/SMTP/Service" + i + "/LogFile"); - - string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/SMTP/Service" + i + "/LogLevel"); - switch (logLevel.ToUpper()) - { - case "NONE": - arguments.LogLevel = LogLevel.None; - break; - case "CRITICAL": - arguments.LogLevel = LogLevel.Critical; - break; - case "ERROR": - arguments.LogLevel = LogLevel.Error; - break; - case "RAW": - arguments.LogLevel = LogLevel.Raw; - break; - case "VERBOSE": - arguments.LogLevel = LogLevel.Verbose; - break; - case "WARNING": - arguments.LogLevel = LogLevel.Warning; - break; - case "INFORMATION": - default: - arguments.LogLevel = LogLevel.Information; - break; - } - - arguments.InstanceId = i; - - // Remember the proxy in order to close it when the service stops. - arguments.Proxy = new SmtpProxy(); - smtpProxies.Add(arguments.Proxy); - - Thread proxyThread = new Thread(new ParameterizedThreadStart(StartProxy)); - proxyThread.Name = "OpaqueMail SMTP Proxy"; - proxyThread.Start(arguments); - } - } - } - catch - { - } - - return smtpProxies; - } - #endregion Public Methods - - #region Private Methods - /// - /// Handle an incoming SMTP connection, from connection to completion. - /// - /// SmtpProxyConnectionArguments object containing all parameters for this connection. - private async void ProcessConnection(object parameters) - { - // Cast the passed-in parameters back to their original objects. - SmtpProxyConnectionArguments arguments = (SmtpProxyConnectionArguments)parameters; - - // The overall number of bytes transmitted on this connection. - ulong bytesTransmitted = 0; - - TcpClient client = null; - Stream clientStream = null; - StreamReader clientStreamReader = null; - StreamWriter clientStreamWriter = null; - try - { - client = arguments.TcpClient; - clientStream = client.GetStream(); - - // Placeholder variables to be populated throughout the client session. - NetworkCredential credential = arguments.RemoteServerCredential; - string fromAddress = ""; - string identity = ""; - string ip = ""; - List toList = new List(); - bool sending = false, inPlainAuth = false, inLoginAuth = false; - - // A byte array to streamline bit shuffling. - char[] buffer = new char[Constants.SMALLBUFFERSIZE]; - - // Capture the client's IP information. - PropertyInfo pi = clientStream.GetType().GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance); - ip = ((Socket)pi.GetValue(clientStream, null)).RemoteEndPoint.ToString(); - if (ip.IndexOf(":") > -1) - ip = ip.Substring(0, ip.IndexOf(":")); - - // If the IP address range filter contains the localhost entry 0.0.0.0, check if the client IP is a local address and update it to 0.0.0.0 if so. - if (arguments.AcceptedIPs.IndexOf("0.0.0.0") > -1) - { - if (ip == "127.0.0.1") - ip = "0.0.0.0"; - else - { - IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); - foreach (IPAddress hostIP in hostEntry.AddressList) - { - if (hostIP.ToString() == ip) - { - ip = "0.0.0.0"; - break; - } - } - } - } - - clientStreamReader = new StreamReader(clientStream); - clientStreamWriter = new StreamWriter(clientStream); - clientStreamWriter.AutoFlush = true; - - // Validate that the IP address is within an accepted range. - if (!ProxyFunctions.ValidateIP(arguments.AcceptedIPs, ip)) - { - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection rejected from {" + ip + "} due to its IP address.", Proxy.LogLevel.Warning, LogLevel); - - await Functions.SendStreamStringAsync(clientStreamWriter, "500 IP address [" + ip + "] rejected.\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 IP address [" + ip + "] rejected.", Proxy.LogLevel.Raw, LogLevel); - - if (clientStream != null) - clientStream.Dispose(); - if (client != null) - client.Close(); - - return; - } - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "New connection established from {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); - - // Send our welcome message. - await Functions.SendStreamStringAsync(clientStreamWriter, "220 " + WelcomeMessage + "\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "220 " + WelcomeMessage, Proxy.LogLevel.Raw, LogLevel); - - // Instantiate an SmtpClient for sending messages to the remote server. - using (SmtpClient smtpClient = new SmtpClient(arguments.RemoteServerHostName, arguments.RemoteServerPort)) - { - smtpClient.EnableSsl = arguments.RemoteServerEnableSsl; - smtpClient.Credentials = arguments.RemoteServerCredential; - - // Loop through each received command. - string command = ""; - bool stillReceiving = true; - while (Started && stillReceiving) - { - int bytesRead = await clientStreamReader.ReadAsync(buffer, 0, Constants.SMALLBUFFERSIZE); - - if (bytesRead > 0) - { - bytesTransmitted += (ulong)bytesRead; - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "C: " + new string(buffer, 0, bytesRead), Proxy.LogLevel.Raw, LogLevel); - - command += new string(buffer, 0, bytesRead); - - if (command.EndsWith("\r\n")) - { - // Handle continuations of current "DATA" commands. - if (sending) - { - // Handle the finalization of a "DATA" command. - if (command.EndsWith("\r\n.\r\n")) - { - sending = false; - - string messageFrom = "", messageSubject = "", messageSize = ""; - try - { - ReadOnlyMailMessage message = new ReadOnlyMailMessage(command.Substring(0, command.Length - 5), ReadOnlyMailMessageProcessingFlags.IncludeRawHeaders | ReadOnlyMailMessageProcessingFlags.IncludeRawBody); - - // If the received message is already signed or encrypted and we don't want to remove previous S/MIME operations, forward it as-is. - string contentType = message.ContentType; - if ((contentType.StartsWith("application/pkcs7-mime") || contentType.StartsWith("application/x-pkcs7-mime") || contentType.StartsWith("application/x-pkcs7-signature")) && !arguments.SmimeRemovePreviousOperations) - { - message.SmimeSigned = message.SmimeEncryptedEnvelope = message.SmimeTripleWrapped = false; - await smtpClient.SendAsync(message); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: " + message, Proxy.LogLevel.Raw, LogLevel); - } - else - { - messageFrom = message.From.Address; - messageSubject = message.Subject; - messageSize = message.Size.ToString(); - - if (LogLevel == Proxy.LogLevel.Verbose) - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Forwarding message from {" + message.From.Address + "} with subject {" + message.Subject + "} and size of {" + message.Size + "}.", Proxy.LogLevel.Verbose, LogLevel); - - foreach (string toListAddress in toList) - { - if (!message.AllRecipients.Contains(toListAddress)) - { - message.AllRecipients.Add(toListAddress); - message.Bcc.Add(toListAddress); - } - } - - // Attempt to sign and encrypt the envelopes of all messages, but still send if unable to. - message.SmimeSettingsMode = SmimeSettingsMode.BestEffort; - - // Apply S/MIME settings. - message.SmimeSigned = arguments.SmimeSigned; - message.SmimeEncryptedEnvelope = arguments.SmimeEncryptedEnvelope; - message.SmimeTripleWrapped = arguments.SmimeTripleWrapped; - - // Look up the S/MIME signing certificate for the current sender. If it doesn't exist, create one. - message.SmimeSigningCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, message.From.Address); - if (message.SmimeSigningCertificate == null) - message.SmimeSigningCertificate = CertHelper.CreateSelfSignedCertificate("E=" + message.From.Address, message.From.Address, true, 4096, 10); - - // Send the message. - await smtpClient.SendAsync(message.AsMailMessage()); - - // Check the signing certificate's expiration to determine if we should send a reminder. - if (arguments.SendCertificateReminders && message.SmimeSigningCertificate != null) - { - string expirationDateString = message.SmimeSigningCertificate.GetExpirationDateString(); - TimeSpan expirationTime = DateTime.Parse(expirationDateString) - DateTime.Now; - if (expirationTime.TotalDays < 30) - { - bool sendReminder = true; - if (CertificateReminders.ContainsKey(message.SmimeSigningCertificate)) - { - TimeSpan timeSinceLastReminder = DateTime.Now - CertificateReminders[message.SmimeSigningCertificate]; - if (timeSinceLastReminder.TotalHours < 24) - sendReminder = false; - } - - // Send the reminder message. - if (sendReminder) - { - MailMessage reminderMessage = new MailMessage(message.From, message.From); - reminderMessage.Subject = "OpaqueMail: S/MIME Certificate Expires " + expirationDateString; - reminderMessage.Body = "Your OpaqueMail S/MIME Certificate will expire in " + ((int)expirationTime.TotalDays) + " days on " + expirationDateString + ".\r\n\r\n" + - "Certificate Subject Name: " + message.SmimeSigningCertificate.Subject + "\r\n" + - "Certificate Serial Number: " + message.SmimeSigningCertificate.SerialNumber + "\r\n" + - "Certificate Issuer: " + message.SmimeSigningCertificate.Issuer + "\r\n\r\n" + - "Please renew or enroll a new certificate to continue protecting your e-mail privacy.\r\n\r\n" + - "This is an automated message sent from the OpaqueMail Proxy on " + Functions.GetLocalFQDN() + ". " + - "For more information, visit http://opaquemail.org/."; - - reminderMessage.SmimeEncryptedEnvelope = message.SmimeEncryptedEnvelope; - reminderMessage.SmimeEncryptionOptionFlags = message.SmimeEncryptionOptionFlags; - reminderMessage.SmimeSettingsMode = message.SmimeSettingsMode; - reminderMessage.SmimeSigned = message.SmimeSigned; - reminderMessage.SmimeSigningCertificate = message.SmimeSigningCertificate; - reminderMessage.SmimeSigningOptionFlags = message.SmimeSigningOptionFlags; - reminderMessage.SmimeTripleWrapped = message.SmimeTripleWrapped; - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Certificate with Serial Number {" + message.SmimeSigningCertificate.SerialNumber + "} expiring. Sending reminder to {" + message.From.Address + "}.", Proxy.LogLevel.Information, LogLevel); - - await smtpClient.SendAsync(reminderMessage); - - CertificateReminders[message.SmimeSigningCertificate] = DateTime.Now; - } - } - } - } - - await Functions.SendStreamStringAsync(clientStreamWriter, "250 Forwarded\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 Forwarded", Proxy.LogLevel.Raw, LogLevel); - - if (LogLevel == Proxy.LogLevel.Verbose) - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Message from {" + message.From.Address + "} with subject {" + message.Subject + "} and size of {" + message.Size + "} successfully forwarded.", Proxy.LogLevel.Verbose, LogLevel); - } - catch (Exception ex) - { - // Report if an exception was encountering sending the message. - Functions.SendStreamString(clientStreamWriter, "500 Error occurred when forwarding\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 Error occurred when forwarding", Proxy.LogLevel.Raw, LogLevel); - - if (LogLevel == Proxy.LogLevel.Verbose) - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Error when forwarding message from {" + messageFrom + "} with subject {" + messageSubject + "} and size of {" + messageSize + "}. Exception: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - command = ""; - } - } - // Handle continuations of current "AUTH PLAIN" commands. - else if (inPlainAuth) - { - inPlainAuth = false; - // Split up an AUTH PLAIN handshake into its components. - string authString = Encoding.UTF8.GetString(Convert.FromBase64String(command)); - string[] authStringParts = authString.Split(new char[] { '\0' }, 3); - if (authStringParts.Length > 2 && arguments.RemoteServerCredential == null) - smtpClient.Credentials = new NetworkCredential(authStringParts[1], authStringParts[2]); - - await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); - - command = ""; - } - // Handle continuations of current "AUTH LOGIN" commands. - else if (inLoginAuth) - { - if (smtpClient.Credentials == null) - { - // Handle the username being received for the first time. - smtpClient.Credentials = new NetworkCredential(); - ((NetworkCredential)smtpClient.Credentials).UserName = Functions.FromBase64(command.Substring(0, command.Length - 2)); - - await Functions.SendStreamStringAsync(clientStreamWriter, "334 UGFzc3dvcmQ6\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 334 UGFzc3dvcmQ6", Proxy.LogLevel.Raw, LogLevel); - } - else - { - // Handle the password. - inLoginAuth = false; - ((NetworkCredential)smtpClient.Credentials).Password = Functions.FromBase64(command.Substring(0, command.Length - 2)); - - await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); - } - command = ""; - } - else - { - // Otherwise, look at the verb of the incoming command. - string[] commandParts = command.Substring(0, command.Length - 2).Replace("\r", "").Split(new char[] { ' ' }, 2); - - if (LogLevel == Proxy.LogLevel.Verbose) - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Command {" + commandParts[0] + "} received.", Proxy.LogLevel.Verbose, LogLevel); - - switch (commandParts[0].ToUpper()) - { - case "AUTH": - // Support authentication. - if (commandParts.Length > 1) - { - commandParts = command.Substring(0, command.Length - 2).Replace("\r", "").Split(new char[] { ' ' }); - switch (commandParts[1].ToUpper()) - { - case "PLAIN": - // Prepare to handle a continuation command. - inPlainAuth = true; - await Functions.SendStreamStringAsync(clientStreamWriter, "334 Proceed\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 334 Proceed", Proxy.LogLevel.Raw, LogLevel); - - break; - case "LOGIN": - inLoginAuth = true; - if (commandParts.Length > 2) - { - // Parse the username and request a password. - smtpClient.Credentials = new NetworkCredential(); - ((NetworkCredential)smtpClient.Credentials).UserName = Functions.FromBase64(commandParts[2]); - - await Functions.SendStreamStringAsync(clientStreamWriter, "334 UGFzc3dvcmQ6\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 334 UGFzc3dvcmQ6", Proxy.LogLevel.Raw, LogLevel); - } - else - { - // Request a username only. - await Functions.SendStreamStringAsync(clientStreamWriter, "334 VXNlcm5hbWU6\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 334 VXNlcm5hbWU6", Proxy.LogLevel.Raw, LogLevel); - } - break; - default: - // Split up an AUTH PLAIN handshake into its components. - string authString = Encoding.UTF8.GetString(Convert.FromBase64String(commandParts[1].Substring(6))); - string[] authStringParts = authString.Split(new char[] { '\0' }, 3); - if (authStringParts.Length > 2 && arguments.RemoteServerCredential == null) - smtpClient.Credentials = new NetworkCredential(authStringParts[1], authStringParts[2]); - - await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); - break; - } - } - else - { - await Functions.SendStreamStringAsync(clientStreamWriter, "500 Unknown verb\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 Unknown verb", Proxy.LogLevel.Raw, LogLevel); - } - break; - case "DATA": - // Prepare to handle continuation data. - sending = true; - command = command.Substring(6); - await Functions.SendStreamStringAsync(clientStreamWriter, "354 Send message content; end with .\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 354 Send message content; end with .", Proxy.LogLevel.Raw, LogLevel); - break; - case "EHLO": - // Proceed with the login and send a list of supported commands. - if (commandParts.Length > 1) - identity = commandParts[1] + " "; - if (arguments.LocalEnableSsl) - { - await Functions.SendStreamStringAsync(clientStreamWriter, "250-Hello " + identity + "[" + ip + "], please proceed\r\n250-AUTH LOGIN PLAIN\r\n250-RSET\r\n250 STARTTLS\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250-Hello " + identity + "[" + ip + "], please proceed\r\n250-AUTH LOGIN PLAIN\r\n250-RSET\r\n250 STARTTLS", Proxy.LogLevel.Raw, LogLevel); - } - else - { - await Functions.SendStreamStringAsync(clientStreamWriter, "250-Hello " + identity + "[" + ip + "], please proceed\r\n250-AUTH LOGIN PLAIN\r\n250 RSET\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250-Hello " + identity + "[" + ip + "], please proceed\r\n250-AUTH LOGIN PLAIN\r\n250 RSET", Proxy.LogLevel.Raw, LogLevel); - } - break; - case "HELO": - // Proceed with the login. - if (commandParts.Length > 1) - identity = commandParts[1]; - await Functions.SendStreamStringAsync(clientStreamWriter, "250 Hello " + identity + " [" + ip + "], please proceed\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: Hello " + identity + " [" + ip + "], please proceed", Proxy.LogLevel.Raw, LogLevel); - break; - case "MAIL": - case "SAML": - case "SEND": - case "SOML": - // Accept the from address. - if (commandParts.Length > 1 && commandParts[1].Length > 5) - fromAddress = commandParts[1].Substring(5); - await Functions.SendStreamStringAsync(clientStreamWriter, "250 OK\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 OK", Proxy.LogLevel.Raw, LogLevel); - break; - case "NOOP": - // Prolong the current session. - await Functions.SendStreamStringAsync(clientStreamWriter, "250 Still here\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 Still here", Proxy.LogLevel.Raw, LogLevel); - break; - case "PASS": - // Support authentication. - if (commandParts.Length > 1 && arguments.RemoteServerCredential == null) - ((NetworkCredential)smtpClient.Credentials).Password = commandParts[1]; - await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); - break; - case "QUIT": - // Wait one second then force the current connection closed. - await Functions.SendStreamStringAsync(clientStreamWriter, "221 Bye\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 221 Bye", Proxy.LogLevel.Raw, LogLevel); - - Thread.Sleep(1000); - - if (clientStream != null) - clientStream.Dispose(); - if (client != null) - client.Close(); - break; - case "RCPT": - // Acknolwedge recipients. - if (commandParts.Length > 1 && commandParts[1].Length > 6) - toList.Add(commandParts[1].Substring(5, commandParts[1].Length - 6)); - await Functions.SendStreamStringAsync(clientStreamWriter, "250 OK\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 OK", Proxy.LogLevel.Raw, LogLevel); - break; - case "RSET": - // Reset the current message arguments. - fromAddress = ""; - toList.Clear(); - - await Functions.SendStreamStringAsync(clientStreamWriter, "250 OK\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 OK", Proxy.LogLevel.Raw, LogLevel); - break; - case "STARTTLS": - // If supported, upgrade the session's security through a TLS handshake. - if (arguments.LocalEnableSsl) - { - await Functions.SendStreamStringAsync(clientStreamWriter, "220 Go ahead\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 220 Go ahead", Proxy.LogLevel.Raw, LogLevel); - - if (!(clientStream is SslStream)) - { - clientStream = new SslStream(clientStream); - ((SslStream)clientStream).AuthenticateAsServer(arguments.Certificate); - - clientStreamReader = new StreamReader(clientStream); - clientStreamWriter = new StreamWriter(clientStream); - clientStreamWriter.AutoFlush = true; - } - } - else - { - await Functions.SendStreamStringAsync(clientStreamWriter, "500 Unknown verb\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 Unknown verb", Proxy.LogLevel.Raw, LogLevel); - } - break; - case "USER": - // Support authentication. - if (commandParts.Length > 1 && arguments.RemoteServerCredential == null) - ((NetworkCredential)smtpClient.Credentials).UserName = commandParts[1]; - - await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); - break; - case "VRFY": - // Notify that we can't verify addresses. - await Functions.SendStreamStringAsync(clientStreamWriter, "252 I'm just a proxy\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 252 I'm just a proxy", Proxy.LogLevel.Raw, LogLevel); - break; - default: - await Functions.SendStreamStringAsync(clientStreamWriter, "500 Unknown verb\r\n"); - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 Unknown verb", Proxy.LogLevel.Raw, LogLevel); - break; - } - - command = ""; - } - } - } - else - stillReceiving = false; - } - } - - ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + ip + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); - } - catch (SocketException ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception communicating with {" + arguments.RemoteServerHostName + "} on port {" + arguments.RemoteServerPort + "}: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - catch (Exception ex) - { - ProxyFunctions.Log(LogWriter, SessionId, "Exception: " + ex.Message, Proxy.LogLevel.Error, LogLevel); - } - finally - { - // Clean up after any unexpectedly closed connections. - if (clientStreamWriter != null) - clientStreamWriter.Dispose(); - if (clientStreamReader != null) - clientStreamReader.Dispose(); - if (clientStream != null) - clientStream.Dispose(); - if (client != null) - client.Close(); - } - } - - /// - /// Start an individual SMTP proxy on its own thread. - /// - /// SmtpProxyArguments object containing all parameters for this connection. - private static void StartProxy(object parameters) - { - SmtpProxyArguments arguments = (SmtpProxyArguments)parameters; - - // Start the proxy using passed-in settings. - arguments.Proxy.Start(arguments.AcceptedIPs, arguments.LocalIpAddress, arguments.LocalPort, arguments.LocalEnableSsl, arguments.RemoteServerHostName, arguments.RemoteServerPort, arguments.RemoteServerEnableSsl, arguments.RemoteServerCredential, arguments.SmimeSettingsMode, arguments.SmimeSigned, arguments.SmimeEncryptedEnvelope, arguments.SmimeTripleWrapped, arguments.SmimeRemovePreviousOperations, arguments.SendCertificateReminders, arguments.LogFile, arguments.LogLevel, arguments.InstanceId); - } - #endregion Private Methods - } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Security; +using System.Net.Sockets; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.XPath; + +namespace OpaqueMail.Net.Proxy +{ + public class SmtpProxy : ProxyBase + { + #region Private Members + private Dictionary CertificateReminders = new Dictionary(); + #endregion Private Members + + #region Public Methods + /// + /// Start an SMTP proxy instance. + /// + /// IP addresses to accept connections from. + /// Local IP address to bind to. + /// Local port to listen on. + /// Whether the local server supports TLS/SSL. + /// Remote server hostname to forward all SMTP messages to. + /// Remote server port to connect to. + /// Whether the remote SMTP server requires TLS/SSL. + public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl) + { + Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, null, "", SmimeSettingsMode.BestEffort, true, true, true, true, true, "", LogLevel.None, 0); + } + + /// + /// Start an SMTP proxy instance. + /// + /// IP addresses to accept connections from. + /// Local IP address to bind to. + /// Local port to listen on. + /// Whether the local server supports TLS/SSL. + /// Remote server hostname to forward all SMTP messages to. + /// Remote server port to connect to. + /// Whether the remote SMTP server requires TLS/SSL. + /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. + /// (Optional) "From" address for all sent messages. When supplied, it will override any values sent from the client. + /// File where event logs and exception information will be written. + /// Proxy logging level, determining how much information is logged. + /// Proxy logging level, determining how much information is logged. + /// The instance number of the proxy. + public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string remoteServerFrom, string logFile, LogLevel logLevel, int instanceId) + { + Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, remoteServerCredential, remoteServerFrom, SmimeSettingsMode.BestEffort, true, true, true, true, true, logFile, logLevel, instanceId); + } + + /// + /// Start an SMTP proxy instance. + /// + /// IP addresses to accept connections from. + /// Local IP address to bind to. + /// Local port to listen on. + /// Whether the local server supports TLS/SSL. + /// Remote server hostname to forward all SMTP messages to. + /// Remote server port to connect to. + /// Whether the remote SMTP server requires TLS/SSL. + /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. + /// (Optional) "From" address for all sent messages. When supplied, it will override any values sent from the client. + /// Whether S/MIME settings for encryption and signing are explicitly required or only preferred. + /// Whether the e-mail's envelope should be encrypted. When SmimeSign is true, encryption is the second S/MIME operation. + /// Whether the e-mail should be signed. When true, signing is the first S/MIME operation + /// Whether the e-mail should be triple-wrapped by signing, then encrypting the envelope, then signing the encrypted envelope. + /// Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied. + /// Send e-mail reminders when a signing certificate is due to expire within 30 days. + public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string remoteServerFrom, SmimeSettingsMode smimeSettingsMode, bool smimeSigned, bool smimeEncryptedEnvelope, bool smimeTripleWrapped, bool smimeRemovePreviousOperations, bool sendCertificateReminders) + { + Start(acceptedIPs, localIPAddress, localPort, localEnableSsl, remoteServerHostName, remoteServerPort, remoteServerEnableSsl, remoteServerCredential, remoteServerFrom, smimeSettingsMode, smimeSigned, smimeEncryptedEnvelope, smimeTripleWrapped, smimeRemovePreviousOperations, sendCertificateReminders, "", LogLevel.None, 0); + } + + /// + /// Start an SMTP proxy instance. + /// + /// IP addresses to accept connections from. + /// Local IP address to bind to. + /// Local port to listen on. + /// Whether the local server supports TLS/SSL. + /// Remote server hostname to forward all SMTP messages to. + /// Remote server port to connect to. + /// Whether the remote SMTP server requires TLS/SSL. + /// (Optional) Credentials to be used for all connections to the remote SMTP server. When set, this overrides any credentials passed locally. + /// (Optional) "From" address for all sent messages. When supplied, it will override any values sent from the client. + /// Whether S/MIME settings for encryption and signing are explicitly required or only preferred. + /// Whether the e-mail's envelope should be encrypted. When SmimeSign is true, encryption is the second S/MIME operation. + /// Whether the e-mail should be signed. When true, signing is the first S/MIME operation + /// Whether the e-mail should be triple-wrapped by signing, then encrypting the envelope, then signing the encrypted envelope. + /// Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied. + /// Send e-mail reminders when a signing certificate is due to expire within 30 days. + /// File where event logs and exception information will be written. + /// Proxy logging level, determining how much information is logged. + /// The instance number of the proxy. + public void Start(string acceptedIPs, IPAddress localIPAddress, int localPort, bool localEnableSsl, string remoteServerHostName, int remoteServerPort, bool remoteServerEnableSsl, NetworkCredential remoteServerCredential, string remoteServerFrom, SmimeSettingsMode smimeSettingsMode, bool smimeSigned, bool smimeEncryptedEnvelope, bool smimeTripleWrapped, bool smimeRemovePreviousOperations, bool sendCertificateReminders, string logFile, LogLevel logLevel, int instanceId) + { + // Create the log writer. + string logFileName = ""; + if (!string.IsNullOrEmpty(logFile)) + { + logFileName = ProxyFunctions.GetLogFileName(logFile, instanceId, localIPAddress.ToString(), remoteServerHostName, localPort, remoteServerPort); + LogWriter = new StreamWriter(logFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); + LogWriter.AutoFlush = true; + + LogLevel = logLevel; + } + + // Make sure the remote server isn't an infinite loop back to this server. + string fqdn = Functions.GetLocalFQDN(); + if (remoteServerHostName.ToUpper() == fqdn.ToUpper() && remoteServerPort == localPort) + { + ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server host name {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); + return; + } + IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress hostIP in hostEntry.AddressList) + { + if (remoteServerHostName == hostIP.ToString() && remoteServerPort == localPort) + { + ProxyFunctions.Log(LogWriter, SessionId, "Cannot start service because the remote server hostname {" + remoteServerHostName + "} and port {" + remoteServerPort.ToString() + "} is the same as this proxy, which would cause an infinite loop.", Proxy.LogLevel.Critical, LogLevel); + return; + } + } + + ProxyFunctions.Log(LogWriter, SessionId, "Starting service.", Proxy.LogLevel.Information, LogLevel); + + // Attempt to start up to 3 times in case another service using the port is shutting down. + int startAttempts = 0; + while (startAttempts < 3) + { + startAttempts++; + + // If we've failed to start once, wait an extra 10 seconds. + if (startAttempts > 1) + { + ProxyFunctions.Log(LogWriter, SessionId, "Attempting to start for the " + (startAttempts == 2 ? "2nd" : "3rd") + " time.", Proxy.LogLevel.Information, LogLevel); + Thread.Sleep(10000 * startAttempts); + } + + try + { + X509Certificate serverCertificate = null; + + // Generate a unique session ID for logging. + SessionId = Guid.NewGuid().ToString(); + ConnectionId = 0; + + // If local SSL is supported via STARTTLS, ensure we have a valid server certificate. + if (localEnableSsl) + { + serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, fqdn); + // In case the service as running as the current user, check the Current User certificate store as well. + if (serverCertificate == null) + serverCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, fqdn); + + // If no certificate was found, generate a self-signed certificate. + if (serverCertificate == null) + { + ProxyFunctions.Log(LogWriter, SessionId, "No signing certificate found, so generating new certificate.", Proxy.LogLevel.Warning, LogLevel); + + List oids = new List(); + oids.Add("1.3.6.1.5.5.7.3.1"); // Server Authentication. + + // Generate the certificate with a duration of 10 years, 4096-bits, and a key usage of server authentication. + serverCertificate = CertHelper.CreateSelfSignedCertificate(fqdn, fqdn, true, 4096, 10, oids); + + ProxyFunctions.Log(LogWriter, SessionId, "New certificate generated with Serial Number {" + Encoding.UTF8.GetString(serverCertificate.GetSerialNumber()) + "}.", Proxy.LogLevel.Information, LogLevel); + } + } + + // Start listening on the specified port and IP address. + Listener = new TcpListener(localIPAddress, localPort); + Listener.Start(); + + ProxyFunctions.Log(LogWriter, SessionId, "Service started.", Proxy.LogLevel.Information, LogLevel); + ProxyFunctions.Log(LogWriter, SessionId, "Listening on address {" + localIPAddress.ToString() + "}, port {" + localPort + "}.", Proxy.LogLevel.Information, LogLevel); + + Started = true; + + // Accept client requests, forking each into its own thread. + while (Started) + { + TcpClient client = Listener.AcceptTcpClient(); + + string newLogFileName = ProxyFunctions.GetLogFileName(logFile, instanceId, localIPAddress.ToString(), remoteServerHostName, localPort, remoteServerPort); + if (newLogFileName != logFileName) + { + LogWriter.Close(); + LogWriter = new StreamWriter(newLogFileName, true, Encoding.UTF8, Constants.SMALLBUFFERSIZE); + LogWriter.AutoFlush = true; + } + + // Prepare the arguments for our new thread. + SmtpProxyConnectionArguments arguments = new SmtpProxyConnectionArguments(); + arguments.AcceptedIPs = acceptedIPs; + arguments.TcpClient = client; + arguments.Certificate = serverCertificate; + arguments.LocalIpAddress = localIPAddress; + arguments.LocalPort = localPort; + arguments.LocalEnableSsl = localEnableSsl; + arguments.RemoteServerHostName = remoteServerHostName; + arguments.RemoteServerPort = remoteServerPort; + arguments.RemoteServerEnableSsl = remoteServerEnableSsl; + arguments.RemoteServerCredential = remoteServerCredential; + arguments.RemoteServerFrom = remoteServerFrom; + + arguments.SmimeSettingsMode = smimeSettingsMode; + arguments.SmimeSigned = smimeSigned; + arguments.SmimeEncryptedEnvelope = smimeEncryptedEnvelope; + arguments.SmimeTripleWrapped = smimeTripleWrapped; + arguments.SmimeRemovePreviousOperations = smimeRemovePreviousOperations; + + arguments.SendCertificateReminders = sendCertificateReminders; + + // Increment the connection counter; + arguments.ConnectionId = (unchecked(++ConnectionId)).ToString(); + + // Fork the thread and continue listening for new connections. + Thread processThread = new Thread(new ParameterizedThreadStart(ProcessConnection)); + processThread.Name = "OpaqueMail SMTP Proxy Connection"; + processThread.Start(arguments); + } + return; + } + catch (Exception ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception when starting proxy: " + ex.Message, Proxy.LogLevel.Critical, LogLevel); + } + } + } + + /// + /// Stop the SMTP proxy and close all existing connections. + /// + public void Stop() + { + ProxyFunctions.Log(LogWriter, SessionId, "Stopping service.", Proxy.LogLevel.Information, LogLevel); + + Started = false; + + if (Listener != null) + Listener.Stop(); + + ProxyFunctions.Log(LogWriter, SessionId, "Service stopped.", Proxy.LogLevel.Information, LogLevel); + } + + /// + /// Start all SMTP proxy instances from the specified settings file. + /// + /// File containing the SMTP proxy settings. + public static List StartProxiesFromSettingsFile(string fileName) + { + List smtpProxies = new List(); + + try + { + if (File.Exists(fileName)) + { + XPathDocument document = new XPathDocument(fileName); + XPathNavigator navigator = document.CreateNavigator(); + + int smtpServiceCount = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/SMTP/ServiceCount"); + for (int i = 1; i <= smtpServiceCount; i++) + { + SmtpProxyArguments arguments = new SmtpProxyArguments(); + arguments.AcceptedIPs = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/AcceptedIPs"); + + string localIpAddress = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/LocalIPAddress").ToUpper(); + switch (localIpAddress) + { + // Treat blank values as "Any". + case "": + case "ANY": + arguments.LocalIpAddress = IPAddress.Any; + break; + case "BROADCAST": + arguments.LocalIpAddress = IPAddress.Broadcast; + break; + case "IPV6ANY": + arguments.LocalIpAddress = IPAddress.IPv6Any; + break; + case "IPV6LOOPBACK": + arguments.LocalIpAddress = IPAddress.IPv6Loopback; + break; + case "LOOPBACK": + arguments.LocalIpAddress = IPAddress.Loopback; + break; + default: + // Try to parse the local IP address. If unable to, proceed to the next service instance. + if (!IPAddress.TryParse(localIpAddress, out arguments.LocalIpAddress)) + continue; + break; + } + + arguments.LocalPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/LocalPort"); + // If the port is invalid, proceed to the next service instance. + if (arguments.LocalPort < 1) + continue; + + arguments.LocalEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/LocalEnableSSL"); + + arguments.RemoteServerHostName = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerHostName"); + // If the host name is invalid, proceed to the next service instance. + if (string.IsNullOrEmpty(arguments.RemoteServerHostName)) + continue; + + arguments.RemoteServerPort = ProxyFunctions.GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerPort"); + // If the port is invalid, proceed to the next service instance. + if (arguments.RemoteServerPort < 1) + continue; + + arguments.RemoteServerEnableSsl = ProxyFunctions.GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerEnableSSL"); + + string remoteServerUsername = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerUsername"); + if (!string.IsNullOrEmpty(remoteServerUsername)) + { + arguments.RemoteServerCredential = new NetworkCredential(); + arguments.RemoteServerCredential.UserName = remoteServerUsername; + arguments.RemoteServerCredential.Password = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerPassword"); + } + + arguments.RemoteServerFrom = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerFrom"); + + string certificateLocationValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/Location"); + StoreLocation certificateLocation = StoreLocation.LocalMachine; + if (certificateLocationValue.ToUpper() == "CURRENTUSER") + certificateLocation = StoreLocation.CurrentUser; + + // Try to load the signing certificate based on its serial number first, then fallback to its subject name. + string certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/SerialNumber"); + if (!string.IsNullOrEmpty(certificateValue)) + arguments.Certificate = CertHelper.GetCertificateBySerialNumber(certificateLocation, certificateValue); + else + { + certificateValue = ProxyFunctions.GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/SubjectName"); + if (!string.IsNullOrEmpty(certificateValue)) + arguments.Certificate = CertHelper.GetCertificateBySubjectName(certificateLocation, certificateValue); + } + + arguments.SendCertificateReminders = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SendCertificateReminders"); + + arguments.SmimeEncryptedEnvelope = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SMIMEEncrypt"); + arguments.SmimeRemovePreviousOperations = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SMIMERemovePreviousOperations"); + arguments.SmimeSigned = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SMIMESign"); + arguments.SmimeTripleWrapped = ProxyFunctions.GetXmlBoolValue(navigator, "Settings/SMTP/Service" + i + "/SMIMETripleWrap"); + + // Look up the S/MIME settings mode, defaulting to requiring exact settings. + string smimeSettingsMode = ProxyFunctions.GetXmlStringValue(navigator, "Settings/SMTP/Service" + i + "/SMIMESettingsMode"); + if (smimeSettingsMode.ToUpper() == "BESTEFFORT") + arguments.SmimeSettingsMode = SmimeSettingsMode.BestEffort; + else + arguments.SmimeSettingsMode = SmimeSettingsMode.RequireExactSettings; + + arguments.LogFile = ProxyFunctions.GetXmlStringValue(navigator, "Settings/SMTP/Service" + i + "/LogFile"); + + string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/SMTP/Service" + i + "/LogLevel"); + switch (logLevel.ToUpper()) + { + case "NONE": + arguments.LogLevel = LogLevel.None; + break; + case "CRITICAL": + arguments.LogLevel = LogLevel.Critical; + break; + case "ERROR": + arguments.LogLevel = LogLevel.Error; + break; + case "RAW": + arguments.LogLevel = LogLevel.Raw; + break; + case "VERBOSE": + arguments.LogLevel = LogLevel.Verbose; + break; + case "WARNING": + arguments.LogLevel = LogLevel.Warning; + break; + case "INFORMATION": + default: + arguments.LogLevel = LogLevel.Information; + break; + } + + arguments.InstanceId = i; + + // Remember the proxy in order to close it when the service stops. + arguments.Proxy = new SmtpProxy(); + smtpProxies.Add(arguments.Proxy); + + Thread proxyThread = new Thread(new ParameterizedThreadStart(StartProxy)); + proxyThread.Name = "OpaqueMail SMTP Proxy"; + proxyThread.Start(arguments); + } + } + } + catch + { + } + + return smtpProxies; + } + #endregion Public Methods + + #region Private Methods + /// + /// Handle an incoming SMTP connection, from connection to completion. + /// + /// SmtpProxyConnectionArguments object containing all parameters for this connection. + private async void ProcessConnection(object parameters) + { + // Cast the passed-in parameters back to their original objects. + SmtpProxyConnectionArguments arguments = (SmtpProxyConnectionArguments)parameters; + + // The overall number of bytes transmitted on this connection. + ulong bytesTransmitted = 0; + + TcpClient client = null; + Stream clientStream = null; + StreamReader clientStreamReader = null; + StreamWriter clientStreamWriter = null; + + string ip = ""; + + try + { + client = arguments.TcpClient; + clientStream = client.GetStream(); + + // Placeholder variables to be populated throughout the client session. + NetworkCredential credential = arguments.RemoteServerCredential; + string fromAddress = ""; + string identity = ""; + List toList = new List(); + bool sending = false, inPlainAuth = false, inLoginAuth = false; + + // A byte array to streamline bit shuffling. + char[] buffer = new char[Constants.SMALLBUFFERSIZE]; + + // Capture the client's IP information. + PropertyInfo pi = clientStream.GetType().GetProperty("Socket", BindingFlags.NonPublic | BindingFlags.Instance); + ip = ((Socket)pi.GetValue(clientStream, null)).RemoteEndPoint.ToString(); + if (ip.IndexOf(":") > -1) + ip = ip.Substring(0, ip.IndexOf(":")); + + // If the IP address range filter contains the localhost entry 0.0.0.0, check if the client IP is a local address and update it to 0.0.0.0 if so. + if (arguments.AcceptedIPs.IndexOf("0.0.0.0") > -1) + { + if (ip == "127.0.0.1") + ip = "0.0.0.0"; + else + { + IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress hostIP in hostEntry.AddressList) + { + if (hostIP.ToString() == ip) + { + ip = "0.0.0.0"; + break; + } + } + } + } + + clientStreamReader = new StreamReader(clientStream); + clientStreamWriter = new StreamWriter(clientStream); + clientStreamWriter.AutoFlush = true; + + // Validate that the IP address is within an accepted range. + if (!ProxyFunctions.ValidateIP(arguments.AcceptedIPs, ip)) + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection rejected from {" + ip + "} due to its IP address.", Proxy.LogLevel.Warning, LogLevel); + + await Functions.SendStreamStringAsync(clientStreamWriter, "500 IP address [" + ip + "] rejected.\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 IP address [" + ip + "] rejected.", Proxy.LogLevel.Raw, LogLevel); + + if (clientStream != null) + clientStream.Dispose(); + if (client != null) + client.Close(); + + return; + } + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "New connection established from {" + ip + "}.", Proxy.LogLevel.Information, LogLevel); + + // Send our welcome message. + await Functions.SendStreamStringAsync(clientStreamWriter, "220 " + WelcomeMessage + "\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "220 " + WelcomeMessage, Proxy.LogLevel.Raw, LogLevel); + + // Instantiate an SmtpClient for sending messages to the remote server. + using (SmtpClient smtpClient = new SmtpClient(arguments.RemoteServerHostName, arguments.RemoteServerPort)) + { + smtpClient.EnableSsl = arguments.RemoteServerEnableSsl; + smtpClient.Credentials = arguments.RemoteServerCredential; + + // Loop through each received command. + string command = ""; + bool stillReceiving = true; + while (Started && stillReceiving) + { + int bytesRead = await clientStreamReader.ReadAsync(buffer, 0, Constants.SMALLBUFFERSIZE); + + if (bytesRead > 0) + { + bytesTransmitted += (ulong)bytesRead; + + command += new string(buffer, 0, bytesRead); + + if (command.EndsWith("\r\n")) + { + // Handle continuations of current "DATA" commands. + if (sending) + { + // Handle the finalization of a "DATA" command. + if (command.EndsWith("\r\n.\r\n")) + { + sending = false; + + string messageFrom = "", messageSubject = "", messageSize = ""; + try + { + ReadOnlyMailMessage message = new ReadOnlyMailMessage(command.Substring(0, command.Length - 5), ReadOnlyMailMessageProcessingFlags.IncludeRawHeaders | ReadOnlyMailMessageProcessingFlags.IncludeRawBody); + + if (!string.IsNullOrEmpty(arguments.RemoteServerFrom)) + message.From = Functions.FromMailAddressString(arguments.RemoteServerFrom)[0]; + + // If the received message is already signed or encrypted and we don't want to remove previous S/MIME operations, forward it as-is. + string contentType = message.ContentType; + if ((contentType.StartsWith("application/pkcs7-mime") || contentType.StartsWith("application/x-pkcs7-mime") || contentType.StartsWith("application/x-pkcs7-signature")) && !arguments.SmimeRemovePreviousOperations) + { + message.SmimeSigned = message.SmimeEncryptedEnvelope = message.SmimeTripleWrapped = false; + await smtpClient.SendAsync(message); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: " + message, Proxy.LogLevel.Raw, LogLevel); + } + else + { + messageFrom = message.From.Address; + messageSubject = message.Subject; + messageSize = message.Size.ToString("N0"); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Forwarding message from {" + message.From.Address + "} with subject {" + message.Subject + "} and size of {" + message.Size.ToString("N0") + "}.", Proxy.LogLevel.Verbose, LogLevel); + + foreach (string toListAddress in toList) + { + if (!message.AllRecipients.Contains(toListAddress)) + { + message.AllRecipients.Add(toListAddress); + message.Bcc.Add(toListAddress); + } + } + + // Attempt to sign and encrypt the envelopes of all messages, but still send if unable to. + message.SmimeSettingsMode = SmimeSettingsMode.BestEffort; + + // Apply S/MIME settings. + message.SmimeSigned = arguments.SmimeSigned; + message.SmimeEncryptedEnvelope = arguments.SmimeEncryptedEnvelope; + message.SmimeTripleWrapped = arguments.SmimeTripleWrapped; + + // Look up the S/MIME signing certificate for the current sender. If it doesn't exist, create one. + message.SmimeSigningCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, message.From.Address); + if (message.SmimeSigningCertificate == null) + message.SmimeSigningCertificate = CertHelper.CreateSelfSignedCertificate("E=" + message.From.Address, message.From.Address, true, 4096, 10); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "C: " + Functions.ReplaceBetween(message.RawHeaders, "From: ", "\r\n", Functions.ToMailAddressString(message.From)) + "\r\n\r\n" + message.RawBody, Proxy.LogLevel.Raw, LogLevel); + + // Send the message. + await smtpClient.SendAsync(message.AsMailMessage()); + + // Check the signing certificate's expiration to determine if we should send a reminder. + if (arguments.SendCertificateReminders && message.SmimeSigningCertificate != null) + { + string expirationDateString = message.SmimeSigningCertificate.GetExpirationDateString(); + TimeSpan expirationTime = DateTime.Parse(expirationDateString) - DateTime.Now; + if (expirationTime.TotalDays < 30) + { + bool sendReminder = true; + if (CertificateReminders.ContainsKey(message.SmimeSigningCertificate)) + { + TimeSpan timeSinceLastReminder = DateTime.Now - CertificateReminders[message.SmimeSigningCertificate]; + if (timeSinceLastReminder.TotalHours < 24) + sendReminder = false; + } + + // Send the reminder message. + if (sendReminder) + { + MailMessage reminderMessage = new MailMessage(message.From, message.From); + reminderMessage.Subject = "OpaqueMail: S/MIME Certificate Expires " + expirationDateString; + reminderMessage.Body = "Your OpaqueMail S/MIME Certificate will expire in " + ((int)expirationTime.TotalDays) + " days on " + expirationDateString + ".\r\n\r\n" + + "Certificate Subject Name: " + message.SmimeSigningCertificate.Subject + "\r\n" + + "Certificate Serial Number: " + message.SmimeSigningCertificate.SerialNumber + "\r\n" + + "Certificate Issuer: " + message.SmimeSigningCertificate.Issuer + "\r\n\r\n" + + "Please renew or enroll a new certificate to continue protecting your e-mail privacy.\r\n\r\n" + + "This is an automated message sent from the OpaqueMail Proxy on " + Functions.GetLocalFQDN() + ". " + + "For more information, visit http://opaquemail.org/."; + + reminderMessage.SmimeEncryptedEnvelope = message.SmimeEncryptedEnvelope; + reminderMessage.SmimeEncryptionOptionFlags = message.SmimeEncryptionOptionFlags; + reminderMessage.SmimeSettingsMode = message.SmimeSettingsMode; + reminderMessage.SmimeSigned = message.SmimeSigned; + reminderMessage.SmimeSigningCertificate = message.SmimeSigningCertificate; + reminderMessage.SmimeSigningOptionFlags = message.SmimeSigningOptionFlags; + reminderMessage.SmimeTripleWrapped = message.SmimeTripleWrapped; + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Certificate with Serial Number {" + message.SmimeSigningCertificate.SerialNumber + "} expiring. Sending reminder to {" + message.From.Address + "}.", Proxy.LogLevel.Information, LogLevel); + + await smtpClient.SendAsync(reminderMessage); + + CertificateReminders[message.SmimeSigningCertificate] = DateTime.Now; + } + } + } + } + + await Functions.SendStreamStringAsync(clientStreamWriter, "250 Forwarded\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 Forwarded", Proxy.LogLevel.Raw, LogLevel); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Message from {" + message.From.Address + "} with subject {" + message.Subject + "} and size of {" + message.Size.ToString("N0") + "} successfully forwarded.", Proxy.LogLevel.Verbose, LogLevel); + } + catch (Exception ex) + { + // Report if an exception was encountering sending the message. + Functions.SendStreamString(clientStreamWriter, "500 Error occurred when forwarding\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 Error occurred when forwarding", Proxy.LogLevel.Raw, LogLevel); + + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Error when forwarding message from {" + messageFrom + "} with subject {" + messageSubject + "} and size of {" + messageSize + "}. Exception: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + command = ""; + } + } + else + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "C: " + new string(buffer, 0, bytesRead), Proxy.LogLevel.Raw, LogLevel); + + // Handle continuations of current "AUTH PLAIN" commands. + if (inPlainAuth) + { + inPlainAuth = false; + // Split up an AUTH PLAIN handshake into its components. + string authString = Encoding.UTF8.GetString(Convert.FromBase64String(command)); + string[] authStringParts = authString.Split(new char[] { '\0' }, 3); + if (authStringParts.Length > 2 && arguments.RemoteServerCredential == null) + smtpClient.Credentials = new NetworkCredential(authStringParts[1], authStringParts[2]); + + await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); + + command = ""; + } + // Handle continuations of current "AUTH LOGIN" commands. + else if (inLoginAuth) + { + if (smtpClient.Credentials == null) + { + // Handle the username being received for the first time. + smtpClient.Credentials = new NetworkCredential(); + ((NetworkCredential)smtpClient.Credentials).UserName = Functions.FromBase64(command.Substring(0, command.Length - 2)); + + await Functions.SendStreamStringAsync(clientStreamWriter, "334 UGFzc3dvcmQ6\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 334 UGFzc3dvcmQ6", Proxy.LogLevel.Raw, LogLevel); + } + else + { + // Handle the password. + inLoginAuth = false; + ((NetworkCredential)smtpClient.Credentials).Password = Functions.FromBase64(command.Substring(0, command.Length - 2)); + + await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); + } + command = ""; + } + else + { + // Otherwise, look at the verb of the incoming command. + string[] commandParts = command.Substring(0, command.Length - 2).Replace("\r", "").Split(new char[] { ' ' }, 2); + + if (LogLevel == Proxy.LogLevel.Verbose) + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Command {" + commandParts[0] + "} received.", Proxy.LogLevel.Verbose, LogLevel); + + switch (commandParts[0].ToUpper()) + { + case "AUTH": + // Support authentication. + if (commandParts.Length > 1) + { + commandParts = command.Substring(0, command.Length - 2).Replace("\r", "").Split(new char[] { ' ' }); + switch (commandParts[1].ToUpper()) + { + case "PLAIN": + // Prepare to handle a continuation command. + inPlainAuth = true; + await Functions.SendStreamStringAsync(clientStreamWriter, "334 Proceed\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 334 Proceed", Proxy.LogLevel.Raw, LogLevel); + + break; + case "LOGIN": + inLoginAuth = true; + if (commandParts.Length > 2) + { + // Parse the username and request a password. + smtpClient.Credentials = new NetworkCredential(); + ((NetworkCredential)smtpClient.Credentials).UserName = Functions.FromBase64(commandParts[2]); + + await Functions.SendStreamStringAsync(clientStreamWriter, "334 UGFzc3dvcmQ6\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 334 UGFzc3dvcmQ6", Proxy.LogLevel.Raw, LogLevel); + } + else + { + // Request a username only. + await Functions.SendStreamStringAsync(clientStreamWriter, "334 VXNlcm5hbWU6\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 334 VXNlcm5hbWU6", Proxy.LogLevel.Raw, LogLevel); + } + break; + default: + // Split up an AUTH PLAIN handshake into its components. + string authString = Encoding.UTF8.GetString(Convert.FromBase64String(commandParts[1].Substring(6))); + string[] authStringParts = authString.Split(new char[] { '\0' }, 3); + if (authStringParts.Length > 2 && arguments.RemoteServerCredential == null) + smtpClient.Credentials = new NetworkCredential(authStringParts[1], authStringParts[2]); + + await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); + break; + } + } + else + { + await Functions.SendStreamStringAsync(clientStreamWriter, "500 Unknown verb\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 Unknown verb", Proxy.LogLevel.Raw, LogLevel); + } + break; + case "DATA": + // Prepare to handle continuation data. + sending = true; + command = command.Substring(6); + await Functions.SendStreamStringAsync(clientStreamWriter, "354 Send message content; end with .\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 354 Send message content; end with .", Proxy.LogLevel.Raw, LogLevel); + break; + case "EHLO": + // Proceed with the login and send a list of supported commands. + if (commandParts.Length > 1) + identity = commandParts[1] + " "; + if (arguments.LocalEnableSsl) + { + await Functions.SendStreamStringAsync(clientStreamWriter, "250-Hello " + identity + "[" + ip + "], please proceed\r\n250-AUTH LOGIN PLAIN\r\n250-RSET\r\n250 STARTTLS\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250-Hello " + identity + "[" + ip + "], please proceed\r\n250-AUTH LOGIN PLAIN\r\n250-RSET\r\n250 STARTTLS", Proxy.LogLevel.Raw, LogLevel); + } + else + { + await Functions.SendStreamStringAsync(clientStreamWriter, "250-Hello " + identity + "[" + ip + "], please proceed\r\n250-AUTH LOGIN PLAIN\r\n250 RSET\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250-Hello " + identity + "[" + ip + "], please proceed\r\n250-AUTH LOGIN PLAIN\r\n250 RSET", Proxy.LogLevel.Raw, LogLevel); + } + break; + case "HELO": + // Proceed with the login. + if (commandParts.Length > 1) + identity = commandParts[1]; + await Functions.SendStreamStringAsync(clientStreamWriter, "250 Hello " + identity + " [" + ip + "], please proceed\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: Hello " + identity + " [" + ip + "], please proceed", Proxy.LogLevel.Raw, LogLevel); + break; + case "MAIL": + case "SAML": + case "SEND": + case "SOML": + // Accept the from address. + if (commandParts.Length > 1 && commandParts[1].Length > 5) + fromAddress = commandParts[1].Substring(5); + await Functions.SendStreamStringAsync(clientStreamWriter, "250 OK\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 OK", Proxy.LogLevel.Raw, LogLevel); + break; + case "NOOP": + // Prolong the current session. + await Functions.SendStreamStringAsync(clientStreamWriter, "250 Still here\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 Still here", Proxy.LogLevel.Raw, LogLevel); + break; + case "PASS": + // Support authentication. + if (commandParts.Length > 1 && arguments.RemoteServerCredential == null) + ((NetworkCredential)smtpClient.Credentials).Password = commandParts[1]; + await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); + break; + case "QUIT": + // Wait one second then force the current connection closed. + await Functions.SendStreamStringAsync(clientStreamWriter, "221 Bye\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 221 Bye", Proxy.LogLevel.Raw, LogLevel); + + Thread.Sleep(1000); + + if (clientStream != null) + clientStream.Dispose(); + if (client != null) + client.Close(); + break; + case "RCPT": + // Acknolwedge recipients. + if (commandParts.Length > 1 && commandParts[1].Length > 6) + toList.Add(commandParts[1].Substring(5, commandParts[1].Length - 6)); + await Functions.SendStreamStringAsync(clientStreamWriter, "250 OK\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 OK", Proxy.LogLevel.Raw, LogLevel); + break; + case "RSET": + // Reset the current message arguments. + fromAddress = ""; + toList.Clear(); + + await Functions.SendStreamStringAsync(clientStreamWriter, "250 OK\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 250 OK", Proxy.LogLevel.Raw, LogLevel); + break; + case "STARTTLS": + // If supported, upgrade the session's security through a TLS handshake. + if (arguments.LocalEnableSsl) + { + await Functions.SendStreamStringAsync(clientStreamWriter, "220 Go ahead\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 220 Go ahead", Proxy.LogLevel.Raw, LogLevel); + + if (!(clientStream is SslStream)) + { + clientStream = new SslStream(clientStream); + ((SslStream)clientStream).AuthenticateAsServer(arguments.Certificate); + + clientStreamReader = new StreamReader(clientStream); + clientStreamWriter = new StreamWriter(clientStream); + clientStreamWriter.AutoFlush = true; + } + } + else + { + await Functions.SendStreamStringAsync(clientStreamWriter, "500 Unknown verb\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 Unknown verb", Proxy.LogLevel.Raw, LogLevel); + } + break; + case "USER": + // Support authentication. + if (commandParts.Length > 1 && arguments.RemoteServerCredential == null) + ((NetworkCredential)smtpClient.Credentials).UserName = commandParts[1]; + + await Functions.SendStreamStringAsync(clientStreamWriter, "235 OK\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 235 OK", Proxy.LogLevel.Raw, LogLevel); + break; + case "VRFY": + // Notify that we can't verify addresses. + await Functions.SendStreamStringAsync(clientStreamWriter, "252 I'm just a proxy\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 252 I'm just a proxy", Proxy.LogLevel.Raw, LogLevel); + break; + default: + await Functions.SendStreamStringAsync(clientStreamWriter, "500 Unknown verb\r\n"); + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "S: 500 Unknown verb", Proxy.LogLevel.Raw, LogLevel); + break; + } + + command = ""; + } + } + } + } + else + stillReceiving = false; + } + } + } + catch (ObjectDisposedException) + { + // Ignore either stream being closed. + } + catch (SocketException ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception communicating with {" + arguments.RemoteServerHostName + "} on port {" + arguments.RemoteServerPort + "}: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + catch (Exception ex) + { + ProxyFunctions.Log(LogWriter, SessionId, "Exception: " + ex.Message, Proxy.LogLevel.Error, LogLevel); + } + finally + { + ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + ip + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); + + // Clean up after any unexpectedly closed connections. + if (clientStreamWriter != null) + clientStreamWriter.Dispose(); + if (clientStreamReader != null) + clientStreamReader.Dispose(); + if (clientStream != null) + clientStream.Dispose(); + if (client != null) + client.Close(); + } + } + + /// + /// Start an individual SMTP proxy on its own thread. + /// + /// SmtpProxyArguments object containing all parameters for this connection. + private static void StartProxy(object parameters) + { + SmtpProxyArguments arguments = (SmtpProxyArguments)parameters; + + // Start the proxy using passed-in settings. + arguments.Proxy.Start(arguments.AcceptedIPs, arguments.LocalIpAddress, arguments.LocalPort, arguments.LocalEnableSsl, arguments.RemoteServerHostName, arguments.RemoteServerPort, arguments.RemoteServerEnableSsl, arguments.RemoteServerCredential, arguments.RemoteServerFrom, arguments.SmimeSettingsMode, arguments.SmimeSigned, arguments.SmimeEncryptedEnvelope, arguments.SmimeTripleWrapped, arguments.SmimeRemovePreviousOperations, arguments.SendCertificateReminders, arguments.LogFile, arguments.LogLevel, arguments.InstanceId); + } + #endregion Private Methods + } +} diff --git a/OpaqueMail.Net.ProxyInstaller/OpaqueMail.Net.ProxyInstaller.isl b/OpaqueMail.Net.ProxyInstaller/OpaqueMail.Net.ProxyInstaller.isl new file mode 100644 index 0000000..95fc467 --- /dev/null +++ b/OpaqueMail.Net.ProxyInstaller/OpaqueMail.Net.ProxyInstaller.isl @@ -0,0 +1,5889 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]> + + + + 1252 + Installation Database + + InstallShield + Installer,MSI,Database + Contact: Your local administrator + + Administrator + {71D91E6E-59C8-44F1-B441-74D904CC3B41} + + 06/21/1999 08:00 + 07/14/2000 11:50 + 200 + 0 + + InstallShield Express + 1 + + + + Action + Description + Template + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Advertise##IDS_ACTIONTEXT_Advertising## + AllocateRegistrySpace##IDS_ACTIONTEXT_AllocatingRegistry####IDS_ACTIONTEXT_FreeSpace##AppSearch##IDS_ACTIONTEXT_SearchInstalled####IDS_ACTIONTEXT_PropertySignature##BindImage##IDS_ACTIONTEXT_BindingExes####IDS_ACTIONTEXT_File##CCPSearch##IDS_ACTIONTEXT_UnregisterModules## + CostFinalize##IDS_ACTIONTEXT_ComputingSpace3## + CostInitialize##IDS_ACTIONTEXT_ComputingSpace## + CreateFolders##IDS_ACTIONTEXT_CreatingFolders####IDS_ACTIONTEXT_Folder##CreateShortcuts##IDS_ACTIONTEXT_CreatingShortcuts####IDS_ACTIONTEXT_Shortcut##DeleteServices##IDS_ACTIONTEXT_DeletingServices####IDS_ACTIONTEXT_Service##DuplicateFiles##IDS_ACTIONTEXT_CreatingDuplicate####IDS_ACTIONTEXT_FileDirectorySize##FileCost##IDS_ACTIONTEXT_ComputingSpace2## + FindRelatedProducts##IDS_ACTIONTEXT_SearchForRelated####IDS_ACTIONTEXT_FoundApp##GenerateScript##IDS_ACTIONTEXT_GeneratingScript####IDS_ACTIONTEXT_1##ISLockPermissionsCost##IDS_ACTIONTEXT_ISLockPermissionsCost## + ISLockPermissionsInstall##IDS_ACTIONTEXT_ISLockPermissionsInstall## + InstallAdminPackage##IDS_ACTIONTEXT_CopyingNetworkFiles####IDS_ACTIONTEXT_FileDirSize##InstallFiles##IDS_ACTIONTEXT_CopyingNewFiles####IDS_ACTIONTEXT_FileDirSize2##InstallODBC##IDS_ACTIONTEXT_InstallODBC## + InstallSFPCatalogFile##IDS_ACTIONTEXT_InstallingSystemCatalog####IDS_ACTIONTEXT_FileDependencies##InstallServices##IDS_ACTIONTEXT_InstallServices####IDS_ACTIONTEXT_Service2##InstallValidate##IDS_ACTIONTEXT_Validating## + LaunchConditions##IDS_ACTIONTEXT_EvaluateLaunchConditions## + MigrateFeatureStates##IDS_ACTIONTEXT_MigratingFeatureStates####IDS_ACTIONTEXT_Application##MoveFiles##IDS_ACTIONTEXT_MovingFiles####IDS_ACTIONTEXT_FileDirSize3##PatchFiles##IDS_ACTIONTEXT_PatchingFiles####IDS_ACTIONTEXT_FileDirSize4##ProcessComponents##IDS_ACTIONTEXT_UpdateComponentRegistration## + PublishComponents##IDS_ACTIONTEXT_PublishingQualifiedComponents####IDS_ACTIONTEXT_ComponentIDQualifier##PublishFeatures##IDS_ACTIONTEXT_PublishProductFeatures####IDS_ACTIONTEXT_FeatureColon##PublishProduct##IDS_ACTIONTEXT_PublishProductInfo## + RMCCPSearch##IDS_ACTIONTEXT_SearchingQualifyingProducts## + RegisterClassInfo##IDS_ACTIONTEXT_RegisterClassServer####IDS_ACTIONTEXT_ClassId##RegisterComPlus##IDS_ACTIONTEXT_RegisteringComPlus####IDS_ACTIONTEXT_AppIdAppTypeRSN##RegisterExtensionInfo##IDS_ACTIONTEXT_RegisterExtensionServers####IDS_ACTIONTEXT_Extension2##RegisterFonts##IDS_ACTIONTEXT_RegisterFonts####IDS_ACTIONTEXT_Font##RegisterMIMEInfo##IDS_ACTIONTEXT_RegisterMimeInfo####IDS_ACTIONTEXT_ContentTypeExtension##RegisterProduct##IDS_ACTIONTEXT_RegisteringProduct####IDS_ACTIONTEXT_1b##RegisterProgIdInfo##IDS_ACTIONTEXT_RegisteringProgIdentifiers####IDS_ACTIONTEXT_ProgID2##RegisterTypeLibraries##IDS_ACTIONTEXT_RegisterTypeLibs####IDS_ACTIONTEXT_LibId##RegisterUser##IDS_ACTIONTEXT_RegUser####IDS_ACTIONTEXT_1c##RemoveDuplicateFiles##IDS_ACTIONTEXT_RemovingDuplicates####IDS_ACTIONTEXT_FileDir##RemoveEnvironmentStrings##IDS_ACTIONTEXT_UpdateEnvironmentStrings####IDS_ACTIONTEXT_NameValueAction2##RemoveExistingProducts##IDS_ACTIONTEXT_RemoveApps####IDS_ACTIONTEXT_AppCommandLine##RemoveFiles##IDS_ACTIONTEXT_RemovingFiles####IDS_ACTIONTEXT_FileDir2##RemoveFolders##IDS_ACTIONTEXT_RemovingFolders####IDS_ACTIONTEXT_Folder1##RemoveIniValues##IDS_ACTIONTEXT_RemovingIni####IDS_ACTIONTEXT_FileSectionKeyValue##RemoveODBC##IDS_ACTIONTEXT_RemovingODBC## + RemoveRegistryValues##IDS_ACTIONTEXT_RemovingRegistry####IDS_ACTIONTEXT_KeyName##RemoveShortcuts##IDS_ACTIONTEXT_RemovingShortcuts####IDS_ACTIONTEXT_Shortcut1##Rollback##IDS_ACTIONTEXT_RollingBack####IDS_ACTIONTEXT_1d##RollbackCleanup##IDS_ACTIONTEXT_RemovingBackup####IDS_ACTIONTEXT_File2##SelfRegModules##IDS_ACTIONTEXT_RegisteringModules####IDS_ACTIONTEXT_FileFolder##SelfUnregModules##IDS_ACTIONTEXT_UnregisterModules####IDS_ACTIONTEXT_FileFolder2##SetODBCFolders##IDS_ACTIONTEXT_InitializeODBCDirs## + StartServices##IDS_ACTIONTEXT_StartingServices####IDS_ACTIONTEXT_Service3##StopServices##IDS_ACTIONTEXT_StoppingServices####IDS_ACTIONTEXT_Service4##UnmoveFiles##IDS_ACTIONTEXT_RemovingMoved####IDS_ACTIONTEXT_FileDir3##UnpublishComponents##IDS_ACTIONTEXT_UnpublishQualified####IDS_ACTIONTEXT_ComponentIdQualifier2##UnpublishFeatures##IDS_ACTIONTEXT_UnpublishProductFeatures####IDS_ACTIONTEXT_Feature##UnpublishProduct##IDS_ACTIONTEXT_UnpublishingProductInfo## + UnregisterClassInfo##IDS_ACTIONTEXT_UnregisterClassServers####IDS_ACTIONTEXT_ClsID##UnregisterComPlus##IDS_ACTIONTEXT_UnregisteringComPlus####IDS_ACTIONTEXT_AppId##UnregisterExtensionInfo##IDS_ACTIONTEXT_UnregisterExtensionServers####IDS_ACTIONTEXT_Extension##UnregisterFonts##IDS_ACTIONTEXT_UnregisteringFonts####IDS_ACTIONTEXT_Font2##UnregisterMIMEInfo##IDS_ACTIONTEXT_UnregisteringMimeInfo####IDS_ACTIONTEXT_ContentTypeExtension2##UnregisterProgIdInfo##IDS_ACTIONTEXT_UnregisteringProgramIds####IDS_ACTIONTEXT_ProgID##UnregisterTypeLibraries##IDS_ACTIONTEXT_UnregTypeLibs####IDS_ACTIONTEXT_Libid2##WriteEnvironmentStrings##IDS_ACTIONTEXT_EnvironmentStrings####IDS_ACTIONTEXT_NameValueAction##WriteIniValues##IDS_ACTIONTEXT_WritingINI####IDS_ACTIONTEXT_FileSectionKeyValue2##WriteRegistryValues##IDS_ACTIONTEXT_WritingRegistry####IDS_ACTIONTEXT_KeyNameValue##
+ + + Action + Condition + Sequence + ISComments + ISAttributes +
CostFinalize1000CostFinalize + CostInitialize800CostInitialize + FileCost900FileCost + InstallAdminPackage3900InstallAdminPackage + InstallFiles4000InstallFiles + InstallFinalize6600InstallFinalize + InstallInitialize1500InstallInitialize + InstallValidate1400InstallValidate + ScheduleRebootISSCHEDULEREBOOT4010ScheduleReboot +
+ + + Action + Condition + Sequence + ISComments + ISAttributes +
AdminWelcome1010AdminWelcome + CostFinalize1000CostFinalize + CostInitialize800CostInitialize + ExecuteAction1300ExecuteAction + FileCost900FileCost + SetupCompleteError-3SetupCompleteError + SetupCompleteSuccess-1SetupCompleteSuccess + SetupInitialization50SetupInitialization + SetupInterrupted-2SetupInterrupted + SetupProgress1020SetupProgress +
+ + + Action + Condition + Sequence + ISComments + ISAttributes +
CostFinalize1000CostFinalize + CostInitialize800CostInitialize + CreateShortcuts4500CreateShortcuts + InstallFinalize6600InstallFinalize + InstallInitialize1500InstallInitialize + InstallValidate1400InstallValidate + MsiPublishAssemblies6250MsiPublishAssemblies + PublishComponents6200PublishComponents + PublishFeatures6300PublishFeatures + PublishProduct6400PublishProduct + RegisterClassInfo4600RegisterClassInfo + RegisterExtensionInfo4700RegisterExtensionInfo + RegisterMIMEInfo4900RegisterMIMEInfo + RegisterProgIdInfo4800RegisterProgIdInfo + RegisterTypeLibraries4910RegisterTypeLibraries + ScheduleRebootISSCHEDULEREBOOT6410ScheduleReboot +
+ + + Action + Condition + Sequence + ISComments + ISAttributes +
+ + + AppId + RemoteServerName + LocalService + ServiceParameters + DllSurrogate + ActivateAtStorage + RunAsInteractiveUser +
+ + + Property + Signature_ +
+ + + Billboard_ + BBControl + Type + X + Y + Width + Height + Attributes + Text +
+ + + Billboard + Feature_ + Action + Ordering +
+ + + Name + Data + ISBuildSourcePath + + + + + + + + + + + + + + + + + + + + +
ISExpHlp.dll<ISProductFolder>\redist\language independent\i386\ISExpHlp.dllNewBinary1<ISProductFolder>\Support\Themes\InstallShield Blue Theme\banner.jpgNewBinary10<ISProductFolder>\Redist\Language Independent\OS Independent\CompleteSetupIco.ibdNewBinary11<ISProductFolder>\Redist\Language Independent\OS Independent\CustomSetupIco.ibdNewBinary12<ISProductFolder>\Redist\Language Independent\OS Independent\DestIcon.ibdNewBinary13<ISProductFolder>\Redist\Language Independent\OS Independent\NetworkInstall.icoNewBinary14<ISProductFolder>\Redist\Language Independent\OS Independent\DontInstall.icoNewBinary15<ISProductFolder>\Redist\Language Independent\OS Independent\Install.icoNewBinary16<ISProductFolder>\Redist\Language Independent\OS Independent\InstallFirstUse.icoNewBinary17<ISProductFolder>\Redist\Language Independent\OS Independent\InstallPartial.icoNewBinary18<ISProductFolder>\Redist\Language Independent\OS Independent\InstallStateMenu.icoNewBinary2<ISProductFolder>\Redist\Language Independent\OS Independent\New.ibdNewBinary3<ISProductFolder>\Redist\Language Independent\OS Independent\Up.ibdNewBinary4<ISProductFolder>\Redist\Language Independent\OS Independent\WarningIcon.ibdNewBinary5<ISProductFolder>\Support\Themes\InstallShield Blue Theme\welcome.jpgNewBinary6<ISProductFolder>\Redist\Language Independent\OS Independent\CustomSetupIco.ibdNewBinary7<ISProductFolder>\Redist\Language Independent\OS Independent\ReinstIco.ibdNewBinary8<ISProductFolder>\Redist\Language Independent\OS Independent\RemoveIco.ibdNewBinary9<ISProductFolder>\Redist\Language Independent\OS Independent\SetupIcon.ibdSetAllUsers.dll<ISProductFolder>\redist\language independent\i386\SetAllUsers.dll
+ + + File_ + Path +
+ + + Signature_ +
+ + + Property + Value + + + +
ISCHECKFORPRODUCTUPDATES1LAUNCHPROGRAM1LAUNCHREADME1
+ + + CLSID + Context + Component_ + ProgId_Default + Description + AppId_ + FileTypeMask + Icon_ + IconIndex + DefInprocHandler + Argument + Feature_ + Attributes +
+ + + Property + Order + Value + Text +
+ + + Signature_ + ComponentId + Type +
+ + + Component_ + ExpType +
+ + + Component + ComponentId + Directory_ + Attributes + Condition + KeyPath + ISAttributes + ISComments + ISScanAtBuildFile + ISRegFileToMergeAtBuild + ISDotNetInstallerArgsInstall + ISDotNetInstallerArgsCommit + ISDotNetInstallerArgsUninstall + ISDotNetInstallerArgsRollback +
+ + + Feature_ + Level + Condition +
+ + + Dialog_ + Control + Type + X + Y + Width + Height + Attributes + Property + Text + Control_Next + Help + ISWindowStyle + ISControlId + ISBuildSourcePath + Binary_ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AdminChangeFolderBannerBitmap003744410NewBinary1AdminChangeFolderBannerLineLine044374010 + AdminChangeFolderBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + AdminChangeFolderBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + AdminChangeFolderCancelPushButton30124366173##IDS_CANCEL##ComboText0 + AdminChangeFolderComboDirectoryCombo216427780458755TARGETDIR##IDS__IsAdminInstallBrowse_4##Up0 + AdminChangeFolderComboTextText215099143##IDS__IsAdminInstallBrowse_LookIn##Combo0 + AdminChangeFolderDlgDescText21232922565539##IDS__IsAdminInstallBrowse_BrowseDestination##0 + AdminChangeFolderDlgLineLine48234326010 + AdminChangeFolderDlgTitleText1362922565539##IDS__IsAdminInstallBrowse_ChangeDestination##0 + AdminChangeFolderListDirectoryList2190332977TARGETDIR##IDS__IsAdminInstallBrowse_8##TailText0 + AdminChangeFolderNewFolderPushButton3356619193670019List##IDS__IsAdminInstallBrowse_CreateFolder##0NewBinary2AdminChangeFolderOKPushButton23024366173##IDS_OK##Cancel0 + AdminChangeFolderTailPathEdit21207332173TARGETDIR##IDS__IsAdminInstallBrowse_11##OK0 + AdminChangeFolderTailTextText2119399133##IDS__IsAdminInstallBrowse_FolderName##Tail0 + AdminChangeFolderUpPushButton3106619193670019NewFolder##IDS__IsAdminInstallBrowse_UpOneLevel##0NewBinary3AdminNetworkLocationBackPushButton16424366173##IDS_BACK##InstallNow0 + AdminNetworkLocationBannerBitmap003744410NewBinary1AdminNetworkLocationBannerLineLine044374010 + AdminNetworkLocationBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + AdminNetworkLocationBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + AdminNetworkLocationBrowsePushButton28612466173##IDS__IsAdminInstallPoint_Change##Back0 + AdminNetworkLocationCancelPushButton30124366173##IDS_CANCEL##SetupPathEdit0 + AdminNetworkLocationDlgDescText21232922565539##IDS__IsAdminInstallPoint_SpecifyNetworkLocation##0 + AdminNetworkLocationDlgLineLine48234326010 + AdminNetworkLocationDlgTextText215132640131075##IDS__IsAdminInstallPoint_EnterNetworkLocation##0 + AdminNetworkLocationDlgTitleText1362922565539##IDS__IsAdminInstallPoint_NetworkLocationFormatted##0 + AdminNetworkLocationInstallNowPushButton23024366173##IDS__IsAdminInstallPoint_Install##Cancel0 + AdminNetworkLocationLBBrowseText2190100103##IDS__IsAdminInstallPoint_NetworkLocation##0 + AdminNetworkLocationSetupPathEditPathEdit21102330173TARGETDIRBrowse0 + AdminWelcomeBackPushButton16424366171##IDS_BACK##Next0 + AdminWelcomeCancelPushButton30124366173##IDS_CANCEL##Back0 + AdminWelcomeDlgLineLine0234326010 + AdminWelcomeImageBitmap0037423410NewBinary5AdminWelcomeNextPushButton23024366173##IDS_NEXT##Cancel0 + AdminWelcomeTextLine1Text135822545196611##IDS__IsAdminInstallPointWelcome_Wizard##0 + AdminWelcomeTextLine2Text1355522845196611##IDS__IsAdminInstallPointWelcome_ServerImage##0 + CancelSetupIconIcon1515242452428810NewBinary4CancelSetupNoPushButton1355766173##IDS__IsCancelDlg_No##Yes0 + CancelSetupTextText481519430131075##IDS__IsCancelDlg_ConfirmCancel##0 + CancelSetupYesPushButton625766173##IDS__IsCancelDlg_Yes##No0 + CustomSetupBackPushButton16424366173##IDS_BACK##Next0 + CustomSetupBannerBitmap003744410NewBinary1CustomSetupBannerLineLine044374010 + CustomSetupBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + CustomSetupBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + CustomSetupCancelPushButton30124366173##IDS_CANCEL##Tree0 + CustomSetupChangeFolderPushButton30120366173##IDS__IsCustomSelectionDlg_Change##Help0 + CustomSetupDetailsPushButton9324366173##IDS__IsCustomSelectionDlg_Space##Back0 + CustomSetupDlgDescText17232922565539##IDS__IsCustomSelectionDlg_SelectFeatures##0 + CustomSetupDlgLineLine48234326010 + CustomSetupDlgTextText951360103##IDS__IsCustomSelectionDlg_ClickFeatureIcon##0 + CustomSetupDlgTitleText962922565539##IDS__IsCustomSelectionDlg_CustomSetup##0 + CustomSetupFeatureGroupGroupBox235671311201##IDS__IsCustomSelectionDlg_FeatureDescription##0 + CustomSetupHelpPushButton2224366173##IDS__IsCustomSelectionDlg_Help##Details0 + CustomSetupInstallLabelText8190360103##IDS__IsCustomSelectionDlg_InstallTo##0 + CustomSetupItemDescriptionText24180120503##IDS__IsCustomSelectionDlg_MultilineDescription##0 + CustomSetupLocationText8203291203##IDS__IsCustomSelectionDlg_FeaturePath##0 + CustomSetupNextPushButton23024366173##IDS_NEXT##Cancel0 + CustomSetupSizeText241133120503##IDS__IsCustomSelectionDlg_FeatureSize##0 + CustomSetupTreeSelectionTree8702201187_BrowsePropertyChangeFolder0 + CustomSetupTipsBannerBitmap003744410NewBinary1CustomSetupTipsBannerLineLine044374010 + CustomSetupTipsBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + CustomSetupTipsBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + CustomSetupTipsDlgDescText21232922565539##IDS_SetupTips_CustomSetupDescription##0 + CustomSetupTipsDlgLineLine48234326010 + CustomSetupTipsDlgTitleText1362922565539##IDS_SetupTips_CustomSetup##0 + CustomSetupTipsDontInstallIcon21155242452428810NewBinary14CustomSetupTipsDontInstallTextText60155300203##IDS_SetupTips_WillNotBeInstalled##0 + CustomSetupTipsFirstInstallTextText60180300203##IDS_SetupTips_Advertise##0 + CustomSetupTipsInstallIcon21105242452428810NewBinary15CustomSetupTipsInstallFirstUseIcon21180242452428810NewBinary16CustomSetupTipsInstallPartialIcon21130242452428810NewBinary17CustomSetupTipsInstallStateMenuIcon2152242452428810NewBinary18CustomSetupTipsInstallStateTextText2191300103##IDS_SetupTips_InstallState##00 + CustomSetupTipsInstallTextText60105300203##IDS_SetupTips_AllInstalledLocal##0 + CustomSetupTipsMenuTextText5052300363##IDS_SetupTips_IconInstallState##0 + CustomSetupTipsNetworkInstallIcon21205242452428810NewBinary13CustomSetupTipsNetworkInstallTextText60205300203##IDS_SetupTips_Network##0 + CustomSetupTipsOKPushButton30124366173##IDS_SetupTips_OK##0 + CustomSetupTipsPartialTextText60130300203##IDS_SetupTips_SubFeaturesInstalledLocal##0 + CustomerInformationBackPushButton16424366173##IDS_BACK##Next0 + CustomerInformationBannerBitmap003744410NewBinary1CustomerInformationBannerLineLine044374010 + CustomerInformationBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + CustomerInformationBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + CustomerInformationCancelPushButton30124366173##IDS_CANCEL##NameLabel0 + CustomerInformationCompanyEditEdit21100237173COMPANYNAME##IDS__IsRegisterUserDlg_Tahoma80##SerialLabel0 + CustomerInformationCompanyLabelText218975103##IDS__IsRegisterUserDlg_Organization##CompanyEdit0 + CustomerInformationDlgDescText21232922565539##IDS__IsRegisterUserDlg_PleaseEnterInfo##0 + CustomerInformationDlgLineLine48234326010 + CustomerInformationDlgRadioGroupTextText21161300142##IDS__IsRegisterUserDlg_InstallFor##0 + CustomerInformationDlgTitleText1362922565539##IDS__IsRegisterUserDlg_CustomerInformation##0 + CustomerInformationNameEditEdit2163237173USERNAME##IDS__IsRegisterUserDlg_Tahoma50##CompanyLabel0 + CustomerInformationNameLabelText215275103##IDS__IsRegisterUserDlg_UserName##NameEdit0 + CustomerInformationNextPushButton23024366173##IDS_NEXT##Cancel0 + CustomerInformationRadioGroupRadioButtonGroup63170300502ApplicationUsers##IDS__IsRegisterUserDlg_16##Back0 + CustomerInformationSerialLabelText21127109102##IDS__IsRegisterUserDlg_SerialNumber##SerialNumber0 + CustomerInformationSerialNumberMaskedEdit21138237172ISX_SERIALNUMRadioGroup0 + DatabaseFolderBackPushButton16424366173##IDS_BACK##Next0 + DatabaseFolderBannerBitmap003744410NewBinary1DatabaseFolderBannerLineLine044374010 + DatabaseFolderBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + DatabaseFolderBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + DatabaseFolderCancelPushButton30124366173##IDS_CANCEL##ChangeFolder0 + DatabaseFolderChangeFolderPushButton3016566173##IDS_CHANGE##Back0 + DatabaseFolderDatabaseFolderIcon2152242452428810NewBinary12DatabaseFolderDlgDescText21232922565539##IDS__DatabaseFolder_ChangeFolder##0 + DatabaseFolderDlgLineLine48234326010 + DatabaseFolderDlgTitleText1362922565539##IDS__DatabaseFolder_DatabaseFolder##0 + DatabaseFolderLocLabelText575229010131075##IDS_DatabaseFolder_InstallDatabaseTo##0 + DatabaseFolderLocationText5765240403_BrowseProperty##IDS__DatabaseFolder_DatabaseDir##0 + DatabaseFolderNextPushButton23024366173##IDS_NEXT##Cancel0 + DestinationFolderBackPushButton16424366173##IDS_BACK##Next0 + DestinationFolderBannerBitmap003744410NewBinary1DestinationFolderBannerLineLine044374010 + DestinationFolderBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + DestinationFolderBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + DestinationFolderCancelPushButton30124366173##IDS_CANCEL##ChangeFolder0 + DestinationFolderChangeFolderPushButton3016566173##IDS__DestinationFolder_Change##Back0 + DestinationFolderDestFolderIcon2152242452428810NewBinary12DestinationFolderDlgDescText21232922565539##IDS__DestinationFolder_ChangeFolder##0 + DestinationFolderDlgLineLine48234326010 + DestinationFolderDlgTitleText1362922565539##IDS__DestinationFolder_DestinationFolder##0 + DestinationFolderLocLabelText575229010131075##IDS__DestinationFolder_InstallTo##0 + DestinationFolderLocationText5765240403_BrowseProperty##IDS_INSTALLDIR##0 + DestinationFolderNextPushButton23024366173##IDS_NEXT##Cancel0 + DiskSpaceRequirementsBannerBitmap003744410NewBinary1DiskSpaceRequirementsBannerLineLine044374010 + DiskSpaceRequirementsBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + DiskSpaceRequirementsBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + DiskSpaceRequirementsDlgDescText17232922565539##IDS__IsFeatureDetailsDlg_SpaceRequired##0 + DiskSpaceRequirementsDlgLineLine48234326010 + DiskSpaceRequirementsDlgTextText10185358413##IDS__IsFeatureDetailsDlg_VolumesTooSmall##0 + DiskSpaceRequirementsDlgTitleText962922565539##IDS__IsFeatureDetailsDlg_DiskSpaceRequirements##0 + DiskSpaceRequirementsListVolumeCostList855358125393223##IDS__IsFeatureDetailsDlg_Numbers##0 + DiskSpaceRequirementsOKPushButton30124366173##IDS__IsFeatureDetailsDlg_OK##0 + FilesInUseBannerBitmap003744410NewBinary1FilesInUseBannerLineLine044374010 + FilesInUseBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + FilesInUseBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + FilesInUseDlgDescText21232922565539##IDS__IsFilesInUse_FilesInUseMessage##0 + FilesInUseDlgLineLine48234326010 + FilesInUseDlgTextText2151348333##IDS__IsFilesInUse_ApplicationsUsingFiles##0 + FilesInUseDlgTitleText1362922565539##IDS__IsFilesInUse_FilesInUse##0 + FilesInUseExitPushButton30124366173##IDS__IsFilesInUse_Exit##List0 + FilesInUseIgnorePushButton23024366173##IDS__IsFilesInUse_Ignore##Exit0 + FilesInUseListListBox21873311357FileInUseProcessRetry0 + FilesInUseRetryPushButton16424366173##IDS__IsFilesInUse_Retry##Ignore0 + InstallChangeFolderBannerBitmap003744410NewBinary1InstallChangeFolderBannerLineLine044374010 + InstallChangeFolderBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + InstallChangeFolderBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + InstallChangeFolderCancelPushButton30124366173##IDS_CANCEL##ComboText0 + InstallChangeFolderComboDirectoryCombo2164277804128779_BrowseProperty##IDS__IsBrowseFolderDlg_4##Up0 + InstallChangeFolderComboTextText215099143##IDS__IsBrowseFolderDlg_LookIn##Combo0 + InstallChangeFolderDlgDescText21232922565539##IDS__IsBrowseFolderDlg_BrowseDestFolder##0 + InstallChangeFolderDlgLineLine48234326010 + InstallChangeFolderDlgTitleText1362922565539##IDS__IsBrowseFolderDlg_ChangeCurrentFolder##0 + InstallChangeFolderListDirectoryList21903329715_BrowseProperty##IDS__IsBrowseFolderDlg_8##TailText0 + InstallChangeFolderNewFolderPushButton3356619193670019List##IDS__IsBrowseFolderDlg_CreateFolder##0NewBinary2InstallChangeFolderOKPushButton23024366173##IDS__IsBrowseFolderDlg_OK##Cancel0 + InstallChangeFolderTailPathEdit212073321715_BrowseProperty##IDS__IsBrowseFolderDlg_11##OK0 + InstallChangeFolderTailTextText2119399133##IDS__IsBrowseFolderDlg_FolderName##Tail0 + InstallChangeFolderUpPushButton3106619193670019NewFolder##IDS__IsBrowseFolderDlg_UpOneLevel##0NewBinary3InstallWelcomeBackPushButton16424366171##IDS_BACK##Copyright0 + InstallWelcomeCancelPushButton30124366173##IDS_CANCEL##Back0 + InstallWelcomeCopyrightText1351442287365539##IDS__IsWelcomeDlg_WarningCopyright##Next0 + InstallWelcomeDlgLineLine0234374010 + InstallWelcomeImageBitmap0037423410NewBinary5InstallWelcomeNextPushButton23024366173##IDS_NEXT##Cancel0 + InstallWelcomeTextLine1Text135822545196611##IDS__IsWelcomeDlg_WelcomeProductName##0 + InstallWelcomeTextLine2Text1355522845196611##IDS__IsWelcomeDlg_InstallProductName##0 + LicenseAgreementAgreeRadioButtonGroup8190291403AgreeToLicenseBack0 + LicenseAgreementBackPushButton16424366173##IDS_BACK##Next0 + LicenseAgreementBannerBitmap003744410NewBinary1LicenseAgreementBannerLineLine044374010 + LicenseAgreementBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + LicenseAgreementBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + LicenseAgreementCancelPushButton30124366173##IDS_CANCEL##ISPrintButton0 + LicenseAgreementDlgDescText21232922565539##IDS__IsLicenseDlg_ReadLicenseAgreement##0 + LicenseAgreementDlgLineLine48234326010 + LicenseAgreementDlgTitleText1362922565539##IDS__IsLicenseDlg_LicenseAgreement##0 + LicenseAgreementISPrintButtonPushButton30118865173##IDS_PRINT_BUTTON##Agree0 + LicenseAgreementMemoScrollableText85535813070<ISProductFolder>\Redist\0409\Eula.rtf + LicenseAgreementNextPushButton23024366173##IDS_NEXT##Cancel0 + MaintenanceTypeBackPushButton16424366173##IDS_BACK##Next0 + MaintenanceTypeBannerBitmap003744410NewBinary1MaintenanceTypeBannerLineLine044374010 + MaintenanceTypeBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + MaintenanceTypeBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + MaintenanceTypeCancelPushButton30124366173##IDS_CANCEL##RadioGroup0 + MaintenanceTypeDlgDescText21232922565539##IDS__IsMaintenanceDlg_MaitenanceOptions##0 + MaintenanceTypeDlgLineLine48234326010 + MaintenanceTypeDlgTitleText1362922565539##IDS__IsMaintenanceDlg_ProgramMaintenance##0 + MaintenanceTypeIco1Icon3575242452428810NewBinary6MaintenanceTypeIco2Icon35135242452428810NewBinary7MaintenanceTypeIco3Icon35195242452428810NewBinary8MaintenanceTypeNextPushButton23024366173##IDS_NEXT##Cancel0 + MaintenanceTypeRadioGroupRadioButtonGroup21552901703_IsMaintenanceBack0 + MaintenanceTypeText1Text8072260353##IDS__IsMaintenanceDlg_ChangeFeatures##0 + MaintenanceTypeText2Text80135260353##IDS__IsMaintenanceDlg_RepairMessage##0 + MaintenanceTypeText3Text8019226035131075##IDS__IsMaintenanceDlg_RemoveProductName##0 + MaintenanceWelcomeBackPushButton16424366171##IDS_BACK##Next0 + MaintenanceWelcomeCancelPushButton30124366173##IDS_CANCEL##Back0 + MaintenanceWelcomeDlgLineLine0234374010 + MaintenanceWelcomeImageBitmap0037423410NewBinary5MaintenanceWelcomeNextPushButton23024366173##IDS_NEXT##Cancel0 + MaintenanceWelcomeTextLine1Text135822545196611##IDS__IsMaintenanceWelcome_WizardWelcome##0 + MaintenanceWelcomeTextLine2Text1355522850196611##IDS__IsMaintenanceWelcome_MaintenanceOptionsDescription##0 + MsiRMFilesInUseBannerBitmap003744410NewBinary1MsiRMFilesInUseBannerLineLine044374010 + MsiRMFilesInUseBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + MsiRMFilesInUseBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + MsiRMFilesInUseCancelPushButton30124366173##IDS_CANCEL##Restart0 + MsiRMFilesInUseDlgDescText21232922565539##IDS__IsFilesInUse_FilesInUseMessage##0 + MsiRMFilesInUseDlgLineLine48234326010 + MsiRMFilesInUseDlgTextText2151348143##IDS__IsMsiRMFilesInUse_ApplicationsUsingFiles##0 + MsiRMFilesInUseDlgTitleText1362922565539##IDS__IsFilesInUse_FilesInUse##0 + MsiRMFilesInUseListListBox21663311303FileInUseProcessOK0 + MsiRMFilesInUseOKPushButton23024366173##IDS_OK##Cancel0 + MsiRMFilesInUseRestartRadioButtonGroup19187343403RestartManagerOptionList0 + OutOfSpaceBannerBitmap003744410NewBinary1OutOfSpaceBannerLineLine044374010 + OutOfSpaceBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + OutOfSpaceBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + OutOfSpaceDlgDescText21232922565539##IDS__IsDiskSpaceDlg_DiskSpace##0 + OutOfSpaceDlgLineLine48234326010 + OutOfSpaceDlgTextText2151326433##IDS__IsDiskSpaceDlg_HighlightedVolumes##0 + OutOfSpaceDlgTitleText1362922565539##IDS__IsDiskSpaceDlg_OutOfDiskSpace##0 + OutOfSpaceListVolumeCostList2195332120393223##IDS__IsDiskSpaceDlg_Numbers##0 + OutOfSpaceResumePushButton30124366173##IDS__IsDiskSpaceDlg_OK##0 + PatchWelcomeBackPushButton16424366171##IDS_BACK##Next0 + PatchWelcomeCancelPushButton30124366173##IDS_CANCEL##Back0 + PatchWelcomeDlgLineLine0234374010 + PatchWelcomeImageBitmap0037423410NewBinary5PatchWelcomeNextPushButton23024366173##IDS__IsPatchDlg_Update##Cancel0 + PatchWelcomeTextLine1Text135822545196611##IDS__IsPatchDlg_WelcomePatchWizard##0 + PatchWelcomeTextLine2Text1355422845196611##IDS__IsPatchDlg_PatchClickUpdate##0 + ReadmeInformationBackPushButton16424366171048579##IDS_BACK##Next0 + ReadmeInformationBannerBitmap00374443DlgTitle0NewBinary1ReadmeInformationBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##00 + ReadmeInformationBranding2Text3228501365537##IDS_INSTALLSHIELD##00 + ReadmeInformationCancelPushButton30124366171048579##IDS__IsReadmeDlg_Cancel##Readme0 + ReadmeInformationDlgDescText21232321665539##IDS__IsReadmeDlg_PleaseReadInfo##Back00 + ReadmeInformationDlgLineLine482343260300 + ReadmeInformationDlgTitleText1361931365539##IDS__IsReadmeDlg_ReadMeInfo##DlgDesc0 + ReadmeInformationNextPushButton23024366171048579##IDS_NEXT##Cancel0 + ReadmeInformationReadmeScrollableText10553531663Banner0<ISProductFolder>\Redist\0409\Readme.rtf + ReadyToInstallBackPushButton16424366173##IDS_BACK##GroupBox10 + ReadyToInstallBannerBitmap003744410NewBinary1ReadyToInstallBannerLineLine044374010 + ReadyToInstallBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + ReadyToInstallBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + ReadyToInstallCancelPushButton30124366173##IDS_CANCEL##Back0 + ReadyToInstallCompanyNameTextText3819821193##IDS__IsVerifyReadyDlg_Company##SerialNumberText0 + ReadyToInstallCurrentSettingsTextText198081103##IDS__IsVerifyReadyDlg_CurrentSettings##InstallNow0 + ReadyToInstallDlgDescText21232922565539##IDS__IsVerifyReadyDlg_WizardReady##0 + ReadyToInstallDlgLineLine482343260100 + ReadyToInstallDlgText1Text2154330243##IDS__IsVerifyReadyDlg_BackOrCancel##0 + ReadyToInstallDlgText2Text2199330202##IDS__IsRegisterUserDlg_InstallFor##0 + ReadyToInstallDlgTitleText1362922565538##IDS__IsVerifyReadyDlg_ModifyReady##0 + ReadyToInstallDlgTitle2Text1362922565538##IDS__IsVerifyReadyDlg_ReadyRepair##0 + ReadyToInstallDlgTitle3Text1362922565538##IDS__IsVerifyReadyDlg_ReadyInstall##0 + ReadyToInstallGroupBox1Text199233013365541SetupTypeText10 + ReadyToInstallInstallNowPushButton23024366178388611##IDS__IsVerifyReadyDlg_Install##InstallPerMachine0 + ReadyToInstallInstallPerMachinePushButton63123248178388610##IDS__IsRegisterUserDlg_Anyone##InstallPerUser0 + ReadyToInstallInstallPerUserPushButton63143248172##IDS__IsRegisterUserDlg_OnlyMe##Cancel0 + ReadyToInstallSerialNumberTextText3821130693##IDS__IsVerifyReadyDlg_Serial##CurrentSettingsText0 + ReadyToInstallSetupTypeText1Text2397306133##IDS__IsVerifyReadyDlg_SetupType##SetupTypeText20 + ReadyToInstallSetupTypeText2Text37114306143##IDS__IsVerifyReadyDlg_SelectedSetupType##TargetFolderText10 + ReadyToInstallTargetFolderText1Text24136306113##IDS__IsVerifyReadyDlg_DestFolder##TargetFolderText20 + ReadyToInstallTargetFolderText2Text37151306133##IDS__IsVerifyReadyDlg_Installdir##UserInformationText0 + ReadyToInstallUserInformationTextText23171306133##IDS__IsVerifyReadyDlg_UserInfo##UserNameText0 + ReadyToInstallUserNameTextText3818430693##IDS__IsVerifyReadyDlg_UserName##CompanyNameText0 + ReadyToRemoveBackPushButton16424366173##IDS_BACK##RemoveNow0 + ReadyToRemoveBannerBitmap003744410NewBinary1ReadyToRemoveBannerLineLine044374010 + ReadyToRemoveBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + ReadyToRemoveBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + ReadyToRemoveCancelPushButton30124366173##IDS_CANCEL##Back0 + ReadyToRemoveDlgDescText21232922565539##IDS__IsVerifyRemoveAllDlg_ChoseRemoveProgram##0 + ReadyToRemoveDlgLineLine48234326010 + ReadyToRemoveDlgTextText215132624131075##IDS__IsVerifyRemoveAllDlg_ClickRemove##0 + ReadyToRemoveDlgText1Text2179330233##IDS__IsVerifyRemoveAllDlg_ClickBack##0 + ReadyToRemoveDlgText2Text211023302430 + ReadyToRemoveDlgTitleText1362922565539##IDS__IsVerifyRemoveAllDlg_RemoveProgram##0 + ReadyToRemoveRemoveNowPushButton23024366178388611##IDS__IsVerifyRemoveAllDlg_Remove##Cancel0 + SetupCompleteErrorBackPushButton16424366171##IDS_BACK##Finish0 + SetupCompleteErrorCancelPushButton30124366171##IDS_CANCEL##Back0 + SetupCompleteErrorCheckShowMsiLogCheckBox1511721092ISSHOWMSILOGCancel0 + SetupCompleteErrorDlgLineLine0234374010 + SetupCompleteErrorFinishPushButton23024366173##IDS__IsFatalError_Finish##Image0 + SetupCompleteErrorFinishText1Text135802285065539##IDS__IsFatalError_NotModified##0 + SetupCompleteErrorFinishText2Text1351352282565539##IDS__IsFatalError_ClickFinish##0 + SetupCompleteErrorImageBitmap003742341CheckShowMsiLog0NewBinary5SetupCompleteErrorRestContText1Text135802285065539##IDS__IsFatalError_KeepOrRestore##0 + SetupCompleteErrorRestContText2Text1351352282565539##IDS__IsFatalError_RestoreOrContinueLater##0 + SetupCompleteErrorShowMsiLogTextText1641721981065538##IDS__IsSetupComplete_ShowMsiLog##0 + SetupCompleteErrorTextLine1Text13582254565539##IDS__IsFatalError_WizardCompleted##0 + SetupCompleteErrorTextLine2Text1355522825196611##IDS__IsFatalError_WizardInterrupted##0 + SetupCompleteSuccessBackPushButton16424366171##IDS_BACK##OK0 + SetupCompleteSuccessCancelPushButton30124366171##IDS_CANCEL##Image0 + SetupCompleteSuccessCheckBoxUpdatesCheckBox1351641092ISCHECKFORPRODUCTUPDATESCheckBox1CheckShowMsiLog0 + SetupCompleteSuccessCheckForUpdatesTextText1521621903065538##IDS__IsExitDialog_Update_YesCheckForUpdates##0 + SetupCompleteSuccessCheckLaunchProgramCheckBox1511141092LAUNCHPROGRAMCheckLaunchReadme0 + SetupCompleteSuccessCheckLaunchReadmeCheckBox1511481092LAUNCHREADMECheckBoxUpdates0 + SetupCompleteSuccessCheckShowMsiLogCheckBox1511821092ISSHOWMSILOGBack0 + SetupCompleteSuccessDlgLineLine0234374010 + SetupCompleteSuccessImageBitmap003742341CheckLaunchProgram0NewBinary5SetupCompleteSuccessLaunchProgramTextText164112981565538##IDS__IsExitDialog_LaunchProgram##00 + SetupCompleteSuccessLaunchReadmeTextText1641481201365538##IDS__IsExitDialog_ShowReadMe##00 + SetupCompleteSuccessOKPushButton23024366173##IDS__IsExitDialog_Finish##Cancel0 + SetupCompleteSuccessShowMsiLogTextText1641821981065538##IDS__IsSetupComplete_ShowMsiLog##0 + SetupCompleteSuccessTextLine1Text13582254565539##IDS__IsExitDialog_WizardCompleted##0 + SetupCompleteSuccessTextLine2Text1355522845196610##IDS__IsExitDialog_InstallSuccess##0 + SetupCompleteSuccessTextLine3Text1355522845196610##IDS__IsExitDialog_UninstallSuccess##0 + SetupCompleteSuccessUpdateTextLine1Text1353022845196610##IDS__IsExitDialog_Update_SetupFinished##0 + SetupCompleteSuccessUpdateTextLine2Text1358022845196610##IDS__IsExitDialog_Update_PossibleUpdates##0 + SetupCompleteSuccessUpdateTextLine3Text1351202284565538##IDS__IsExitDialog_Update_InternetConnection##0 + SetupErrorAPushButton1928066173##IDS__IsErrorDlg_Abort##0 + SetupErrorCPushButton1928066173##IDS_CANCEL2##0 + SetupErrorErrorIconIcon1515242452428810NewBinary4SetupErrorErrorTextText501520050131075##IDS__IsErrorDlg_ErrorText##0 + SetupErrorIPushButton1928066173##IDS__IsErrorDlg_Ignore##0 + SetupErrorNPushButton1928066173##IDS__IsErrorDlg_NO##0 + SetupErrorOPushButton1928066173##IDS__IsErrorDlg_OK##0 + SetupErrorRPushButton1928066173##IDS__IsErrorDlg_Retry##0 + SetupErrorYPushButton1928066173##IDS__IsErrorDlg_Yes##0 + SetupInitializationActionDataText1351252281265539##IDS__IsInitDlg_1##0 + SetupInitializationActionTextText1351092203665539##IDS__IsInitDlg_2##0 + SetupInitializationBackPushButton16424366171##IDS_BACK##0 + SetupInitializationCancelPushButton30124366173##IDS_CANCEL##0 + SetupInitializationDlgLineLine0234374010 + SetupInitializationImageBitmap0037423410NewBinary5SetupInitializationNextPushButton23024366171##IDS_NEXT##0 + SetupInitializationTextLine1Text135822545196611##IDS__IsInitDlg_WelcomeWizard##0 + SetupInitializationTextLine2Text1355522830196611##IDS__IsInitDlg_PreparingWizard##0 + SetupInterruptedBackPushButton16424366171##IDS_BACK##Finish0 + SetupInterruptedCancelPushButton30124366171##IDS_CANCEL##Image0 + SetupInterruptedCheckShowMsiLogCheckBox1511721092ISSHOWMSILOGBack0 + SetupInterruptedDlgLineLine0234374010 + SetupInterruptedFinishPushButton23024366173##IDS__IsUserExit_Finish##Cancel0 + SetupInterruptedFinishText1Text135802285065539##IDS__IsUserExit_NotModified##0 + SetupInterruptedFinishText2Text1351352282565539##IDS__IsUserExit_ClickFinish##0 + SetupInterruptedImageBitmap003742341CheckShowMsiLog0NewBinary5SetupInterruptedRestContText1Text135802285065539##IDS__IsUserExit_KeepOrRestore##0 + SetupInterruptedRestContText2Text1351352282565539##IDS__IsUserExit_RestoreOrContinue##0 + SetupInterruptedShowMsiLogTextText1641721981065538##IDS__IsSetupComplete_ShowMsiLog##0 + SetupInterruptedTextLine1Text13582254565539##IDS__IsUserExit_WizardCompleted##0 + SetupInterruptedTextLine2Text1355522825196611##IDS__IsUserExit_WizardInterrupted##0 + SetupProgressActionProgress95ProgressBar591132751265537##IDS__IsProgressDlg_ProgressDone##0 + SetupProgressActionTextText59100275123##IDS__IsProgressDlg_2##0 + SetupProgressBackPushButton16424366171##IDS_BACK##Next0 + SetupProgressBannerBitmap003744410NewBinary1SetupProgressBannerLineLine044374010 + SetupProgressBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + SetupProgressBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + SetupProgressCancelPushButton30124366173##IDS_CANCEL##Back0 + SetupProgressDlgDescText21232922565538##IDS__IsProgressDlg_UninstallingFeatures2##0 + SetupProgressDlgDesc2Text21232922565538##IDS__IsProgressDlg_UninstallingFeatures##0 + SetupProgressDlgLineLine48234326010 + SetupProgressDlgTextText595127530196610##IDS__IsProgressDlg_WaitUninstall2##0 + SetupProgressDlgText2Text595127530196610##IDS__IsProgressDlg_WaitUninstall##0 + SetupProgressDlgTitleText13629225196610##IDS__IsProgressDlg_InstallingProductName##0 + SetupProgressDlgTitle2Text13629225196610##IDS__IsProgressDlg_Uninstalling##0 + SetupProgressLbSecText19213932122##IDS__IsProgressDlg_SecHidden##0 + SetupProgressLbStatusText598570123##IDS__IsProgressDlg_Status##0 + SetupProgressNextPushButton23024366171##IDS_NEXT##Cancel0 + SetupProgressSetupIconIcon2151242452428810NewBinary9SetupProgressShowTimeText17013917122##IDS__IsProgressDlg_Hidden##0 + SetupProgressTextTimeText59139110122##IDS__IsProgressDlg_HiddenTimeRemaining##0 + SetupResumeBackPushButton16424366171##IDS_BACK##Next0 + SetupResumeCancelPushButton30124366173##IDS_CANCEL##Back0 + SetupResumeDlgLineLine0234374010 + SetupResumeImageBitmap0037423410NewBinary5SetupResumeNextPushButton23024366173##IDS_NEXT##Cancel0 + SetupResumePreselectedTextText1355522845196611##IDS__IsResumeDlg_WizardResume##0 + SetupResumeResumeTextText1354622845196611##IDS__IsResumeDlg_ResumeSuspended##0 + SetupResumeTextLine1Text135822545196611##IDS__IsResumeDlg_Resuming##0 + SetupTypeBackPushButton16424366173##IDS_BACK##Next0 + SetupTypeBannerBitmap003744410NewBinary1SetupTypeBannerLineLine044374010 + SetupTypeBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + SetupTypeBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + SetupTypeCancelPushButton30124366173##IDS_CANCEL##RadioGroup0 + SetupTypeCompTextText8080246303##IDS__IsSetupTypeMinDlg_AllFeatures##0 + SetupTypeCompleteIcoIcon3480242452428810NewBinary10SetupTypeCustTextText80171246303##IDS__IsSetupTypeMinDlg_ChooseFeatures##0 + SetupTypeCustomIcoIcon34171242452428810NewBinary6SetupTypeDlgDescText21232922565539##IDS__IsSetupTypeMinDlg_ChooseSetupType##0 + SetupTypeDlgLineLine48234326010 + SetupTypeDlgTextText2249326103##IDS__IsSetupTypeMinDlg_SelectSetupType##00 + SetupTypeDlgTitleText1362922565539##IDS__IsSetupTypeMinDlg_SetupType##0 + SetupTypeMinIcoIcon34125242452428810NewBinary6SetupTypeMinTextText80125246303##IDS__IsSetupTypeMinDlg_MinimumFeatures##0 + SetupTypeNextPushButton23024366173##IDS_NEXT##Cancel0 + SetupTypeRadioGroupRadioButtonGroup20592641391048579_IsSetupTypeMinBack00 + SplashBitmapBackPushButton16424366171##IDS_BACK##Next0 + SplashBitmapBranding1Text422950133##IDS_INSTALLSHIELD_FORMATTED##0 + SplashBitmapBranding2Text3228501365537##IDS_INSTALLSHIELD##0 + SplashBitmapCancelPushButton30124366173##IDS_CANCEL##Back0 + SplashBitmapDlgLineLine48234326010 + SplashBitmapImageBitmap131234921110NewBinary5SplashBitmapNextPushButton23024366173##IDS_NEXT##Cancel0 +
+ + + Dialog_ + Control_ + Action + Condition + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CustomSetupChangeFolderHideInstalledCustomSetupDetailsHideInstalledCustomSetupInstallLabelHideInstalledCustomerInformationDlgRadioGroupTextHideNOT PrivilegedCustomerInformationDlgRadioGroupTextHideProductState > 0CustomerInformationDlgRadioGroupTextHideVersion9XCustomerInformationDlgRadioGroupTextHideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledCustomerInformationRadioGroupHideNOT PrivilegedCustomerInformationRadioGroupHideProductState > 0CustomerInformationRadioGroupHideVersion9XCustomerInformationRadioGroupHideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledCustomerInformationSerialLabelShowSERIALNUMSHOWCustomerInformationSerialNumberShowSERIALNUMSHOWInstallWelcomeCopyrightHideSHOWCOPYRIGHT="No"InstallWelcomeCopyrightShowSHOWCOPYRIGHT="Yes"LicenseAgreementNextDisableAgreeToLicense <> "Yes"LicenseAgreementNextEnableAgreeToLicense = "Yes"ReadyToInstallCompanyNameTextHideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallCurrentSettingsTextHideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallDlgText2HideVersionNT < "601" OR NOT ISSupportPerUser OR InstalledReadyToInstallDlgText2ShowVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallDlgTitleShowProgressType0="Modify"ReadyToInstallDlgTitle2ShowProgressType0="Repair"ReadyToInstallDlgTitle3ShowProgressType0="install"ReadyToInstallGroupBox1HideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallInstallNowDisableVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallInstallNowEnableVersionNT < "601" OR NOT ISSupportPerUser OR InstalledReadyToInstallInstallPerMachineHideVersionNT < "601" OR NOT ISSupportPerUser OR InstalledReadyToInstallInstallPerMachineShowVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallInstallPerUserHideVersionNT < "601" OR NOT ISSupportPerUser OR InstalledReadyToInstallInstallPerUserShowVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallSerialNumberTextHideNOT SERIALNUMSHOWReadyToInstallSerialNumberTextHideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallSetupTypeText1HideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallSetupTypeText2HideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallTargetFolderText1HideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallTargetFolderText2HideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallUserInformationTextHideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledReadyToInstallUserNameTextHideVersionNT >= "601" AND ISSupportPerUser AND NOT InstalledSetupCompleteErrorBackDefaultUpdateStartedSetupCompleteErrorBackDisableNOT UpdateStartedSetupCompleteErrorBackEnableUpdateStartedSetupCompleteErrorCancelDisableNOT UpdateStartedSetupCompleteErrorCancelEnableUpdateStartedSetupCompleteErrorCheckShowMsiLogShowMsiLogFileLocationSetupCompleteErrorFinishDefaultNOT UpdateStartedSetupCompleteErrorFinishText1HideUpdateStartedSetupCompleteErrorFinishText1ShowNOT UpdateStartedSetupCompleteErrorFinishText2HideUpdateStartedSetupCompleteErrorFinishText2ShowNOT UpdateStartedSetupCompleteErrorRestContText1HideNOT UpdateStartedSetupCompleteErrorRestContText1ShowUpdateStartedSetupCompleteErrorRestContText2HideNOT UpdateStartedSetupCompleteErrorRestContText2ShowUpdateStartedSetupCompleteErrorShowMsiLogTextShowMsiLogFileLocationSetupCompleteSuccessCheckBoxUpdatesShowISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"SetupCompleteSuccessCheckForUpdatesTextShowISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"SetupCompleteSuccessCheckLaunchProgramShowSHOWLAUNCHPROGRAM="-1" And PROGRAMFILETOLAUNCHATEND <> "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOGSetupCompleteSuccessCheckLaunchReadmeShowSHOWLAUNCHREADME="-1" And READMEFILETOLAUNCHATEND <> "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOGSetupCompleteSuccessCheckShowMsiLogShowMsiLogFileLocation And NOT ISENABLEDWUSFINISHDIALOGSetupCompleteSuccessLaunchProgramTextShowSHOWLAUNCHPROGRAM="-1" And PROGRAMFILETOLAUNCHATEND <> "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOGSetupCompleteSuccessLaunchReadmeTextShowSHOWLAUNCHREADME="-1" And READMEFILETOLAUNCHATEND <> "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOGSetupCompleteSuccessShowMsiLogTextShowMsiLogFileLocation And NOT ISENABLEDWUSFINISHDIALOGSetupCompleteSuccessTextLine2ShowProgressType2="installed" And ((ACTION<>"INSTALL") OR (NOT ISENABLEDWUSFINISHDIALOG) OR (ISENABLEDWUSFINISHDIALOG And Installed))SetupCompleteSuccessTextLine3ShowProgressType2="uninstalled" And ((ACTION<>"INSTALL") OR (NOT ISENABLEDWUSFINISHDIALOG) OR (ISENABLEDWUSFINISHDIALOG And Installed))SetupCompleteSuccessUpdateTextLine1ShowISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"SetupCompleteSuccessUpdateTextLine2ShowISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"SetupCompleteSuccessUpdateTextLine3ShowISENABLEDWUSFINISHDIALOG And NOT Installed And ACTION="INSTALL"SetupInterruptedBackDefaultUpdateStartedSetupInterruptedBackDisableNOT UpdateStartedSetupInterruptedBackEnableUpdateStartedSetupInterruptedCancelDisableNOT UpdateStartedSetupInterruptedCancelEnableUpdateStartedSetupInterruptedCheckShowMsiLogShowMsiLogFileLocationSetupInterruptedFinishDefaultNOT UpdateStartedSetupInterruptedFinishText1HideUpdateStartedSetupInterruptedFinishText1ShowNOT UpdateStartedSetupInterruptedFinishText2HideUpdateStartedSetupInterruptedFinishText2ShowNOT UpdateStartedSetupInterruptedRestContText1HideNOT UpdateStartedSetupInterruptedRestContText1ShowUpdateStartedSetupInterruptedRestContText2HideNOT UpdateStartedSetupInterruptedRestContText2ShowUpdateStartedSetupInterruptedShowMsiLogTextShowMsiLogFileLocationSetupProgressDlgDescShowProgressType2="installed"SetupProgressDlgDesc2ShowProgressType2="uninstalled"SetupProgressDlgTextShowProgressType3="installs"SetupProgressDlgText2ShowProgressType3="uninstalls"SetupProgressDlgTitleShowProgressType1="Installing"SetupProgressDlgTitle2ShowProgressType1="Uninstalling"SetupResumePreselectedTextHideRESUMESetupResumePreselectedTextShowNOT RESUMESetupResumeResumeTextHideNOT RESUMESetupResumeResumeTextShowRESUME
+ + + Dialog_ + Control_ + Event + Argument + Condition + Ordering + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AdminChangeFolderCancelEndDialogReturn12AdminChangeFolderCancelReset011AdminChangeFolderNewFolderDirectoryListNew010AdminChangeFolderOKEndDialogReturn10AdminChangeFolderOKSetTargetPathTARGETDIR11AdminChangeFolderUpDirectoryListUp010AdminNetworkLocationBackNewDialogAdminWelcome10AdminNetworkLocationBrowseSpawnDialogAdminChangeFolder10AdminNetworkLocationCancelSpawnDialogCancelSetup10AdminNetworkLocationInstallNowEndDialogReturnOutOfNoRbDiskSpace <> 13AdminNetworkLocationInstallNowNewDialogOutOfSpaceOutOfNoRbDiskSpace = 12AdminNetworkLocationInstallNowSetTargetPathTARGETDIR11AdminWelcomeCancelSpawnDialogCancelSetup10AdminWelcomeNextNewDialogAdminNetworkLocation10CancelSetupNoEndDialogReturn10CancelSetupYesDoActionCleanUpISSCRIPTRUNNING="1"1CancelSetupYesEndDialogExit12CustomSetupBackNewDialogMaintenanceTypeInstalled0CustomSetupBackNewDialogSetupTypeNOT Installed0CustomSetupCancelSpawnDialogCancelSetup10CustomSetupChangeFolderSelectionBrowseInstallChangeFolder10CustomSetupDetailsSelectionBrowseDiskSpaceRequirements11CustomSetupHelpSpawnDialogCustomSetupTips11CustomSetupNextNewDialogOutOfSpaceOutOfNoRbDiskSpace = 10CustomSetupNextNewDialogReadyToInstallOutOfNoRbDiskSpace <> 10CustomSetupNext[_IsSetupTypeMin]Custom10CustomSetupTipsOKEndDialogReturn11CustomerInformationBackNewDialogLicenseAgreement11CustomerInformationCancelSpawnDialogCancelSetup10CustomerInformationNextEndDialogExit(SERIALNUMVALRETRYLIMIT) And (SERIALNUMVALRETRYLIMIT<0) And (SERIALNUMVALRETURN<>SERIALNUMVALSUCCESSRETVAL)2CustomerInformationNextNewDialogSetupType(Not SERIALNUMVALRETURN) OR (SERIALNUMVALRETURN=SERIALNUMVALSUCCESSRETVAL)3CustomerInformationNext[ALLUSERS]1ApplicationUsers = "AllUsers" And Privileged1CustomerInformationNext[ALLUSERS]{}ApplicationUsers = "OnlyCurrentUser" And Privileged2DatabaseFolderBackNewDialogCustomerInformation11DatabaseFolderCancelSpawnDialogCancelSetup11DatabaseFolderChangeFolderSpawnDialogInstallChangeFolder11DatabaseFolderChangeFolder[_BrowseProperty]DATABASEDIR12DatabaseFolderNextNewDialogSetupType11DestinationFolderBackNewDialogCustomerInformation10DestinationFolderCancelSpawnDialogCancelSetup11DestinationFolderChangeFolderSpawnDialogInstallChangeFolder11DestinationFolderChangeFolder[_BrowseProperty]INSTALLDIR12DestinationFolderNextNewDialogReadyToInstall10DiskSpaceRequirementsOKEndDialogReturn10FilesInUseExitEndDialogExit10FilesInUseIgnoreEndDialogIgnore10FilesInUseRetryEndDialogRetry10InstallChangeFolderCancelEndDialogReturn12InstallChangeFolderCancelReset011InstallChangeFolderNewFolderDirectoryListNew010InstallChangeFolderOKEndDialogReturn13InstallChangeFolderOKSetTargetPath[_BrowseProperty]12InstallChangeFolderUpDirectoryListUp010InstallWelcomeBackNewDialogSplashBitmapDisplay_IsBitmapDlg0InstallWelcomeCancelSpawnDialogCancelSetup10InstallWelcomeNextNewDialogLicenseAgreement10LicenseAgreementBackNewDialogInstallWelcome10LicenseAgreementCancelSpawnDialogCancelSetup10LicenseAgreementISPrintButtonDoActionISPrint10LicenseAgreementNextNewDialogCustomerInformationAgreeToLicense = "Yes"0MaintenanceTypeBackNewDialogMaintenanceWelcome10MaintenanceTypeCancelSpawnDialogCancelSetup10MaintenanceTypeNextNewDialogCustomSetup_IsMaintenance = "Change"12MaintenanceTypeNextNewDialogReadyToInstall_IsMaintenance = "Reinstall"13MaintenanceTypeNextNewDialogReadyToRemove_IsMaintenance = "Remove"11MaintenanceTypeNextReinstallALL_IsMaintenance = "Reinstall"10MaintenanceTypeNextReinstallMode[ReinstallModeText]_IsMaintenance = "Reinstall"9MaintenanceTypeNext[ProgressType0]Modify_IsMaintenance = "Change"2MaintenanceTypeNext[ProgressType0]Repair_IsMaintenance = "Reinstall"1MaintenanceTypeNext[ProgressType1]Modifying_IsMaintenance = "Change"3MaintenanceTypeNext[ProgressType1]Repairing_IsMaintenance = "Reinstall"4MaintenanceTypeNext[ProgressType2]modified_IsMaintenance = "Change"6MaintenanceTypeNext[ProgressType2]repairs_IsMaintenance = "Reinstall"5MaintenanceTypeNext[ProgressType3]modifies_IsMaintenance = "Change"7MaintenanceTypeNext[ProgressType3]repairs_IsMaintenance = "Reinstall"8MaintenanceWelcomeCancelSpawnDialogCancelSetup10MaintenanceWelcomeNextNewDialogMaintenanceType10MsiRMFilesInUseCancelEndDialogExit11MsiRMFilesInUseOKEndDialogReturn11MsiRMFilesInUseOKRMShutdownAndRestart0RestartManagerOption="CloseRestart"2OutOfSpaceResumeNewDialogAdminNetworkLocationACTION = "ADMIN"0OutOfSpaceResumeNewDialogCustomSetupACTION <> "ADMIN"0PatchWelcomeCancelSpawnDialogCancelSetup11PatchWelcomeNextEndDialogReturn13PatchWelcomeNextReinstallALLPATCH And REINSTALL=""1PatchWelcomeNextReinstallModeomusPATCH And REINSTALLMODE=""2ReadmeInformationBackNewDialogLicenseAgreement11ReadmeInformationCancelSpawnDialogCancelSetup11ReadmeInformationNextNewDialogCustomerInformation11ReadyToInstallBackNewDialogCustomSetupInstalled OR _IsSetupTypeMin = "Custom"2ReadyToInstallBackNewDialogMaintenanceTypeInstalled AND _IsMaintenance = "Reinstall"3ReadyToInstallBackNewDialogSetupTypeNOT Installed AND _IsSetupTypeMin <> "Custom"1ReadyToInstallCancelSpawnDialogCancelSetup10ReadyToInstallInstallNowEndDialogReturnOutOfNoRbDiskSpace <> 10ReadyToInstallInstallNowNewDialogOutOfSpaceOutOfNoRbDiskSpace = 10ReadyToInstallInstallNow[ProgressType1]Installing10ReadyToInstallInstallNow[ProgressType2]installed10ReadyToInstallInstallNow[ProgressType3]installs10ReadyToInstallInstallPerMachineEndDialogReturnOutOfNoRbDiskSpace <> 10ReadyToInstallInstallPerMachineNewDialogOutOfSpaceOutOfNoRbDiskSpace = 10ReadyToInstallInstallPerMachine[ALLUSERS]110ReadyToInstallInstallPerMachine[MSIINSTALLPERUSER]{}10ReadyToInstallInstallPerMachine[ProgressType1]Installing10ReadyToInstallInstallPerMachine[ProgressType2]installed10ReadyToInstallInstallPerMachine[ProgressType3]installs10ReadyToInstallInstallPerUserEndDialogReturnOutOfNoRbDiskSpace <> 10ReadyToInstallInstallPerUserNewDialogOutOfSpaceOutOfNoRbDiskSpace = 10ReadyToInstallInstallPerUser[ALLUSERS]210ReadyToInstallInstallPerUser[MSIINSTALLPERUSER]110ReadyToInstallInstallPerUser[ProgressType1]Installing10ReadyToInstallInstallPerUser[ProgressType2]installed10ReadyToInstallInstallPerUser[ProgressType3]installs10ReadyToRemoveBackNewDialogMaintenanceType10ReadyToRemoveCancelSpawnDialogCancelSetup10ReadyToRemoveRemoveNowEndDialogReturnOutOfNoRbDiskSpace <> 12ReadyToRemoveRemoveNowNewDialogOutOfSpaceOutOfNoRbDiskSpace = 12ReadyToRemoveRemoveNowRemoveALL11ReadyToRemoveRemoveNow[ProgressType1]Uninstalling10ReadyToRemoveRemoveNow[ProgressType2]uninstalled10ReadyToRemoveRemoveNow[ProgressType3]uninstalls10SetupCompleteErrorBackEndDialogReturn12SetupCompleteErrorBack[Suspend]{}11SetupCompleteErrorCancelEndDialogReturn12SetupCompleteErrorCancel[Suspend]111SetupCompleteErrorFinishDoActionCleanUpISSCRIPTRUNNING="1"1SetupCompleteErrorFinishDoActionShowMsiLogMsiLogFileLocation And (ISSHOWMSILOG="1")3SetupCompleteErrorFinishEndDialogExit12SetupCompleteSuccessOKDoActionCleanUpISSCRIPTRUNNING="1"1SetupCompleteSuccessOKDoActionShowMsiLogMsiLogFileLocation And (ISSHOWMSILOG="1") And NOT ISENABLEDWUSFINISHDIALOG6SetupCompleteSuccessOKEndDialogExit12SetupErrorAEndDialogErrorAbort10SetupErrorCEndDialogErrorCancel10SetupErrorIEndDialogErrorIgnore10SetupErrorNEndDialogErrorNo10SetupErrorOEndDialogErrorOk10SetupErrorREndDialogErrorRetry10SetupErrorYEndDialogErrorYes10SetupInitializationCancelSpawnDialogCancelSetup10SetupInterruptedBackEndDialogExit12SetupInterruptedBack[Suspend]{}11SetupInterruptedCancelEndDialogExit12SetupInterruptedCancel[Suspend]111SetupInterruptedFinishDoActionCleanUpISSCRIPTRUNNING="1"1SetupInterruptedFinishDoActionShowMsiLogMsiLogFileLocation And (ISSHOWMSILOG="1")3SetupInterruptedFinishEndDialogExit12SetupProgressCancelSpawnDialogCancelSetup10SetupResumeCancelSpawnDialogCancelSetup10SetupResumeNextEndDialogReturnOutOfNoRbDiskSpace <> 10SetupResumeNextNewDialogOutOfSpaceOutOfNoRbDiskSpace = 10SetupTypeBackNewDialogCustomerInformation11SetupTypeCancelSpawnDialogCancelSetup10SetupTypeNextNewDialogCustomSetup_IsSetupTypeMin = "Custom"2SetupTypeNextNewDialogReadyToInstall_IsSetupTypeMin <> "Custom"1SetupTypeNextSetInstallLevel100_IsSetupTypeMin="Minimal"0SetupTypeNextSetInstallLevel200_IsSetupTypeMin="Typical"0SetupTypeNextSetInstallLevel300_IsSetupTypeMin="Custom"0SetupTypeNext[ISRUNSETUPTYPEADDLOCALEVENT]110SetupTypeNext[SelectedSetupType][DisplayNameCustom]_IsSetupTypeMin = "Custom"0SetupTypeNext[SelectedSetupType][DisplayNameMinimal]_IsSetupTypeMin = "Minimal"0SetupTypeNext[SelectedSetupType][DisplayNameTypical]_IsSetupTypeMin = "Typical"0SplashBitmapCancelSpawnDialogCancelSetup10SplashBitmapNextNewDialogInstallWelcome10
+ + + Directory_ + Component_ +
+ + + Action + Type + Source + Target + ExtendedType + ISComments + + + + +
ISPreventDowngrade19[IS_PREVENT_DOWNGRADE_EXIT]Exits install when a newer version of this product is foundISPrint1SetAllUsers.dllPrintScrollableTextPrints the contents of a ScrollableText control on a dialog.ISRunSetupTypeAddLocalEvent1ISExpHlp.dllRunSetupTypeAddLocalEventRun the AddLocal events associated with the Next button on the Setup Type dialog.SetARPINSTALLLOCATION51ARPINSTALLLOCATION[INSTALLDIR] + SetAllUsersProfileNT51ALLUSERSPROFILE[%SystemRoot]\Profiles\All Users + ShowMsiLog226SystemFolder[SystemFolder]notepad.exe "[MsiLogFileLocation]"Shows Property-driven MSI LogsetAllUsersProfile2K51ALLUSERSPROFILE[%ALLUSERSPROFILE] + setUserProfileNT51USERPROFILE[%USERPROFILE] +
+ + + Dialog + HCentering + VCentering + Width + Height + Attributes + Title + Control_First + Control_Default + Control_Cancel + ISComments + TextStyle_ + ISWindowStyle + ISResourceId + +
AdminChangeFolder50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##TailOKCancelInstall Point Browse0 + AdminNetworkLocation50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##InstallNowInstallNowCancelNetwork Location0 + AdminWelcome50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##NextNextCancelAdministration Welcome0 + CancelSetup5050260853##IDS_PRODUCTNAME_INSTALLSHIELD##NoNoNoCancel0 + CustomSetup505037426635##IDS_PRODUCTNAME_INSTALLSHIELD##TreeNextCancelCustom Selection0 + CustomSetupTips50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##OKOKOKCustom Setup Tips0 + CustomerInformation50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##NameEditNextCancelIdentification0 + DatabaseFolder50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##NextNextCancelDatabase Folder0 + DestinationFolder50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##NextNextCancelDestination Folder0 + DiskSpaceRequirements50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##OKOKOKFeature Details0 + FilesInUse505037426619##IDS_PRODUCTNAME_INSTALLSHIELD##RetryRetryExitFiles in Use0 + InstallChangeFolder50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##TailOKCancelBrowse0 + InstallWelcome50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##NextNextCancelWelcome Panel0 + LicenseAgreement50503742662##IDS_PRODUCTNAME_INSTALLSHIELD##AgreeNextCancelLicense Agreement0 + MaintenanceType50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##RadioGroupNextCancelChange, Reinstall, Remove0 + MaintenanceWelcome50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##NextNextCancelMaintenance Welcome0 + MsiRMFilesInUse505037426619##IDS_PRODUCTNAME_INSTALLSHIELD##OKOKCancelRestartManager Files in Use0 + OutOfSpace50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##ResumeResumeResumeOut Of Disk Space0 + PatchWelcome50503742663##IDS__IsPatchDlg_PatchWizard##NextNextCancelPatch Panel0 + ReadmeInformation50503742667##IDS_PRODUCTNAME_INSTALLSHIELD##NextNextCancelReadme Information00ReadyToInstall505037426635##IDS_PRODUCTNAME_INSTALLSHIELD##InstallNowInstallNowCancelReady to Install0 + ReadyToRemove50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##RemoveNowRemoveNowCancelVerify Remove0 + SetupCompleteError50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##FinishFinishFinishFatal Error0 + SetupCompleteSuccess50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##OKOKOKExit0 + SetupError505027011065543##IDS__IsErrorDlg_InstallerInfo##ErrorTextOCError0 + SetupInitialization50503742665##IDS_PRODUCTNAME_INSTALLSHIELD##CancelCancelCancelSetup Initialization0 + SetupInterrupted50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##FinishFinishFinishUser Exit0 + SetupProgress50503742665##IDS_PRODUCTNAME_INSTALLSHIELD##CancelCancelCancelProgress0 + SetupResume50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##NextNextCancelResume0 + SetupType50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##RadioGroupNextCancelSetup Type0 + SplashBitmap50503742663##IDS_PRODUCTNAME_INSTALLSHIELD##NextNextCancelWelcome Bitmap0 +
+ + + Directory + Directory_Parent + DefaultDir + ISDescription + ISAttributes + ISFolderName +
ALLUSERSPROFILETARGETDIR.:ALLUSE~1|All Users0 + AdminToolsFolderTARGETDIR.:Admint~1|AdminTools0 + AppDataFolderTARGETDIR.:APPLIC~1|Application Data0 + CommonAppDataFolderTARGETDIR.:Common~1|CommonAppData0 + CommonFilesFolderTARGETDIR.:Common0 + DATABASEDIRISYourDataBaseDir.0 + DesktopFolderTARGETDIR.:Desktop3 + FavoritesFolderTARGETDIR.:FAVORI~1|Favorites0 + FontsFolderTARGETDIR.:Fonts0 + GlobalAssemblyCacheTARGETDIR.:Global~1|GlobalAssemblyCache0 + INSTALLDIRISMyProductDir.0 + ISCommonFilesFolderCommonFilesFolderInstal~1|InstallShield0 + ISMyCompanyDirProgramFilesFolderMYCOMP~1|My Company Name0 + ISMyProductDirISMyCompanyDirMYPROD~1|My Product Name0 + ISYourDataBaseDirINSTALLDIRDatabase0 + LocalAppDataFolderTARGETDIR.:LocalA~1|LocalAppData0 + MyPicturesFolderTARGETDIR.:MyPict~1|MyPictures0 + NetHoodFolderTARGETDIR.:NetHood0 + PersonalFolderTARGETDIR.:Personal0 + PrimaryVolumePathTARGETDIR.:Primar~1|PrimaryVolumePath0 + PrintHoodFolderTARGETDIR.:PRINTH~1|PrintHood0 + ProgramFilesFolderTARGETDIR.:PROGRA~1|program files0 + ProgramMenuFolderTARGETDIR.:Programs3 + RecentFolderTARGETDIR.:Recent0 + SendToFolderTARGETDIR.:SendTo3 + StartMenuFolderTARGETDIR.:STARTM~1|Start Menu3 + StartupFolderTARGETDIR.:StartUp3 + System16FolderTARGETDIR.:System0 + SystemFolderTARGETDIR.:System320 + TARGETDIRSourceDir0 + TempFolderTARGETDIR.:Temp0 + TemplateFolderTARGETDIR.:ShellNew0 + USERPROFILETARGETDIR.:USERPR~1|UserProfile0 + WindowsFolderTARGETDIR.:Windows0 + WindowsVolumeTARGETDIR.:WinRoot0 +
+ + + Signature_ + Parent + Path + Depth +
+ + + FileKey + Component_ + File_ + DestName + DestFolder +
+ + + Environment + Name + Value + Component_ +
+ + + Error + Message + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
0##IDS_ERROR_0##1##IDS_ERROR_1##10##IDS_ERROR_8##11##IDS_ERROR_9##1101##IDS_ERROR_22##12##IDS_ERROR_10##13##IDS_ERROR_11##1301##IDS_ERROR_23##1302##IDS_ERROR_24##1303##IDS_ERROR_25##1304##IDS_ERROR_26##1305##IDS_ERROR_27##1306##IDS_ERROR_28##1307##IDS_ERROR_29##1308##IDS_ERROR_30##1309##IDS_ERROR_31##1310##IDS_ERROR_32##1311##IDS_ERROR_33##1312##IDS_ERROR_34##1313##IDS_ERROR_35##1314##IDS_ERROR_36##1315##IDS_ERROR_37##1316##IDS_ERROR_38##1317##IDS_ERROR_39##1318##IDS_ERROR_40##1319##IDS_ERROR_41##1320##IDS_ERROR_42##1321##IDS_ERROR_43##1322##IDS_ERROR_44##1323##IDS_ERROR_45##1324##IDS_ERROR_46##1325##IDS_ERROR_47##1326##IDS_ERROR_48##1327##IDS_ERROR_49##1328##IDS_ERROR_122##1329##IDS_ERROR_1329##1330##IDS_ERROR_1330##1331##IDS_ERROR_1331##1332##IDS_ERROR_1332##1333##IDS_ERROR_1333##1334##IDS_ERROR_1334##1335##IDS_ERROR_1335##1336##IDS_ERROR_1336##14##IDS_ERROR_12##1401##IDS_ERROR_50##1402##IDS_ERROR_51##1403##IDS_ERROR_52##1404##IDS_ERROR_53##1405##IDS_ERROR_54##1406##IDS_ERROR_55##1407##IDS_ERROR_56##1408##IDS_ERROR_57##1409##IDS_ERROR_58##1410##IDS_ERROR_59##15##IDS_ERROR_13##1500##IDS_ERROR_60##1501##IDS_ERROR_61##1502##IDS_ERROR_62##1503##IDS_ERROR_63##16##IDS_ERROR_14##1601##IDS_ERROR_64##1602##IDS_ERROR_65##1603##IDS_ERROR_66##1604##IDS_ERROR_67##1605##IDS_ERROR_68##1606##IDS_ERROR_69##1607##IDS_ERROR_70##1608##IDS_ERROR_71##1609##IDS_ERROR_1609##1651##IDS_ERROR_1651##17##IDS_ERROR_15##1701##IDS_ERROR_72##1702##IDS_ERROR_73##1703##IDS_ERROR_74##1704##IDS_ERROR_75##1705##IDS_ERROR_76##1706##IDS_ERROR_77##1707##IDS_ERROR_78##1708##IDS_ERROR_79##1709##IDS_ERROR_80##1710##IDS_ERROR_81##1711##IDS_ERROR_82##1712##IDS_ERROR_83##1713##IDS_ERROR_123##1714##IDS_ERROR_124##1715##IDS_ERROR_1715##1716##IDS_ERROR_1716##1717##IDS_ERROR_1717##1718##IDS_ERROR_1718##1719##IDS_ERROR_1719##1720##IDS_ERROR_1720##1721##IDS_ERROR_1721##1722##IDS_ERROR_1722##1723##IDS_ERROR_1723##1724##IDS_ERROR_1724##1725##IDS_ERROR_1725##1726##IDS_ERROR_1726##1727##IDS_ERROR_1727##1728##IDS_ERROR_1728##1729##IDS_ERROR_1729##1730##IDS_ERROR_1730##1731##IDS_ERROR_1731##1732##IDS_ERROR_1732##18##IDS_ERROR_16##1801##IDS_ERROR_84##1802##IDS_ERROR_85##1803##IDS_ERROR_86##1804##IDS_ERROR_87##1805##IDS_ERROR_88##1806##IDS_ERROR_89##1807##IDS_ERROR_90##19##IDS_ERROR_17##1901##IDS_ERROR_91##1902##IDS_ERROR_92##1903##IDS_ERROR_93##1904##IDS_ERROR_94##1905##IDS_ERROR_95##1906##IDS_ERROR_96##1907##IDS_ERROR_97##1908##IDS_ERROR_98##1909##IDS_ERROR_99##1910##IDS_ERROR_100##1911##IDS_ERROR_101##1912##IDS_ERROR_102##1913##IDS_ERROR_103##1914##IDS_ERROR_104##1915##IDS_ERROR_105##1916##IDS_ERROR_106##1917##IDS_ERROR_107##1918##IDS_ERROR_108##1919##IDS_ERROR_109##1920##IDS_ERROR_110##1921##IDS_ERROR_111##1922##IDS_ERROR_112##1923##IDS_ERROR_113##1924##IDS_ERROR_114##1925##IDS_ERROR_115##1926##IDS_ERROR_116##1927##IDS_ERROR_117##1928##IDS_ERROR_118##1929##IDS_ERROR_119##1930##IDS_ERROR_125##1931##IDS_ERROR_126##1932##IDS_ERROR_127##1933##IDS_ERROR_128##1934##IDS_ERROR_129##1935##IDS_ERROR_1935##1936##IDS_ERROR_1936##1937##IDS_ERROR_1937##1938##IDS_ERROR_1938##2##IDS_ERROR_2##20##IDS_ERROR_18##21##IDS_ERROR_19##2101##IDS_ERROR_2101##2102##IDS_ERROR_2102##2103##IDS_ERROR_2103##2104##IDS_ERROR_2104##2105##IDS_ERROR_2105##2106##IDS_ERROR_2106##2107##IDS_ERROR_2107##2108##IDS_ERROR_2108##2109##IDS_ERROR_2109##2110##IDS_ERROR_2110##2111##IDS_ERROR_2111##2112##IDS_ERROR_2112##2113##IDS_ERROR_2113##22##IDS_ERROR_120##2200##IDS_ERROR_2200##2201##IDS_ERROR_2201##2202##IDS_ERROR_2202##2203##IDS_ERROR_2203##2204##IDS_ERROR_2204##2205##IDS_ERROR_2205##2206##IDS_ERROR_2206##2207##IDS_ERROR_2207##2208##IDS_ERROR_2208##2209##IDS_ERROR_2209##2210##IDS_ERROR_2210##2211##IDS_ERROR_2211##2212##IDS_ERROR_2212##2213##IDS_ERROR_2213##2214##IDS_ERROR_2214##2215##IDS_ERROR_2215##2216##IDS_ERROR_2216##2217##IDS_ERROR_2217##2218##IDS_ERROR_2218##2219##IDS_ERROR_2219##2220##IDS_ERROR_2220##2221##IDS_ERROR_2221##2222##IDS_ERROR_2222##2223##IDS_ERROR_2223##2224##IDS_ERROR_2224##2225##IDS_ERROR_2225##2226##IDS_ERROR_2226##2227##IDS_ERROR_2227##2228##IDS_ERROR_2228##2229##IDS_ERROR_2229##2230##IDS_ERROR_2230##2231##IDS_ERROR_2231##2232##IDS_ERROR_2232##2233##IDS_ERROR_2233##2234##IDS_ERROR_2234##2235##IDS_ERROR_2235##2236##IDS_ERROR_2236##2237##IDS_ERROR_2237##2238##IDS_ERROR_2238##2239##IDS_ERROR_2239##2240##IDS_ERROR_2240##2241##IDS_ERROR_2241##2242##IDS_ERROR_2242##2243##IDS_ERROR_2243##2244##IDS_ERROR_2244##2245##IDS_ERROR_2245##2246##IDS_ERROR_2246##2247##IDS_ERROR_2247##2248##IDS_ERROR_2248##2249##IDS_ERROR_2249##2250##IDS_ERROR_2250##2251##IDS_ERROR_2251##2252##IDS_ERROR_2252##2253##IDS_ERROR_2253##2254##IDS_ERROR_2254##2255##IDS_ERROR_2255##2256##IDS_ERROR_2256##2257##IDS_ERROR_2257##2258##IDS_ERROR_2258##2259##IDS_ERROR_2259##2260##IDS_ERROR_2260##2261##IDS_ERROR_2261##2262##IDS_ERROR_2262##2263##IDS_ERROR_2263##2264##IDS_ERROR_2264##2265##IDS_ERROR_2265##2266##IDS_ERROR_2266##2267##IDS_ERROR_2267##2268##IDS_ERROR_2268##2269##IDS_ERROR_2269##2270##IDS_ERROR_2270##2271##IDS_ERROR_2271##2272##IDS_ERROR_2272##2273##IDS_ERROR_2273##2274##IDS_ERROR_2274##2275##IDS_ERROR_2275##2276##IDS_ERROR_2276##2277##IDS_ERROR_2277##2278##IDS_ERROR_2278##2279##IDS_ERROR_2279##2280##IDS_ERROR_2280##2281##IDS_ERROR_2281##2282##IDS_ERROR_2282##23##IDS_ERROR_121##2302##IDS_ERROR_2302##2303##IDS_ERROR_2303##2304##IDS_ERROR_2304##2305##IDS_ERROR_2305##2306##IDS_ERROR_2306##2307##IDS_ERROR_2307##2308##IDS_ERROR_2308##2309##IDS_ERROR_2309##2310##IDS_ERROR_2310##2315##IDS_ERROR_2315##2318##IDS_ERROR_2318##2319##IDS_ERROR_2319##2320##IDS_ERROR_2320##2321##IDS_ERROR_2321##2322##IDS_ERROR_2322##2323##IDS_ERROR_2323##2324##IDS_ERROR_2324##2325##IDS_ERROR_2325##2326##IDS_ERROR_2326##2327##IDS_ERROR_2327##2328##IDS_ERROR_2328##2329##IDS_ERROR_2329##2330##IDS_ERROR_2330##2331##IDS_ERROR_2331##2332##IDS_ERROR_2332##2333##IDS_ERROR_2333##2334##IDS_ERROR_2334##2335##IDS_ERROR_2335##2336##IDS_ERROR_2336##2337##IDS_ERROR_2337##2338##IDS_ERROR_2338##2339##IDS_ERROR_2339##2340##IDS_ERROR_2340##2341##IDS_ERROR_2341##2342##IDS_ERROR_2342##2343##IDS_ERROR_2343##2344##IDS_ERROR_2344##2345##IDS_ERROR_2345##2347##IDS_ERROR_2347##2348##IDS_ERROR_2348##2349##IDS_ERROR_2349##2350##IDS_ERROR_2350##2351##IDS_ERROR_2351##2352##IDS_ERROR_2352##2353##IDS_ERROR_2353##2354##IDS_ERROR_2354##2355##IDS_ERROR_2355##2356##IDS_ERROR_2356##2357##IDS_ERROR_2357##2358##IDS_ERROR_2358##2359##IDS_ERROR_2359##2360##IDS_ERROR_2360##2361##IDS_ERROR_2361##2362##IDS_ERROR_2362##2363##IDS_ERROR_2363##2364##IDS_ERROR_2364##2365##IDS_ERROR_2365##2366##IDS_ERROR_2366##2367##IDS_ERROR_2367##2368##IDS_ERROR_2368##2370##IDS_ERROR_2370##2371##IDS_ERROR_2371##2372##IDS_ERROR_2372##2373##IDS_ERROR_2373##2374##IDS_ERROR_2374##2375##IDS_ERROR_2375##2376##IDS_ERROR_2376##2379##IDS_ERROR_2379##2380##IDS_ERROR_2380##2381##IDS_ERROR_2381##2382##IDS_ERROR_2382##2401##IDS_ERROR_2401##2402##IDS_ERROR_2402##2501##IDS_ERROR_2501##2502##IDS_ERROR_2502##2503##IDS_ERROR_2503##2601##IDS_ERROR_2601##2602##IDS_ERROR_2602##2603##IDS_ERROR_2603##2604##IDS_ERROR_2604##2605##IDS_ERROR_2605##2606##IDS_ERROR_2606##2607##IDS_ERROR_2607##2608##IDS_ERROR_2608##2609##IDS_ERROR_2609##2611##IDS_ERROR_2611##2612##IDS_ERROR_2612##2613##IDS_ERROR_2613##2614##IDS_ERROR_2614##2615##IDS_ERROR_2615##2616##IDS_ERROR_2616##2617##IDS_ERROR_2617##2618##IDS_ERROR_2618##2619##IDS_ERROR_2619##2620##IDS_ERROR_2620##2621##IDS_ERROR_2621##2701##IDS_ERROR_2701##2702##IDS_ERROR_2702##2703##IDS_ERROR_2703##2704##IDS_ERROR_2704##2705##IDS_ERROR_2705##2706##IDS_ERROR_2706##2707##IDS_ERROR_2707##2708##IDS_ERROR_2708##2709##IDS_ERROR_2709##2710##IDS_ERROR_2710##2711##IDS_ERROR_2711##2712##IDS_ERROR_2712##2713##IDS_ERROR_2713##2714##IDS_ERROR_2714##2715##IDS_ERROR_2715##2716##IDS_ERROR_2716##2717##IDS_ERROR_2717##2718##IDS_ERROR_2718##2719##IDS_ERROR_2719##2720##IDS_ERROR_2720##2721##IDS_ERROR_2721##2722##IDS_ERROR_2722##2723##IDS_ERROR_2723##2724##IDS_ERROR_2724##2725##IDS_ERROR_2725##2726##IDS_ERROR_2726##2727##IDS_ERROR_2727##2728##IDS_ERROR_2728##2729##IDS_ERROR_2729##2730##IDS_ERROR_2730##2731##IDS_ERROR_2731##2732##IDS_ERROR_2732##2733##IDS_ERROR_2733##2734##IDS_ERROR_2734##2735##IDS_ERROR_2735##2736##IDS_ERROR_2736##2737##IDS_ERROR_2737##2738##IDS_ERROR_2738##2739##IDS_ERROR_2739##2740##IDS_ERROR_2740##2741##IDS_ERROR_2741##2742##IDS_ERROR_2742##2743##IDS_ERROR_2743##2744##IDS_ERROR_2744##2745##IDS_ERROR_2745##2746##IDS_ERROR_2746##2747##IDS_ERROR_2747##2748##IDS_ERROR_2748##2749##IDS_ERROR_2749##2750##IDS_ERROR_2750##27500##IDS_ERROR_130##27501##IDS_ERROR_131##27502##IDS_ERROR_27502##27503##IDS_ERROR_27503##27504##IDS_ERROR_27504##27505##IDS_ERROR_27505##27506##IDS_ERROR_27506##27507##IDS_ERROR_27507##27508##IDS_ERROR_27508##27509##IDS_ERROR_27509##2751##IDS_ERROR_2751##27510##IDS_ERROR_27510##27511##IDS_ERROR_27511##27512##IDS_ERROR_27512##27513##IDS_ERROR_27513##27514##IDS_ERROR_27514##27515##IDS_ERROR_27515##27516##IDS_ERROR_27516##27517##IDS_ERROR_27517##27518##IDS_ERROR_27518##27519##IDS_ERROR_27519##2752##IDS_ERROR_2752##27520##IDS_ERROR_27520##27521##IDS_ERROR_27521##27522##IDS_ERROR_27522##27523##IDS_ERROR_27523##27524##IDS_ERROR_27524##27525##IDS_ERROR_27525##27526##IDS_ERROR_27526##27527##IDS_ERROR_27527##27528##IDS_ERROR_27528##27529##IDS_ERROR_27529##2753##IDS_ERROR_2753##27530##IDS_ERROR_27530##27531##IDS_ERROR_27531##27532##IDS_ERROR_27532##27533##IDS_ERROR_27533##27534##IDS_ERROR_27534##27535##IDS_ERROR_27535##27536##IDS_ERROR_27536##27537##IDS_ERROR_27537##27538##IDS_ERROR_27538##27539##IDS_ERROR_27539##2754##IDS_ERROR_2754##27540##IDS_ERROR_27540##27541##IDS_ERROR_27541##27542##IDS_ERROR_27542##27543##IDS_ERROR_27543##27544##IDS_ERROR_27544##27545##IDS_ERROR_27545##27546##IDS_ERROR_27546##27547##IDS_ERROR_27547##27548##IDS_ERROR_27548##27549##IDS_ERROR_27549##2755##IDS_ERROR_2755##27550##IDS_ERROR_27550##27551##IDS_ERROR_27551##27552##IDS_ERROR_27552##27553##IDS_ERROR_27553##27554##IDS_ERROR_27554##27555##IDS_ERROR_27555##2756##IDS_ERROR_2756##2757##IDS_ERROR_2757##2758##IDS_ERROR_2758##2759##IDS_ERROR_2759##2760##IDS_ERROR_2760##2761##IDS_ERROR_2761##2762##IDS_ERROR_2762##2763##IDS_ERROR_2763##2765##IDS_ERROR_2765##2766##IDS_ERROR_2766##2767##IDS_ERROR_2767##2768##IDS_ERROR_2768##2769##IDS_ERROR_2769##2770##IDS_ERROR_2770##2771##IDS_ERROR_2771##2772##IDS_ERROR_2772##2801##IDS_ERROR_2801##2802##IDS_ERROR_2802##2803##IDS_ERROR_2803##2804##IDS_ERROR_2804##2806##IDS_ERROR_2806##2807##IDS_ERROR_2807##2808##IDS_ERROR_2808##2809##IDS_ERROR_2809##2810##IDS_ERROR_2810##2811##IDS_ERROR_2811##2812##IDS_ERROR_2812##2813##IDS_ERROR_2813##2814##IDS_ERROR_2814##2815##IDS_ERROR_2815##2816##IDS_ERROR_2816##2817##IDS_ERROR_2817##2818##IDS_ERROR_2818##2819##IDS_ERROR_2819##2820##IDS_ERROR_2820##2821##IDS_ERROR_2821##2822##IDS_ERROR_2822##2823##IDS_ERROR_2823##2824##IDS_ERROR_2824##2825##IDS_ERROR_2825##2826##IDS_ERROR_2826##2827##IDS_ERROR_2827##2828##IDS_ERROR_2828##2829##IDS_ERROR_2829##2830##IDS_ERROR_2830##2831##IDS_ERROR_2831##2832##IDS_ERROR_2832##2833##IDS_ERROR_2833##2834##IDS_ERROR_2834##2835##IDS_ERROR_2835##2836##IDS_ERROR_2836##2837##IDS_ERROR_2837##2838##IDS_ERROR_2838##2839##IDS_ERROR_2839##2840##IDS_ERROR_2840##2841##IDS_ERROR_2841##2842##IDS_ERROR_2842##2843##IDS_ERROR_2843##2844##IDS_ERROR_2844##2845##IDS_ERROR_2845##2846##IDS_ERROR_2846##2847##IDS_ERROR_2847##2848##IDS_ERROR_2848##2849##IDS_ERROR_2849##2850##IDS_ERROR_2850##2851##IDS_ERROR_2851##2852##IDS_ERROR_2852##2853##IDS_ERROR_2853##2854##IDS_ERROR_2854##2855##IDS_ERROR_2855##2856##IDS_ERROR_2856##2857##IDS_ERROR_2857##2858##IDS_ERROR_2858##2859##IDS_ERROR_2859##2860##IDS_ERROR_2860##2861##IDS_ERROR_2861##2862##IDS_ERROR_2862##2863##IDS_ERROR_2863##2864##IDS_ERROR_2864##2865##IDS_ERROR_2865##2866##IDS_ERROR_2866##2867##IDS_ERROR_2867##2868##IDS_ERROR_2868##2869##IDS_ERROR_2869##2870##IDS_ERROR_2870##2871##IDS_ERROR_2871##2872##IDS_ERROR_2872##2873##IDS_ERROR_2873##2874##IDS_ERROR_2874##2875##IDS_ERROR_2875##2876##IDS_ERROR_2876##2877##IDS_ERROR_2877##2878##IDS_ERROR_2878##2879##IDS_ERROR_2879##2880##IDS_ERROR_2880##2881##IDS_ERROR_2881##2882##IDS_ERROR_2882##2883##IDS_ERROR_2883##2884##IDS_ERROR_2884##2885##IDS_ERROR_2885##2886##IDS_ERROR_2886##2887##IDS_ERROR_2887##2888##IDS_ERROR_2888##2889##IDS_ERROR_2889##2890##IDS_ERROR_2890##2891##IDS_ERROR_2891##2892##IDS_ERROR_2892##2893##IDS_ERROR_2893##2894##IDS_ERROR_2894##2895##IDS_ERROR_2895##2896##IDS_ERROR_2896##2897##IDS_ERROR_2897##2898##IDS_ERROR_2898##2899##IDS_ERROR_2899##2901##IDS_ERROR_2901##2902##IDS_ERROR_2902##2903##IDS_ERROR_2903##2904##IDS_ERROR_2904##2905##IDS_ERROR_2905##2906##IDS_ERROR_2906##2907##IDS_ERROR_2907##2908##IDS_ERROR_2908##2909##IDS_ERROR_2909##2910##IDS_ERROR_2910##2911##IDS_ERROR_2911##2912##IDS_ERROR_2912##2919##IDS_ERROR_2919##2920##IDS_ERROR_2920##2924##IDS_ERROR_2924##2927##IDS_ERROR_2927##2928##IDS_ERROR_2928##2929##IDS_ERROR_2929##2932##IDS_ERROR_2932##2933##IDS_ERROR_2933##2934##IDS_ERROR_2934##2935##IDS_ERROR_2935##2936##IDS_ERROR_2936##2937##IDS_ERROR_2937##2938##IDS_ERROR_2938##2939##IDS_ERROR_2939##2940##IDS_ERROR_2940##2941##IDS_ERROR_2941##2942##IDS_ERROR_2942##2943##IDS_ERROR_2943##2944##IDS_ERROR_2944##2945##IDS_ERROR_2945##3001##IDS_ERROR_3001##3002##IDS_ERROR_3002##32##IDS_ERROR_20##33##IDS_ERROR_21##4##IDS_ERROR_3##5##IDS_ERROR_4##7##IDS_ERROR_5##8##IDS_ERROR_6##9##IDS_ERROR_7##
+ + + Dialog_ + Control_ + Event + Attribute + + + + + + + + + + + + + + + +
CustomSetupItemDescriptionSelectionDescriptionTextCustomSetupLocationSelectionPathTextCustomSetupSizeSelectionSizeTextSetupInitializationActionDataActionDataTextSetupInitializationActionTextActionTextTextSetupProgressActionProgress95AdminInstallFinalizeProgressSetupProgressActionProgress95InstallFilesProgressSetupProgressActionProgress95MoveFilesProgressSetupProgressActionProgress95RemoveFilesProgressSetupProgressActionProgress95RemoveRegistryValuesProgressSetupProgressActionProgress95SetProgressProgressSetupProgressActionProgress95UnmoveFilesProgressSetupProgressActionProgress95WriteIniValuesProgressSetupProgressActionProgress95WriteRegistryValuesProgressSetupProgressActionTextActionTextText
+ + + Extension + Component_ + ProgId_ + MIME_ + Feature_ +
+ + + Feature + Feature_Parent + Title + Description + Display + Level + Directory_ + Attributes + ISReleaseFlags + ISComments + ISFeatureCabName + ISProFeatureName +
AlwaysInstall##DN_AlwaysInstall##Enter the description for this feature here.01INSTALLDIR16Enter comments regarding this feature here. +
+ + + Feature_ + Component_ +
+ + + File + Component_ + FileName + FileSize + Version + Language + Attributes + Sequence + ISBuildSourcePath + ISAttributes + ISComponentSubFolder_ +
+ + + File_ + SFPCatalog_ +
+ + + File_ + FontTitle +
+ + + Tag + Data +
+ + + ISBillboard + Duration + Origin + X + Y + Effect + Sequence + Target + Color + Style + Font + Title + DisplayName +
+ + + AppKey + AppName + CompanyName + DefDir + IconPath + IconIndex + DeviceFile + DesktopTargetDir + Description + DeleteMedia + InstallNetCF + InstallSQLServer + InstallSQLClient + InstallSQLDev + PreXML + PostXML + NoUninstall + SPCFile + PVKFile + Attributes + RawDeviceFile + Component_ + InstallNetCF2 + InstallSQLServer2 + InstallSQLClient2 + InstallSQLDev2 + SPCPwd +
+ + + AppKey + DirKey + DirParent + DirValue +
+ + + AppKey + FileKey + Name + Destination + Source + Processor + Platform + CopyOption + FileOption + AdvancedOptions +
+ + + AppKey + ExtKey + FileKey + Description + Extension + IconIndex +
+ + + CEInstallKey + CEAppName + CEDesktopDir + CEIniFileKey + CECabs + CEIcoFile + DeleteMedia + Component_ +
+ + + AppKey + FileKey + BuildSourcePath +
+ + + AppKey + Name + Platforms +
+ + + AppKey + RegKey + Root + Key + Name + Value + Processor + Platform + Overwrite +
+ + + AppKey + SetupFileKey + Name + Source + Processor + Platform +
+ + + AppKey + ShtCutKey + DisplayName + Destination + Target + Platform + StartScreenIcon +
+ + + Package + SourcePath + ProductCode + Order + Options + InstallCondition + RemoveCondition + InstallProperties + RemoveProperties + ISReleaseFlags + DisplayName +
+ + + Package_ + File + FilePath + Options + Data + ISBuildSourcePath +
+ + + Action_ + Name + Value +
+ + + ISComCatalogObject_ + ItemName + ItemValue +
+ + + ISComCatalogCollection + ISComCatalogObject_ + CollectionName +
+ + + ISComCatalogCollection_ + ISComCatalogObject_ +
+ + + ISComCatalogObject + DisplayName +
+ + + ISComCatalogObject_ + ComputerName + Component_ + ISAttributes + DepFiles +
+ + + ISComPlusApplicationDLL + ISComPlusApplication_ + ISComCatalogObject_ + CLSID + ProgId + DLL + AlterDLL +
+ + + ISComPlusProxy + ISComPlusApplication_ + Component_ + ISAttributes + DepFiles +
+ + + ISComPlusApplication_ + File_ + ISPath +
+ + + File_ + ISComPlusApplicationDLL_ +
+ + + ISComPlusApplication_ + File_ + ISPath +
+ + + File_ + ISComPlusApplicationDLL_ +
+ + + Component_ + OS + Language + FilterProperty + Platforms + FTPLocation + HTTPLocation + Miscellaneous +
+ + + Action_ + Description + FileType + ISCAReferenceFilePath +
+ + + ISDIMReference_ + RequiredUUID + RequiredMajorVersion + RequiredMinorVersion + RequiredBuildVersion + RequiredRevisionVersion +
+ + + ISDIMReference + ISBuildSourcePath +
+ + + ISDIMReference_Parent + ISDIMDependency_ +
+ + + ISDIMVariable + ISDIMReference_ + Name + NewValue + Type +
+ + + EntryPoint + Type + Source + Target +
+ + + ISDRMFile + File_ + ISDRMLicense_ + Shell +
+ + + ISDRMFile_ + Property + Value +
+ + + ISDRMLicense + Description + ProjectVersion + Attributes + LicenseNumber + RequestCode + ResponseCode +
+ + + ISDependency + Exclude +
+ + + ISDisk1File + ISBuildSourcePath + Disk +
+ + + Component_ + SourceFolder + IncludeFlags + IncludeFiles + ExcludeFiles + ISAttributes +
+ + + Feature_ + ISDIMReference_ +
+ + + Feature_ + ModuleID + Language +
+ + + Feature_ + ISMergeModule_ + Language_ +
+ + + Feature_ + ISSetupPrerequisites_ +
+ + + File_ + Manifest_ +
+ + + ISIISItem + ISIISItem_Parent + DisplayName + Type + Component_ +
+ + + ISIISProperty + ISIISItem_ + Schema + FriendlyName + MetaDataProp + MetaDataType + MetaDataUserType + MetaDataAttributes + MetaDataValue + Order + ISAttributes +
+ + + EntryPoint + Type + Source + Target +
+ + + ISLanguage + Included + +
10331
+ + + ISLinkerLibrary + Library + Order + + +
isrt.oblisrt.obl2iswi.obliswi.obl1
+ + + Dialog_ + Control_ + ISLanguage_ + Attributes + X + Y + Width + Height + Binary_ + ISBuildSourcePath +
+ + + Dialog_ + ISLanguage_ + Attributes + TextStyle_ + Width + Height +
+ + + Property + Order + ISLanguage_ + X + Y + Width + Height +
+ + + LockObject + Table + Domain + User + Permission + Attributes +
+ + + DiskId + ISProductConfiguration_ + ISRelease_ + LastSequence + DiskPrompt + Cabinet + VolumeLabel + Source +
+ + + ISLogicalDisk_ + ISProductConfiguration_ + ISRelease_ + Feature_ + Sequence + ISAttributes +
+ + + ISMergeModule + Language + Name + Destination + ISAttributes +
+ + + ISMergeModule_ + Language_ + ModuleConfiguration_ + Value + Format + Type + ContextData + DefaultValue + Attributes + DisplayName + Description + HelpLocation + HelpKeyword +
+ + + ObjectName + Language +
+ + + ObjectName + Property + Value + IncludeInBuild +
+ + + PalmApp + Component +
+ + + PalmApp + FileKey + Destination +
+ + + PatchConfiguration_ + UpgradedImage_ +
+ + + Name + CanPCDiffer + CanPVDiffer + IncludeWholeFiles + LeaveDecompressed + OptimizeForSize + EnablePatchCache + PatchCacheDir + Flags + PatchGuidsToReplace + TargetProductCodes + PatchGuid + OutputPath + MinMsiVersion + Attributes +
+ + + ISPatchConfiguration_ + Property + Value +
+ + + Name + ISUpgradedImage_ + FileKey + FilePath +
+ + + UpgradedImage + FileKey + Component +
+ + + ISPathVariable + Value + TestValue + Type + + + + + + + + +
CommonFilesFolder1ISPROJECTDIR1ISProductFolder1ISProjectDataFolder1ISProjectFolder1ProgramFilesFolder1SystemFolder1WindowsFolder1
+ + + Action_ + Name + Value +
+ + + ISProductConfiguration + ProductConfigurationFlags + GeneratePackageCode + +
Express1
+ + + ISProductConfiguration_ + InstanceId + Property + Value +
+ + + ISProductConfiguration_ + Property + Value +
+ + + ISRelease + ISProductConfiguration_ + BuildLocation + PackageName + Type + SupportedLanguagesUI + MsiSourceType + ReleaseType + Platforms + SupportedLanguagesData + DefaultLanguage + SupportedOSs + DiskSize + DiskSizeUnit + DiskClusterSize + ReleaseFlags + DiskSpanning + SynchMsi + MediaLocation + URLLocation + DigitalURL + DigitalPVK + DigitalSPC + Password + VersionCopyright + Attributes + CDBrowser + DotNetBuildConfiguration + MsiCommandLine + ISSetupPrerequisiteLocation + + + + + + + + +
CD_ROMExpress<ISProjectDataFolder>Default0103302Intel10330650020480MediaLocationhttp://758053CustomExpress<ISProjectDataFolder>Default2103302Intel10330100010240MediaLocationhttp://758053DVD-10Express<ISProjectDataFolder>Default3103302Intel103308.75120480MediaLocationhttp://758053DVD-18Express<ISProjectDataFolder>Default3103302Intel1033015.83120480MediaLocationhttp://758053DVD-5Express<ISProjectDataFolder>Default3103302Intel103304.38120480MediaLocationhttp://758053DVD-9Express<ISProjectDataFolder>Default3103302Intel103307.95120480MediaLocationhttp://758053SingleImageExpress<ISProjectDataFolder>PackageName1103301Intel103300000MediaLocationhttp://1085733WebDeploymentExpress<ISProjectDataFolder>PackageName4103321Intel103300000MediaLocationhttp://1249413
+ + + ISRelease_ + ISProductConfiguration_ + Property + Value +
+ + + ISRelease_ + ISProductConfiguration_ + WebType + WebURL + WebCabSize + OneClickCabName + OneClickHtmlName + WebLocalCachePath + EngineLocation + Win9xMsiUrl + WinNTMsiUrl + ISEngineLocation + ISEngineURL + OneClickTargetBrowser + DigitalCertificateIdNS + DigitalCertificateDBaseNS + DigitalCertificatePasswordNS + DotNetRedistLocation + DotNetRedistURL + DotNetVersion + DotNetBaseLanguage + DotNetLangaugePacks + DotNetFxCmdLine + DotNetLangPackCmdLine + JSharpCmdLine + Attributes + JSharpRedistLocation + MsiEngineVersion + WinMsi30Url + CertPassword +
CD_ROMExpress0http://0installinstall[LocalAppDataFolder]Downloaded Installations0http://www.installengine.com/Msiengine20http://www.installengine.com/Msiengine200http://www.installengine.com/cert05/isengine03http://www.installengine.com/cert05/dotnetfx010333http://www.installengine.com/Msiengine30 + CustomExpress0http://0installinstall[LocalAppDataFolder]Downloaded Installations0http://www.installengine.com/Msiengine20http://www.installengine.com/Msiengine200http://www.installengine.com/cert05/isengine03http://www.installengine.com/cert05/dotnetfx010333http://www.installengine.com/Msiengine30 + DVD-10Express0http://0installinstall[LocalAppDataFolder]Downloaded Installations0http://www.installengine.com/Msiengine20http://www.installengine.com/Msiengine200http://www.installengine.com/cert05/isengine03http://www.installengine.com/cert05/dotnetfx010333http://www.installengine.com/Msiengine30 + DVD-18Express0http://0installinstall[LocalAppDataFolder]Downloaded Installations0http://www.installengine.com/Msiengine20http://www.installengine.com/Msiengine200http://www.installengine.com/cert05/isengine03http://www.installengine.com/cert05/dotnetfx010333http://www.installengine.com/Msiengine30 + DVD-5Express0http://0installinstall[LocalAppDataFolder]Downloaded Installations0http://www.installengine.com/Msiengine20http://www.installengine.com/Msiengine200http://www.installengine.com/cert05/isengine03http://www.installengine.com/cert05/dotnetfx010333http://www.installengine.com/Msiengine30 + DVD-9Express0http://0installinstall[LocalAppDataFolder]Downloaded Installations0http://www.installengine.com/Msiengine20http://www.installengine.com/Msiengine200http://www.installengine.com/cert05/isengine03http://www.installengine.com/cert05/dotnetfx010333http://www.installengine.com/Msiengine30 + SingleImageExpress0http://0installinstall[LocalAppDataFolder]Downloaded Installations1http://www.installengine.com/Msiengine20http://www.installengine.com/Msiengine200http://www.installengine.com/cert05/isengine03http://www.installengine.com/cert05/dotnetfx010333http://www.installengine.com/Msiengine30 + WebDeploymentExpress0http://0setupDefault[LocalAppDataFolder]Downloaded Installations2http://www.Installengine.com/Msiengine20http://www.Installengine.com/Msiengine200http://www.installengine.com/cert05/isengine23http://www.installengine.com/cert05/dotnetfx010333http://www.installengine.com/Msiengine30 +
+ + + ISRelease_ + ISProductConfiguration_ + Name + Value +
+ + + ISRelease_ + ISProductConfiguration_ + Repository + DisplayName + Publisher + Description + ISAttributes +
+ + + ISSQLConnection + Server + Database + UserName + Password + Authentication + Attributes + Order + Comments + CmdTimeout + BatchSeparator + ScriptVersion_Table + ScriptVersion_Column +
+ + + ISSQLConnectionDBServer + ISSQLConnection_ + ISSQLDBMetaData_ + Order +
+ + + ISSQLConnection_ + ISSQLScriptFile_ + Order +
+ + + ISSQLDBMetaData + DisplayName + AdoDriverName + AdoCxnDriver + AdoCxnServer + AdoCxnDatabase + AdoCxnUserID + AdoCxnPassword + AdoCxnWindowsSecurity + AdoCxnNetLibrary + TestDatabaseCmd + TestTableCmd + VersionInfoCmd + VersionBeginToken + VersionEndToken + LocalInstanceNames + CreateDbCmd + SwitchDbCmd + ISAttributes + TestTableCmd2 + WinAuthentUserId + DsnODBCName + AdoCxnPort + AdoCxnAdditional + QueryDatabasesCmd + CreateTableCmd + InsertRecordCmd + SelectTableCmd + ScriptVersion_Table + ScriptVersion_Column + ScriptVersion_ColumnType +
+ + + ISSQLRequirement + ISSQLConnection_ + MajorVersion + ServicePackLevel + Attributes + ISSQLConnectionDBServer_ +
+ + + ErrNumber + ISSQLScriptFile_ + ErrHandling + Message + Attributes +
+ + + ISSQLScriptFile + Component_ + Scheduling + InstallText + UninstallText + ISBuildSourcePath + Comments + ErrorHandling + Attributes + Version + Condition + DisplayName +
+ + + ISSQLScriptFile_ + Server + Database + UserName + Password + Authentication + IncludeTables + ExcludeTables + Attributes +
+ + + ISSQLScriptReplace + ISSQLScriptFile_ + Search + Replace + Attributes +
+ + + ISScriptFile +
+ + + FileKey + Cost + Order + CmdLine +
+ + + ISSetupFile + FileName + Stream + Language + Splash + Path +
+ + + ISSetupPrerequisites + ISBuildSourcePath + Order + ISSetupLocation + ISReleaseFlags +
+ + + ISSetupType + Description + Display_Name + Display + Comments +
Custom##IDS__IsSetupTypeMinDlg_ChooseFeatures####IDS__IsSetupTypeMinDlg_Custom##3 + Minimal##IDS__IsSetupTypeMinDlg_MinimumFeatures####IDS__IsSetupTypeMinDlg_Minimal##2 + Typical##IDS__IsSetupTypeMinDlg_AllFeatures####IDS__IsSetupTypeMinDlg_Typical##1 +
+ + + ISSetupType_ + Feature_ + + + +
CustomAlwaysInstallMinimalAlwaysInstallTypicalAlwaysInstall
+ + + Name + ISBuildSourcePath +
+ + + ISString + ISLanguage_ + Value + Encoded + Comment + TimeStamp + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
COMPANY_NAME1033Your Company Name01948325229DN_AlwaysInstall1033Always Install01948325229IDPROP_EXPRESS_LAUNCH_CONDITION_COLOR1033The color settings of your system are not adequate for running [ProductName].01948325229IDPROP_EXPRESS_LAUNCH_CONDITION_OS1033The operating system is not adequate for running [ProductName].01948325229IDPROP_EXPRESS_LAUNCH_CONDITION_PROCESSOR1033The processor is not adequate for running [ProductName].01948325229IDPROP_EXPRESS_LAUNCH_CONDITION_RAM1033The amount of RAM is not adequate for running [ProductName].01948325229IDPROP_EXPRESS_LAUNCH_CONDITION_SCREEN1033The screen resolution is not adequate for running [ProductName].01948325229IDPROP_SETUPTYPE_COMPACT1033Compact01948325229IDPROP_SETUPTYPE_COMPACT_DESC1033Compact Description01948325229IDPROP_SETUPTYPE_COMPLETE1033Complete01948325229IDPROP_SETUPTYPE_COMPLETE_DESC1033Complete01948325229IDPROP_SETUPTYPE_CUSTOM1033Custom01948325229IDPROP_SETUPTYPE_CUSTOM_DESC1033Custom Description01948325229IDPROP_SETUPTYPE_CUSTOM_DESC_PRO1033Custom01948325229IDPROP_SETUPTYPE_TYPICAL1033Typical01948325229IDPROP_SETUPTYPE_TYPICAL_DESC1033Typical Description01948325229IDS_ACTIONTEXT_11033[1]01948325229IDS_ACTIONTEXT_1b1033[1]01948325229IDS_ACTIONTEXT_1c1033[1]01948325229IDS_ACTIONTEXT_1d1033[1]01948325229IDS_ACTIONTEXT_Advertising1033Advertising application01948325229IDS_ACTIONTEXT_AllocatingRegistry1033Allocating registry space01948325229IDS_ACTIONTEXT_AppCommandLine1033Application: [1], Command line: [2]01948325229IDS_ACTIONTEXT_AppId1033AppId: [1]{{, AppType: [2]}}01948325229IDS_ACTIONTEXT_AppIdAppTypeRSN1033AppId: [1]{{, AppType: [2], Users: [3], RSN: [4]}}01948325229IDS_ACTIONTEXT_Application1033Application: [1]01948325229IDS_ACTIONTEXT_BindingExes1033Binding executables01948325229IDS_ACTIONTEXT_ClassId1033Class ID: [1]01948325229IDS_ACTIONTEXT_ClsID1033Class ID: [1]01948325229IDS_ACTIONTEXT_ComponentIDQualifier1033Component ID: [1], Qualifier: [2]01948325229IDS_ACTIONTEXT_ComponentIdQualifier21033Component ID: [1], Qualifier: [2]01948325229IDS_ACTIONTEXT_ComputingSpace1033Computing space requirements01948325229IDS_ACTIONTEXT_ComputingSpace21033Computing space requirements01948325229IDS_ACTIONTEXT_ComputingSpace31033Computing space requirements01948325229IDS_ACTIONTEXT_ContentTypeExtension1033MIME Content Type: [1], Extension: [2]01948325229IDS_ACTIONTEXT_ContentTypeExtension21033MIME Content Type: [1], Extension: [2]01948325229IDS_ACTIONTEXT_CopyingNetworkFiles1033Copying files to the network01948325229IDS_ACTIONTEXT_CopyingNewFiles1033Copying new files01948325229IDS_ACTIONTEXT_CreatingDuplicate1033Creating duplicate files01948325229IDS_ACTIONTEXT_CreatingFolders1033Creating folders01948325229IDS_ACTIONTEXT_CreatingIISRoots1033Creating IIS Virtual Roots...01948325229IDS_ACTIONTEXT_CreatingShortcuts1033Creating shortcuts01948325229IDS_ACTIONTEXT_DeletingServices1033Deleting services01948325229IDS_ACTIONTEXT_EnvironmentStrings1033Updating environment strings01948325229IDS_ACTIONTEXT_EvaluateLaunchConditions1033Evaluating launch conditions01948325229IDS_ACTIONTEXT_Extension1033Extension: [1]01948325229IDS_ACTIONTEXT_Extension21033Extension: [1]01948325229IDS_ACTIONTEXT_Feature1033Feature: [1]01948325229IDS_ACTIONTEXT_FeatureColon1033Feature: [1]01948325229IDS_ACTIONTEXT_File1033File: [1]01948325229IDS_ACTIONTEXT_File21033File: [1]01948325229IDS_ACTIONTEXT_FileDependencies1033File: [1], Dependencies: [2]01948325229IDS_ACTIONTEXT_FileDir1033File: [1], Directory: [9]01948325229IDS_ACTIONTEXT_FileDir21033File: [1], Directory: [9]01948325229IDS_ACTIONTEXT_FileDir31033File: [1], Directory: [9]01948325229IDS_ACTIONTEXT_FileDirSize1033File: [1], Directory: [9], Size: [6]01948325229IDS_ACTIONTEXT_FileDirSize21033File: [1], Directory: [9], Size: [6]01948325229IDS_ACTIONTEXT_FileDirSize31033File: [1], Directory: [9], Size: [6]01948325229IDS_ACTIONTEXT_FileDirSize41033File: [1], Directory: [2], Size: [3]01948325229IDS_ACTIONTEXT_FileDirectorySize1033File: [1], Directory: [9], Size: [6]01948325229IDS_ACTIONTEXT_FileFolder1033File: [1], Folder: [2]01948325229IDS_ACTIONTEXT_FileFolder21033File: [1], Folder: [2]01948325229IDS_ACTIONTEXT_FileSectionKeyValue1033File: [1], Section: [2], Key: [3], Value: [4]01948325229IDS_ACTIONTEXT_FileSectionKeyValue21033File: [1], Section: [2], Key: [3], Value: [4]01948325229IDS_ACTIONTEXT_Folder1033Folder: [1]01948325229IDS_ACTIONTEXT_Folder11033Folder: [1]01948325229IDS_ACTIONTEXT_Font1033Font: [1]01948325229IDS_ACTIONTEXT_Font21033Font: [1]01948325229IDS_ACTIONTEXT_FoundApp1033Found application: [1]01948325229IDS_ACTIONTEXT_FreeSpace1033Free space: [1]01948325229IDS_ACTIONTEXT_GeneratingScript1033Generating script operations for action:01948325229IDS_ACTIONTEXT_ISLockPermissionsCost1033Gathering permissions information for objects...01948325229IDS_ACTIONTEXT_ISLockPermissionsInstall1033Applying permissions information for objects...01948325229IDS_ACTIONTEXT_InitializeODBCDirs1033Initializing ODBC directories01948325229IDS_ACTIONTEXT_InstallODBC1033Installing ODBC components01948325229IDS_ACTIONTEXT_InstallServices1033Installing new services01948325229IDS_ACTIONTEXT_InstallingSystemCatalog1033Installing system catalog01948325229IDS_ACTIONTEXT_KeyName1033Key: [1], Name: [2]01948325229IDS_ACTIONTEXT_KeyNameValue1033Key: [1], Name: [2], Value: [3]01948325229IDS_ACTIONTEXT_LibId1033LibID: [1]01948325229IDS_ACTIONTEXT_Libid21033LibID: [1]01948325229IDS_ACTIONTEXT_MigratingFeatureStates1033Migrating feature states from related applications01948325229IDS_ACTIONTEXT_MovingFiles1033Moving files01948325229IDS_ACTIONTEXT_NameValueAction1033Name: [1], Value: [2], Action [3]01948325229IDS_ACTIONTEXT_NameValueAction21033Name: [1], Value: [2], Action [3]01948325229IDS_ACTIONTEXT_PatchingFiles1033Patching files01948325229IDS_ACTIONTEXT_ProgID1033ProgID: [1]01948325229IDS_ACTIONTEXT_ProgID21033ProgID: [1]01948325229IDS_ACTIONTEXT_PropertySignature1033Property: [1], Signature: [2]01948325229IDS_ACTIONTEXT_PublishProductFeatures1033Publishing product features01948325229IDS_ACTIONTEXT_PublishProductInfo1033Publishing product information01948325229IDS_ACTIONTEXT_PublishingQualifiedComponents1033Publishing qualified components01948325229IDS_ACTIONTEXT_RegUser1033Registering user01948325229IDS_ACTIONTEXT_RegisterClassServer1033Registering class servers01948325229IDS_ACTIONTEXT_RegisterExtensionServers1033Registering extension servers01948325229IDS_ACTIONTEXT_RegisterFonts1033Registering fonts01948325229IDS_ACTIONTEXT_RegisterMimeInfo1033Registering MIME info01948325229IDS_ACTIONTEXT_RegisterTypeLibs1033Registering type libraries01948325229IDS_ACTIONTEXT_RegisteringComPlus1033Registering COM+ Applications and Components01948325229IDS_ACTIONTEXT_RegisteringModules1033Registering modules01948325229IDS_ACTIONTEXT_RegisteringProduct1033Registering product01948325229IDS_ACTIONTEXT_RegisteringProgIdentifiers1033Registering program identifiers01948325229IDS_ACTIONTEXT_RemoveApps1033Removing applications01948325229IDS_ACTIONTEXT_RemovingBackup1033Removing backup files01948325229IDS_ACTIONTEXT_RemovingDuplicates1033Removing duplicated files01948325229IDS_ACTIONTEXT_RemovingFiles1033Removing files01948325229IDS_ACTIONTEXT_RemovingFolders1033Removing folders01948325229IDS_ACTIONTEXT_RemovingIISRoots1033Removing IIS Virtual Roots...01948325229IDS_ACTIONTEXT_RemovingIni1033Removing INI file entries01948325229IDS_ACTIONTEXT_RemovingMoved1033Removing moved files01948325229IDS_ACTIONTEXT_RemovingODBC1033Removing ODBC components01948325229IDS_ACTIONTEXT_RemovingRegistry1033Removing system registry values01948325229IDS_ACTIONTEXT_RemovingShortcuts1033Removing shortcuts01948325229IDS_ACTIONTEXT_RollingBack1033Rolling back action:01948325229IDS_ACTIONTEXT_SearchForRelated1033Searching for related applications01948325229IDS_ACTIONTEXT_SearchInstalled1033Searching for installed applications01948325229IDS_ACTIONTEXT_SearchingQualifyingProducts1033Searching for qualifying products01948325229IDS_ACTIONTEXT_SearchingQualifyingProducts21033Searching for qualifying products01948325229IDS_ACTIONTEXT_Service1033Service: [1]01948325229IDS_ACTIONTEXT_Service21033Service: [2]01948325229IDS_ACTIONTEXT_Service31033Service: [1]01948325229IDS_ACTIONTEXT_Service41033Service: [1]01948325229IDS_ACTIONTEXT_Shortcut1033Shortcut: [1]01948325229IDS_ACTIONTEXT_Shortcut11033Shortcut: [1]01948325229IDS_ACTIONTEXT_StartingServices1033Starting services01948325229IDS_ACTIONTEXT_StoppingServices1033Stopping services01948325229IDS_ACTIONTEXT_UnpublishProductFeatures1033Unpublishing product features01948325229IDS_ACTIONTEXT_UnpublishQualified1033Unpublishing Qualified Components01948325229IDS_ACTIONTEXT_UnpublishingProductInfo1033Unpublishing product information01948325229IDS_ACTIONTEXT_UnregTypeLibs1033Unregistering type libraries01948325229IDS_ACTIONTEXT_UnregisterClassServers1033Unregister class servers01948325229IDS_ACTIONTEXT_UnregisterExtensionServers1033Unregistering extension servers01948325229IDS_ACTIONTEXT_UnregisterModules1033Unregistering modules01948325229IDS_ACTIONTEXT_UnregisteringComPlus1033Unregistering COM+ Applications and Components01948325229IDS_ACTIONTEXT_UnregisteringFonts1033Unregistering fonts01948325229IDS_ACTIONTEXT_UnregisteringMimeInfo1033Unregistering MIME info01948325229IDS_ACTIONTEXT_UnregisteringProgramIds1033Unregistering program identifiers01948325229IDS_ACTIONTEXT_UpdateComponentRegistration1033Updating component registration01948325229IDS_ACTIONTEXT_UpdateEnvironmentStrings1033Updating environment strings01948325229IDS_ACTIONTEXT_Validating1033Validating install01948325229IDS_ACTIONTEXT_WritingINI1033Writing INI file values01948325229IDS_ACTIONTEXT_WritingRegistry1033Writing system registry values01948325229IDS_BACK1033< &Back01948325229IDS_CANCEL1033Cancel01948325229IDS_CANCEL21033&Cancel01948325229IDS_CHANGE1033&Change...01948325229IDS_COMPLUS_PROGRESSTEXT_COST1033Costing COM+ application: [1]01948325229IDS_COMPLUS_PROGRESSTEXT_INSTALL1033Installing COM+ application: [1]01948325229IDS_COMPLUS_PROGRESSTEXT_UNINSTALL1033Uninstalling COM+ application: [1]01948325229IDS_DIALOG_TEXT2_DESCRIPTION1033Dialog Normal Description01948325229IDS_DIALOG_TEXT_DESCRIPTION_EXTERIOR1033{&TahomaBold10}Dialog Bold Title01948325229IDS_DIALOG_TEXT_DESCRIPTION_INTERIOR1033{&MSSansBold8}Dialog Bold Title01948325229IDS_DIFX_AMD641033[ProductName] requires an X64 processor. Click OK to exit the wizard.01948325229IDS_DIFX_IA641033[ProductName] requires an IA64 processor. Click OK to exit the wizard.01948325229IDS_DIFX_X861033[ProductName] requires an X86 processor. Click OK to exit the wizard.01948325229IDS_DatabaseFolder_InstallDatabaseTo1033Install [ProductName] database to:01948325229IDS_ERROR_01033{{Fatal error: }}01948325229IDS_ERROR_11033Error [1]. 01948325229IDS_ERROR_101033=== Logging started: [Date] [Time] ===01948325229IDS_ERROR_1001033Could not remove shortcut [2]. Verify that the shortcut file exists and that you can access it.01948325229IDS_ERROR_1011033Could not register type library for file [2]. Contact your support personnel.01948325229IDS_ERROR_1021033Could not unregister type library for file [2]. Contact your support personnel.01948325229IDS_ERROR_1031033Could not update the INI file [2][3]. Verify that the file exists and that you can access it.01948325229IDS_ERROR_1041033Could not schedule file [2] to replace file [3] on reboot. Verify that you have write permissions to file [3].01948325229IDS_ERROR_1051033Error removing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel.01948325229IDS_ERROR_1061033Error installing ODBC driver manager, ODBC error [2]: [3]. Contact your support personnel.01948325229IDS_ERROR_1071033Error removing ODBC driver [4], ODBC error [2]: [3]. Verify that you have sufficient privileges to remove ODBC drivers.01948325229IDS_ERROR_1081033Error installing ODBC driver [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it.01948325229IDS_ERROR_1091033Error configuring ODBC data source [4], ODBC error [2]: [3]. Verify that the file [4] exists and that you can access it.01948325229IDS_ERROR_111033=== Logging stopped: [Date] [Time] ===01948325229IDS_ERROR_1101033Service [2] ([3]) failed to start. Verify that you have sufficient privileges to start system services.01948325229IDS_ERROR_1111033Service [2] ([3]) could not be stopped. Verify that you have sufficient privileges to stop system services.01948325229IDS_ERROR_1121033Service [2] ([3]) could not be deleted. Verify that you have sufficient privileges to remove system services.01948325229IDS_ERROR_1131033Service [2] ([3]) could not be installed. Verify that you have sufficient privileges to install system services.01948325229IDS_ERROR_1141033Could not update environment variable [2]. Verify that you have sufficient privileges to modify environment variables.01948325229IDS_ERROR_1151033You do not have sufficient privileges to complete this installation for all users of the machine. Log on as an administrator and then retry this installation.01948325229IDS_ERROR_1161033Could not set file security for file [3]. Error: [2]. Verify that you have sufficient privileges to modify the security permissions for this file.01948325229IDS_ERROR_1171033Component Services (COM+ 1.0) are not installed on this computer. This installation requires Component Services in order to complete successfully. Component Services are available on Windows 2000.01948325229IDS_ERROR_1181033Error registering COM+ application. Contact your support personnel for more information.01948325229IDS_ERROR_1191033Error unregistering COM+ application. Contact your support personnel for more information.01948325229IDS_ERROR_121033Action start [Time]: [1].01948325229IDS_ERROR_1201033Removing older versions of this application01948325229IDS_ERROR_1211033Preparing to remove older versions of this application01948325229IDS_ERROR_1221033Error applying patch to file [2]. It has probably been updated by other means, and can no longer be modified by this patch. For more information contact your patch vendor. {{System Error: [3]}}01948325229IDS_ERROR_1231033[2] cannot install one of its required products. Contact your technical support group. {{System Error: [3].}}01948325229IDS_ERROR_1241033The older version of [2] cannot be removed. Contact your technical support group. {{System Error [3].}}01948325229IDS_ERROR_1251033The description for service '[2]' ([3]) could not be changed.01948325229IDS_ERROR_1261033The Windows Installer service cannot update the system file [2] because the file is protected by Windows. You may need to update your operating system for this program to work correctly. {{Package version: [3], OS Protected version: [4]}}01948325229IDS_ERROR_1271033The Windows Installer service cannot update the protected Windows file [2]. {{Package version: [3], OS Protected version: [4], SFP Error: [5]}}01948325229IDS_ERROR_1281033The Windows Installer service cannot update one or more protected Windows files. SFP Error: [2]. List of protected files: [3]01948325229IDS_ERROR_1291033User installations are disabled via policy on the machine.01948325229IDS_ERROR_131033Action ended [Time]: [1]. Return value [2].01948325229IDS_ERROR_1301033This setup requires Internet Information Server 4.0 or higher for configuring IIS Virtual Roots. Please make sure that you have IIS 4.0 or higher.01948325229IDS_ERROR_1311033This setup requires Administrator privileges for configuring IIS Virtual Roots.01948325229IDS_ERROR_13291033A file that is required cannot be installed because the cabinet file [2] is not digitally signed. This may indicate that the cabinet file is corrupt.01948325229IDS_ERROR_13301033A file that is required cannot be installed because the cabinet file [2] has an invalid digital signature. This may indicate that the cabinet file is corrupt.{ Error [3] was returned by WinVerifyTrust.}01948325229IDS_ERROR_13311033Failed to correctly copy [2] file: CRC error.01948325229IDS_ERROR_13321033Failed to correctly patch [2] file: CRC error.01948325229IDS_ERROR_13331033Failed to correctly patch [2] file: CRC error.01948325229IDS_ERROR_13341033The file '[2]' cannot be installed because the file cannot be found in cabinet file '[3]'. This could indicate a network error, an error reading from the CD-ROM, or a problem with this package.01948325229IDS_ERROR_13351033The cabinet file '[2]' required for this installation is corrupt and cannot be used. This could indicate a network error, an error reading from the CD-ROM, or a problem with this package.01948325229IDS_ERROR_13361033There was an error creating a temporary file that is needed to complete this installation. Folder: [3]. System error code: [2]01948325229IDS_ERROR_141033Time remaining: {[1] minutes }{[2] seconds}01948325229IDS_ERROR_151033Out of memory. Shut down other applications before retrying.01948325229IDS_ERROR_161033Installer is no longer responding.01948325229IDS_ERROR_16091033An error occurred while applying security settings. [2] is not a valid user or group. This could be a problem with the package, or a problem connecting to a domain controller on the network. Check your network connection and click Retry, or Cancel to end the install. Unable to locate the user's SID, system error [3]01948325229IDS_ERROR_16511033Admin user failed to apply patch for a per-user managed or a per-machine application which is in advertise state.01948325229IDS_ERROR_171033Installer terminated prematurely.01948325229IDS_ERROR_17151033Installed [2].01948325229IDS_ERROR_17161033Configured [2].01948325229IDS_ERROR_17171033Removed [2].01948325229IDS_ERROR_17181033File [2] was rejected by digital signature policy.01948325229IDS_ERROR_17191033Windows Installer service could not be accessed. Contact your support personnel to verify that it is properly registered and enabled.01948325229IDS_ERROR_17201033There is a problem with this Windows Installer package. A script required for this install to complete could not be run. Contact your support personnel or package vendor. Custom action [2] script error [3], [4]: [5] Line [6], Column [7], [8]01948325229IDS_ERROR_17211033There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: [2], location: [3], command: [4]01948325229IDS_ERROR_17221033There is a problem with this Windows Installer package. A program run as part of the setup did not finish as expected. Contact your support personnel or package vendor. Action [2], location: [3], command: [4]01948325229IDS_ERROR_17231033There is a problem with this Windows Installer package. A DLL required for this install to complete could not be run. Contact your support personnel or package vendor. Action [2], entry: [3], library: [4]01948325229IDS_ERROR_17241033Removal completed successfully.01948325229IDS_ERROR_17251033Removal failed.01948325229IDS_ERROR_17261033Advertisement completed successfully.01948325229IDS_ERROR_17271033Advertisement failed.01948325229IDS_ERROR_17281033Configuration completed successfully.01948325229IDS_ERROR_17291033Configuration failed.01948325229IDS_ERROR_17301033You must be an Administrator to remove this application. To remove this application, you can log on as an administrator, or contact your technical support group for assistance.01948325229IDS_ERROR_17311033The source installation package for the product [2] is out of sync with the client package. Try the installation again using a valid copy of the installation package '[3]'.01948325229IDS_ERROR_17321033In order to complete the installation of [2], you must restart the computer. Other users are currently logged on to this computer, and restarting may cause them to lose their work. Do you want to restart now?01948325229IDS_ERROR_181033Please wait while Windows configures [ProductName]01948325229IDS_ERROR_191033Gathering required information...01948325229IDS_ERROR_19351033An error occurred during the installation of assembly component [2]. HRESULT: [3]. {{assembly interface: [4], function: [5], assembly name: [6]}}01948325229IDS_ERROR_19361033An error occurred during the installation of assembly '[6]'. The assembly is not strongly named or is not signed with the minimal key length. HRESULT: [3]. {{assembly interface: [4], function: [5], component: [2]}}01948325229IDS_ERROR_19371033An error occurred during the installation of assembly '[6]'. The signature or catalog could not be verified or is not valid. HRESULT: [3]. {{assembly interface: [4], function: [5], component: [2]}}01948325229IDS_ERROR_19381033An error occurred during the installation of assembly '[6]'. One or more modules of the assembly could not be found. HRESULT: [3]. {{assembly interface: [4], function: [5], component: [2]}}01948325229IDS_ERROR_21033Warning [1]. 01948325229IDS_ERROR_201033{[ProductName] }Setup completed successfully.01948325229IDS_ERROR_211033{[ProductName] }Setup failed.01948325229IDS_ERROR_21011033Shortcuts not supported by the operating system.01948325229IDS_ERROR_21021033Invalid .ini action: [2]01948325229IDS_ERROR_21031033Could not resolve path for shell folder [2].01948325229IDS_ERROR_21041033Writing .ini file: [3]: System error: [2].01948325229IDS_ERROR_21051033Shortcut Creation [3] Failed. System error: [2].01948325229IDS_ERROR_21061033Shortcut Deletion [3] Failed. System error: [2].01948325229IDS_ERROR_21071033Error [3] registering type library [2].01948325229IDS_ERROR_21081033Error [3] unregistering type library [2].01948325229IDS_ERROR_21091033Section missing for .ini action.01948325229IDS_ERROR_21101033Key missing for .ini action.01948325229IDS_ERROR_21111033Detection of running applications failed, could not get performance data. Registered operation returned : [2].01948325229IDS_ERROR_21121033Detection of running applications failed, could not get performance index. Registered operation returned : [2].01948325229IDS_ERROR_21131033Detection of running applications failed.01948325229IDS_ERROR_221033Error reading from file: [2]. {{ System error [3].}} Verify that the file exists and that you can access it.01948325229IDS_ERROR_22001033Database: [2]. Database object creation failed, mode = [3].01948325229IDS_ERROR_22011033Database: [2]. Initialization failed, out of memory.01948325229IDS_ERROR_22021033Database: [2]. Data access failed, out of memory.01948325229IDS_ERROR_22031033Database: [2]. Cannot open database file. System error [3].01948325229IDS_ERROR_22041033Database: [2]. Table already exists: [3].01948325229IDS_ERROR_22051033Database: [2]. Table does not exist: [3].01948325229IDS_ERROR_22061033Database: [2]. Table could not be dropped: [3].01948325229IDS_ERROR_22071033Database: [2]. Intent violation.01948325229IDS_ERROR_22081033Database: [2]. Insufficient parameters for Execute.01948325229IDS_ERROR_22091033Database: [2]. Cursor in invalid state.01948325229IDS_ERROR_22101033Database: [2]. Invalid update data type in column [3].01948325229IDS_ERROR_22111033Database: [2]. Could not create database table [3].01948325229IDS_ERROR_22121033Database: [2]. Database not in writable state.01948325229IDS_ERROR_22131033Database: [2]. Error saving database tables.01948325229IDS_ERROR_22141033Database: [2]. Error writing export file: [3].01948325229IDS_ERROR_22151033Database: [2]. Cannot open import file: [3].01948325229IDS_ERROR_22161033Database: [2]. Import file format error: [3], Line [4].01948325229IDS_ERROR_22171033Database: [2]. Wrong state to CreateOutputDatabase [3].01948325229IDS_ERROR_22181033Database: [2]. Table name not supplied.01948325229IDS_ERROR_22191033Database: [2]. Invalid Installer database format.01948325229IDS_ERROR_22201033Database: [2]. Invalid row/field data.01948325229IDS_ERROR_22211033Database: [2]. Code page conflict in import file: [3].01948325229IDS_ERROR_22221033Database: [2]. Transform or merge code page [3] differs from database code page [4].01948325229IDS_ERROR_22231033Database: [2]. Databases are the same. No transform generated.01948325229IDS_ERROR_22241033Database: [2]. GenerateTransform: Database corrupt. Table: [3].01948325229IDS_ERROR_22251033Database: [2]. Transform: Cannot transform a temporary table. Table: [3].01948325229IDS_ERROR_22261033Database: [2]. Transform failed.01948325229IDS_ERROR_22271033Database: [2]. Invalid identifier '[3]' in SQL query: [4].01948325229IDS_ERROR_22281033Database: [2]. Unknown table '[3]' in SQL query: [4].01948325229IDS_ERROR_22291033Database: [2]. Could not load table '[3]' in SQL query: [4].01948325229IDS_ERROR_22301033Database: [2]. Repeated table '[3]' in SQL query: [4].01948325229IDS_ERROR_22311033Database: [2]. Missing ')' in SQL query: [3].01948325229IDS_ERROR_22321033Database: [2]. Unexpected token '[3]' in SQL query: [4].01948325229IDS_ERROR_22331033Database: [2]. No columns in SELECT clause in SQL query: [3].01948325229IDS_ERROR_22341033Database: [2]. No columns in ORDER BY clause in SQL query: [3].01948325229IDS_ERROR_22351033Database: [2]. Column '[3]' not present or ambiguous in SQL query: [4].01948325229IDS_ERROR_22361033Database: [2]. Invalid operator '[3]' in SQL query: [4].01948325229IDS_ERROR_22371033Database: [2]. Invalid or missing query string: [3].01948325229IDS_ERROR_22381033Database: [2]. Missing FROM clause in SQL query: [3].01948325229IDS_ERROR_22391033Database: [2]. Insufficient values in INSERT SQL statement.01948325229IDS_ERROR_22401033Database: [2]. Missing update columns in UPDATE SQL statement.01948325229IDS_ERROR_22411033Database: [2]. Missing insert columns in INSERT SQL statement.01948325229IDS_ERROR_22421033Database: [2]. Column '[3]' repeated.01948325229IDS_ERROR_22431033Database: [2]. No primary columns defined for table creation.01948325229IDS_ERROR_22441033Database: [2]. Invalid type specifier '[3]' in SQL query [4].01948325229IDS_ERROR_22451033IStorage::Stat failed with error [3].01948325229IDS_ERROR_22461033Database: [2]. Invalid Installer transform format.01948325229IDS_ERROR_22471033Database: [2] Transform stream read/write failure.01948325229IDS_ERROR_22481033Database: [2] GenerateTransform/Merge: Column type in base table does not match reference table. Table: [3] Col #: [4].01948325229IDS_ERROR_22491033Database: [2] GenerateTransform: More columns in base table than in reference table. Table: [3].01948325229IDS_ERROR_22501033Database: [2] Transform: Cannot add existing row. Table: [3].01948325229IDS_ERROR_22511033Database: [2] Transform: Cannot delete row that does not exist. Table: [3].01948325229IDS_ERROR_22521033Database: [2] Transform: Cannot add existing table. Table: [3].01948325229IDS_ERROR_22531033Database: [2] Transform: Cannot delete table that does not exist. Table: [3].01948325229IDS_ERROR_22541033Database: [2] Transform: Cannot update row that does not exist. Table: [3].01948325229IDS_ERROR_22551033Database: [2] Transform: Column with this name already exists. Table: [3] Col: [4].01948325229IDS_ERROR_22561033Database: [2] GenerateTransform/Merge: Number of primary keys in base table does not match reference table. Table: [3].01948325229IDS_ERROR_22571033Database: [2]. Intent to modify read only table: [3].01948325229IDS_ERROR_22581033Database: [2]. Type mismatch in parameter: [3].01948325229IDS_ERROR_22591033Database: [2] Table(s) Update failed01948325229IDS_ERROR_22601033Storage CopyTo failed. System error: [3].01948325229IDS_ERROR_22611033Could not remove stream [2]. System error: [3].01948325229IDS_ERROR_22621033Stream does not exist: [2]. System error: [3].01948325229IDS_ERROR_22631033Could not open stream [2]. System error: [3].01948325229IDS_ERROR_22641033Could not remove stream [2]. System error: [3].01948325229IDS_ERROR_22651033Could not commit storage. System error: [3].01948325229IDS_ERROR_22661033Could not rollback storage. System error: [3].01948325229IDS_ERROR_22671033Could not delete storage [2]. System error: [3].01948325229IDS_ERROR_22681033Database: [2]. Merge: There were merge conflicts reported in [3] tables.01948325229IDS_ERROR_22691033Database: [2]. Merge: The column count differed in the '[3]' table of the two databases.01948325229IDS_ERROR_22701033Database: [2]. GenerateTransform/Merge: Column name in base table does not match reference table. Table: [3] Col #: [4].01948325229IDS_ERROR_22711033SummaryInformation write for transform failed.01948325229IDS_ERROR_22721033Database: [2]. MergeDatabase will not write any changes because the database is open read-only.01948325229IDS_ERROR_22731033Database: [2]. MergeDatabase: A reference to the base database was passed as the reference database.01948325229IDS_ERROR_22741033Database: [2]. MergeDatabase: Unable to write errors to Error table. Could be due to a non-nullable column in a predefined Error table.01948325229IDS_ERROR_22751033Database: [2]. Specified Modify [3] operation invalid for table joins.01948325229IDS_ERROR_22761033Database: [2]. Code page [3] not supported by the system.01948325229IDS_ERROR_22771033Database: [2]. Failed to save table [3].01948325229IDS_ERROR_22781033Database: [2]. Exceeded number of expressions limit of 32 in WHERE clause of SQL query: [3].01948325229IDS_ERROR_22791033Database: [2] Transform: Too many columns in base table [3].01948325229IDS_ERROR_22801033Database: [2]. Could not create column [3] for table [4].01948325229IDS_ERROR_22811033Could not rename stream [2]. System error: [3].01948325229IDS_ERROR_22821033Stream name invalid [2].01948325229IDS_ERROR_231033Cannot create the file [3]. A directory with this name already exists. Cancel the installation and try installing to a different location.01948325229IDS_ERROR_23021033Patch notify: [2] bytes patched to far.01948325229IDS_ERROR_23031033Error getting volume info. GetLastError: [2].01948325229IDS_ERROR_23041033Error getting disk free space. GetLastError: [2]. Volume: [3].01948325229IDS_ERROR_23051033Error waiting for patch thread. GetLastError: [2].01948325229IDS_ERROR_23061033Could not create thread for patch application. GetLastError: [2].01948325229IDS_ERROR_23071033Source file key name is null.01948325229IDS_ERROR_23081033Destination file name is null.01948325229IDS_ERROR_23091033Attempting to patch file [2] when patch already in progress.01948325229IDS_ERROR_23101033Attempting to continue patch when no patch is in progress.01948325229IDS_ERROR_23151033Missing path separator: [2].01948325229IDS_ERROR_23181033File does not exist: [2].01948325229IDS_ERROR_23191033Error setting file attribute: [3] GetLastError: [2].01948325229IDS_ERROR_23201033File not writable: [2].01948325229IDS_ERROR_23211033Error creating file: [2].01948325229IDS_ERROR_23221033User canceled.01948325229IDS_ERROR_23231033Invalid file attribute.01948325229IDS_ERROR_23241033Could not open file: [3] GetLastError: [2].01948325229IDS_ERROR_23251033Could not get file time for file: [3] GetLastError: [2].01948325229IDS_ERROR_23261033Error in FileToDosDateTime.01948325229IDS_ERROR_23271033Could not remove directory: [3] GetLastError: [2].01948325229IDS_ERROR_23281033Error getting file version info for file: [2].01948325229IDS_ERROR_23291033Error deleting file: [3]. GetLastError: [2].01948325229IDS_ERROR_23301033Error getting file attributes: [3]. GetLastError: [2].01948325229IDS_ERROR_23311033Error loading library [2] or finding entry point [3].01948325229IDS_ERROR_23321033Error getting file attributes. GetLastError: [2].01948325229IDS_ERROR_23331033Error setting file attributes. GetLastError: [2].01948325229IDS_ERROR_23341033Error converting file time to local time for file: [3]. GetLastError: [2].01948325229IDS_ERROR_23351033Path: [2] is not a parent of [3].01948325229IDS_ERROR_23361033Error creating temp file on path: [3]. GetLastError: [2].01948325229IDS_ERROR_23371033Could not close file: [3] GetLastError: [2].01948325229IDS_ERROR_23381033Could not update resource for file: [3] GetLastError: [2].01948325229IDS_ERROR_23391033Could not set file time for file: [3] GetLastError: [2].01948325229IDS_ERROR_23401033Could not update resource for file: [3], Missing resource.01948325229IDS_ERROR_23411033Could not update resource for file: [3], Resource too large.01948325229IDS_ERROR_23421033Could not update resource for file: [3] GetLastError: [2].01948325229IDS_ERROR_23431033Specified path is empty.01948325229IDS_ERROR_23441033Could not find required file IMAGEHLP.DLL to validate file:[2].01948325229IDS_ERROR_23451033[2]: File does not contain a valid checksum value.01948325229IDS_ERROR_23471033User ignore.01948325229IDS_ERROR_23481033Error attempting to read from cabinet stream.01948325229IDS_ERROR_23491033Copy resumed with different info.01948325229IDS_ERROR_23501033FDI server error01948325229IDS_ERROR_23511033File key '[2]' not found in cabinet '[3]'. The installation cannot continue.01948325229IDS_ERROR_23521033Could not initialize cabinet file server. The required file 'CABINET.DLL' may be missing.01948325229IDS_ERROR_23531033Not a cabinet.01948325229IDS_ERROR_23541033Cannot handle cabinet.01948325229IDS_ERROR_23551033Corrupt cabinet.01948325229IDS_ERROR_23561033Could not locate cabinet in stream: [2].01948325229IDS_ERROR_23571033Cannot set attributes.01948325229IDS_ERROR_23581033Error determining whether file is in-use: [3]. GetLastError: [2].01948325229IDS_ERROR_23591033Unable to create the target file - file may be in use.01948325229IDS_ERROR_23601033Progress tick.01948325229IDS_ERROR_23611033Need next cabinet.01948325229IDS_ERROR_23621033Folder not found: [2].01948325229IDS_ERROR_23631033Could not enumerate subfolders for folder: [2].01948325229IDS_ERROR_23641033Bad enumeration constant in CreateCopier call.01948325229IDS_ERROR_23651033Could not BindImage exe file [2].01948325229IDS_ERROR_23661033User failure.01948325229IDS_ERROR_23671033User abort.01948325229IDS_ERROR_23681033Failed to get network resource information. Error [2], network path [3]. Extended error: network provider [5], error code [4], error description [6].01948325229IDS_ERROR_23701033Invalid CRC checksum value for [2] file.{ Its header says [3] for checksum, its computed value is [4].}01948325229IDS_ERROR_23711033Could not apply patch to file [2]. GetLastError: [3].01948325229IDS_ERROR_23721033Patch file [2] is corrupt or of an invalid format. Attempting to patch file [3]. GetLastError: [4].01948325229IDS_ERROR_23731033File [2] is not a valid patch file.01948325229IDS_ERROR_23741033File [2] is not a valid destination file for patch file [3].01948325229IDS_ERROR_23751033Unknown patching error: [2].01948325229IDS_ERROR_23761033Cabinet not found.01948325229IDS_ERROR_23791033Error opening file for read: [3] GetLastError: [2].01948325229IDS_ERROR_23801033Error opening file for write: [3]. GetLastError: [2].01948325229IDS_ERROR_23811033Directory does not exist: [2].01948325229IDS_ERROR_23821033Drive not ready: [2].01948325229IDS_ERROR_241033Please insert the disk: [2]01948325229IDS_ERROR_2401103364-bit registry operation attempted on 32-bit operating system for key [2].01948325229IDS_ERROR_24021033Out of memory.01948325229IDS_ERROR_251033The installer has insufficient privileges to access this directory: [2]. The installation cannot continue. Log on as an administrator or contact your system administrator.01948325229IDS_ERROR_25011033Could not create rollback script enumerator.01948325229IDS_ERROR_25021033Called InstallFinalize when no install in progress.01948325229IDS_ERROR_25031033Called RunScript when not marked in progress.01948325229IDS_ERROR_261033Error writing to file [2]. Verify that you have access to that directory.01948325229IDS_ERROR_26011033Invalid value for property [2]: '[3]'01948325229IDS_ERROR_26021033The [2] table entry '[3]' has no associated entry in the Media table.01948325229IDS_ERROR_26031033Duplicate table name [2].01948325229IDS_ERROR_26041033[2] Property undefined.01948325229IDS_ERROR_26051033Could not find server [2] in [3] or [4].01948325229IDS_ERROR_26061033Value of property [2] is not a valid full path: '[3]'.01948325229IDS_ERROR_26071033Media table not found or empty (required for installation of files).01948325229IDS_ERROR_26081033Could not create security descriptor for object. Error: '[2]'.01948325229IDS_ERROR_26091033Attempt to migrate product settings before initialization.01948325229IDS_ERROR_26111033The file [2] is marked as compressed, but the associated media entry does not specify a cabinet.01948325229IDS_ERROR_26121033Stream not found in '[2]' column. Primary key: '[3]'.01948325229IDS_ERROR_26131033RemoveExistingProducts action sequenced incorrectly.01948325229IDS_ERROR_26141033Could not access IStorage object from installation package.01948325229IDS_ERROR_26151033Skipped unregistration of Module [2] due to source resolution failure.01948325229IDS_ERROR_26161033Companion file [2] parent missing.01948325229IDS_ERROR_26171033Shared component [2] not found in Component table.01948325229IDS_ERROR_26181033Isolated application component [2] not found in Component table.01948325229IDS_ERROR_26191033Isolated components [2], [3] not part of same feature.01948325229IDS_ERROR_26201033Key file of isolated application component [2] not in File table.01948325229IDS_ERROR_26211033Resource DLL or Resource ID information for shortcut [2] set incorrectly.01948325229IDS_ERROR_271033Error reading from file [2]. Verify that the file exists and that you can access it.01948325229IDS_ERROR_27011033The depth of a feature exceeds the acceptable tree depth of [2] levels.01948325229IDS_ERROR_27021033A Feature table record ([2]) references a non-existent parent in the Attributes field.01948325229IDS_ERROR_27031033Property name for root source path not defined: [2]01948325229IDS_ERROR_27041033Root directory property undefined: [2]01948325229IDS_ERROR_27051033Invalid table: [2]; Could not be linked as tree.01948325229IDS_ERROR_27061033Source paths not created. No path exists for entry [2] in Directory table.01948325229IDS_ERROR_27071033Target paths not created. No path exists for entry [2] in Directory table.01948325229IDS_ERROR_27081033No entries found in the file table.01948325229IDS_ERROR_27091033The specified Component name ('[2]') not found in Component table.01948325229IDS_ERROR_27101033The requested 'Select' state is illegal for this Component.01948325229IDS_ERROR_27111033The specified Feature name ('[2]') not found in Feature table.01948325229IDS_ERROR_27121033Invalid return from modeless dialog: [3], in action [2].01948325229IDS_ERROR_27131033Null value in a non-nullable column ('[2]' in '[3]' column of the '[4]' table.01948325229IDS_ERROR_27141033Invalid value for default folder name: [2].01948325229IDS_ERROR_27151033The specified File key ('[2]') not found in the File table.01948325229IDS_ERROR_27161033Could not create a random subcomponent name for component '[2]'.01948325229IDS_ERROR_27171033Bad action condition or error calling custom action '[2]'.01948325229IDS_ERROR_27181033Missing package name for product code '[2]'.01948325229IDS_ERROR_27191033Neither UNC nor drive letter path found in source '[2]'.01948325229IDS_ERROR_27201033Error opening source list key. Error: '[2]'01948325229IDS_ERROR_27211033Custom action [2] not found in Binary table stream.01948325229IDS_ERROR_27221033Custom action [2] not found in File table.01948325229IDS_ERROR_27231033Custom action [2] specifies unsupported type.01948325229IDS_ERROR_27241033The volume label '[2]' on the media you're running from does not match the label '[3]' given in the Media table. This is allowed only if you have only 1 entry in your Media table.01948325229IDS_ERROR_27251033Invalid database tables01948325229IDS_ERROR_27261033Action not found: [2].01948325229IDS_ERROR_27271033The directory entry '[2]' does not exist in the Directory table.01948325229IDS_ERROR_27281033Table definition error: [2]01948325229IDS_ERROR_27291033Install engine not initialized.01948325229IDS_ERROR_27301033Bad value in database. Table: '[2]'; Primary key: '[3]'; Column: '[4]'01948325229IDS_ERROR_27311033Selection Manager not initialized.01948325229IDS_ERROR_27321033Directory Manager not initialized.01948325229IDS_ERROR_27331033Bad foreign key ('[2]') in '[3]' column of the '[4]' table.01948325229IDS_ERROR_27341033Invalid reinstall mode character.01948325229IDS_ERROR_27351033Custom action '[2]' has caused an unhandled exception and has been stopped. This may be the result of an internal error in the custom action, such as an access violation.01948325229IDS_ERROR_27361033Generation of custom action temp file failed: [2].01948325229IDS_ERROR_27371033Could not access custom action [2], entry [3], library [4]01948325229IDS_ERROR_27381033Could not access VBScript run time for custom action [2].01948325229IDS_ERROR_27391033Could not access JavaScript run time for custom action [2].01948325229IDS_ERROR_27401033Custom action [2] script error [3], [4]: [5] Line [6], Column [7], [8].01948325229IDS_ERROR_27411033Configuration information for product [2] is corrupt. Invalid info: [2].01948325229IDS_ERROR_27421033Marshaling to Server failed: [2].01948325229IDS_ERROR_27431033Could not execute custom action [2], location: [3], command: [4].01948325229IDS_ERROR_27441033EXE failed called by custom action [2], location: [3], command: [4].01948325229IDS_ERROR_27451033Transform [2] invalid for package [3]. Expected language [4], found language [5].01948325229IDS_ERROR_27461033Transform [2] invalid for package [3]. Expected product [4], found product [5].01948325229IDS_ERROR_27471033Transform [2] invalid for package [3]. Expected product version < [4], found product version [5].01948325229IDS_ERROR_27481033Transform [2] invalid for package [3]. Expected product version <= [4], found product version [5].01948325229IDS_ERROR_27491033Transform [2] invalid for package [3]. Expected product version == [4], found product version [5].01948325229IDS_ERROR_27501033Transform [2] invalid for package [3]. Expected product version >= [4], found product version [5].01948325229IDS_ERROR_275021033Could not connect to [2] '[3]'. [4]01948325229IDS_ERROR_275031033Error retrieving version string from [2] '[3]'. [4]01948325229IDS_ERROR_275041033SQL version requirements not met: [3]. This installation requires [2] [4] or later.01948325229IDS_ERROR_275051033Could not open SQL script file [2].01948325229IDS_ERROR_275061033Error executing SQL script [2]. Line [3]. [4]01948325229IDS_ERROR_275071033Connection or browsing for database servers requires that MDAC be installed.01948325229IDS_ERROR_275081033Error installing COM+ application [2]. [3]01948325229IDS_ERROR_275091033Error uninstalling COM+ application [2]. [3]01948325229IDS_ERROR_27511033Transform [2] invalid for package [3]. Expected product version > [4], found product version [5].01948325229IDS_ERROR_275101033Error installing COM+ application [2]. Could not load Microsoft(R) .NET class libraries. Registering .NET serviced components requires that Microsoft(R) .NET Framework be installed.01948325229IDS_ERROR_275111033Could not execute SQL script file [2]. Connection not open: [3]01948325229IDS_ERROR_275121033Error beginning transactions for [2] '[3]'. Database [4]. [5]01948325229IDS_ERROR_275131033Error committing transactions for [2] '[3]'. Database [4]. [5]01948325229IDS_ERROR_275141033This installation requires a Microsoft SQL Server. The specified server '[3]' is a Microsoft SQL Server Desktop Engine or SQL Server Express.01948325229IDS_ERROR_275151033Error retrieving schema version from [2] '[3]'. Database: '[4]'. [5]01948325229IDS_ERROR_275161033Error writing schema version to [2] '[3]'. Database: '[4]'. [5]01948325229IDS_ERROR_275171033This installation requires Administrator privileges for installing COM+ applications. Log on as an administrator and then retry this installation.01948325229IDS_ERROR_275181033The COM+ application "[2]" is configured to run as an NT service; this requires COM+ 1.5 or later on the system. Since your system has COM+ 1.0, this application will not be installed.01948325229IDS_ERROR_275191033Error updating XML file [2]. [3]01948325229IDS_ERROR_27521033Could not open transform [2] stored as child storage of package [4].01948325229IDS_ERROR_275201033Error opening XML file [2]. [3]01948325229IDS_ERROR_275211033This setup requires MSXML 3.0 or higher for configuring XML files. Please make sure that you have version 3.0 or higher.01948325229IDS_ERROR_275221033Error creating XML file [2]. [3]01948325229IDS_ERROR_275231033Error loading servers.01948325229IDS_ERROR_275241033Error loading NetApi32.DLL. The ISNetApi.dll needs to have NetApi32.DLL properly loaded and requires an NT based operating system.01948325229IDS_ERROR_275251033Server not found. Verify that the specified server exists. The server name can not be empty.01948325229IDS_ERROR_275261033Unspecified error from ISNetApi.dll.01948325229IDS_ERROR_275271033The buffer is too small.01948325229IDS_ERROR_275281033Access denied. Check administrative rights.01948325229IDS_ERROR_275291033Invalid computer.01948325229IDS_ERROR_27531033The File '[2]' is not marked for installation.01948325229IDS_ERROR_275301033Unknown error returned from NetAPI. System error: [2]01948325229IDS_ERROR_275311033Unhandled exception.01948325229IDS_ERROR_275321033Invalid user name for this server or domain.01948325229IDS_ERROR_275331033The case-sensitive passwords do not match.01948325229IDS_ERROR_275341033The list is empty.01948325229IDS_ERROR_275351033Access violation.01948325229IDS_ERROR_275361033Error getting group.01948325229IDS_ERROR_275371033Error adding user to group. Verify that the group exists for this domain or server.01948325229IDS_ERROR_275381033Error creating user.01948325229IDS_ERROR_275391033ERROR_NETAPI_ERROR_NOT_PRIMARY returned from NetAPI.01948325229IDS_ERROR_27541033The File '[2]' is not a valid patch file.01948325229IDS_ERROR_275401033The specified user already exists.01948325229IDS_ERROR_275411033The specified group already exists.01948325229IDS_ERROR_275421033Invalid password. Verify that the password is in accordance with your network password policy.01948325229IDS_ERROR_275431033Invalid name.01948325229IDS_ERROR_275441033Invalid group.01948325229IDS_ERROR_275451033The user name can not be empty and must be in the format DOMAIN\Username.01948325229IDS_ERROR_275461033Error loading or creating INI file in the user TEMP directory.01948325229IDS_ERROR_275471033ISNetAPI.dll is not loaded or there was an error loading the dll. This dll needs to be loaded for this operation. Verify that the dll is in the SUPPORTDIR directory.01948325229IDS_ERROR_275481033Error deleting INI file containing new user information from the user's TEMP directory.01948325229IDS_ERROR_275491033Error getting the primary domain controller (PDC).01948325229IDS_ERROR_27551033Server returned unexpected error [2] attempting to install package [3].01948325229IDS_ERROR_275501033Every field must have a value in order to create a user.01948325229IDS_ERROR_275511033ODBC driver for [2] not found. This is required to connect to [2] database servers.01948325229IDS_ERROR_275521033Error creating database [4]. Server: [2] [3]. [5]01948325229IDS_ERROR_275531033Error connecting to database [4]. Server: [2] [3]. [5]01948325229IDS_ERROR_275541033Error attempting to open connection [2]. No valid database metadata associated with this connection.01948325229IDS_ERROR_275551033Error attempting to apply permissions to object '[2]'. System error: [3] ([4])01948325229IDS_ERROR_27561033The property '[2]' was used as a directory property in one or more tables, but no value was ever assigned.01948325229IDS_ERROR_27571033Could not create summary info for transform [2].01948325229IDS_ERROR_27581033Transform [2] does not contain an MSI version.01948325229IDS_ERROR_27591033Transform [2] version [3] incompatible with engine; Min: [4], Max: [5].01948325229IDS_ERROR_27601033Transform [2] invalid for package [3]. Expected upgrade code [4], found [5].01948325229IDS_ERROR_27611033Cannot begin transaction. Global mutex not properly initialized.01948325229IDS_ERROR_27621033Cannot write script record. Transaction not started.01948325229IDS_ERROR_27631033Cannot run script. Transaction not started.01948325229IDS_ERROR_27651033Assembly name missing from AssemblyName table : Component: [4].01948325229IDS_ERROR_27661033The file [2] is an invalid MSI storage file.01948325229IDS_ERROR_27671033No more data{ while enumerating [2]}.01948325229IDS_ERROR_27681033Transform in patch package is invalid.01948325229IDS_ERROR_27691033Custom Action [2] did not close [3] MSIHANDLEs.01948325229IDS_ERROR_27701033Cached folder [2] not defined in internal cache folder table.01948325229IDS_ERROR_27711033Upgrade of feature [2] has a missing component.01948325229IDS_ERROR_27721033New upgrade feature [2] must be a leaf feature.01948325229IDS_ERROR_281033Another application has exclusive access to the file [2]. Please shut down all other applications, then click Retry.01948325229IDS_ERROR_28011033Unknown Message -- Type [2]. No action is taken.01948325229IDS_ERROR_28021033No publisher is found for the event [2].01948325229IDS_ERROR_28031033Dialog View did not find a record for the dialog [2].01948325229IDS_ERROR_28041033On activation of the control [3] on dialog [2] CMsiDialog failed to evaluate the condition [3].01948325229IDS_ERROR_28061033The dialog [2] failed to evaluate the condition [3].01948325229IDS_ERROR_28071033The action [2] is not recognized.01948325229IDS_ERROR_28081033Default button is ill-defined on dialog [2].01948325229IDS_ERROR_28091033On the dialog [2] the next control pointers do not form a cycle. There is a pointer from [3] to [4], but there is no further pointer.01948325229IDS_ERROR_28101033On the dialog [2] the next control pointers do not form a cycle. There is a pointer from both [3] and [5] to [4].01948325229IDS_ERROR_28111033On dialog [2] control [3] has to take focus, but it is unable to do so.01948325229IDS_ERROR_28121033The event [2] is not recognized.01948325229IDS_ERROR_28131033The EndDialog event was called with the argument [2], but the dialog has a parent.01948325229IDS_ERROR_28141033On the dialog [2] the control [3] names a nonexistent control [4] as the next control.01948325229IDS_ERROR_28151033ControlCondition table has a row without condition for the dialog [2].01948325229IDS_ERROR_28161033The EventMapping table refers to an invalid control [4] on dialog [2] for the event [3].01948325229IDS_ERROR_28171033The event [2] failed to set the attribute for the control [4] on dialog [3].01948325229IDS_ERROR_28181033In the ControlEvent table EndDialog has an unrecognized argument [2].01948325229IDS_ERROR_28191033Control [3] on dialog [2] needs a property linked to it.01948325229IDS_ERROR_28201033Attempted to initialize an already initialized handler.01948325229IDS_ERROR_28211033Attempted to initialize an already initialized dialog: [2].01948325229IDS_ERROR_28221033No other method can be called on dialog [2] until all the controls are added.01948325229IDS_ERROR_28231033Attempted to initialize an already initialized control: [3] on dialog [2].01948325229IDS_ERROR_28241033The dialog attribute [3] needs a record of at least [2] field(s).01948325229IDS_ERROR_28251033The control attribute [3] needs a record of at least [2] field(s).01948325229IDS_ERROR_28261033Control [3] on dialog [2] extends beyond the boundaries of the dialog [4] by [5] pixels.01948325229IDS_ERROR_28271033The button [4] on the radio button group [3] on dialog [2] extends beyond the boundaries of the group [5] by [6] pixels.01948325229IDS_ERROR_28281033Tried to remove control [3] from dialog [2], but the control is not part of the dialog.01948325229IDS_ERROR_28291033Attempt to use an uninitialized dialog.01948325229IDS_ERROR_28301033Attempt to use an uninitialized control on dialog [2].01948325229IDS_ERROR_28311033The control [3] on dialog [2] does not support [5] the attribute [4].01948325229IDS_ERROR_28321033The dialog [2] does not support the attribute [3].01948325229IDS_ERROR_28331033Control [4] on dialog [3] ignored the message [2].01948325229IDS_ERROR_28341033The next pointers on the dialog [2] do not form a single loop.01948325229IDS_ERROR_28351033The control [2] was not found on dialog [3].01948325229IDS_ERROR_28361033The control [3] on the dialog [2] cannot take focus.01948325229IDS_ERROR_28371033The control [3] on dialog [2] wants the winproc to return [4].01948325229IDS_ERROR_28381033The item [2] in the selection table has itself as a parent.01948325229IDS_ERROR_28391033Setting the property [2] failed.01948325229IDS_ERROR_28401033Error dialog name mismatch.01948325229IDS_ERROR_28411033No OK button was found on the error dialog.01948325229IDS_ERROR_28421033No text field was found on the error dialog.01948325229IDS_ERROR_28431033The ErrorString attribute is not supported for standard dialogs.01948325229IDS_ERROR_28441033Cannot execute an error dialog if the Errorstring is not set.01948325229IDS_ERROR_28451033The total width of the buttons exceeds the size of the error dialog.01948325229IDS_ERROR_28461033SetFocus did not find the required control on the error dialog.01948325229IDS_ERROR_28471033The control [3] on dialog [2] has both the icon and the bitmap style set.01948325229IDS_ERROR_28481033Tried to set control [3] as the default button on dialog [2], but the control does not exist.01948325229IDS_ERROR_28491033The control [3] on dialog [2] is of a type, that cannot be integer valued.01948325229IDS_ERROR_28501033Unrecognized volume type.01948325229IDS_ERROR_28511033The data for the icon [2] is not valid.01948325229IDS_ERROR_28521033At least one control has to be added to dialog [2] before it is used.01948325229IDS_ERROR_28531033Dialog [2] is a modeless dialog. The execute method should not be called on it.01948325229IDS_ERROR_28541033On the dialog [2] the control [3] is designated as first active control, but there is no such control.01948325229IDS_ERROR_28551033The radio button group [3] on dialog [2] has fewer than 2 buttons.01948325229IDS_ERROR_28561033Creating a second copy of the dialog [2].01948325229IDS_ERROR_28571033The directory [2] is mentioned in the selection table but not found.01948325229IDS_ERROR_28581033The data for the bitmap [2] is not valid.01948325229IDS_ERROR_28591033Test error message.01948325229IDS_ERROR_28601033Cancel button is ill-defined on dialog [2].01948325229IDS_ERROR_28611033The next pointers for the radio buttons on dialog [2] control [3] do not form a cycle.01948325229IDS_ERROR_28621033The attributes for the control [3] on dialog [2] do not define a valid icon size. Setting the size to 16.01948325229IDS_ERROR_28631033The control [3] on dialog [2] needs the icon [4] in size [5]x[5], but that size is not available. Loading the first available size.01948325229IDS_ERROR_28641033The control [3] on dialog [2] received a browse event, but there is no configurable directory for the present selection. Likely cause: browse button is not authored correctly.01948325229IDS_ERROR_28651033Control [3] on billboard [2] extends beyond the boundaries of the billboard [4] by [5] pixels.01948325229IDS_ERROR_28661033The dialog [2] is not allowed to return the argument [3].01948325229IDS_ERROR_28671033The error dialog property is not set.01948325229IDS_ERROR_28681033The error dialog [2] does not have the error style bit set.01948325229IDS_ERROR_28691033The dialog [2] has the error style bit set, but is not an error dialog.01948325229IDS_ERROR_28701033The help string [4] for control [3] on dialog [2] does not contain the separator character.01948325229IDS_ERROR_28711033The [2] table is out of date: [3].01948325229IDS_ERROR_28721033The argument of the CheckPath control event on dialog [2] is invalid.01948325229IDS_ERROR_28731033On the dialog [2] the control [3] has an invalid string length limit: [4].01948325229IDS_ERROR_28741033Changing the text font to [2] failed.01948325229IDS_ERROR_28751033Changing the text color to [2] failed.01948325229IDS_ERROR_28761033The control [3] on dialog [2] had to truncate the string: [4].01948325229IDS_ERROR_28771033The binary data [2] was not found01948325229IDS_ERROR_28781033On the dialog [2] the control [3] has a possible value: [4]. This is an invalid or duplicate value.01948325229IDS_ERROR_28791033The control [3] on dialog [2] cannot parse the mask string: [4].01948325229IDS_ERROR_28801033Do not perform the remaining control events.01948325229IDS_ERROR_28811033CMsiHandler initialization failed.01948325229IDS_ERROR_28821033Dialog window class registration failed.01948325229IDS_ERROR_28831033CreateNewDialog failed for the dialog [2].01948325229IDS_ERROR_28841033Failed to create a window for the dialog [2].01948325229IDS_ERROR_28851033Failed to create the control [3] on the dialog [2].01948325229IDS_ERROR_28861033Creating the [2] table failed.01948325229IDS_ERROR_28871033Creating a cursor to the [2] table failed.01948325229IDS_ERROR_28881033Executing the [2] view failed.01948325229IDS_ERROR_28891033Creating the window for the control [3] on dialog [2] failed.01948325229IDS_ERROR_28901033The handler failed in creating an initialized dialog.01948325229IDS_ERROR_28911033Failed to destroy window for dialog [2].01948325229IDS_ERROR_28921033[2] is an integer only control, [3] is not a valid integer value.01948325229IDS_ERROR_28931033The control [3] on dialog [2] can accept property values that are at most [5] characters long. The value [4] exceeds this limit, and has been truncated.01948325229IDS_ERROR_28941033Loading RICHED20.DLL failed. GetLastError() returned: [2].01948325229IDS_ERROR_28951033Freeing RICHED20.DLL failed. GetLastError() returned: [2].01948325229IDS_ERROR_28961033Executing action [2] failed.01948325229IDS_ERROR_28971033Failed to create any [2] font on this system.01948325229IDS_ERROR_28981033For [2] textstyle, the system created a '[3]' font, in [4] character set.01948325229IDS_ERROR_28991033Failed to create [2] textstyle. GetLastError() returned: [3].01948325229IDS_ERROR_291033There is not enough disk space to install the file [2]. Free some disk space and click Retry, or click Cancel to exit.01948325229IDS_ERROR_29011033Invalid parameter to operation [2]: Parameter [3].01948325229IDS_ERROR_29021033Operation [2] called out of sequence.01948325229IDS_ERROR_29031033The file [2] is missing.01948325229IDS_ERROR_29041033Could not BindImage file [2].01948325229IDS_ERROR_29051033Could not read record from script file [2].01948325229IDS_ERROR_29061033Missing header in script file [2].01948325229IDS_ERROR_29071033Could not create secure security descriptor. Error: [2].01948325229IDS_ERROR_29081033Could not register component [2].01948325229IDS_ERROR_29091033Could not unregister component [2].01948325229IDS_ERROR_29101033Could not determine user's security ID.01948325229IDS_ERROR_29111033Could not remove the folder [2].01948325229IDS_ERROR_29121033Could not schedule file [2] for removal on restart.01948325229IDS_ERROR_29191033No cabinet specified for compressed file: [2].01948325229IDS_ERROR_29201033Source directory not specified for file [2].01948325229IDS_ERROR_29241033Script [2] version unsupported. Script version: [3], minimum version: [4], maximum version: [5].01948325229IDS_ERROR_29271033ShellFolder id [2] is invalid.01948325229IDS_ERROR_29281033Exceeded maximum number of sources. Skipping source '[2]'.01948325229IDS_ERROR_29291033Could not determine publishing root. Error: [2].01948325229IDS_ERROR_29321033Could not create file [2] from script data. Error: [3].01948325229IDS_ERROR_29331033Could not initialize rollback script [2].01948325229IDS_ERROR_29341033Could not secure transform [2]. Error [3].01948325229IDS_ERROR_29351033Could not unsecure transform [2]. Error [3].01948325229IDS_ERROR_29361033Could not find transform [2].01948325229IDS_ERROR_29371033Windows Installer cannot install a system file protection catalog. Catalog: [2], Error: [3].01948325229IDS_ERROR_29381033Windows Installer cannot retrieve a system file protection catalog from the cache. Catalog: [2], Error: [3].01948325229IDS_ERROR_29391033Windows Installer cannot delete a system file protection catalog from the cache. Catalog: [2], Error: [3].01948325229IDS_ERROR_29401033Directory Manager not supplied for source resolution.01948325229IDS_ERROR_29411033Unable to compute the CRC for file [2].01948325229IDS_ERROR_29421033BindImage action has not been executed on [2] file.01948325229IDS_ERROR_29431033This version of Windows does not support deploying 64-bit packages. The script [2] is for a 64-bit package.01948325229IDS_ERROR_29441033GetProductAssignmentType failed.01948325229IDS_ERROR_29451033Installation of ComPlus App [2] failed with error [3].01948325229IDS_ERROR_31033Info [1]. 01948325229IDS_ERROR_301033Source file not found: [2]. Verify that the file exists and that you can access it.01948325229IDS_ERROR_30011033The patches in this list contain incorrect sequencing information: [2][3][4][5][6][7][8][9][10][11][12][13][14][15][16].01948325229IDS_ERROR_30021033Patch [2] contains invalid sequencing information. 01948325229IDS_ERROR_311033Error reading from file: [3]. {{ System error [2].}} Verify that the file exists and that you can access it.01948325229IDS_ERROR_321033Error writing to file: [3]. {{ System error [2].}} Verify that you have access to that directory.01948325229IDS_ERROR_331033Source file not found{{(cabinet)}}: [2]. Verify that the file exists and that you can access it.01948325229IDS_ERROR_341033Cannot create the directory [2]. A file with this name already exists. Please rename or remove the file and click Retry, or click Cancel to exit.01948325229IDS_ERROR_351033The volume [2] is currently unavailable. Please select another.01948325229IDS_ERROR_361033The specified path [2] is unavailable.01948325229IDS_ERROR_371033Unable to write to the specified folder [2].01948325229IDS_ERROR_381033A network error occurred while attempting to read from the file [2]01948325229IDS_ERROR_391033An error occurred while attempting to create the directory [2]01948325229IDS_ERROR_41033Internal Error [1]. [2]{, [3]}{, [4]}01948325229IDS_ERROR_401033A network error occurred while attempting to create the directory [2]01948325229IDS_ERROR_411033A network error occurred while attempting to open the source file cabinet [2].01948325229IDS_ERROR_421033The specified path is too long [2].01948325229IDS_ERROR_431033The Installer has insufficient privileges to modify the file [2].01948325229IDS_ERROR_441033A portion of the path [2] exceeds the length allowed by the system.01948325229IDS_ERROR_451033The path [2] contains words that are not valid in folders.01948325229IDS_ERROR_461033The path [2] contains an invalid character.01948325229IDS_ERROR_471033[2] is not a valid short file name.01948325229IDS_ERROR_481033Error getting file security: [3] GetLastError: [2]01948325229IDS_ERROR_491033Invalid Drive: [2]01948325229IDS_ERROR_51033{{Disk full: }}01948325229IDS_ERROR_501033Could not create key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_511033Could not open key: [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_521033Could not delete value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_531033Could not delete key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_541033Could not read value [2] from key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_551033Could not write value [2] to key [3]. {{ System error [4].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_561033Could not get value names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_571033Could not get sub key names for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_581033Could not read security information for key [2]. {{ System error [3].}} Verify that you have sufficient access to that key, or contact your support personnel.01948325229IDS_ERROR_591033Could not increase the available registry space. [2] KB of free registry space is required for the installation of this application.01948325229IDS_ERROR_61033Action [Time]: [1]. [2]01948325229IDS_ERROR_601033Another installation is in progress. You must complete that installation before continuing this one.01948325229IDS_ERROR_611033Error accessing secured data. Please make sure the Windows Installer is configured properly and try the installation again.01948325229IDS_ERROR_621033User [2] has previously initiated an installation for product [3]. That user will need to run that installation again before using that product. Your current installation will now continue.01948325229IDS_ERROR_631033User [2] has previously initiated an installation for product [3]. That user will need to run that installation again before using that product.01948325229IDS_ERROR_641033Out of disk space -- Volume: '[2]'; required space: [3] KB; available space: [4] KB. Free some disk space and retry.01948325229IDS_ERROR_651033Are you sure you want to cancel?01948325229IDS_ERROR_661033The file [2][3] is being held in use{ by the following process: Name: [4], ID: [5], Window Title: [6]}. Close that application and retry.01948325229IDS_ERROR_671033The product [2] is already installed, preventing the installation of this product. The two products are incompatible.01948325229IDS_ERROR_681033Out of disk space -- Volume: [2]; required space: [3] KB; available space: [4] KB. If rollback is disabled, enough space is available. Click Cancel to quit, Retry to check available disk space again, or Ignore to continue without rollback.01948325229IDS_ERROR_691033Could not access network location [2].01948325229IDS_ERROR_71033[ProductName]01948325229IDS_ERROR_701033The following applications should be closed before continuing the installation:01948325229IDS_ERROR_711033Could not find any previously installed compliant products on the machine for installing this product.01948325229IDS_ERROR_721033The key [2] is not valid. Verify that you entered the correct key.01948325229IDS_ERROR_731033The installer must restart your system before configuration of [2] can continue. Click Yes to restart now or No if you plan to restart later.01948325229IDS_ERROR_741033You must restart your system for the configuration changes made to [2] to take effect. Click Yes to restart now or No if you plan to restart later.01948325229IDS_ERROR_751033An installation for [2] is currently suspended. You must undo the changes made by that installation to continue. Do you want to undo those changes?01948325229IDS_ERROR_761033A previous installation for this product is in progress. You must undo the changes made by that installation to continue. Do you want to undo those changes?01948325229IDS_ERROR_771033No valid source could be found for product [2]. The Windows Installer cannot continue.01948325229IDS_ERROR_781033Installation operation completed successfully.01948325229IDS_ERROR_791033Installation operation failed.01948325229IDS_ERROR_81033{[2]}{, [3]}{, [4]}01948325229IDS_ERROR_801033Product: [2] -- [3]01948325229IDS_ERROR_811033You may either restore your computer to its previous state or continue the installation later. Would you like to restore?01948325229IDS_ERROR_821033An error occurred while writing installation information to disk. Check to make sure enough disk space is available, and click Retry, or Cancel to end the installation.01948325229IDS_ERROR_831033One or more of the files required to restore your computer to its previous state could not be found. Restoration will not be possible.01948325229IDS_ERROR_841033The path [2] is not valid. Please specify a valid path.01948325229IDS_ERROR_851033Out of memory. Shut down other applications before retrying.01948325229IDS_ERROR_861033There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to go back to the previously selected volume.01948325229IDS_ERROR_871033There is no disk in drive [2]. Please insert one and click Retry, or click Cancel to return to the browse dialog and select a different volume.01948325229IDS_ERROR_881033The folder [2] does not exist. Please enter a path to an existing folder.01948325229IDS_ERROR_891033You have insufficient privileges to read this folder.01948325229IDS_ERROR_91033Message type: [1], Argument: [2]01948325229IDS_ERROR_901033A valid destination folder for the installation could not be determined.01948325229IDS_ERROR_911033Error attempting to read from the source installation database: [2].01948325229IDS_ERROR_921033Scheduling reboot operation: Renaming file [2] to [3]. Must reboot to complete operation.01948325229IDS_ERROR_931033Scheduling reboot operation: Deleting file [2]. Must reboot to complete operation.01948325229IDS_ERROR_941033Module [2] failed to register. HRESULT [3]. Contact your support personnel.01948325229IDS_ERROR_951033Module [2] failed to unregister. HRESULT [3]. Contact your support personnel.01948325229IDS_ERROR_961033Failed to cache package [2]. Error: [3]. Contact your support personnel.01948325229IDS_ERROR_971033Could not register font [2]. Verify that you have sufficient permissions to install fonts, and that the system supports this font.01948325229IDS_ERROR_981033Could not unregister font [2]. Verify that you have sufficient permissions to remove fonts.01948325229IDS_ERROR_991033Could not create shortcut [2]. Verify that the destination folder exists and that you can access it.01948325229IDS_INSTALLDIR1033[INSTALLDIR]01948325229IDS_INSTALLSHIELD1033InstallShield01948325229IDS_INSTALLSHIELD_FORMATTED1033{&MSSWhiteSerif8}InstallShield01948325229IDS_ISSCRIPT_VERSION_MISSING1033The InstallScript engine is missing from this machine. If available, please run ISScript.msi, or contact your support personnel for further assistance.01948325229IDS_ISSCRIPT_VERSION_OLD1033The InstallScript engine on this machine is older than the version required to run this setup. If available, please install the latest version of ISScript.msi, or contact your support personnel for further assistance.01948325229IDS_NEXT1033&Next >01948325229IDS_OK1033OK01948325229IDS_PREREQUISITE_SETUP_BROWSE1033Open [ProductName]'s original [SETUPEXENAME]01948325229IDS_PREREQUISITE_SETUP_INVALID1033This executable file does not appear to be the original executable file for [ProductName]. Without using the original [SETUPEXENAME] to install additional dependencies, [ProductName] may not work correctly. Would you like to find the original [SETUPEXENAME]?01948325229IDS_PREREQUISITE_SETUP_SEARCH1033This installation may require additional dependencies. Without its dependencies, [ProductName] may not work correctly. Would you like to find the original [SETUPEXENAME]?01948325229IDS_PREVENT_DOWNGRADE_EXIT1033A newer version of this application is already installed on this computer. If you wish to install this version, please uninstall the newer version first. Click OK to exit the wizard.01948325229IDS_PRINT_BUTTON1033&Print01948325229IDS_PRODUCTNAME_INSTALLSHIELD1033[ProductName] - InstallShield Wizard01948325229IDS_PROGMSG_IIS_CREATEAPPPOOL1033Creating application pool %s01948325229IDS_PROGMSG_IIS_CREATEAPPPOOLS1033Creating application Pools...01948325229IDS_PROGMSG_IIS_CREATEVROOT1033Creating IIS virtual directory %s01948325229IDS_PROGMSG_IIS_CREATEVROOTS1033Creating IIS virtual directories...01948325229IDS_PROGMSG_IIS_CREATEWEBSERVICEEXTENSION1033Creating web service extension01948325229IDS_PROGMSG_IIS_CREATEWEBSERVICEEXTENSIONS1033Creating web service extensions...01948325229IDS_PROGMSG_IIS_CREATEWEBSITE1033Creating IIS website %s01948325229IDS_PROGMSG_IIS_CREATEWEBSITES1033Creating IIS websites...01948325229IDS_PROGMSG_IIS_EXTRACT1033Extracting information for IIS virtual directories...01948325229IDS_PROGMSG_IIS_EXTRACTDONE1033Extracted information for IIS virtual directories...01948325229IDS_PROGMSG_IIS_REMOVEAPPPOOL1033Removing application pool01948325229IDS_PROGMSG_IIS_REMOVEAPPPOOLS1033Removing application pools...01948325229IDS_PROGMSG_IIS_REMOVESITE1033Removing web site at port %d01948325229IDS_PROGMSG_IIS_REMOVEVROOT1033Removing IIS virtual directory %s01948325229IDS_PROGMSG_IIS_REMOVEVROOTS1033Removing IIS virtual directories...01948325229IDS_PROGMSG_IIS_REMOVEWEBSERVICEEXTENSION1033Removing web service extension01948325229IDS_PROGMSG_IIS_REMOVEWEBSERVICEEXTENSIONS1033Removing web service extensions...01948325229IDS_PROGMSG_IIS_REMOVEWEBSITES1033Removing IIS websites...01948325229IDS_PROGMSG_IIS_ROLLBACKAPPPOOLS1033Rolling back application pools...01948325229IDS_PROGMSG_IIS_ROLLBACKVROOTS1033Rolling back virtual directory and web site changes...01948325229IDS_PROGMSG_IIS_ROLLBACKWEBSERVICEEXTENSIONS1033Rolling back web service extensions...01948325229IDS_PROGMSG_TEXTFILECHANGS_REPLACE1033Replacing %s with %s in %s...01948325229IDS_PROGMSG_XML_COSTING1033Costing XML files...01948325229IDS_PROGMSG_XML_CREATE_FILE1033Creating XML file %s...01948325229IDS_PROGMSG_XML_FILES1033Performing XML file changes...01948325229IDS_PROGMSG_XML_REMOVE_FILE1033Removing XML file %s...01948325229IDS_PROGMSG_XML_ROLLBACK_FILES1033Rolling back XML file changes...01948325229IDS_PROGMSG_XML_UPDATE_FILE1033Updating XML file %s...01948325229IDS_SETUPEXE_EXPIRE_MSG1033This setup works until %s. The setup will now exit.01948325229IDS_SQLBROWSE_INTRO1033From the list of servers below, select the database server you would like to target.01948325229IDS_SQLBROWSE_INTRO_DB1033From the list of catalog names below, select the database catalog you would like to target.01948325229IDS_SQLBROWSE_INTRO_TEMPLATE1033[IS_SQLBROWSE_INTRO]01948325229IDS_SQLLOGIN_BROWSE1033B&rowse...01948325229IDS_SQLLOGIN_BROWSE_DB1033Br&owse...01948325229IDS_SQLLOGIN_CATALOG1033&Name of database catalog:01948325229IDS_SQLLOGIN_CONNECT1033Connect using:01948325229IDS_SQLLOGIN_DESC1033Select database server and authentication method01948325229IDS_SQLLOGIN_ID1033&Login ID:01948325229IDS_SQLLOGIN_INTRO1033Select the database server to install to from the list below or click Browse to see a list of all database servers. You can also specify the way to authenticate your login using your current credentials or a SQL Login ID and Password.01948325229IDS_SQLLOGIN_PSWD1033&Password:01948325229IDS_SQLLOGIN_SERVER1033&Database Server:01948325229IDS_SQLLOGIN_SERVER21033&Database server that you are installing to:01948325229IDS_SQLLOGIN_SQL1033S&erver authentication using the Login ID and password below01948325229IDS_SQLLOGIN_TITLE1033{&MSSansBold8}Database Server01948325229IDS_SQLLOGIN_WIN1033&Windows authentication credentials of current user01948325229IDS_SQLSCRIPT_INSTALLING1033Executing SQL Install Script...01948325229IDS_SQLSCRIPT_UNINSTALLING1033Executing SQL Uninstall Script...01948325229IDS_STANDARD_USE_SETUPEXE1033This installation cannot be run by directly launching the MSI package. You must run setup.exe.01948325229IDS_SetupTips_Advertise1033Will be installed on first use. (Available only if the feature supports this option.)01948325229IDS_SetupTips_AllInstalledLocal1033Will be completely installed to the local hard drive.01948325229IDS_SetupTips_CustomSetup1033{&MSSansBold8}Custom Setup Tips01948325229IDS_SetupTips_CustomSetupDescription1033Custom Setup allows you to selectively install program features.01948325229IDS_SetupTips_IconInstallState1033The icon next to the feature name indicates the install state of the feature. Click the icon to drop down the install state menu for each feature.01948325229IDS_SetupTips_InstallState1033This install state means the feature...01948325229IDS_SetupTips_Network1033Will be installed to run from the network. (Available only if the feature supports this option.)01948325229IDS_SetupTips_OK1033OK01948325229IDS_SetupTips_SubFeaturesInstalledLocal1033Will have some subfeatures installed to the local hard drive. (Available only if the feature has subfeatures.)01948325229IDS_SetupTips_WillNotBeInstalled1033Will not be installed.01948325229IDS_UITEXT_Available1033Available01948325229IDS_UITEXT_Bytes1033bytes01948325229IDS_UITEXT_CompilingFeaturesCost1033Compiling cost for this feature...01948325229IDS_UITEXT_Differences1033Differences01948325229IDS_UITEXT_DiskSize1033Disk Size01948325229IDS_UITEXT_FeatureCompletelyRemoved1033This feature will be completely removed.01948325229IDS_UITEXT_FeatureContinueNetwork1033This feature will continue to be run from the network01948325229IDS_UITEXT_FeatureFreeSpace1033This feature frees up [1] on your hard drive.01948325229IDS_UITEXT_FeatureInstalledCD1033This feature, and all subfeatures, will be installed to run from the CD.01948325229IDS_UITEXT_FeatureInstalledCD21033This feature will be installed to run from CD.01948325229IDS_UITEXT_FeatureInstalledLocal1033This feature, and all subfeatures, will be installed on local hard drive.01948325229IDS_UITEXT_FeatureInstalledLocal21033This feature will be installed on local hard drive.01948325229IDS_UITEXT_FeatureInstalledNetwork1033This feature, and all subfeatures, will be installed to run from the network.01948325229IDS_UITEXT_FeatureInstalledNetwork21033This feature will be installed to run from network.01948325229IDS_UITEXT_FeatureInstalledRequired1033Will be installed when required.01948325229IDS_UITEXT_FeatureInstalledWhenRequired1033This feature will be set to be installed when required.01948325229IDS_UITEXT_FeatureInstalledWhenRequired21033This feature will be installed when required.01948325229IDS_UITEXT_FeatureLocal1033This feature will be installed on the local hard drive.01948325229IDS_UITEXT_FeatureLocal21033This feature will be installed on your local hard drive.01948325229IDS_UITEXT_FeatureNetwork1033This feature will be installed to run from the network.01948325229IDS_UITEXT_FeatureNetwork21033This feature will be available to run from the network.01948325229IDS_UITEXT_FeatureNotAvailable1033This feature will not be available.01948325229IDS_UITEXT_FeatureOnCD1033This feature will be installed to run from CD.01948325229IDS_UITEXT_FeatureOnCD21033This feature will be available to run from CD.01948325229IDS_UITEXT_FeatureRemainLocal1033This feature will remain on your local hard drive.01948325229IDS_UITEXT_FeatureRemoveNetwork1033This feature will be removed from your local hard drive, but will be still available to run from the network.01948325229IDS_UITEXT_FeatureRemovedCD1033This feature will be removed from your local hard drive but will still be available to run from CD.01948325229IDS_UITEXT_FeatureRemovedUnlessRequired1033This feature will be removed from your local hard drive but will be set to be installed when required.01948325229IDS_UITEXT_FeatureRequiredSpace1033This feature requires [1] on your hard drive.01948325229IDS_UITEXT_FeatureRunFromCD1033This feature will continue to be run from the CD01948325229IDS_UITEXT_FeatureSpaceFree1033This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive.01948325229IDS_UITEXT_FeatureSpaceFree21033This feature frees up [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive.01948325229IDS_UITEXT_FeatureSpaceFree31033This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures free up [4] on your hard drive.01948325229IDS_UITEXT_FeatureSpaceFree41033This feature requires [1] on your hard drive. It has [2] of [3] subfeatures selected. The subfeatures require [4] on your hard drive.01948325229IDS_UITEXT_FeatureUnavailable1033This feature will become unavailable.01948325229IDS_UITEXT_FeatureUninstallNoNetwork1033This feature will be uninstalled completely, and you won't be able to run it from the network.01948325229IDS_UITEXT_FeatureWasCD1033This feature was run from the CD but will be set to be installed when required.01948325229IDS_UITEXT_FeatureWasCDLocal1033This feature was run from the CD but will be installed on the local hard drive.01948325229IDS_UITEXT_FeatureWasOnNetworkInstalled1033This feature was run from the network but will be installed when required.01948325229IDS_UITEXT_FeatureWasOnNetworkLocal1033This feature was run from the network but will be installed on the local hard drive.01948325229IDS_UITEXT_FeatureWillBeUninstalled1033This feature will be uninstalled completely, and you won't be able to run it from CD.01948325229IDS_UITEXT_Folder1033Fldr|New Folder01948325229IDS_UITEXT_GB1033GB01948325229IDS_UITEXT_KB1033KB01948325229IDS_UITEXT_MB1033MB01948325229IDS_UITEXT_Required1033Required01948325229IDS_UITEXT_TimeRemaining1033Time remaining: {[1] min }{[2] sec}01948325229IDS_UITEXT_Volume1033Volume01948325229IDS__AgreeToLicense_01033I &do not accept the terms in the license agreement01948325229IDS__AgreeToLicense_11033I &accept the terms in the license agreement01948325229IDS__DatabaseFolder_ChangeFolder1033Click Next to install to this folder, or click Change to install to a different folder.01948325229IDS__DatabaseFolder_DatabaseDir1033[DATABASEDIR]01948325229IDS__DatabaseFolder_DatabaseFolder1033{&MSSansBold8}Database Folder01948325229IDS__DestinationFolder_Change1033&Change...01948325229IDS__DestinationFolder_ChangeFolder1033Click Next to install to this folder, or click Change to install to a different folder.01948325229IDS__DestinationFolder_DestinationFolder1033{&MSSansBold8}Destination Folder01948325229IDS__DestinationFolder_InstallTo1033Install [ProductName] to:01948325229IDS__DisplayName_Custom1033Custom01948325229IDS__DisplayName_Minimal1033Minimal01948325229IDS__DisplayName_Typical1033Typical01948325229IDS__IsAdminInstallBrowse_11103301948325229IDS__IsAdminInstallBrowse_4103301948325229IDS__IsAdminInstallBrowse_8103301948325229IDS__IsAdminInstallBrowse_BrowseDestination1033Browse to the destination folder.01948325229IDS__IsAdminInstallBrowse_ChangeDestination1033{&MSSansBold8}Change Current Destination Folder01948325229IDS__IsAdminInstallBrowse_CreateFolder1033Create new folder|01948325229IDS__IsAdminInstallBrowse_FolderName1033&Folder name:01948325229IDS__IsAdminInstallBrowse_LookIn1033&Look in:01948325229IDS__IsAdminInstallBrowse_UpOneLevel1033Up one level|01948325229IDS__IsAdminInstallPointWelcome_ServerImage1033The InstallShield(R) Wizard will create a server image of [ProductName] at a specified network location. To continue, click Next.01948325229IDS__IsAdminInstallPointWelcome_Wizard1033{&TahomaBold10}Welcome to the InstallShield Wizard for [ProductName]01948325229IDS__IsAdminInstallPoint_Change1033&Change...01948325229IDS__IsAdminInstallPoint_EnterNetworkLocation1033Enter the network location or click Change to browse to a location. Click Install to create a server image of [ProductName] at the specified network location or click Cancel to exit the wizard.01948325229IDS__IsAdminInstallPoint_Install1033&Install01948325229IDS__IsAdminInstallPoint_NetworkLocation1033&Network location:01948325229IDS__IsAdminInstallPoint_NetworkLocationFormatted1033{&MSSansBold8}Network Location01948325229IDS__IsAdminInstallPoint_SpecifyNetworkLocation1033Specify a network location for the server image of the product.01948325229IDS__IsBrowseButton1033&Browse...01948325229IDS__IsBrowseFolderDlg_11103301948325229IDS__IsBrowseFolderDlg_4103301948325229IDS__IsBrowseFolderDlg_8103301948325229IDS__IsBrowseFolderDlg_BrowseDestFolder1033Browse to the destination folder.01948325229IDS__IsBrowseFolderDlg_ChangeCurrentFolder1033{&MSSansBold8}Change Current Destination Folder01948325229IDS__IsBrowseFolderDlg_CreateFolder1033Create New Folder|01948325229IDS__IsBrowseFolderDlg_FolderName1033&Folder name:01948325229IDS__IsBrowseFolderDlg_LookIn1033&Look in:01948325229IDS__IsBrowseFolderDlg_OK1033OK01948325229IDS__IsBrowseFolderDlg_UpOneLevel1033Up One Level|01948325229IDS__IsBrowseForAccount1033Browse for a User Account01948325229IDS__IsBrowseGroup1033Select a Group01948325229IDS__IsBrowseUsernameTitle1033Select a User Name01948325229IDS__IsCancelDlg_ConfirmCancel1033Are you sure you want to cancel [ProductName] installation?01948325229IDS__IsCancelDlg_No1033&No01948325229IDS__IsCancelDlg_Yes1033&Yes01948325229IDS__IsConfirmPassword1033Con&firm password:01948325229IDS__IsCreateNewUserTitle1033New User Information01948325229IDS__IsCreateUserBrowse1033N&ew User Information...01948325229IDS__IsCustomSelectionDlg_Change1033&Change...01948325229IDS__IsCustomSelectionDlg_ClickFeatureIcon1033Click on an icon in the list below to change how a feature is installed.01948325229IDS__IsCustomSelectionDlg_CustomSetup1033{&MSSansBold8}Custom Setup01948325229IDS__IsCustomSelectionDlg_FeatureDescription1033Feature Description01948325229IDS__IsCustomSelectionDlg_FeaturePath1033<selected feature path>01948325229IDS__IsCustomSelectionDlg_FeatureSize1033Feature size01948325229IDS__IsCustomSelectionDlg_Help1033&Help01948325229IDS__IsCustomSelectionDlg_InstallTo1033Install to:01948325229IDS__IsCustomSelectionDlg_MultilineDescription1033Multiline description of the currently selected item01948325229IDS__IsCustomSelectionDlg_SelectFeatures1033Select the program features you want installed.01948325229IDS__IsCustomSelectionDlg_Space1033&Space01948325229IDS__IsDiskSpaceDlg_DiskSpace1033Disk space required for the installation exceeds available disk space.01948325229IDS__IsDiskSpaceDlg_HighlightedVolumes1033The highlighted volumes do not have enough disk space available for the currently selected features. You can remove files from the highlighted volumes, choose to install fewer features onto local drives, or select different destination drives.01948325229IDS__IsDiskSpaceDlg_Numbers1033{120}{70}{70}{70}{70}01948325229IDS__IsDiskSpaceDlg_OK1033OK01948325229IDS__IsDiskSpaceDlg_OutOfDiskSpace1033{&MSSansBold8}Out of Disk Space01948325229IDS__IsDomainOrServer1033&Domain or server:01948325229IDS__IsErrorDlg_Abort1033&Abort01948325229IDS__IsErrorDlg_ErrorText1033<error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here><error text goes here>01948325229IDS__IsErrorDlg_Ignore1033&Ignore01948325229IDS__IsErrorDlg_InstallerInfo1033[ProductName] Installer Information01948325229IDS__IsErrorDlg_NO1033&No01948325229IDS__IsErrorDlg_OK1033&OK01948325229IDS__IsErrorDlg_Retry1033&Retry01948325229IDS__IsErrorDlg_Yes1033&Yes01948325229IDS__IsExitDialog_Finish1033&Finish01948325229IDS__IsExitDialog_InstallSuccess1033The InstallShield Wizard has successfully installed [ProductName]. Click Finish to exit the wizard.01948325229IDS__IsExitDialog_LaunchProgram1033Launch the program01948325229IDS__IsExitDialog_ShowReadMe1033Show the readme file01948325229IDS__IsExitDialog_UninstallSuccess1033The InstallShield Wizard has successfully uninstalled [ProductName]. Click Finish to exit the wizard.01948325229IDS__IsExitDialog_Update_InternetConnection1033Your Internet connection can be used to make sure that you have the latest updates.01948325229IDS__IsExitDialog_Update_PossibleUpdates1033Some program files might have been updated since you purchased your copy of [ProductName].01948325229IDS__IsExitDialog_Update_SetupFinished1033Setup has finished installing [ProductName].01948325229IDS__IsExitDialog_Update_YesCheckForUpdates1033&Yes, check for program updates (Recommended) after the setup completes.01948325229IDS__IsExitDialog_WizardCompleted1033{&TahomaBold10}InstallShield Wizard Completed01948325229IDS__IsFatalError_ClickFinish1033Click Finish to exit the wizard.01948325229IDS__IsFatalError_Finish1033&Finish01948325229IDS__IsFatalError_KeepOrRestore1033You can either keep any existing installed elements on your system to continue this installation at a later time or you can restore your system to its original state prior to the installation.01948325229IDS__IsFatalError_NotModified1033Your system has not been modified. To complete installation at another time, please run setup again.01948325229IDS__IsFatalError_RestoreOrContinueLater1033Click Restore or Continue Later to exit the wizard.01948325229IDS__IsFatalError_WizardCompleted1033{&TahomaBold10}InstallShield Wizard Completed01948325229IDS__IsFatalError_WizardInterrupted1033The wizard was interrupted before [ProductName] could be completely installed.01948325229IDS__IsFeatureDetailsDlg_DiskSpaceRequirements1033{&MSSansBold8}Disk Space Requirements01948325229IDS__IsFeatureDetailsDlg_Numbers1033{120}{70}{70}{70}{70}01948325229IDS__IsFeatureDetailsDlg_OK1033OK01948325229IDS__IsFeatureDetailsDlg_SpaceRequired1033The disk space required for the installation of the selected features.01948325229IDS__IsFeatureDetailsDlg_VolumesTooSmall1033The highlighted volumes do not have enough disk space available for the currently selected features. You can remove files from the highlighted volumes, choose to install fewer features onto local drives, or select different destination drives.01948325229IDS__IsFilesInUse_ApplicationsUsingFiles1033The following applications are using files that need to be updated by this setup. Close these applications and click Retry to continue.01948325229IDS__IsFilesInUse_Exit1033&Exit01948325229IDS__IsFilesInUse_FilesInUse1033{&MSSansBold8}Files in Use01948325229IDS__IsFilesInUse_FilesInUseMessage1033Some files that need to be updated are currently in use.01948325229IDS__IsFilesInUse_Ignore1033&Ignore01948325229IDS__IsFilesInUse_Retry1033&Retry01948325229IDS__IsGroup1033&Group:01948325229IDS__IsGroupLabel1033Gr&oup:01948325229IDS__IsInitDlg_1103301948325229IDS__IsInitDlg_2103301948325229IDS__IsInitDlg_PreparingWizard1033[ProductName] Setup is preparing the InstallShield Wizard which will guide you through the program setup process. Please wait.01948325229IDS__IsInitDlg_WelcomeWizard1033{&TahomaBold10}Welcome to the InstallShield Wizard for [ProductName]01948325229IDS__IsLicenseDlg_LicenseAgreement1033{&MSSansBold8}License Agreement01948325229IDS__IsLicenseDlg_ReadLicenseAgreement1033Please read the following license agreement carefully.01948325229IDS__IsLogonInfoDescription1033Specify the user name and password of the user account that will logon to use this application. The user account must be in the form DOMAIN\Username.01948325229IDS__IsLogonInfoTitle1033{&MSSansBold8}Logon Information01948325229IDS__IsLogonInfoTitleDescription1033Specify a user name and password01948325229IDS__IsLogonNewUserDescription1033Select the button below to specify information about a new user that will be created during the installation.01948325229IDS__IsMaintenanceDlg_ChangeFeatures1033Change which program features are installed. This option displays the Custom Selection dialog in which you can change the way features are installed.01948325229IDS__IsMaintenanceDlg_MaitenanceOptions1033Modify, repair, or remove the program.01948325229IDS__IsMaintenanceDlg_Modify1033{&MSSansBold8}&Modify01948325229IDS__IsMaintenanceDlg_ProgramMaintenance1033{&MSSansBold8}Program Maintenance01948325229IDS__IsMaintenanceDlg_Remove1033{&MSSansBold8}&Remove01948325229IDS__IsMaintenanceDlg_RemoveProductName1033Remove [ProductName] from your computer.01948325229IDS__IsMaintenanceDlg_Repair1033{&MSSansBold8}Re&pair01948325229IDS__IsMaintenanceDlg_RepairMessage1033Repair installation errors in the program. This option fixes missing or corrupt files, shortcuts, and registry entries.01948325229IDS__IsMaintenanceWelcome_MaintenanceOptionsDescription1033The InstallShield(R) Wizard will allow you to modify, repair, or remove [ProductName]. To continue, click Next.01948325229IDS__IsMaintenanceWelcome_WizardWelcome1033{&TahomaBold10}Welcome to the InstallShield Wizard for [ProductName]01948325229IDS__IsMsiRMFilesInUse_ApplicationsUsingFiles1033The following applications are using files that need to be updated by this setup.01948325229IDS__IsMsiRMFilesInUse_CloseRestart1033Automatically close and attempt to restart applications.01948325229IDS__IsMsiRMFilesInUse_RebootAfter1033Do not close applications. (A reboot will be required.)01948325229IDS__IsPatchDlg_PatchClickUpdate1033The InstallShield(R) Wizard will install the Patch for [ProductName] on your computer. To continue, click Update.01948325229IDS__IsPatchDlg_PatchWizard1033[ProductName] Patch - InstallShield Wizard01948325229IDS__IsPatchDlg_Update1033&Update >01948325229IDS__IsPatchDlg_WelcomePatchWizard1033{&TahomaBold10}Welcome to the Patch for [ProductName]01948325229IDS__IsProgressDlg_2103301948325229IDS__IsProgressDlg_Hidden1033(Hidden for now)01948325229IDS__IsProgressDlg_HiddenTimeRemaining1033)Hidden for now)Estimated time remaining:01948325229IDS__IsProgressDlg_InstallingProductName1033{&MSSansBold8}Installing [ProductName]01948325229IDS__IsProgressDlg_ProgressDone1033Progress done01948325229IDS__IsProgressDlg_SecHidden1033(Hidden for now)Sec.01948325229IDS__IsProgressDlg_Status1033Status:01948325229IDS__IsProgressDlg_Uninstalling1033{&MSSansBold8}Uninstalling [ProductName]01948325229IDS__IsProgressDlg_UninstallingFeatures1033The program features you selected are being uninstalled.01948325229IDS__IsProgressDlg_UninstallingFeatures21033The program features you selected are being installed.01948325229IDS__IsProgressDlg_WaitUninstall1033Please wait while the InstallShield Wizard uninstalls [ProductName]. This may take several minutes.01948325229IDS__IsProgressDlg_WaitUninstall21033Please wait while the InstallShield Wizard installs [ProductName]. This may take several minutes.01948325229IDS__IsReadmeDlg_Cancel1033&Cancel01948325229IDS__IsReadmeDlg_PleaseReadInfo1033Please read the following readme information carefully.01948325229IDS__IsReadmeDlg_ReadMeInfo1033{&MSSansBold8}Readme Information01948325229IDS__IsRegisterUserDlg_16103301948325229IDS__IsRegisterUserDlg_Anyone1033&Anyone who uses this computer (all users)01948325229IDS__IsRegisterUserDlg_CustomerInformation1033{&MSSansBold8}Customer Information01948325229IDS__IsRegisterUserDlg_InstallFor1033Install this application for:01948325229IDS__IsRegisterUserDlg_OnlyMe1033Only for &me ([USERNAME])01948325229IDS__IsRegisterUserDlg_Organization1033&Organization:01948325229IDS__IsRegisterUserDlg_PleaseEnterInfo1033Please enter your information.01948325229IDS__IsRegisterUserDlg_SerialNumber1033&Serial Number:01948325229IDS__IsRegisterUserDlg_Tahoma501033{\Tahoma8}{50}01948325229IDS__IsRegisterUserDlg_Tahoma801033{\Tahoma8}{80}01948325229IDS__IsRegisterUserDlg_UserName1033&User Name:01948325229IDS__IsResumeDlg_ResumeSuspended1033The InstallShield(R) Wizard will complete the suspended installation of [ProductName] on your computer. To continue, click Next.01948325229IDS__IsResumeDlg_Resuming1033{&TahomaBold10}Resuming the InstallShield Wizard for [ProductName]01948325229IDS__IsResumeDlg_WizardResume1033The InstallShield(R) Wizard will complete the installation of [ProductName] on your computer. To continue, click Next.01948325229IDS__IsSelectDomainOrServer1033Select a Domain or Server01948325229IDS__IsSelectDomainUserInstructions1033Use the browse buttons to select a domain\server and a user name.01948325229IDS__IsSetupComplete_ShowMsiLog1033Show the Windows Installer log01948325229IDS__IsSetupTypeMinDlg_13103301948325229IDS__IsSetupTypeMinDlg_AllFeatures1033All program features will be installed. (Requires the most disk space.)01948325229IDS__IsSetupTypeMinDlg_ChooseFeatures1033Choose which program features you want installed and where they will be installed. Recommended for advanced users.01948325229IDS__IsSetupTypeMinDlg_ChooseSetupType1033Choose the setup type that best suits your needs.01948325229IDS__IsSetupTypeMinDlg_Complete1033{&MSSansBold8}&Complete01948325229IDS__IsSetupTypeMinDlg_Custom1033{&MSSansBold8}Cu&stom01948325229IDS__IsSetupTypeMinDlg_Minimal1033{&MSSansBold8}&Minimal01948325229IDS__IsSetupTypeMinDlg_MinimumFeatures1033Minimum required features will be installed.01948325229IDS__IsSetupTypeMinDlg_SelectSetupType1033Please select a setup type.01948325229IDS__IsSetupTypeMinDlg_SetupType1033{&MSSansBold8}Setup Type01948325229IDS__IsSetupTypeMinDlg_Typical1033{&MSSansBold8}&Typical01948325229IDS__IsUserExit_ClickFinish1033Click Finish to exit the wizard.01948325229IDS__IsUserExit_Finish1033&Finish01948325229IDS__IsUserExit_KeepOrRestore1033You can either keep any existing installed elements on your system to continue this installation at a later time or you can restore your system to its original state prior to the installation.01948325229IDS__IsUserExit_NotModified1033Your system has not been modified. To install this program at a later time, please run the installation again.01948325229IDS__IsUserExit_RestoreOrContinue1033Click Restore or Continue Later to exit the wizard.01948325229IDS__IsUserExit_WizardCompleted1033{&TahomaBold10}InstallShield Wizard Completed01948325229IDS__IsUserExit_WizardInterrupted1033The wizard was interrupted before [ProductName] could be completely installed.01948325229IDS__IsUserNameLabel1033&User name:01948325229IDS__IsVerifyReadyDlg_BackOrCancel1033If you want to review or change any of your installation settings, click Back. Click Cancel to exit the wizard.01948325229IDS__IsVerifyReadyDlg_ClickInstall1033Click Install to begin the installation.01948325229IDS__IsVerifyReadyDlg_Company1033Company: [COMPANYNAME]01948325229IDS__IsVerifyReadyDlg_CurrentSettings1033Current Settings:01948325229IDS__IsVerifyReadyDlg_DestFolder1033Destination Folder:01948325229IDS__IsVerifyReadyDlg_Install1033&Install01948325229IDS__IsVerifyReadyDlg_Installdir1033[INSTALLDIR]01948325229IDS__IsVerifyReadyDlg_ModifyReady1033{&MSSansBold8}Ready to Modify the Program01948325229IDS__IsVerifyReadyDlg_ReadyInstall1033{&MSSansBold8}Ready to Install the Program01948325229IDS__IsVerifyReadyDlg_ReadyRepair1033{&MSSansBold8}Ready to Repair the Program01948325229IDS__IsVerifyReadyDlg_SelectedSetupType1033[SelectedSetupType]01948325229IDS__IsVerifyReadyDlg_Serial1033Serial: [ISX_SERIALNUM]01948325229IDS__IsVerifyReadyDlg_SetupType1033Setup Type:01948325229IDS__IsVerifyReadyDlg_UserInfo1033User Information:01948325229IDS__IsVerifyReadyDlg_UserName1033Name: [USERNAME]01948325229IDS__IsVerifyReadyDlg_WizardReady1033The wizard is ready to begin installation.01948325229IDS__IsVerifyRemoveAllDlg_ChoseRemoveProgram1033You have chosen to remove the program from your system.01948325229IDS__IsVerifyRemoveAllDlg_ClickBack1033If you want to review or change any settings, click Back.01948325229IDS__IsVerifyRemoveAllDlg_ClickRemove1033Click Remove to remove [ProductName] from your computer. After removal, this program will no longer be available for use.01948325229IDS__IsVerifyRemoveAllDlg_Remove1033&Remove01948325229IDS__IsVerifyRemoveAllDlg_RemoveProgram1033{&MSSansBold8}Remove the Program01948325229IDS__IsWelcomeDlg_InstallProductName1033The InstallShield(R) Wizard will install [ProductName] on your computer. To continue, click Next.01948325229IDS__IsWelcomeDlg_WarningCopyright1033WARNING: This program is protected by copyright law and international treaties.01948325229IDS__IsWelcomeDlg_WelcomeProductName1033{&TahomaBold10}Welcome to the InstallShield Wizard for [ProductName]01948325229IDS__TargetReq_DESC_COLOR1033The color settings of your system are not adequate for running [ProductName].01948325229IDS__TargetReq_DESC_OS1033The operating system is not adequate for running [ProductName].01948325229IDS__TargetReq_DESC_PROCESSOR1033The processor is not adequate for running [ProductName].01948325229IDS__TargetReq_DESC_RAM1033The amount of RAM is not adequate for running [ProductName].01948325229IDS__TargetReq_DESC_RESOLUTION1033The screen resolution is not adequate for running [ProductName].01948325229IIDS_UITEXT_FeatureUninstalled1033This feature will remain uninstalled.01948325229
+ + + Name + Value + +
UniqueIdEDA44451-FC96-43B9-B201-2E02D59467E7
+ + + UpgradedImage_ + Name + MsiPath + Order + Flags + IgnoreMissingFiles +
+ + + UpgradeItem + ObjectSetupPath + ISReleaseFlags + ISAttributes +
+ + + Name + MsiPath + Family +
+ + + Directory_ + Name + Value +
+ + + File_ + Name + Value +
+ + + Name + Value +
+ + + Registry_ + Name + Value +
+ + + ISRelease_ + ISProductConfiguration_ + Name + Value +
+ + + Shortcut_ + Name + Value +
+ + + ISXmlElement + ISXmlFile_ + ISXmlElement_Parent + XPath + Content + ISAttributes +
+ + + ISXmlElementAttrib + ISXmlElement_ + Name + Value + ISAttributes +
+ + + ISXmlFile + FileName + Component_ + Directory + ISAttributes + SelectionNamespaces + Encoding +
+ + + Signature_ + Parent + Element + Attribute + ISAttributes +
+ + + Name + Data + ISBuildSourcePath + ISIconIndex + +
ARPPRODUCTICON.exe<ISProductFolder>\redist\Language Independent\OS Independent\setupicon.ico0
+ + + IniFile + FileName + DirProperty + Section + Key + Value + Action + Component_ +
+ + + Signature_ + FileName + Section + Key + Field + Type +
+ + + Action + Condition + Sequence + ISComments + ISAttributes +
AllocateRegistrySpaceNOT Installed1550AllocateRegistrySpace + AppSearch400AppSearch + BindImage4300BindImage + CCPSearchCCP_TEST500CCPSearch + CostFinalize1000CostFinalize + CostInitialize800CostInitialize + CreateFolders3700CreateFolders + CreateShortcuts4500CreateShortcuts + DeleteServicesVersionNT2000DeleteServices + DuplicateFiles4210DuplicateFiles + FileCost900FileCost + FindRelatedProductsNOT ISSETUPDRIVEN420FindRelatedProducts + ISPreventDowngradeISFOUNDNEWERPRODUCTVERSION450ISPreventDowngrade + ISRunSetupTypeAddLocalEventNot Installed And Not ISRUNSETUPTYPEADDLOCALEVENT1050ISRunSetupTypeAddLocalEvent + InstallFiles4000InstallFiles + InstallFinalize6600InstallFinalize + InstallInitialize1501InstallInitialize + InstallODBC5400InstallODBC + InstallServicesVersionNT5800InstallServices + InstallValidate1400InstallValidate + IsolateComponents950IsolateComponents + LaunchConditionsNot Installed410LaunchConditions + MigrateFeatureStates1010MigrateFeatureStates + MoveFiles3800MoveFiles + MsiConfigureServicesVersionMsi >= "5.00"5850MSI5 MsiConfigureServices + MsiPublishAssemblies6250MsiPublishAssemblies + MsiUnpublishAssemblies1750MsiUnpublishAssemblies + PatchFiles4090PatchFiles + ProcessComponents1600ProcessComponents + PublishComponents6200PublishComponents + PublishFeatures6300PublishFeatures + PublishProduct6400PublishProduct + RMCCPSearchNot CCP_SUCCESS And CCP_TEST600RMCCPSearch + RegisterClassInfo4600RegisterClassInfo + RegisterComPlus5700RegisterComPlus + RegisterExtensionInfo4700RegisterExtensionInfo + RegisterFonts5300RegisterFonts + RegisterMIMEInfo4900RegisterMIMEInfo + RegisterProduct6100RegisterProduct + RegisterProgIdInfo4800RegisterProgIdInfo + RegisterTypeLibraries5500RegisterTypeLibraries + RegisterUser6000RegisterUser + RemoveDuplicateFiles3400RemoveDuplicateFiles + RemoveEnvironmentStrings3300RemoveEnvironmentStrings + RemoveExistingProducts1410RemoveExistingProducts + RemoveFiles3500RemoveFiles + RemoveFolders3600RemoveFolders + RemoveIniValues3100RemoveIniValues + RemoveODBC2400RemoveODBC + RemoveRegistryValues2600RemoveRegistryValues + RemoveShortcuts3200RemoveShortcuts + ResolveSourceNot Installed850ResolveSource + ScheduleRebootISSCHEDULEREBOOT6410ScheduleReboot + SelfRegModules5600SelfRegModules + SelfUnregModules2200SelfUnregModules + SetARPINSTALLLOCATION1100SetARPINSTALLLOCATION + SetAllUsersProfileNTVersionNT = 400970 + SetODBCFolders1200SetODBCFolders + StartServicesVersionNT5900StartServices + StopServicesVersionNT1900StopServices + UnpublishComponents1700UnpublishComponents + UnpublishFeatures1800UnpublishFeatures + UnregisterClassInfo2700UnregisterClassInfo + UnregisterComPlus2100UnregisterComPlus + UnregisterExtensionInfo2800UnregisterExtensionInfo + UnregisterFonts2500UnregisterFonts + UnregisterMIMEInfo3000UnregisterMIMEInfo + UnregisterProgIdInfo2900UnregisterProgIdInfo + UnregisterTypeLibraries2300UnregisterTypeLibraries + ValidateProductID700ValidateProductID + WriteEnvironmentStrings5200WriteEnvironmentStrings + WriteIniValues5100WriteIniValues + WriteRegistryValues5000WriteRegistryValues + setAllUsersProfile2KVersionNT >= 500980 + setUserProfileNTVersionNT960 +
+ + + Property + Value + + + + + + + + + + + + + + + + + + + + + + +
ActiveLanguage1033Comments + CurrentMedia +UwBpAG4AZwBsAGUASQBtAGEAZwBlAAEARQB4AHAAcgBlAHMAcwA= + EnableSwidtag1ISCompilerOption_CompileBeforeBuild1ISCompilerOption_Debug0ISCompilerOption_IncludePath + ISCompilerOption_LibraryPath + ISCompilerOption_MaxErrors50ISCompilerOption_MaxWarnings50ISCompilerOption_OutputPath<ISProjectDataFolder>\Script FilesISCompilerOption_PreProcessor_ISSCRIPT_NEW_STYLE_DLG_DEFSISCompilerOption_WarningLevel3ISCompilerOption_WarningsAsErrors1ISThemeInstallShield Blue.themeISUSLock{F078D3AD-B84F-4D60-9E14-1EFAB70FEFEC}ISUSSignature{D837593B-D207-4D86-AB33-C4AE964A5ABF}Limited1LockPermissionMode1MsiExecCmdLineOptions + MsiLogFile + OnUpgrade0Owner + PatchFamilyMyPatchFamily1PatchSequence1.0.0SaveAsSchema + SccEnabled0SccPath + SchemaVersion773TypeMSIE
+ + + Action + Condition + Sequence + ISComments + ISAttributes +
AppSearch400AppSearch + CCPSearchCCP_TEST500CCPSearch + CostFinalize1000CostFinalize + CostInitialize800CostInitialize + ExecuteAction1300ExecuteAction + FileCost900FileCost + FindRelatedProducts430FindRelatedProducts + ISPreventDowngradeISFOUNDNEWERPRODUCTVERSION450ISPreventDowngrade + InstallWelcomeNot Installed1210InstallWelcome + IsolateComponents950IsolateComponents + LaunchConditionsNot Installed410LaunchConditions + MaintenanceWelcomeInstalled And Not RESUME And Not Preselected And Not PATCH1230MaintenanceWelcome + MigrateFeatureStates1200MigrateFeatureStates + PatchWelcomeInstalled And PATCH And Not IS_MAJOR_UPGRADE1205Patch Panel + RMCCPSearchNot CCP_SUCCESS And CCP_TEST600RMCCPSearch + ResolveSourceNot Installed990ResolveSource + SetAllUsersProfileNTVersionNT = 400970 + SetupCompleteError-3SetupCompleteError + SetupCompleteSuccess-1SetupCompleteSuccess + SetupInitialization420SetupInitialization + SetupInterrupted-2SetupInterrupted + SetupProgress1240SetupProgress + SetupResumeInstalled And (RESUME Or Preselected) And Not PATCH1220SetupResume + ValidateProductID700ValidateProductID + setAllUsersProfile2KVersionNT >= 500980 + setUserProfileNTVersionNT960 +
+ + + Component_Shared + Component_Application +
+ + + Condition + Description +
+ + + Property + Order + Value + Text +
+ + + Property + Order + Value + Text + Binary_ +
+ + + LockObject + Table + Domain + User + Permission +
+ + + ContentType + Extension_ + CLSID +
+ + + DiskId + LastSequence + DiskPrompt + Cabinet + VolumeLabel + Source +
+ + + FileKey + Component_ + SourceName + DestName + SourceFolder + DestFolder + Options +
+ + + Component_ + Feature_ + File_Manifest + File_Application + Attributes +
+ + + Component_ + Name + Value +
+ + + DigitalCertificate + CertData +
+ + + Table + SignObject + DigitalCertificate_ + Hash +
+ + + Component + Flags + Sequence + ReferenceComponents +
+ + + MsiEmbeddedChainer + Condition + CommandLine + Source + Type +
+ + + MsiEmbeddedUI + FileName + Attributes + MessageFilter + Data + ISBuildSourcePath +
+ + + File_ + Options + HashPart1 + HashPart2 + HashPart3 + HashPart4 +
+ + + MsiLockPermissionsEx + LockObject + Table + SDDLText + Condition +
+ + + PackageCertificate + DigitalCertificate_ +
+ + + PatchCertificate + DigitalCertificate_ +
+ + + PatchConfiguration_ + Company + Property + Value +
+ + + File_ + Assembly_ +
+ + + Assembly + Name + Value +
+ + + PatchConfiguration_ + PatchFamily + Target + Sequence + Supersede +
+ + + MsiServiceConfig + Name + Event + ConfigType + Argument + Component_ +
+ + + MsiServiceConfigFailureActions + Name + Event + ResetPeriod + RebootMessage + Command + Actions + DelayActions + Component_ +
+ + + MsiShortcutProperty + Shortcut_ + PropertyKey + PropVariantValue +
+ + + Driver_ + Attribute + Value +
+ + + DataSource + Component_ + Description + DriverDescription + Registration +
+ + + Driver + Component_ + Description + File_ + File_Setup +
+ + + DataSource_ + Attribute + Value +
+ + + Translator + Component_ + Description + File_ + File_Setup +
+ + + File_ + Sequence + PatchSize + Attributes + Header + StreamRef_ + ISBuildSourcePath +
+ + + PatchId + Media_ +
+ + + ProgId + ProgId_Parent + Class_ + Description + Icon_ + IconIndex + ISAttributes +
+ + + Property + Value + ISComments +
ALLUSERS1 + ARPINSTALLLOCATION + ARPPRODUCTICONARPPRODUCTICON.exe + ARPSIZE + AgreeToLicenseNo + ApplicationUsersAllUsers + DWUSINTERVAL30 + DWUSLINKCE8C17CF3EBB37A8AEACF72F8EBB978FCE4CF78F49DCB7FFCE8B80FF8E1B87CFBEABE7FF9EAC + DefaultUIFontExpressDefault + DialogCaptionInstallShield for Windows Installer + DiskPrompt[1] + DiskSerial1234-5678 + DisplayNameCustom##IDS__DisplayName_Custom## + DisplayNameMinimal##IDS__DisplayName_Minimal## + DisplayNameTypical##IDS__DisplayName_Typical## + Display_IsBitmapDlg1 + ErrorDialogSetupError + INSTALLLEVEL100 + ISCHECKFORPRODUCTUPDATES1 + ISENABLEDWUSFINISHDIALOG + ISSHOWMSILOG + ISVROOT_PORT_NO0 + IS_COMPLUS_PROGRESSTEXT_COST##IDS_COMPLUS_PROGRESSTEXT_COST## + IS_COMPLUS_PROGRESSTEXT_INSTALL##IDS_COMPLUS_PROGRESSTEXT_INSTALL## + IS_COMPLUS_PROGRESSTEXT_UNINSTALL##IDS_COMPLUS_PROGRESSTEXT_UNINSTALL## + IS_PREVENT_DOWNGRADE_EXIT##IDS_PREVENT_DOWNGRADE_EXIT## + IS_PROGMSG_TEXTFILECHANGS_REPLACE##IDS_PROGMSG_TEXTFILECHANGS_REPLACE## + IS_PROGMSG_XML_COSTING##IDS_PROGMSG_XML_COSTING## + IS_PROGMSG_XML_CREATE_FILE##IDS_PROGMSG_XML_CREATE_FILE## + IS_PROGMSG_XML_FILES##IDS_PROGMSG_XML_FILES## + IS_PROGMSG_XML_REMOVE_FILE##IDS_PROGMSG_XML_REMOVE_FILE## + IS_PROGMSG_XML_ROLLBACK_FILES##IDS_PROGMSG_XML_ROLLBACK_FILES## + IS_PROGMSG_XML_UPDATE_FILE##IDS_PROGMSG_XML_UPDATE_FILE## + IS_SQLSERVER_AUTHENTICATION0 + IS_SQLSERVER_DATABASE + IS_SQLSERVER_PASSWORD + IS_SQLSERVER_SERVER + IS_SQLSERVER_USERNAMEsa + InstallChoiceAR + LAUNCHPROGRAM1 + LAUNCHREADME1 + Manufacturer##COMPANY_NAME## + PIDKEY + PIDTemplate12345<###-%%%%%%%>@@@@@ + PROGMSG_IIS_CREATEAPPPOOL##IDS_PROGMSG_IIS_CREATEAPPPOOL## + PROGMSG_IIS_CREATEAPPPOOLS##IDS_PROGMSG_IIS_CREATEAPPPOOLS## + PROGMSG_IIS_CREATEVROOT##IDS_PROGMSG_IIS_CREATEVROOT## + PROGMSG_IIS_CREATEVROOTS##IDS_PROGMSG_IIS_CREATEVROOTS## + PROGMSG_IIS_CREATEWEBSERVICEEXTENSION##IDS_PROGMSG_IIS_CREATEWEBSERVICEEXTENSION## + PROGMSG_IIS_CREATEWEBSERVICEEXTENSIONS##IDS_PROGMSG_IIS_CREATEWEBSERVICEEXTENSIONS## + PROGMSG_IIS_CREATEWEBSITE##IDS_PROGMSG_IIS_CREATEWEBSITE## + PROGMSG_IIS_CREATEWEBSITES##IDS_PROGMSG_IIS_CREATEWEBSITES## + PROGMSG_IIS_EXTRACT##IDS_PROGMSG_IIS_EXTRACT## + PROGMSG_IIS_EXTRACTDONE##IDS_PROGMSG_IIS_EXTRACTDONE## + PROGMSG_IIS_EXTRACTDONEz##IDS_PROGMSG_IIS_EXTRACTDONE## + PROGMSG_IIS_EXTRACTzDONE##IDS_PROGMSG_IIS_EXTRACTDONE## + PROGMSG_IIS_REMOVEAPPPOOL##IDS_PROGMSG_IIS_REMOVEAPPPOOL## + PROGMSG_IIS_REMOVEAPPPOOLS##IDS_PROGMSG_IIS_REMOVEAPPPOOLS## + PROGMSG_IIS_REMOVESITE##IDS_PROGMSG_IIS_REMOVESITE## + PROGMSG_IIS_REMOVEVROOT##IDS_PROGMSG_IIS_REMOVEVROOT## + PROGMSG_IIS_REMOVEVROOTS##IDS_PROGMSG_IIS_REMOVEVROOTS## + PROGMSG_IIS_REMOVEWEBSERVICEEXTENSION##IDS_PROGMSG_IIS_REMOVEWEBSERVICEEXTENSION## + PROGMSG_IIS_REMOVEWEBSERVICEEXTENSIONS##IDS_PROGMSG_IIS_REMOVEWEBSERVICEEXTENSIONS## + PROGMSG_IIS_REMOVEWEBSITES##IDS_PROGMSG_IIS_REMOVEWEBSITES## + PROGMSG_IIS_ROLLBACKAPPPOOLS##IDS_PROGMSG_IIS_ROLLBACKAPPPOOLS## + PROGMSG_IIS_ROLLBACKVROOTS##IDS_PROGMSG_IIS_ROLLBACKVROOTS## + PROGMSG_IIS_ROLLBACKWEBSERVICEEXTENSIONS##IDS_PROGMSG_IIS_ROLLBACKWEBSERVICEEXTENSIONS## + ProductCode{78D40F86-3053-4990-A1D8-00F51663FC0C} + ProductNameOpaqueMail.Net.ProxyInstaller + ProductVersion1.00.0000 + ProgressType0install + ProgressType1Installing + ProgressType2installed + ProgressType3installs + RebootYesNoYes + ReinstallFileVersiono + ReinstallModeTextomus + ReinstallRepairr + RestartManagerOptionCloseRestart + SERIALNUMBER + SERIALNUMVALSUCCESSRETVAL1 + SecureCustomPropertiesISFOUNDNEWERPRODUCTVERSION;USERNAME;COMPANYNAME;ISX_SERIALNUM;SUPPORTDIR + SetupTypeTypical + UpgradeCode{EF349F97-3D00-47EC-A892-AC6B31BF4CC8} + _IsMaintenanceChange + _IsSetupTypeMinTypical +
+ + + ComponentId + Qualifier + Component_ + AppData + Feature_ +
+ + + Property + Order + Value + X + Y + Width + Height + Text + Help + ISControlId +
AgreeToLicense1No01529115##IDS__AgreeToLicense_0## + AgreeToLicense2Yes0029115##IDS__AgreeToLicense_1## + ApplicationUsers1AllUsers1729014##IDS__IsRegisterUserDlg_Anyone## + ApplicationUsers2OnlyCurrentUser12329014##IDS__IsRegisterUserDlg_OnlyMe## + RestartManagerOption1CloseRestart6933114##IDS__IsMsiRMFilesInUse_CloseRestart## + RestartManagerOption2Reboot62133114##IDS__IsMsiRMFilesInUse_RebootAfter## + _IsMaintenance1Change0029014##IDS__IsMaintenanceDlg_Modify## + _IsMaintenance2Reinstall06029014##IDS__IsMaintenanceDlg_Repair## + _IsMaintenance3Remove012029014##IDS__IsMaintenanceDlg_Remove## + _IsSetupTypeMin1Typical1626414##IDS__IsSetupTypeMinDlg_Typical## + _IsSetupTypeMin2Minimal15126414##IDS__IsSetupTypeMinDlg_Minimal## + _IsSetupTypeMin3Custom19826414##IDS__IsSetupTypeMinDlg_Custom## +
+ + + Signature_ + Root + Key + Name + Type +
+ + + Registry + Root + Key + Name + Value + Component_ + ISAttributes +
+ + + FileKey + Component_ + FileName + DirProperty + InstallMode +
+ + + RemoveIniFile + FileName + DirProperty + Section + Key + Value + Action + Component_ +
+ + + RemoveRegistry + Root + Key + Name + Component_ +
+ + + ReserveKey + Component_ + ReserveFolder + ReserveLocal + ReserveSource +
+ + + SFPCatalog + Catalog + Dependency +
+ + + File_ + Cost +
+ + + ServiceControl + Name + Event + Arguments + Wait + Component_ +
+ + + ServiceInstall + Name + DisplayName + ServiceType + StartType + ErrorControl + LoadOrderGroup + Dependencies + StartName + Password + Arguments + Component_ + Description +
+ + + Shortcut + Directory_ + Name + Component_ + Target + Arguments + Description + Hotkey + Icon_ + IconIndex + ShowCmd + WkDir + DisplayResourceDLL + DisplayResourceId + DescriptionResourceDLL + DescriptionResourceId + ISComments + ISShortcutName + ISAttributes +
+ + + Signature + FileName + MinVersion + MaxVersion + MinSize + MaxSize + MinDate + MaxDate + Languages +
+ + + TextStyle + FaceName + Size + Color + StyleBits + + + + + + + +
Arial8Arial8 + Arial9Arial9 + ArialBlue10Arial1016711680 + ArialBlueStrike10Arial10167116808CourierNew8Courier New8 + CourierNew9Courier New9 + ExpressDefaultTahoma8 + MSGothic9MS Gothic9 + MSSGreySerif8MS Sans Serif88421504 + MSSWhiteSerif8Tahoma816777215 + MSSansBold8Tahoma81MSSansSerif8MS Sans Serif8 + MSSansSerif9MS Sans Serif9 + Tahoma10Tahoma10 + Tahoma8Tahoma8 + Tahoma9Tahoma9 + TahomaBold10Tahoma101TahomaBold8Tahoma81Times8Times New Roman8 + Times9Times New Roman9 + TimesItalic12Times New Roman122TimesItalicBlue10Times New Roman10167116802TimesRed16Times New Roman16255 + VerdanaBold14Verdana131
+ + + LibID + Language + Component_ + Version + Description + Directory_ + Feature_ + Cost +
+ + + Key + Text + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AbsentPath + GB##IDS_UITEXT_GB##KB##IDS_UITEXT_KB##MB##IDS_UITEXT_MB##MenuAbsent##IDS_UITEXT_FeatureNotAvailable##MenuAdvertise##IDS_UITEXT_FeatureInstalledWhenRequired2##MenuAllCD##IDS_UITEXT_FeatureInstalledCD##MenuAllLocal##IDS_UITEXT_FeatureInstalledLocal##MenuAllNetwork##IDS_UITEXT_FeatureInstalledNetwork##MenuCD##IDS_UITEXT_FeatureInstalledCD2##MenuLocal##IDS_UITEXT_FeatureInstalledLocal2##MenuNetwork##IDS_UITEXT_FeatureInstalledNetwork2##NewFolder##IDS_UITEXT_Folder##SelAbsentAbsent##IDS_UITEXT_GB##SelAbsentAdvertise##IDS_UITEXT_FeatureInstalledWhenRequired##SelAbsentCD##IDS_UITEXT_FeatureOnCD##SelAbsentLocal##IDS_UITEXT_FeatureLocal##SelAbsentNetwork##IDS_UITEXT_FeatureNetwork##SelAdvertiseAbsent##IDS_UITEXT_FeatureUnavailable##SelAdvertiseAdvertise##IDS_UITEXT_FeatureInstalledRequired##SelAdvertiseCD##IDS_UITEXT_FeatureOnCD2##SelAdvertiseLocal##IDS_UITEXT_FeatureLocal2##SelAdvertiseNetwork##IDS_UITEXT_FeatureNetwork2##SelCDAbsent##IDS_UITEXT_FeatureWillBeUninstalled##SelCDAdvertise##IDS_UITEXT_FeatureWasCD##SelCDCD##IDS_UITEXT_FeatureRunFromCD##SelCDLocal##IDS_UITEXT_FeatureWasCDLocal##SelChildCostNeg##IDS_UITEXT_FeatureFreeSpace##SelChildCostPos##IDS_UITEXT_FeatureRequiredSpace##SelCostPending##IDS_UITEXT_CompilingFeaturesCost##SelLocalAbsent##IDS_UITEXT_FeatureCompletelyRemoved##SelLocalAdvertise##IDS_UITEXT_FeatureRemovedUnlessRequired##SelLocalCD##IDS_UITEXT_FeatureRemovedCD##SelLocalLocal##IDS_UITEXT_FeatureRemainLocal##SelLocalNetwork##IDS_UITEXT_FeatureRemoveNetwork##SelNetworkAbsent##IDS_UITEXT_FeatureUninstallNoNetwork##SelNetworkAdvertise##IDS_UITEXT_FeatureWasOnNetworkInstalled##SelNetworkLocal##IDS_UITEXT_FeatureWasOnNetworkLocal##SelNetworkNetwork##IDS_UITEXT_FeatureContinueNetwork##SelParentCostNegNeg##IDS_UITEXT_FeatureSpaceFree##SelParentCostNegPos##IDS_UITEXT_FeatureSpaceFree2##SelParentCostPosNeg##IDS_UITEXT_FeatureSpaceFree3##SelParentCostPosPos##IDS_UITEXT_FeatureSpaceFree4##TimeRemaining##IDS_UITEXT_TimeRemaining##VolumeCostAvailable##IDS_UITEXT_Available##VolumeCostDifference##IDS_UITEXT_Differences##VolumeCostRequired##IDS_UITEXT_Required##VolumeCostSize##IDS_UITEXT_DiskSize##VolumeCostVolume##IDS_UITEXT_Volume##bytes##IDS_UITEXT_Bytes##
+ + + UpgradeCode + VersionMin + VersionMax + Language + Attributes + Remove + ActionProperty + ISDisplayName + +
{00000000-0000-0000-0000-000000000000}***ALL_VERSIONS***2ISFOUNDNEWERPRODUCTVERSIONISPreventDowngrade
+ + + Extension_ + Verb + Sequence + Command + Argument +
+ + + Table + Column + Nullable + MinValue + MaxValue + KeyTable + KeyColumn + Category + Set + Description + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ActionTextActionNIdentifierName of action to be described.ActionTextDescriptionYTextLocalized description displayed in progress dialog and log when action is executing.ActionTextTemplateYTemplateOptional localized format template used to format action data records for display during action execution.AdminExecuteSequenceActionNIdentifierName of action to invoke, either in the engine or the handler DLL.AdminExecuteSequenceConditionYConditionOptional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.AdminExecuteSequenceISAttributesYThis is used to store MM Custom Action TypesAdminExecuteSequenceISCommentsYTextAuthor’s comments on this Sequence.AdminExecuteSequenceSequenceY-432767Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.AdminUISequenceActionNIdentifierName of action to invoke, either in the engine or the handler DLL.AdminUISequenceConditionYConditionOptional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.AdminUISequenceISAttributesYThis is used to store MM Custom Action TypesAdminUISequenceISCommentsYTextAuthor’s comments on this Sequence.AdminUISequenceSequenceY-432767Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.AdvtExecuteSequenceActionNIdentifierName of action to invoke, either in the engine or the handler DLL.AdvtExecuteSequenceConditionYConditionOptional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.AdvtExecuteSequenceISAttributesYThis is used to store MM Custom Action TypesAdvtExecuteSequenceISCommentsYTextAuthor’s comments on this Sequence.AdvtExecuteSequenceSequenceY-432767Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.AdvtUISequenceActionNIdentifierName of action to invoke, either in the engine or the handler DLL.AdvtUISequenceConditionYConditionOptional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.AdvtUISequenceISAttributesYThis is used to store MM Custom Action TypesAdvtUISequenceISCommentsYTextAuthor’s comments on this Sequence.AdvtUISequenceSequenceY-432767Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.AppIdActivateAtStorageY01 + AppIdAppIdNGuid + AppIdDllSurrogateYText + AppIdLocalServiceYText + AppIdRemoteServerNameYFormatted + AppIdRunAsInteractiveUserY01 + AppIdServiceParametersYText + AppSearchPropertyNIdentifierThe property associated with a SignatureAppSearchSignature_NISXmlLocator;Signature1IdentifierThe Signature_ represents a unique file signature and is also the foreign key in the Signature, RegLocator, IniLocator, CompLocator and the DrLocator tables.BBControlAttributesY02147483647A 32-bit word that specifies the attribute flags to be applied to this control.BBControlBBControlNIdentifierName of the control. This name must be unique within a billboard, but can repeat on different billboard.BBControlBillboard_NBillboard1IdentifierExternal key to the Billboard table, name of the billboard.BBControlHeightN032767Height of the bounding rectangle of the control.BBControlTextYTextA string used to set the initial text contained within a control (if appropriate).BBControlTypeNIdentifierThe type of the control.BBControlWidthN032767Width of the bounding rectangle of the control.BBControlXN032767Horizontal coordinate of the upper left corner of the bounding rectangle of the control.BBControlYN032767Vertical coordinate of the upper left corner of the bounding rectangle of the control.BillboardActionYIdentifierThe name of an action. The billboard is displayed during the progress messages received from this action.BillboardBillboardNIdentifierName of the billboard.BillboardFeature_NFeature1IdentifierAn external key to the Feature Table. The billboard is shown only if this feature is being installed.BillboardOrderingY032767A positive integer. If there is more than one billboard corresponding to an action they will be shown in the order defined by this column.BinaryDataYBinaryBinary stream. The binary icon data in PE (.DLL or .EXE) or icon (.ICO) format.BinaryISBuildSourcePathYTextFull path to the ICO or EXE file.BinaryNameNIdentifierUnique key identifying the binary data.BindImageFile_NFile1IdentifierThe index into the File table. This must be an executable file.BindImagePathYPathsA list of ; delimited paths that represent the paths to be searched for the import DLLS. The list is usually a list of properties each enclosed within square brackets [] .CCPSearchSignature_NSignature1IdentifierThe Signature_ represents a unique file signature and is also the foreign key in the Signature, RegLocator, IniLocator, CompLocator and the DrLocator tables.CheckBoxPropertyNIdentifierA named property to be tied to the item.CheckBoxValueYFormattedThe value string associated with the item.ClassAppId_YAppId1GuidOptional AppID containing DCOM information for associated application (string GUID).ClassArgumentYFormattedoptional argument for LocalServers.ClassAttributesY32767Class registration attributes.ClassCLSIDNGuidThe CLSID of an OLE factory.ClassComponent_NComponent1IdentifierRequired foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.ClassContextNIdentifierThe numeric server context for this server. CLSCTX_xxxxClassDefInprocHandlerYText1;2;3Optional default inproc handler. Only optionally provided if Context=CLSCTX_LOCAL_SERVER. Typically "ole32.dll" or "mapi32.dll"ClassDescriptionYTextLocalized description for the Class.ClassFeature_NFeature1IdentifierRequired foreign key into the Feature Table, specifying the feature to validate or install in order for the CLSID factory to be operational.ClassFileTypeMaskYTextOptional string containing information for the HKCRthis CLSID) key. If multiple patterns exist, they must be delimited by a semicolon, and numeric subkeys will be generated: 0,1,2...ClassIconIndexY-3276732767Optional icon index.ClassIcon_YIcon1IdentifierOptional foreign key into the Icon Table, specifying the icon file associated with this CLSID. Will be written under the DefaultIcon key.ClassProgId_DefaultYProgId1TextOptional ProgId associated with this CLSID.ComboBoxOrderN132767A positive integer used to determine the ordering of the items within one list. The integers do not have to be consecutive.ComboBoxPropertyNIdentifierA named property to be tied to this item. All the items tied to the same property become part of the same combobox.ComboBoxTextYFormattedThe visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.ComboBoxValueNFormattedThe value string associated with this item. Selecting the line will set the associated property to this value.CompLocatorComponentIdNGuidA string GUID unique to this component, version, and language.CompLocatorSignature_NSignature1IdentifierThe table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table.CompLocatorTypeY01A boolean value that determines if the registry value is a filename or a directory location.ComplusComponent_NComponent1IdentifierForeign key referencing Component that controls the ComPlus component.ComplusExpTypeY032767ComPlus component attributes.ComponentAttributesNRemote execution option, one of irsEnumComponentComponentNIdentifierPrimary key used to identify a particular component record.ComponentComponentIdYGuidA string GUID unique to this component, version, and language.ComponentConditionYConditionA conditional statement that will disable this component if the specified condition evaluates to the 'True' state. If a component is disabled, it will not be installed, regardless of the 'Action' state associated with the component.ComponentDirectory_NDirectory1IdentifierRequired key of a Directory table record. This is actually a property name whose value contains the actual path, set either by the AppSearch action or with the default setting obtained from the Directory table.ComponentISAttributesYThis is used to store Installshield custom properties of a component.ComponentISCommentsYTextUser Comments.ComponentISDotNetInstallerArgsCommitYTextArguments passed to the key file of the component if if implements the .NET Installer classComponentISDotNetInstallerArgsInstallYTextArguments passed to the key file of the component if if implements the .NET Installer classComponentISDotNetInstallerArgsRollbackYTextArguments passed to the key file of the component if if implements the .NET Installer classComponentISDotNetInstallerArgsUninstallYTextArguments passed to the key file of the component if if implements the .NET Installer classComponentISRegFileToMergeAtBuildYTextPath and File name of a .REG file to merge into the component at build time.ComponentISScanAtBuildFileYTextFile used by the Dot Net scanner to populate dependant assemblies' File_Application field.ComponentKeyPathYFile;ODBCDataSource;Registry1IdentifierEither the primary key into the File table, Registry table, or ODBCDataSource table. This extract path is stored when the component is installed, and is used to detect the presence of the component and to return the path to it.ConditionConditionYConditionExpression evaluated to determine if Level in the Feature table is to change.ConditionFeature_NFeature1IdentifierReference to a Feature entry in Feature table.ConditionLevelN032767New selection Level to set in Feature table if Condition evaluates to TRUE.ControlAttributesY02147483647A 32-bit word that specifies the attribute flags to be applied to this control.ControlBinary_YBinary1IdentifierExternal key to the Binary table.ControlControlNIdentifierName of the control. This name must be unique within a dialog, but can repeat on different dialogs.ControlControl_NextYControl2IdentifierThe name of an other control on the same dialog. This link defines the tab order of the controls. The links have to form one or more cycles!ControlDialog_NDialog1IdentifierExternal key to the Dialog table, name of the dialog.ControlHeightN032767Height of the bounding rectangle of the control.ControlHelpYTextThe help strings used with the button. The text is optional.ControlISBuildSourcePathYTextFull path to .rtf file for scrollable text controlControlISControlIdYA number used to represent the control ID of the Control, Used in Dialog exportControlISWindowStyleY02147483647A 32-bit word that specifies non-MSI window styles to be applied to this control.ControlPropertyYIdentifierThe name of a defined property to be linked to this control.ControlTextYFormattedA string used to set the initial text contained within a control (if appropriate).ControlTypeNIdentifierThe type of the control.ControlWidthN032767Width of the bounding rectangle of the control.ControlXN032767Horizontal coordinate of the upper left corner of the bounding rectangle of the control.ControlYN032767Vertical coordinate of the upper left corner of the bounding rectangle of the control.ControlConditionActionNDefault;Disable;Enable;Hide;ShowThe desired action to be taken on the specified control.ControlConditionConditionNConditionA standard conditional statement that specifies under which conditions the action should be triggered.ControlConditionControl_NControl2IdentifierA foreign key to the Control table, name of the control.ControlConditionDialog_NDialog1IdentifierA foreign key to the Dialog table, name of the dialog.ControlEventArgumentNFormattedA value to be used as a modifier when triggering a particular event.ControlEventConditionYConditionA standard conditional statement that specifies under which conditions an event should be triggered.ControlEventControl_NControl2IdentifierA foreign key to the Control table, name of the controlControlEventDialog_NDialog1IdentifierA foreign key to the Dialog table, name of the dialog.ControlEventEventNFormattedAn identifier that specifies the type of the event that should take place when the user interacts with control specified by the first two entries.ControlEventOrderingY02147483647An integer used to order several events tied to the same control. Can be left blank.CreateFolderComponent_NComponent1IdentifierForeign key into the Component table.CreateFolderDirectory_NDirectory1IdentifierPrimary key, could be foreign key into the Directory table.CustomActionActionNIdentifierPrimary key, name of action, normally appears in sequence table unless private use.CustomActionExtendedTypeY02147483647The numeric custom action type info flags.CustomActionISCommentsYTextAuthor’s comments for this custom action.CustomActionSourceYCustomSourceThe table reference of the source of the code.CustomActionTargetYISDLLWrapper;ISInstallScriptAction1FormattedExcecution parameter, depends on the type of custom actionCustomActionTypeN132767The numeric custom action type, consisting of source location, code type, entry, option flags.DialogAttributesY02147483647A 32-bit word that specifies the attribute flags to be applied to this dialog.DialogControl_CancelYControl2IdentifierDefines the cancel control. Hitting escape or clicking on the close icon on the dialog is equivalent to pushing this button.DialogControl_DefaultYControl2IdentifierDefines the default control. Hitting return is equivalent to pushing this button.DialogControl_FirstNControl2IdentifierDefines the control that has the focus when the dialog is created.DialogDialogNIdentifierName of the dialog.DialogHCenteringN0100Horizontal position of the dialog on a 0-100 scale. 0 means left end, 100 means right end of the screen, 50 center.DialogHeightN032767Height of the bounding rectangle of the dialog.DialogISCommentsYTextAuthor’s comments for this dialog.DialogISResourceIdYA Number the Specifies the Dialog ID to be used in Dialog ExportDialogISWindowStyleYA 32-bit word that specifies non-MSI window styles to be applied to this control. This is only used in Script Based Setups.DialogTextStyle_YIdentifierForeign Key into TextStyle table, only used in Script Based Projects.DialogTitleYFormattedA text string specifying the title to be displayed in the title bar of the dialog's window.DialogVCenteringN0100Vertical position of the dialog on a 0-100 scale. 0 means top end, 100 means bottom end of the screen, 50 center.DialogWidthN032767Width of the bounding rectangle of the dialog.DirectoryDefaultDirNTextThe default sub-path under parent's path.DirectoryDirectoryNIdentifierUnique identifier for directory entry, primary key. If a property by this name is defined, it contains the full path to the directory.DirectoryDirectory_ParentYDirectory1IdentifierReference to the entry in this table specifying the default parent directory. A record parented to itself or with a Null parent represents a root of the install tree.DirectoryISAttributesY0;1;2;3;4;5;6;7This is used to store Installshield custom properties of a directory. Currently the only one is Shortcut.DirectoryISDescriptionYTextDescription of folderDirectoryISFolderNameYTextThis is used in Pro projects because the pro identifier used in the tree wasn't necessarily unique.DrLocatorDepthY032767The depth below the path to which the Signature_ is recursively searched. If absent, the depth is assumed to be 0.DrLocatorParentYIdentifierThe parent file signature. It is also a foreign key in the Signature table. If null and the Path column does not expand to a full path, then all the fixed drives of the user system are searched using the Path.DrLocatorPathYAnyPathThe path on the user system. This is a either a subpath below the value of the Parent or a full path. The path may contain properties enclosed within [ ] that will be expanded.DrLocatorSignature_NSignature1IdentifierThe Signature_ represents a unique file signature and is also the foreign key in the Signature table.DuplicateFileComponent_NComponent1IdentifierForeign key referencing Component that controls the duplicate file.DuplicateFileDestFolderYIdentifierName of a property whose value is assumed to resolve to the full pathname to a destination folder.DuplicateFileDestNameYTextFilename to be given to the duplicate file.DuplicateFileFileKeyNIdentifierPrimary key used to identify a particular file entryDuplicateFileFile_NFile1IdentifierForeign key referencing the source file to be duplicated.EnvironmentComponent_NComponent1IdentifierForeign key into the Component table referencing component that controls the installing of the environmental value.EnvironmentEnvironmentNIdentifierUnique identifier for the environmental variable settingEnvironmentNameNTextThe name of the environmental value.EnvironmentValueYFormattedThe value to set in the environmental settings.ErrorErrorN032767Integer error number, obtained from header file IError(...) macros.ErrorMessageYTemplateError formatting template, obtained from user ed. or localizers.EventMappingAttributeNIdentifierThe name of the control attribute, that is set when this event is received.EventMappingControl_NControl2IdentifierA foreign key to the Control table, name of the control.EventMappingDialog_NDialog1IdentifierA foreign key to the Dialog table, name of the Dialog.EventMappingEventNIdentifierAn identifier that specifies the type of the event that the control subscribes to.ExtensionComponent_NComponent1IdentifierRequired foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.ExtensionExtensionNTextThe extension associated with the table row.ExtensionFeature_NFeature1IdentifierRequired foreign key into the Feature Table, specifying the feature to validate or install in order for the CLSID factory to be operational.ExtensionMIME_YMIME1TextOptional Context identifier, typically "type/format" associated with the extensionExtensionProgId_YProgId1TextOptional ProgId associated with this extension.FeatureAttributesN0;1;2;4;5;6;8;9;10;16;17;18;20;21;22;24;25;26;32;33;34;36;37;38;48;49;50;52;53;54Feature attributesFeatureDescriptionYTextLonger descriptive text describing a visible feature item.FeatureDirectory_YDirectory1UpperCaseThe name of the Directory that can be configured by the UI. A non-null value will enable the browse button.FeatureDisplayY032767Numeric sort order, used to force a specific display ordering.FeatureFeatureNIdentifierPrimary key used to identify a particular feature record.FeatureFeature_ParentYFeature1IdentifierOptional key of a parent record in the same table. If the parent is not selected, then the record will not be installed. Null indicates a root item.FeatureISCommentsYCommentsFeatureISFeatureCabNameYName of CAB used when compressing CABs by Feature. Used to override build generated name for CAB file.FeatureISProFeatureNameYTextThe name of the feature used by pro projects. This doesn't have to be unique.FeatureISReleaseFlagsYRelease Flags that specify whether this feature will be built in a particular release.FeatureLevelN032767The install level at which record will be initially selected. An install level of 0 will disable an item and prevent its display.FeatureTitleYTextShort text identifying a visible feature item.FeatureComponentsComponent_NComponent1IdentifierForeign key into Component table.FeatureComponentsFeature_NFeature1IdentifierForeign key into Feature table.FileAttributesY032767Integer containing bit flags representing file attributes (with the decimal value of each bit position in parentheses)FileComponent_NComponent1IdentifierForeign key referencing Component that controls the file.FileFileNIdentifierPrimary key, non-localized token, must match identifier in cabinet. For uncompressed files, this field is ignored.FileFileNameNTextFile name used for installation. This may contain a "short name|long name" pair. It may be just a long name, hence it cannot be of the Filename data type.FileFileSizeN02147483647Size of file in bytes (long integer).FileISAttributesY02147483647This field contains the following attributes: UseSystemSettings(0x1)FileISBuildSourcePathYTextFull path, the category is of Text instead of Path because of potential use of path variables.FileISComponentSubFolder_YIdentifierForeign key referencing component subfolder containing this file. Only for Pro.FileLanguageYLanguageList of decimal language Ids, comma-separated if more than one.FileSequenceN132767Sequence with respect to the media images; order must track cabinet order.FileVersionYFile1VersionVersion string for versioned files; Blank for unversioned files.FileSFPCatalogFile_NFile1IdentifierFile associated with the catalogFileSFPCatalogSFPCatalog_NSFPCatalog1TextCatalog associated with the fileFontFile_NFile1IdentifierPrimary key, foreign key into File table referencing font file.FontFontTitleYTextFont name.ISAssistantTagDataY + ISAssistantTagTagN + ISBillBoardColorY + ISBillBoardDisplayNameY + ISBillBoardDurationN032767 + ISBillBoardEffectN032767 + ISBillBoardFontY + ISBillBoardISBillboardN + ISBillBoardOriginN032767 + ISBillBoardSequenceN-3276732767 + ISBillBoardStyleY + ISBillBoardTargetN032767 + ISBillBoardTitleY + ISBillBoardXN032767 + ISBillBoardYN032767 + ISCEAppAppKeyN + ISCEAppAppNameN + ISCEAppAttributesY + ISCEAppCompanyNameN + ISCEAppComponent_YComponent1 + ISCEAppDefDirN + ISCEAppDeleteMediaN + ISCEAppDescriptionY + ISCEAppDesktopTargetDirN + ISCEAppDeviceFileY + ISCEAppIconIndexY + ISCEAppIconPathY + ISCEAppInstallNetCFY + ISCEAppInstallNetCF2Y + ISCEAppInstallSQLClientY + ISCEAppInstallSQLClient2Y + ISCEAppInstallSQLDevY + ISCEAppInstallSQLDev2Y + ISCEAppInstallSQLServerY + ISCEAppInstallSQLServer2Y + ISCEAppNoUninstallY + ISCEAppPVKFileY + ISCEAppPostXMLY + ISCEAppPreXMLY + ISCEAppRawDeviceFileY + ISCEAppSPCFileY + ISCEAppSPCPwdY + ISCEDirAppKeyN + ISCEDirDirKeyN + ISCEDirDirParentN + ISCEDirDirValueN + ISCEFileAdvancedOptionsY + ISCEFileAppKeyN + ISCEFileCopyOptionN + ISCEFileDestinationN + ISCEFileFileKeyN + ISCEFileFileOptionN + ISCEFileNameN + ISCEFilePlatformN + ISCEFileProcessorN + ISCEFileSourceN + ISCEFileExtAppKeyN + ISCEFileExtDescriptionY + ISCEFileExtExtKeyN + ISCEFileExtExtensionN + ISCEFileExtFileKeyN + ISCEFileExtIconIndexN + ISCEInstallCEAppNameN + ISCEInstallCECabsN + ISCEInstallCEDesktopDirN + ISCEInstallCEIcoFileN + ISCEInstallCEIniFileKeyN + ISCEInstallCEInstallKeyN + ISCEInstallComponent_Y + ISCEInstallDeleteMediaN + ISCEOtherAppCABsAppKeyN + ISCEOtherAppCABsBuildSourcePathN + ISCEOtherAppCABsFileKeyN + ISCERedistAppKeyN + ISCERedistNameY + ISCERedistPlatformsY + ISCERegistryAppKeyN + ISCERegistryKeyN + ISCERegistryNameY + ISCERegistryOverwriteN + ISCERegistryPlatformN + ISCERegistryProcessorN + ISCERegistryRegKeyN + ISCERegistryRootN + ISCERegistryValueY + ISCESetupFileAppKeyN + ISCESetupFileNameN + ISCESetupFilePlatformN + ISCESetupFileProcessorN + ISCESetupFileSetupFileKeyN + ISCESetupFileSourceN + ISCEShtCutAppKeyN + ISCEShtCutDestinationN + ISCEShtCutDisplayNameN + ISCEShtCutPlatformN + ISCEShtCutShtCutKeyN + ISCEShtCutStartScreenIconY + ISCEShtCutTargetN + ISChainPackageDisplayNameYTextDisplay name for the chained package. Used only in the IDE.ISChainPackageISReleaseFlagsY + ISChainPackageInstallConditionYCondition + ISChainPackageInstallPropertiesYFormatted + ISChainPackageOptionsNInteger + ISChainPackageOrderNInteger + ISChainPackagePackageNIdentifier + ISChainPackageProductCodeY + ISChainPackageRemoveConditionYCondition + ISChainPackageRemovePropertiesYFormatted + ISChainPackageSourcePathY + ISChainPackageDataDataYBinaryBinary stream. The binary icon data in PE (.DLL or .EXE) or icon (.ICO) format.ISChainPackageDataFileNIdentifier + ISChainPackageDataFilePathNFormatted + ISChainPackageDataISBuildSourcePathYTextFull path to the ICO or EXE file.ISChainPackageDataOptionsY + ISChainPackageDataPackage_NISChainPackage1Identifier + ISClrWrapAction_NCustomAction1IdentifierForeign key into CustomAction tableISClrWrapNameNTextProperty associated with this ActionISClrWrapValueYTextValue associated with this PropertyISComCatalogAttributeISComCatalogObject_NISComCatalogObject1IdentifierForeign key into the ISComCatalogObject table.ISComCatalogAttributeItemNameNTextThe named attribute for a catalog object.ISComCatalogAttributeItemValueYTextA value associated with the attribute defined in the ItemName column.ISComCatalogCollectionCollectionNameNTextA catalog collection name.ISComCatalogCollectionISComCatalogCollectionNIdentifierA unique key for the ISComCatalogCollection table.ISComCatalogCollectionISComCatalogObject_NISComCatalogObject1IdentifierForeign key into the ISComCatalogObject table.ISComCatalogCollectionObjectsISComCatalogCollection_NISComCatalogCollection1IdentifierA unique key for the ISComCatalogCollection table.ISComCatalogCollectionObjectsISComCatalogObject_NISComCatalogObject1IdentifierForeign key into the ISComCatalogObject table.ISComCatalogObjectDisplayNameNThe display name of a catalog object.ISComCatalogObjectISComCatalogObjectNIdentifierA unique key for the ISComCatalogObject table.ISComPlusApplicationComponent_NComponent1IdentifierForeign key into the Component table that a COM+ application belongs to.ISComPlusApplicationComputerNameYTextComputer name that a COM+ application belongs to.ISComPlusApplicationDepFilesYTextList of the dependent files.ISComPlusApplicationISAttributesYInstallShield custom attributes associated with a COM+ application.ISComPlusApplicationISComCatalogObject_NISComCatalogObject1IdentifierForeign key into the ISComCatalogObject table.ISComPlusApplicationDLLAlterDLLYTextAlternate filename of the COM+ application component. Will be used for a .NET serviced component.ISComPlusApplicationDLLCLSIDNTextCLSID of the COM+ application component.ISComPlusApplicationDLLDLLYTextFilename of the COM+ application component.ISComPlusApplicationDLLISComCatalogObject_NISComCatalogObject1IdentifierForeign key into the ISComCatalogObject table.ISComPlusApplicationDLLISComPlusApplicationDLLNIdentifierA unique key for the ISComPlusApplicationDLL table.ISComPlusApplicationDLLISComPlusApplication_NISComPlusApplication1IdentifierForeign key into the ISComPlusApplication table.ISComPlusApplicationDLLProgIdYTextProgId of the COM+ application component.ISComPlusProxyComponent_YComponent1IdentifierForeign key into the Component table that a COM+ application proxy belongs to.ISComPlusProxyDepFilesYTextList of the dependent files.ISComPlusProxyISAttributesYInstallShield custom attributes associated with a COM+ application proxy.ISComPlusProxyISComPlusApplication_NISComPlusApplication1IdentifierForeign key into the ISComPlusApplication table that a COM+ application proxy belongs to.ISComPlusProxyISComPlusProxyNIdentifierA unique key for the ISComPlusProxy table.ISComPlusProxyDepFileFile_NFile1IdentifierForeign key into the File table.ISComPlusProxyDepFileISComPlusApplication_NISComPlusApplication1IdentifierForeign key into the ISComPlusApplication table.ISComPlusProxyDepFileISPathYTextFull path of the dependent file.ISComPlusProxyFileFile_NFile1IdentifierForeign key into the File table.ISComPlusProxyFileISComPlusApplicationDLL_NISComPlusApplicationDLL1IdentifierForeign key into the ISComPlusApplicationDLL table.ISComPlusServerDepFileFile_NFile1IdentifierForeign key into the File table.ISComPlusServerDepFileISComPlusApplication_NISComPlusApplication1IdentifierForeign key into the ISComPlusApplication table.ISComPlusServerDepFileISPathYTextFull path of the dependent file.ISComPlusServerFileFile_NFile1IdentifierForeign key into the File table.ISComPlusServerFileISComPlusApplicationDLL_NISComPlusApplicationDLL1IdentifierForeign key into the ISComPlusApplicationDLL table.ISComponentExtendedComponent_NComponent1IdentifierPrimary key used to identify a particular component record.ISComponentExtendedFTPLocationYTextFTP LocationISComponentExtendedFilterPropertyNIdentifierProperty to set if you want to filter a componentISComponentExtendedHTTPLocationYTextHTTP LocationISComponentExtendedLanguageYTextLanguageISComponentExtendedMiscellaneousYTextMiscellaneousISComponentExtendedOSYbitwise addition of OSsISComponentExtendedPlatformsYbitwise addition of Platforms.ISCustomActionReferenceAction_NCustomAction1IdentifierForeign key into theICustomAction table.ISCustomActionReferenceDescriptionYTextContents of the file speciifed in ISCAReferenceFilePath. This column is only used by MSI.ISCustomActionReferenceFileTypeYTextfile type of the file specified ISCAReferenceFilePath. This column is only used by MSI.ISCustomActionReferenceISCAReferenceFilePathYTextFull path, the category is of Text instead of Path because of potential use of path variables. This column only exists in ISM.ISDIMDependencyISDIMReference_NIdentifierThis is the primary key to the ISDIMDependency tableISDIMDependencyRequiredBuildVersionYTextthe build version identifying the required DIMISDIMDependencyRequiredMajorVersionYTextthe major version identifying the required DIMISDIMDependencyRequiredMinorVersionYTextthe minor version identifying the required DIMISDIMDependencyRequiredRevisionVersionYTextthe revision version identifying the required DIMISDIMDependencyRequiredUUIDNTextthe UUID identifying the required DIMISDIMReferenceISBuildSourcePathYTextFull path, the category is of Text instead of Path because of potential use of path variables.ISDIMReferenceISDIMReferenceNISDIMDependency1IdentifierThis is the primary key to the ISDIMReference tableISDIMReferenceDependenciesISDIMDependency_NISDIMDependency1IdentifierForeign key into ISDIMDependency table.ISDIMReferenceDependenciesISDIMReference_ParentNISDIMReference1IdentifierForeign key into ISDIMReference table.ISDIMVariableISDIMReference_NISDIMReference1IdentifierForeign key into ISDIMReference table.ISDIMVariableISDIMVariableNIdentifierThis is the primary key to the ISDIMVariable tableISDIMVariableNameNTextName of a variable defined in the .dim fileISDIMVariableNewValueYTextNew value that you want to override withISDIMVariableTypeYType of the variable. 0: Build Variable, 1: Runtime VariableISDLLWrapperEntryPointNTextThis is a foreign key to the target column in the CustomAction tableISDLLWrapperSourceNFormattedThis is column points to the source file for the DLLWrapper Custom ActionISDLLWrapperTargetNTextThe function signatureISDLLWrapperTypeYTypeISDRMFileFile_YFile1IdentifierForeign key into File table. A null value will cause a build warning.ISDRMFileISDRMFileNIdentifierUnique identifier for this item.ISDRMFileISDRMLicense_YISDRMLicense1IdentifierForeign key referencing License that packages this file.ISDRMFileShellNTextText indicating the activation shell used at runtime.ISDRMFileAttributeISDRMFile_NISDRMFile1IdentifierPrimary foreign key into ISDRMFile table.ISDRMFileAttributePropertyNTextThe name of the attributeISDRMFileAttributeValueYTextThe value of the attributeISDRMLicenseAttributesYNumberBitwise field used to specify binary attributes of this license.ISDRMLicenseDescriptionYTextAn internal description of this license.ISDRMLicenseISDRMLicenseNIdentifierUnique key identifying the license record.ISDRMLicenseLicenseNumberYTextThe license number.ISDRMLicenseProjectVersionYTextThe version of the project that this license is tied to.ISDRMLicenseRequestCodeYTextThe request code.ISDRMLicenseResponseCodeYTextThe response code.ISDependencyExcludeY + ISDependencyISDependencyY + ISDisk1FileDiskY-1;0;1Used to differentiate between disk1(1), last disk(-1), and other(0).ISDisk1FileISBuildSourcePathNTextFull path of file to be copied to Disk1 folderISDisk1FileISDisk1FileNIdentifierPrimary key for ISDisk1File tableISDynamicFileComponent_NComponent1IdentifierForeign key referencing Component that controls the file.ISDynamicFileExcludeFilesYTextWildcards for excluded files.ISDynamicFileISAttributesY0;1;2;3;4;5;6;7;8;9;10;11;12;13;14;15This is used to store Installshield custom properties of a dynamic filet. Currently the only one is SelfRegister.ISDynamicFileIncludeFilesYTextWildcards for included files.ISDynamicFileIncludeFlagsYInclude flags.ISDynamicFileSourceFolderNTextFull path, the category is of Text instead of Path because of potential use of path variables.ISFeatureDIMReferencesFeature_NFeature1IdentifierForeign key into Feature table.ISFeatureDIMReferencesISDIMReference_NISDIMReference1IdentifierForeign key into ISDIMReference table.ISFeatureMergeModuleExcludesFeature_NIdentifierForeign key into Feature table.ISFeatureMergeModuleExcludesLanguageNForeign key into ISMergeModule table.ISFeatureMergeModuleExcludesModuleIDNIdentifierForeign key into ISMergeModule table.ISFeatureMergeModulesFeature_NFeature1IdentifierForeign key into Feature table.ISFeatureMergeModulesISMergeModule_NISMergeModule1TextForeign key into ISMergeModule table.ISFeatureMergeModulesLanguage_NISMergeModule2Foreign key into ISMergeModule table.ISFeatureSetupPrerequisitesFeature_NFeature1IdentifierForeign key into Feature table.ISFeatureSetupPrerequisitesISSetupPrerequisites_NISSetupPrerequisites1 + ISFileManifestsFile_NIdentifierForeign key into File table.ISFileManifestsManifest_NIdentifierForeign key into File table.ISIISItemComponent_YComponent1IdentifierForeign key to Component table.ISIISItemDisplayNameYTextLocalizable Item Name.ISIISItemISIISItemNIdentifierPrimary key for each item.ISIISItemISIISItem_ParentYISIISItem1IdentifierThis record's parent record.ISIISItemTypeNIIS resource type.ISIISPropertyFriendlyNameYTextIIS property name.ISIISPropertyISAttributesYFlags.ISIISPropertyISIISItem_NISIISItem1IdentifierPrimary key for table, foreign key into ISIISItem.ISIISPropertyISIISPropertyNIdentifierPrimary key for table.ISIISPropertyMetaDataAttributesYIIS property attributes.ISIISPropertyMetaDataPropYIIS property ID.ISIISPropertyMetaDataTypeYIIS property data type.ISIISPropertyMetaDataUserTypeYIIS property user data type.ISIISPropertyMetaDataValueYTextIIS property value.ISIISPropertyOrderYOrder sequencing.ISIISPropertySchemaYTextIIS7 schema information.ISInstallScriptActionEntryPointNTextThis is a foreign key to the target column in the CustomAction tableISInstallScriptActionSourceNFormattedThis is column points to the source file for the DLLWrapper Custom ActionISInstallScriptActionTargetYTextThe function signatureISInstallScriptActionTypeYTypeISLanguageISLanguageNTextThis is the language ID.ISLanguageIncludedY0;1Specify whether this language should be included.ISLinkerLibraryISLinkerLibraryNIdentifierUnique identifier for the link library.ISLinkerLibraryLibraryNTextFull path of the object library (.obl file).ISLinkerLibraryOrderNOrder of the LibraryISLocalControlAttributesYA 32-bit word that specifies the attribute flags to be applied to this control.ISLocalControlBinary_YBinary1IdentifierExternal key to the Binary table.ISLocalControlControl_NControl2IdentifierName of the control. This name must be unique within a dialog, but can repeat on different dialogs.ISLocalControlDialog_NDialog1IdentifierExternal key to the Dialog table, name of the dialog.ISLocalControlHeightYHeight of the bounding rectangle of the control.ISLocalControlISBuildSourcePathYTextFull path to .rtf file for scrollable text controlISLocalControlISLanguage_NISLanguage1TextThis is a foreign key to the ISLanguage table.ISLocalControlWidthYWidth of the bounding rectangle of the control.ISLocalControlXYHorizontal coordinate of the upper left corner of the bounding rectangle of the control.ISLocalControlYYVertical coordinate of the upper left corner of the bounding rectangle of the control.ISLocalDialogAttributesYA 32-bit word that specifies the attribute flags to be applied to this dialog.ISLocalDialogDialog_YDialog1IdentifierName of the dialog.ISLocalDialogHeightN032767Height of the bounding rectangle of the dialog.ISLocalDialogISLanguage_YISLanguage1TextThis is a foreign key to the ISLanguage table.ISLocalDialogTextStyle_YIdentifierForeign Key into TextStyle table, only used in Script Based Projects.ISLocalDialogWidthN032767Width of the bounding rectangle of the dialog.ISLocalRadioButtonHeightN032767The height of the button.ISLocalRadioButtonISLanguage_NISLanguage1TextThis is a foreign key to the ISLanguage table.ISLocalRadioButtonOrderN132767RadioButton2A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.ISLocalRadioButtonPropertyNRadioButton1IdentifierA named property to be tied to this radio button. All the buttons tied to the same property become part of the same group.ISLocalRadioButtonWidthN032767The width of the button.ISLocalRadioButtonXN032767The horizontal coordinate of the upper left corner of the bounding rectangle of the radio button.ISLocalRadioButtonYN032767The vertical coordinate of the upper left corner of the bounding rectangle of the radio button.ISLockPermissionsAttributesY-21474836472147483647Permissions attributes mask, 1==Deny access; 2==No inherit, 4==Ignore apply failures, 8==Target object is 64-bitISLockPermissionsDomainYTextDomain name for user whose permissions are being set.ISLockPermissionsLockObjectNIdentifierForeign key into CreateFolder, Registry, or File tableISLockPermissionsPermissionY-21474836472147483647Permission Access mask.ISLockPermissionsTableNIdentifierCreateFolder;File;RegistryReference to another table nameISLockPermissionsUserNTextUser for permissions to be set. This can be a property, hardcoded named, or SID stringISLogicalDiskCabinetYCabinetIf some or all of the files stored on the media are compressed in a cabinet, the name of that cabinet.ISLogicalDiskDiskIdN132767Primary key, integer to determine sort order for table.ISLogicalDiskDiskPromptYTextDisk name: the visible text actually printed on the disk. This will be used to prompt the user when this disk needs to be inserted.ISLogicalDiskISProductConfiguration_NISProductConfiguration1TextForeign key into the ISProductConfiguration table.ISLogicalDiskISRelease_NISRelease1TextForeign key into the ISRelease table.ISLogicalDiskLastSequenceN032767File sequence number for the last file for this media.ISLogicalDiskSourceYPropertyThe property defining the location of the cabinet file.ISLogicalDiskVolumeLabelYTextThe label attributed to the volume.ISLogicalDiskFeaturesFeature_YFeature1IdentifierRequired foreign key into the Feature Table,ISLogicalDiskFeaturesISAttributesYThis is used to store Installshield custom properties, like Compressed, etc.ISLogicalDiskFeaturesISLogicalDisk_N132767ISLogicalDisk1IdentifierForeign key into the ISLogicalDisk table.ISLogicalDiskFeaturesISProductConfiguration_NISProductConfiguration1TextForeign key into the ISProductConfiguration table.ISLogicalDiskFeaturesISRelease_NISRelease1TextForeign key into the ISRelease table.ISLogicalDiskFeaturesSequenceN032767File sequence number for the file for this media.ISMergeModuleDestinationYTextDestination.ISMergeModuleISAttributesYThis is used to store Installshield custom properties of a merge module.ISMergeModuleISMergeModuleNTextThe GUID identifying the merge module.ISMergeModuleLanguageNDefault decimal language of module.ISMergeModuleNameNTextName of the merge module.ISMergeModuleCfgValuesAttributesYAttributes (from configurable merge module)ISMergeModuleCfgValuesContextDataYTextContextData (from configurable merge module)ISMergeModuleCfgValuesDefaultValueYTextDefaultValue (from configurable merge module)ISMergeModuleCfgValuesDescriptionYTextDescription (from configurable merge module)ISMergeModuleCfgValuesDisplayNameYTextDisplayName (from configurable merge module)ISMergeModuleCfgValuesFormatNFormat (from configurable merge module)ISMergeModuleCfgValuesHelpKeywordYTextHelpKeyword (from configurable merge module)ISMergeModuleCfgValuesHelpLocationYTextHelpLocation (from configurable merge module)ISMergeModuleCfgValuesISMergeModule_NISMergeModule1TextThe module signature, a foreign key into the ISMergeModule tableISMergeModuleCfgValuesLanguage_NISMergeModule2Default decimal language of module.ISMergeModuleCfgValuesModuleConfiguration_NIdentifierIdentifier, foreign key into ModuleConfiguration table (ModuleConfiguration.Name)ISMergeModuleCfgValuesTypeYTextType (from configurable merge module)ISMergeModuleCfgValuesValueYTextValue for this item.ISObjectLanguageNText + ISObjectObjectNameNText + ISObjectPropertyIncludeInBuildYBoolean, 0 for false non 0 for trueISObjectPropertyObjectNameYISObject1Text + ISObjectPropertyPropertyYText + ISObjectPropertyValueYText + ISPalmAppComponentNComponent1 + ISPalmAppPalmAppN + ISPalmAppFileDestinationN + ISPalmAppFileFileKeyNFile1 + ISPalmAppFilePalmAppNISPalmApp1 + ISPatchConfigImagePatchConfiguration_YISPatchConfiguration1TextForeign key to the ISPatchConfigurationTableISPatchConfigImageUpgradedImage_NISUpgradedImage1TextForeign key to the ISUpgradedImageTableISPatchConfigurationAttributesYPatchConfiguration attributesISPatchConfigurationCanPCDifferNThis is determine whether Product Codes may differISPatchConfigurationCanPVDifferNThis is determine whether the Major Product Version may differISPatchConfigurationEnablePatchCacheNThis is determine whether to Enable Patch cacheingISPatchConfigurationFlagsNPatching API FlagsISPatchConfigurationIncludeWholeFilesNThis is determine whether to build a binary level patchISPatchConfigurationLeaveDecompressedNThis is determine whether to leave intermediate files devcompressed when finishedISPatchConfigurationMinMsiVersionNMinimum Required MSI VersionISPatchConfigurationNameNTextName of the Patch ConfigurationISPatchConfigurationOptimizeForSizeNThis is determine whether to Optimize for large filesISPatchConfigurationOutputPathNTextBuild LocationISPatchConfigurationPatchCacheDirYTextDirectory to recieve the Patch Cache informationISPatchConfigurationPatchGuidNTextUnique Patch IdentifierISPatchConfigurationPatchGuidsToReplaceYTextList Of Patch Guids to unregisterISPatchConfigurationTargetProductCodesNTextList Of target Product CodesISPatchConfigurationPropertyISPatchConfiguration_YISPatchConfiguration1TextName of the Patch ConfigurationISPatchConfigurationPropertyPropertyYTextName of the Patch Configuration Property valueISPatchConfigurationPropertyValueYTextValue of the Patch Configuration PropertyISPatchExternalFileFileKeyNTextFilekeyISPatchExternalFileFilePathNTextFilepathISPatchExternalFileISUpgradedImage_NISUpgradedImage1TextForeign key to the isupgraded image tableISPatchExternalFileNameNTextUniqu name to identify this record.ISPatchWholeFileComponentYTextComponent containing file keyISPatchWholeFileFileKeyNTextKey of file to be included as wholeISPatchWholeFileUpgradedImageNISUpgradedImage1TextForeign key to ISUpgradedImage TableISPathVariableISPathVariableNThe name of the path variable.ISPathVariableTestValueYTextThe test value of the path variable.ISPathVariableTypeN1;2;4;8The type of the path variable.ISPathVariableValueYTextThe value of the path variable.ISPowerShellWrapAction_NCustomAction1IdentifierForeign key into CustomAction tableISPowerShellWrapNameNTextProperty associated with this ActionISPowerShellWrapValueYTextValue associated with this PropertyISProductConfigurationGeneratePackageCodeYNumber0;1Indicates whether or not to generate a package code.ISProductConfigurationISProductConfigurationNTextThe name of the product configuration.ISProductConfigurationProductConfigurationFlagsYTextProduct configuration (release) flags.ISProductConfigurationInstanceISProductConfiguration_NISProductConfiguration1TextForeign key into the ISProductConfiguration table.ISProductConfigurationInstanceInstanceIdN032767Identifies the instance number of this instance. This value is stored in the Property InstanceId.ISProductConfigurationInstancePropertyNTextProduct Congiuration property nameISProductConfigurationInstanceValueNTextString value for property.ISProductConfigurationPropertyISProductConfiguration_NISProductConfiguration1TextForeign key into the ISProductConfiguration table.ISProductConfigurationPropertyPropertyNProperty1TextProduct Congiuration property nameISProductConfigurationPropertyValueYTextString value for property. Never null or empty.ISReleaseAttributesNBitfield holding boolean values for various release attributes.ISReleaseBuildLocationNTextBuild location.ISReleaseCDBrowserYTextDemoshield browser location.ISReleaseDefaultLanguageNTextDefault language for setup.ISReleaseDigitalPVKYTextDigital signing private key (.pvk) file.ISReleaseDigitalSPCYTextDigital signing Software Publisher Certificate (.spc) file.ISReleaseDigitalURLYTextDigital signing URL.ISReleaseDiskClusterSizeNDisk cluster size.ISReleaseDiskSizeNTextDisk size.ISReleaseDiskSizeUnitN0;1;2Disk size units (KB or MB).ISReleaseDiskSpanningN0;1;2Disk spanning (automatic, enforce size, etc.).ISReleaseDotNetBuildConfigurationYTextBuild Configuration for .NET solutions.ISReleaseISProductConfiguration_NISProductConfiguration1TextForeign key into the ISProductConfiguration table.ISReleaseISReleaseNTextThe name of the release.ISReleaseISSetupPrerequisiteLocationY0;1;2;3Location the Setup Prerequisites will be placed inISReleaseMediaLocationNTextMedia location on disk.ISReleaseMsiCommandLineYTextCommand line passed to the msi package from setup.exeISReleaseMsiSourceTypeN-14MSI media source type.ISReleasePackageNameNTextPackage name.ISReleasePasswordYTextPassword.ISReleasePlatformsNTextPlatforms supported (Intel, Alpha, etc.).ISReleaseReleaseFlagsYTextRelease flags.ISReleaseReleaseTypeN1;2;4Release type (single, uncompressed, etc.).ISReleaseSupportedLanguagesDataYTextLanguages supported (for component filtering).ISReleaseSupportedLanguagesUINTextUI languages supported.ISReleaseSupportedOSsNIndicate which operating systmes are supported.ISReleaseSynchMsiYTextMSI file to synchronize file keys and other data with (patch-like functionality).ISReleaseTypeN06Release type (CDROM, Network, etc.).ISReleaseURLLocationYTextMedia location via URL.ISReleaseVersionCopyrightYTextVersion stamp information.ISReleaseASPublishInfoISProductConfiguration_NISProductConfiguration1TextForeign key into the ISProductConfiguration table.ISReleaseASPublishInfoISRelease_NISRelease1TextForeign key into the ISRelease table.ISReleaseASPublishInfoPropertyYTextAS Repository property nameISReleaseASPublishInfoValueYTextAS Repository property valueISReleaseExtendedAttributesYBitfield holding boolean values for various release attributes.ISReleaseExtendedCertPasswordYTextDigital certificate passwordISReleaseExtendedDigitalCertificateDBaseNSYTextPath to cerificate database for Netscape digital signatureISReleaseExtendedDigitalCertificateIdNSYTextPath to cerificate ID for Netscape digital signatureISReleaseExtendedDigitalCertificatePasswordNSYTextPassword for Netscape digital signatureISReleaseExtendedDotNetBaseLanguageYTextBase Languge of .NET RedistISReleaseExtendedDotNetFxCmdLineYTextCommand Line to pass to DotNetFx.exeISReleaseExtendedDotNetLangPackCmdLineYTextCommand Line to pass to LangPack.exeISReleaseExtendedDotNetLangaugePacksYText.NET Redist language packs to includeISReleaseExtendedDotNetRedistLocationY03Location of .NET framework Redist (Web, SetupExe, Source, None)ISReleaseExtendedDotNetRedistURLYTextURL to .NET framework RedistISReleaseExtendedDotNetVersionY02Version of .NET framework Redist (1.0, 1.1)ISReleaseExtendedEngineLocationY02Location of msi engine (Web, SetupExe...)ISReleaseExtendedISEngineLocationY02Location of ISScript engine (Web, SetupExe...)ISReleaseExtendedISEngineURLYTextURL to InstallShield scripting engineISReleaseExtendedISProductConfiguration_NTextForeign key into the ISProductConfiguration table.ISReleaseExtendedISRelease_NTextThe name of the release.ISReleaseExtendedJSharpCmdLineYTextCommand Line to pass to vjredist.exeISReleaseExtendedJSharpRedistLocationY03Location of J# framework Redist (Web, SetupExe, Source, None)ISReleaseExtendedMsiEngineVersionYBitfield holding selected MSI engine versions included in this releaseISReleaseExtendedOneClickCabNameYTextFile name of generated cabfileISReleaseExtendedOneClickHtmlNameYTextFile name of generated html pageISReleaseExtendedOneClickTargetBrowserY02Target browser (IE, Netscape, both...)ISReleaseExtendedWebCabSizeY02147483647Size of the cabfileISReleaseExtendedWebLocalCachePathYTextDirectory to cache downloaded packageISReleaseExtendedWebTypeY02Type of web install (One Executable, Downloader...)ISReleaseExtendedWebURLYTextURL to .msi packageISReleaseExtendedWin9xMsiUrlYTextURL to Ansi MSI engineISReleaseExtendedWinMsi30UrlYTextURL to MSI 3.0 engineISReleaseExtendedWinNTMsiUrlYTextURL to Unicode MSI engineISReleasePropertyISProductConfiguration_NTextForeign key into ISProductConfiguration table.ISReleasePropertyISRelease_NTextForeign key into ISRelease table.ISReleasePropertyNameNProperty nameISReleasePropertyValueNProperty valueISReleasePublishInfoDescriptionYTextRepository item descriptionISReleasePublishInfoDisplayNameYTextRepository item display nameISReleasePublishInfoISAttributesYBitfield holding various attributesISReleasePublishInfoISProductConfiguration_NISProductConfiguration1TextForeign key into the ISProductConfiguration table.ISReleasePublishInfoISRelease_NISRelease1TextThe name of the release.ISReleasePublishInfoPublisherYTextRepository item publisherISReleasePublishInfoRepositoryYTextRepository which to publish the built merge moduleISSQLConnectionAttributesN + ISSQLConnectionAuthenticationN + ISSQLConnectionBatchSeparatorY + ISSQLConnectionCmdTimeoutY + ISSQLConnectionCommentsY + ISSQLConnectionDatabaseN + ISSQLConnectionISSQLConnectionNIdentifierPrimary key used to identify a particular ISSQLConnection record.ISSQLConnectionOrderN + ISSQLConnectionPasswordN + ISSQLConnectionScriptVersion_ColumnY + ISSQLConnectionScriptVersion_TableY + ISSQLConnectionServerN + ISSQLConnectionUserNameN + ISSQLConnectionDBServerISSQLConnectionDBServerNIdentifierPrimary key used to identify a particular ISSQLConnectionDBServer record.ISSQLConnectionDBServerISSQLConnection_NISSQLConnection1IdentifierForeign key into ISSQLConnection table.ISSQLConnectionDBServerISSQLDBMetaData_NISSQLDBMetaData1IdentifierForeign key into ISSQLDBMetaData table.ISSQLConnectionDBServerOrderN + ISSQLConnectionScriptISSQLConnection_NISSQLConnection1IdentifierForeign key into ISSQLConnection table.ISSQLConnectionScriptISSQLScriptFile_NISSQLScriptFile1IdentifierForeign key into ISSQLScriptFile table.ISSQLConnectionScriptOrderN + ISSQLDBMetaDataAdoCxnAdditionalY + ISSQLDBMetaDataAdoCxnDatabaseY + ISSQLDBMetaDataAdoCxnDriverY + ISSQLDBMetaDataAdoCxnNetLibraryY + ISSQLDBMetaDataAdoCxnPasswordY + ISSQLDBMetaDataAdoCxnPortY + ISSQLDBMetaDataAdoCxnServerY + ISSQLDBMetaDataAdoCxnUserIDY + ISSQLDBMetaDataAdoCxnWindowsSecurityY + ISSQLDBMetaDataAdoDriverNameY + ISSQLDBMetaDataCreateDbCmdY + ISSQLDBMetaDataCreateTableCmdY + ISSQLDBMetaDataDisplayNameY + ISSQLDBMetaDataDsnODBCNameY + ISSQLDBMetaDataISAttributesY + ISSQLDBMetaDataISSQLDBMetaDataNIdentifierPrimary key used to identify a particular ISSQLDBMetaData record.ISSQLDBMetaDataInsertRecordCmdY + ISSQLDBMetaDataLocalInstanceNamesY + ISSQLDBMetaDataQueryDatabasesCmdY + ISSQLDBMetaDataScriptVersion_ColumnY + ISSQLDBMetaDataScriptVersion_ColumnTypeY + ISSQLDBMetaDataScriptVersion_TableY + ISSQLDBMetaDataSelectTableCmdY + ISSQLDBMetaDataSwitchDbCmdY + ISSQLDBMetaDataTestDatabaseCmdY + ISSQLDBMetaDataTestTableCmdY + ISSQLDBMetaDataTestTableCmd2Y + ISSQLDBMetaDataVersionBeginTokenY + ISSQLDBMetaDataVersionEndTokenY + ISSQLDBMetaDataVersionInfoCmdY + ISSQLDBMetaDataWinAuthentUserIdY + ISSQLRequirementAttributesN + ISSQLRequirementISSQLConnectionDBServer_YISSQLConnectionDBServer1IdentifierForeign key into ISSQLConnectionDBServer table.ISSQLRequirementISSQLConnection_NISSQLConnection1IdentifierForeign key into ISSQLConnection table.ISSQLRequirementISSQLRequirementNIdentifierPrimary key used to identify a particular ISSQLRequirement record.ISSQLRequirementMajorVersionY + ISSQLRequirementServicePackLevelY + ISSQLScriptErrorAttributesN + ISSQLScriptErrorErrHandlingN + ISSQLScriptErrorErrNumberN + ISSQLScriptErrorISSQLScriptFile_YISSQLScriptFile1IdentifierForeign key into ISSQLScriptFile tableISSQLScriptErrorMessageYTextCustom end-user message. Reserved for future use.ISSQLScriptFileAttributesN + ISSQLScriptFileCommentsYTextCommentsISSQLScriptFileComponent_NComponent1IdentifierForeign key referencing Component that controls the SQL script.ISSQLScriptFileConditionYConditionA conditional statement that will disable this script if the specified condition evaluates to the 'False' state. If a script is disabled, it will not be installed regardless of the 'Action' state associated with the component.ISSQLScriptFileDisplayNameYTextDisplay name for the SQL script file.ISSQLScriptFileErrorHandlingN + ISSQLScriptFileISBuildSourcePathYTextFull path, the category is of Text instead of Path because of potential use of path variables.ISSQLScriptFileISSQLScriptFileNIdentifierThis is the primary key to the ISSQLScriptFile tableISSQLScriptFileInstallTextYTextFeedback end-user text at installISSQLScriptFileSchedulingN + ISSQLScriptFileUninstallTextYTextFeedback end-user text at UninstallISSQLScriptFileVersionYTextSchema Version (#####.#####.#####.#####)ISSQLScriptImportAttributesN + ISSQLScriptImportAuthenticationN + ISSQLScriptImportDatabaseY + ISSQLScriptImportExcludeTablesY + ISSQLScriptImportISSQLScriptFile_NISSQLScriptFile1IdentifierForeign key into ISSQLScriptFile table.ISSQLScriptImportIncludeTablesY + ISSQLScriptImportPasswordY + ISSQLScriptImportServerY + ISSQLScriptImportUserNameY + ISSQLScriptReplaceAttributesN + ISSQLScriptReplaceISSQLScriptFile_NISSQLScriptFile1IdentifierForeign key into ISSQLScriptFile table.ISSQLScriptReplaceISSQLScriptReplaceNIdentifierPrimary key used to identify a particular ISSQLScriptReplace record.ISSQLScriptReplaceReplaceY + ISSQLScriptReplaceSearchY + ISScriptFileISScriptFileNTextThis is the full path of the script file. The path portion may be expressed in path variable form.ISSelfRegCmdLineY + ISSelfRegCostY + ISSelfRegFileKeyNFile1IdentifierForeign key to the file tableISSelfRegOrderY + ISSetupFileFileNameYTextThis is the file name to use when streaming the file to the support files locationISSetupFileISSetupFileNIdentifierThis is the primary key to the ISSetupFile tableISSetupFileLanguageYTextFour digit language identifier. 0 for Language NeutralISSetupFilePathYTextLink to the source file on the build machineISSetupFileSplashYShortBoolean value indication whether his setup file entry belongs in the Splasc Screen sectionISSetupFileStreamYBinaryBinary stream. The bits to stream to the support locationISSetupPrerequisitesISBuildSourcePathY + ISSetupPrerequisitesISReleaseFlagsYRelease Flags that specify whether this prereq will be included in a particular release.ISSetupPrerequisitesISSetupLocationY0;1;2 + ISSetupPrerequisitesISSetupPrerequisitesN + ISSetupPrerequisitesOrderY + ISSetupTypeCommentsYTextUser Comments.ISSetupTypeDescriptionYTextLonger descriptive text describing a visible feature item.ISSetupTypeDisplayN032767Numeric sort order, used to force a specific display ordering.ISSetupTypeDisplay_NameYFormattedA string used to set the initial text contained within a control (if appropriate).ISSetupTypeISSetupTypeNIdentifierPrimary key used to identify a particular feature record.ISSetupTypeFeaturesFeature_NFeature1IdentifierForeign key into Feature table.ISSetupTypeFeaturesISSetupType_NISSetupType1IdentifierForeign key into ISSetupType table.ISStoragesISBuildSourcePathYPath to the file to stream into sub-storageISStoragesNameNName of the sub-storage keyISStringCommentYTextCommentISStringEncodedYEncoding for multi-byte strings.ISStringISLanguage_NTextThis is a foreign key to the ISLanguage table.ISStringISStringNTextString id.ISStringTimeStampYTime/DateTime Stamp. MSI's Time/Date column type is just an int, with bits packed in a certain order.ISStringValueYTextreal string value.ISSwidtagPropertyNameNIdentifierProperty nameISSwidtagPropertyValueNTextProperty valueISTargetImageFlagsYrelative order of the target imageISTargetImageIgnoreMissingFilesNIf true, ignore missing source files when creating patchISTargetImageMsiPathNTextPath to the target imageISTargetImageNameNIdentifierName of the TargetImageISTargetImageOrderNrelative order of the target imageISTargetImageUpgradedImage_NISUpgradedImage1Textforeign key to the upgraded Image tableISUpgradeMsiItemISAttributesN0;1 + ISUpgradeMsiItemISReleaseFlagsY + ISUpgradeMsiItemObjectSetupPathNTextThe path to the setup you want to upgrade.ISUpgradeMsiItemUpgradeItemNTextThe name of the Upgrade Item.ISUpgradedImageFamilyNTextName of the image familyISUpgradedImageMsiPathNTextPath to the upgraded imageISUpgradedImageNameNIdentifierName of the UpgradedImageISVirtualDirectoryDirectory_NDirectory1IdentifierForeign key into Directory table.ISVirtualDirectoryNameNIdentifierProperty nameISVirtualDirectoryValueNProperty valueISVirtualFileFile_NFile1IdentifierForeign key into File table.ISVirtualFileNameNIdentifierProperty nameISVirtualFileValueNProperty valueISVirtualPackageNameNIdentifierProperty nameISVirtualPackageValueNProperty valueISVirtualRegistryNameNIdentifierProperty nameISVirtualRegistryRegistry_NRegistry1IdentifierForeign key into Registry table.ISVirtualRegistryValueNProperty valueISVirtualReleaseISProductConfiguration_NTextForeign key into ISProductConfiguration table.ISVirtualReleaseISRelease_NTextForeign key into ISRelease table.ISVirtualReleaseNameNProperty nameISVirtualReleaseValueNProperty valueISVirtualShortcutNameNIdentifierProperty nameISVirtualShortcutShortcut_NShortcut1IdentifierForeign key into Shortcut table.ISVirtualShortcutValueNProperty valueISXmlElementContentYTextElement contentsISXmlElementISAttributesYNumberInternal XML element attributesISXmlElementISXmlElementNIdentifierPrimary key, non-localized, internal token for Xml elementISXmlElementISXmlElement_ParentYISXmlElement1IdentifierForeign key into ISXMLElement table.ISXmlElementISXmlFile_NISXmlFile1IdentifierForeign key into XmlFile table.ISXmlElementXPathYTextXPath fragment including any operatorsISXmlElementAttribISAttributesYNumberInternal XML elementattib attributesISXmlElementAttribISXmlElementAttribNIdentifierPrimary key, non-localized, internal token for Xml element attributeISXmlElementAttribISXmlElement_NISXmlElement1IdentifierForeign key into ISXMLElement table.ISXmlElementAttribNameYTextLocalized attribute nameISXmlElementAttribValueYTextLocalized attribute valueISXmlFileComponent_NComponent1IdentifierForeign key into Component table.ISXmlFileDirectoryNIdentifierForeign key into Directory table.ISXmlFileEncodingYTextXML File EncodingISXmlFileFileNameNTextLocalized XML file nameISXmlFileISAttributesYNumberInternal XML file attributesISXmlFileISXmlFileNIdentifierPrimary key, non-localized,internal token for Xml fileISXmlFileSelectionNamespacesYTextSelection namespacesISXmlLocatorAttributeYThe name of an attribute within the XML element.ISXmlLocatorElementYXPath query that will locate an element in an XML file.ISXmlLocatorISAttributesY0;1;2 + ISXmlLocatorParentYIdentifierThe parent file signature. It is also a foreign key in the Signature table.ISXmlLocatorSignature_NIdentifierThe Signature_ represents a unique file signature and is also the foreign key in the Signature, RegLocator, IniLocator, ISXmlLocator, CompLocator and the DrLocator tables.IconDataYBinaryBinary stream. The binary icon data in PE (.DLL or .EXE) or icon (.ICO) format.IconISBuildSourcePathYTextFull path to the ICO or EXE file.IconISIconIndexY-3276732767Optional icon index to be extracted.IconNameNIdentifierPrimary key. Name of the icon file.IniFileActionN0;1;3The type of modification to be made, one of iifEnumIniFileComponent_NComponent1IdentifierForeign key into the Component table referencing component that controls the installing of the .INI value.IniFileDirPropertyYIdentifierForeign key into the Directory table denoting the directory where the .INI file is.IniFileFileNameNTextThe .INI file name in which to write the informationIniFileIniFileNIdentifierPrimary key, non-localized token.IniFileKeyNFormattedThe .INI file key below Section.IniFileSectionNFormattedThe .INI file Section.IniFileValueNFormattedThe value to be written.IniLocatorFieldY032767The field in the .INI line. If Field is null or 0 the entire line is read.IniLocatorFileNameNTextThe .INI file name.IniLocatorKeyNTextKey value (followed by an equals sign in INI file).IniLocatorSectionNTextSection name within in file (within square brackets in INI file).IniLocatorSignature_NSignature1IdentifierThe table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table.IniLocatorTypeY02An integer value that determines if the .INI value read is a filename or a directory location or to be used as is w/o interpretation.InstallExecuteSequenceActionNIdentifierName of action to invoke, either in the engine or the handler DLL.InstallExecuteSequenceConditionYConditionOptional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.InstallExecuteSequenceISAttributesYThis is used to store MM Custom Action TypesInstallExecuteSequenceISCommentsYTextAuthor’s comments on this Sequence.InstallExecuteSequenceSequenceY-432767Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.InstallShieldPropertyNIdentifierName of property, uppercase if settable by launcher or loader.InstallShieldValueYTextString value for property.InstallUISequenceActionNIdentifierName of action to invoke, either in the engine or the handler DLL.InstallUISequenceConditionYConditionOptional expression which skips the action if evaluates to expFalse.If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.InstallUISequenceISAttributesYThis is used to store MM Custom Action TypesInstallUISequenceISCommentsYTextAuthor’s comments on this Sequence.InstallUISequenceSequenceY-432767Number that determines the sort order in which the actions are to be executed. Leave blank to suppress action.IsolatedComponentComponent_ApplicationNComponent1IdentifierKey to Component table item for applicationIsolatedComponentComponent_SharedNComponent1IdentifierKey to Component table item to be isolatedLaunchConditionConditionNConditionExpression which must evaluate to TRUE in order for install to commence.LaunchConditionDescriptionNTextLocalizable text to display when condition fails and install must abort.ListBoxOrderN132767A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.ListBoxPropertyNIdentifierA named property to be tied to this item. All the items tied to the same property become part of the same listbox.ListBoxTextYTextThe visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.ListBoxValueNFormattedThe value string associated with this item. Selecting the line will set the associated property to this value.ListViewBinary_YBinary1IdentifierThe name of the icon to be displayed with the icon. The binary information is looked up from the Binary Table.ListViewOrderN132767A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.ListViewPropertyNIdentifierA named property to be tied to this item. All the items tied to the same property become part of the same listview.ListViewTextYTextThe visible text to be assigned to the item. Optional. If this entry or the entire column is missing, the text is the same as the value.ListViewValueNTextThe value string associated with this item. Selecting the line will set the associated property to this value.LockPermissionsDomainYTextDomain name for user whose permissions are being set. (usually a property)LockPermissionsLockObjectNIdentifierForeign key into Registry or File tableLockPermissionsPermissionY-21474836472147483647Permission Access mask. Full Control = 268435456 (GENERIC_ALL = 0x10000000)LockPermissionsTableNIdentifierDirectory;File;RegistryReference to another table nameLockPermissionsUserNTextUser for permissions to be set. (usually a property)MIMECLSIDYClass1GuidOptional associated CLSID.MIMEContentTypeNTextPrimary key. Context identifier, typically "type/format".MIMEExtension_NExtension1TextOptional associated extension (without dot)MediaCabinetYCabinetIf some or all of the files stored on the media are compressed in a cabinet, the name of that cabinet.MediaDiskIdN132767Primary key, integer to determine sort order for table.MediaDiskPromptYTextDisk name: the visible text actually printed on the disk. This will be used to prompt the user when this disk needs to be inserted.MediaLastSequenceN032767File sequence number for the last file for this media.MediaSourceYPropertyThe property defining the location of the cabinet file.MediaVolumeLabelYTextThe label attributed to the volume.MoveFileComponent_NComponent1IdentifierIf this component is not "selected" for installation or removal, no action will be taken on the associated MoveFile entryMoveFileDestFolderNIdentifierName of a property whose value is assumed to resolve to the full path to the destination directoryMoveFileDestNameYTextName to be given to the original file after it is moved or copied. If blank, the destination file will be given the same name as the source fileMoveFileFileKeyNIdentifierPrimary key that uniquely identifies a particular MoveFile recordMoveFileOptionsN01Integer value specifying the MoveFile operating mode, one of imfoEnumMoveFileSourceFolderYIdentifierName of a property whose value is assumed to resolve to the full path to the source directoryMoveFileSourceNameYTextName of the source file(s) to be moved or copied. Can contain the '*' or '?' wildcards.MsiAssemblyAttributesYAssembly attributesMsiAssemblyComponent_NComponent1IdentifierForeign key into Component table.MsiAssemblyFeature_NFeature1IdentifierForeign key into Feature table.MsiAssemblyFile_ApplicationYFile1IdentifierForeign key into File table, denoting the application context for private assemblies. Null for global assemblies.MsiAssemblyFile_ManifestYFile1IdentifierForeign key into the File table denoting the manifest file for the assembly.MsiAssemblyNameComponent_NComponent1IdentifierForeign key into Component table.MsiAssemblyNameNameNTextThe name part of the name-value pairs for the assembly name.MsiAssemblyNameValueNTextThe value part of the name-value pairs for the assembly name.MsiDigitalCertificateCertDataNBinaryA certificate context blob for a signer certificateMsiDigitalCertificateDigitalCertificateNMsiPackageCertificate2IdentifierA unique identifier for the rowMsiDigitalSignatureDigitalCertificate_NMsiDigitalCertificate1IdentifierForeign key to MsiDigitalCertificate table identifying the signer certificateMsiDigitalSignatureHashYBinaryThe encoded hash blob from the digital signatureMsiDigitalSignatureSignObjectNTextForeign key to Media tableMsiDigitalSignatureTableNIdentifierReference to another table name (only Media table is supported)MsiDriverPackagesComponentNComponent1IdentifierPrimary key used to identify a particular component record.MsiDriverPackagesFlagsNDriver package flagsMsiDriverPackagesReferenceComponentsY + MsiDriverPackagesSequenceYInstallation sequence numberMsiEmbeddedChainerCommandLineYFormatted + MsiEmbeddedChainerConditionYCondition + MsiEmbeddedChainerMsiEmbeddedChainerNIdentifier + MsiEmbeddedChainerSourceNCustomSource + MsiEmbeddedChainerTypeYInteger2;18;50 + MsiEmbeddedUIAttributesN03IntegerInformation about the data in the Data column.MsiEmbeddedUIDataYBinaryThis column contains binary information.MsiEmbeddedUIFileNameNFilenameThe name of the file that receives the binary information in the Data column.MsiEmbeddedUIISBuildSourcePathYText + MsiEmbeddedUIMessageFilterY0234913791IntegerSpecifies the types of messages that are sent to the user interface DLL. This column is only relevant for rows with the msidbEmbeddedUI attribute.MsiEmbeddedUIMsiEmbeddedUINIdentifierThe primary key for the table.MsiFileHashFile_NFile1IdentifierPrimary key, foreign key into File table referencing file with this hashMsiFileHashHashPart1NSize of file in bytes (long integer).MsiFileHashHashPart2NSize of file in bytes (long integer).MsiFileHashHashPart3NSize of file in bytes (long integer).MsiFileHashHashPart4NSize of file in bytes (long integer).MsiFileHashOptionsN032767Various options and attributes for this hash.MsiLockPermissionsExConditionYFormattedExpression which must evaluate to TRUE in order for this set of permissions to be appliedMsiLockPermissionsExLockObjectNIdentifierForeign key into Registry, File, CreateFolder, or ServiceInstall tableMsiLockPermissionsExMsiLockPermissionsExNIdentifierPrimary key, non-localized tokenMsiLockPermissionsExSDDLTextNFormattedSDDLTextString to indicate permissions to be applied to the LockObjectMsiLockPermissionsExTableNIdentifierCreateFolder;File;Registry;ServiceInstallReference to another table nameMsiPackageCertificateDigitalCertificate_NIdentifierA foreign key to the digital certificate tableMsiPackageCertificatePackageCertificateNIdentifierA unique identifier for the rowMsiPatchCertificateDigitalCertificate_NMsiDigitalCertificate1IdentifierA foreign key to the digital certificate tableMsiPatchCertificatePatchCertificateNIdentifierA unique identifier for the rowMsiPatchMetadataCompanyYTextOptional company nameMsiPatchMetadataPatchConfiguration_NISPatchConfiguration1TextForeign key to the ISPatchConfiguration tableMsiPatchMetadataPropertyNTextName of the metadataMsiPatchMetadataValueYTextValue of the metadataMsiPatchOldAssemblyFileAssembly_YMsiPatchOldAssemblyName1 + MsiPatchOldAssemblyFileFile_NFile1 + MsiPatchOldAssemblyNameAssemblyN + MsiPatchOldAssemblyNameNameN + MsiPatchOldAssemblyNameValueY + MsiPatchSequencePatchConfiguration_NISPatchConfiguration1TextForeign key to the patch configuration tableMsiPatchSequencePatchFamilyNTextName of the family to which this patch belongsMsiPatchSequenceSequenceNVersionThe version of this patch in this familyMsiPatchSequenceSupersedeNIntegerSupersedeMsiPatchSequenceTargetYTextTarget product codes for this patch familyMsiServiceConfigArgumentYTextArgument(s) for service configuration. Value depends on the content of the ConfigType fieldMsiServiceConfigComponent_NComponent1IdentifierRequired foreign key into the Component Table that controls the configuration of the serviceMsiServiceConfigConfigTypeN-21474836472147483647Service Configuration OptionMsiServiceConfigEventN07Bit field: 0x1 = Install, 0x2 = Uninstall, 0x4 = ReinstallMsiServiceConfigMsiServiceConfigNIdentifierPrimary key, non-localized token.MsiServiceConfigNameNFormattedName of a service. /, \, comma and space are invalidMsiServiceConfigFailureActionsActionsYTextA list of integer actions separated by [~] delimiters: 0 = SC_ACTION_NONE, 1 = SC_ACTION_RESTART, 2 = SC_ACTION_REBOOT, 3 = SC_ACTION_RUN_COMMAND. Terminate with [~][~]MsiServiceConfigFailureActionsCommandYFormattedCommand line of the process to CreateProcess function to executeMsiServiceConfigFailureActionsComponent_NComponent1IdentifierRequired foreign key into the Component Table that controls the configuration of the serviceMsiServiceConfigFailureActionsDelayActionsYTextA list of delays (time in milli-seconds), separated by [~] delmiters, to wait before taking the corresponding Action. Terminate with [~][~]MsiServiceConfigFailureActionsEventN07Bit field: 0x1 = Install, 0x2 = Uninstall, 0x4 = ReinstallMsiServiceConfigFailureActionsMsiServiceConfigFailureActionsNIdentifierPrimary key, non-localized tokenMsiServiceConfigFailureActionsNameNFormattedName of a service. /, \, comma and space are invalidMsiServiceConfigFailureActionsRebootMessageYFormattedMessage to be broadcast to server users before rebootingMsiServiceConfigFailureActionsResetPeriodY02147483647Time in seconds after which to reset the failure count to zero. Leave blank if it should never be resetMsiShortcutPropertyMsiShortcutPropertyNIdentifierPrimary key, non-localized tokenMsiShortcutPropertyPropVariantValueNFormattedString representation of the value in the propertyMsiShortcutPropertyPropertyKeyNFormattedCanonical string representation of the Property Key being setMsiShortcutPropertyShortcut_NShortcut1IdentifierForeign key into the Shortcut tableODBCAttributeAttributeNTextName of ODBC driver attributeODBCAttributeDriver_NODBCDriver1IdentifierReference to ODBC driver in ODBCDriver tableODBCAttributeValueYTextValue for ODBC driver attributeODBCDataSourceComponent_NComponent1IdentifierReference to associated componentODBCDataSourceDataSourceNIdentifierPrimary key, non-localized.internal token for data sourceODBCDataSourceDescriptionNTextText used as registered name for data sourceODBCDataSourceDriverDescriptionNTextReference to driver description, may be existing driverODBCDataSourceRegistrationN01Registration option: 0=machine, 1=user, others t.b.d.ODBCDriverComponent_NComponent1IdentifierReference to associated componentODBCDriverDescriptionNTextText used as registered name for driver, non-localizedODBCDriverDriverNIdentifierPrimary key, non-localized.internal token for driverODBCDriverFile_NFile1IdentifierReference to key driver fileODBCDriverFile_SetupYFile1IdentifierOptional reference to key driver setup DLLODBCSourceAttributeAttributeNTextName of ODBC data source attributeODBCSourceAttributeDataSource_NODBCDataSource1IdentifierReference to ODBC data source in ODBCDataSource tableODBCSourceAttributeValueYTextValue for ODBC data source attributeODBCTranslatorComponent_NComponent1IdentifierReference to associated componentODBCTranslatorDescriptionNTextText used as registered name for translatorODBCTranslatorFile_NFile1IdentifierReference to key translator fileODBCTranslatorFile_SetupYFile1IdentifierOptional reference to key translator setup DLLODBCTranslatorTranslatorNIdentifierPrimary key, non-localized.internal token for translatorPatchAttributesN032767Integer containing bit flags representing patch attributesPatchFile_NFile1IdentifierPrimary key, non-localized token, foreign key to File table, must match identifier in cabinet.PatchHeaderYBinaryBinary stream. The patch header, used for patch validation.PatchISBuildSourcePathYTextFull path to patch header.PatchPatchSizeN02147483647Size of patch in bytes (long integer).PatchSequenceN032767Primary key, sequence with respect to the media images; order must track cabinet order.PatchStreamRef_YIdentifierExternal key into the MsiPatchHeaders table specifying the row that contains the patch header stream.PatchPackageMedia_N032767Foreign key to DiskId column of Media table. Indicates the disk containing the patch package.PatchPackagePatchIdNGuidA unique string GUID representing this patch.ProgIdClass_YClass1GuidThe CLSID of an OLE factory corresponding to the ProgId.ProgIdDescriptionYTextLocalized description for the Program identifier.ProgIdISAttributesYThis is used to store Installshield custom properties of a component, like ExtractIcon, etc.ProgIdIconIndexY-3276732767Optional icon index.ProgIdIcon_YIcon1IdentifierOptional foreign key into the Icon Table, specifying the icon file associated with this ProgId. Will be written under the DefaultIcon key.ProgIdProgIdNTextThe Program Identifier. Primary key.ProgIdProgId_ParentYProgId1TextThe Parent Program Identifier. If specified, the ProgId column becomes a version independent prog id.PropertyISCommentsYTextUser Comments.PropertyPropertyNIdentifierName of property, uppercase if settable by launcher or loader.PropertyValueYTextString value for property.PublishComponentAppDataYTextThis is localisable Application specific data that can be associated with a Qualified Component.PublishComponentComponentIdNGuidA string GUID that represents the component id that will be requested by the alien product.PublishComponentComponent_NComponent1IdentifierForeign key into the Component table.PublishComponentFeature_NFeature1IdentifierForeign key into the Feature table.PublishComponentQualifierNTextThis is defined only when the ComponentId column is an Qualified Component Id. This is the Qualifier for ProvideComponentIndirect.RadioButtonHeightN032767The height of the button.RadioButtonHelpYTextThe help strings used with the button. The text is optional.RadioButtonISControlIdYA number used to represent the control ID of the Control, Used in Dialog exportRadioButtonOrderN132767A positive integer used to determine the ordering of the items within one list..The integers do not have to be consecutive.RadioButtonPropertyNIdentifierA named property to be tied to this radio button. All the buttons tied to the same property become part of the same group.RadioButtonTextYTextThe visible title to be assigned to the radio button.RadioButtonValueNFormattedThe value string associated with this button. Selecting the button will set the associated property to this value.RadioButtonWidthN032767The width of the button.RadioButtonXN032767The horizontal coordinate of the upper left corner of the bounding rectangle of the radio button.RadioButtonYN032767The vertical coordinate of the upper left corner of the bounding rectangle of the radio button.RegLocatorKeyNRegPathThe key for the registry value.RegLocatorNameYFormattedThe registry value name.RegLocatorRootN03The predefined root key for the registry value, one of rrkEnum.RegLocatorSignature_NSignature1IdentifierThe table key. The Signature_ represents a unique file signature and is also the foreign key in the Signature table. If the type is 0, the registry values refers a directory, and _Signature is not a foreign key.RegLocatorTypeY018An integer value that determines if the registry value is a filename or a directory location or to be used as is w/o interpretation.RegistryComponent_NComponent1IdentifierForeign key into the Component table referencing component that controls the installing of the registry value.RegistryISAttributesYThis is used to store Installshield custom properties of a registry item. Currently the only one is Automatic.RegistryKeyNRegPathThe key for the registry value.RegistryNameYFormattedThe registry value name.RegistryRegistryNIdentifierPrimary key, non-localized token.RegistryRootN-13The predefined root key for the registry value, one of rrkEnum.RegistryValueYTextThe registry value.RemoveFileComponent_NComponent1IdentifierForeign key referencing Component that controls the file to be removed.RemoveFileDirPropertyNIdentifierName of a property whose value is assumed to resolve to the full pathname to the folder of the file to be removed.RemoveFileFileKeyNIdentifierPrimary key used to identify a particular file entryRemoveFileFileNameYTextName of the file to be removed.RemoveFileInstallModeN1;2;3Installation option, one of iimEnum.RemoveIniFileActionN2;4The type of modification to be made, one of iifEnum.RemoveIniFileComponent_NComponent1IdentifierForeign key into the Component table referencing component that controls the deletion of the .INI value.RemoveIniFileDirPropertyYIdentifierForeign key into the Directory table denoting the directory where the .INI file is.RemoveIniFileFileNameNTextThe .INI file name in which to delete the informationRemoveIniFileKeyNFormattedThe .INI file key below Section.RemoveIniFileRemoveIniFileNIdentifierPrimary key, non-localized token.RemoveIniFileSectionNFormattedThe .INI file Section.RemoveIniFileValueYFormattedThe value to be deleted. The value is required when Action is iifIniRemoveTagRemoveRegistryComponent_NComponent1IdentifierForeign key into the Component table referencing component that controls the deletion of the registry value.RemoveRegistryKeyNRegPathThe key for the registry value.RemoveRegistryNameYFormattedThe registry value name.RemoveRegistryRemoveRegistryNIdentifierPrimary key, non-localized token.RemoveRegistryRootN-13The predefined root key for the registry value, one of rrkEnumReserveCostComponent_NComponent1IdentifierReserve a specified amount of space if this component is to be installed.ReserveCostReserveFolderYIdentifierName of a property whose value is assumed to resolve to the full path to the destination directoryReserveCostReserveKeyNIdentifierPrimary key that uniquely identifies a particular ReserveCost recordReserveCostReserveLocalN02147483647Disk space to reserve if linked component is installed locally.ReserveCostReserveSourceN02147483647Disk space to reserve if linked component is installed to run from the source location.SFPCatalogCatalogYBinarySFP CatalogSFPCatalogDependencyYFormattedParent catalog - only used by SFPSFPCatalogSFPCatalogNFilenameFile name for the catalog.SelfRegCostY032767The cost of registering the module.SelfRegFile_NFile1IdentifierForeign key into the File table denoting the module that needs to be registered.ServiceControlArgumentsYFormattedArguments for the service. Separate by [~].ServiceControlComponent_NComponent1IdentifierRequired foreign key into the Component Table that controls the startup of the serviceServiceControlEventN0187Bit field: Install: 0x1 = Start, 0x2 = Stop, 0x8 = Delete, Uninstall: 0x10 = Start, 0x20 = Stop, 0x80 = DeleteServiceControlNameNFormattedName of a service. /, \, comma and space are invalidServiceControlServiceControlNIdentifierPrimary key, non-localized token.ServiceControlWaitY01Boolean for whether to wait for the service to fully startServiceInstallArgumentsYFormattedArguments to include in every start of the service, passed to WinMainServiceInstallComponent_NComponent1IdentifierRequired foreign key into the Component Table that controls the startup of the serviceServiceInstallDependenciesYFormattedOther services this depends on to start. Separate by [~], and end with [~][~]ServiceInstallDescriptionYTextDescription of service.ServiceInstallDisplayNameYFormattedExternal Name of the ServiceServiceInstallErrorControlN-21474836472147483647Severity of error if service fails to startServiceInstallLoadOrderGroupYFormattedLoadOrderGroupServiceInstallNameNFormattedInternal Name of the ServiceServiceInstallPasswordYFormattedpassword to run service with. (with StartName)ServiceInstallServiceInstallNIdentifierPrimary key, non-localized token.ServiceInstallServiceTypeN-21474836472147483647Type of the serviceServiceInstallStartNameYFormattedUser or object name to run service asServiceInstallStartTypeN04Type of the serviceShortcutArgumentsYFormattedThe command-line arguments for the shortcut.ShortcutComponent_NComponent1IdentifierForeign key into the Component table denoting the component whose selection gates the the shortcut creation/deletion.ShortcutDescriptionYTextThe description for the shortcut.ShortcutDescriptionResourceDLLYFormattedThis field contains a Formatted string value for the full path to the language neutral file that contains the MUI manifest.ShortcutDescriptionResourceIdY032767The description name index for the shortcut.ShortcutDirectory_NDirectory1IdentifierForeign key into the Directory table denoting the directory where the shortcut file is created.ShortcutDisplayResourceDLLYFormattedThis field contains a Formatted string value for the full path to the language neutral file that contains the MUI manifest.ShortcutDisplayResourceIdY032767The display name index for the shortcut.ShortcutHotkeyY032767The hotkey for the shortcut. It has the virtual-key code for the key in the low-order byte, and the modifier flags in the high-order byte.ShortcutISAttributesYThis is used to store Installshield custom properties of a shortcut. Mainly used in pro project types.ShortcutISCommentsYTextAuthor’s comments on this Shortcut.ShortcutISShortcutNameYTextA non-unique name for the shortcut. Mainly used by pro pro project types.ShortcutIconIndexY-3276732767The icon index for the shortcut.ShortcutIcon_YIcon1IdentifierForeign key into the File table denoting the external icon file for the shortcut.ShortcutNameNTextThe name of the shortcut to be created.ShortcutShortcutNIdentifierPrimary key, non-localized token.ShortcutShowCmdY1;3;7The show command for the application window.The following values may be used.ShortcutTargetNShortcutThe shortcut target. This is usually a property that is expanded to a file or a folder that the shortcut points to.ShortcutWkDirYIdentifierName of property defining location of working directory.SignatureFileNameNTextThe name of the file. This may contain a "short name|long name" pair.SignatureLanguagesYLanguageThe languages supported by the file.SignatureMaxDateY02147483647The maximum creation date of the file.SignatureMaxSizeY02147483647The maximum size of the file.SignatureMaxVersionYTextThe maximum version of the file.SignatureMinDateY02147483647The minimum creation date of the file.SignatureMinSizeY02147483647The minimum size of the file.SignatureMinVersionYTextThe minimum version of the file.SignatureSignatureNIdentifierThe table key. The Signature represents a unique file signature.TextStyleColorY016777215A long integer indicating the color of the string in the RGB format (Red, Green, Blue each 0-255, RGB = R + 256*G + 256^2*B).TextStyleFaceNameNTextA string indicating the name of the font used. Required. The string must be at most 31 characters long.TextStyleSizeN032767The size of the font used. This size is given in our units (1/12 of the system font height). Assuming that the system font is set to 12 point size, this is equivalent to the point size.TextStyleStyleBitsY015A combination of style bits.TextStyleTextStyleNIdentifierName of the style. The primary key of this table. This name is embedded in the texts to indicate a style change.TypeLibComponent_NComponent1IdentifierRequired foreign key into the Component Table, specifying the component for which to return a path when called through LocateComponent.TypeLibCostY02147483647The cost associated with the registration of the typelib. This column is currently optional.TypeLibDescriptionYText + TypeLibDirectory_YDirectory1IdentifierOptional. The foreign key into the Directory table denoting the path to the help file for the type library.TypeLibFeature_NFeature1IdentifierRequired foreign key into the Feature Table, specifying the feature to validate or install in order for the type library to be operational.TypeLibLanguageN032767The language of the library.TypeLibLibIDNGuidThe GUID that represents the library.TypeLibVersionY02147483647The version of the library. The major version is in the upper 8 bits of the short integer. The minor version is in the lower 8 bits.UITextKeyNIdentifierA unique key that identifies the particular string.UITextTextYTextThe localized version of the string.UpgradeActionPropertyNUpperCaseThe property to set when a product in this set is found.UpgradeAttributesN02147483647The attributes of this product set.UpgradeISDisplayNameYISUpgradeMsiItem1 + UpgradeLanguageYLanguageA comma-separated list of languages for either products in this set or products not in this set.UpgradeRemoveYFormattedThe list of features to remove when uninstalling a product from this set. The default is "ALL".UpgradeUpgradeCodeNGuidThe UpgradeCode GUID belonging to the products in this set.UpgradeVersionMaxYTextThe maximum ProductVersion of the products in this set. The set may or may not include products with this particular version.UpgradeVersionMinYTextThe minimum ProductVersion of the products in this set. The set may or may not include products with this particular version.VerbArgumentYFormattedOptional value for the command arguments.VerbCommandYFormattedThe command text.VerbExtension_NExtension1TextThe extension associated with the table row.VerbSequenceY032767Order within the verbs for a particular extension. Also used simply to specify the default verb.VerbVerbNTextThe verb for the command._ValidationCategoryY"Text";"Formatted";"Template";"Condition";"Guid";"Path";"Version";"Language";"Identifier";"Binary";"UpperCase";"LowerCase";"Filename";"Paths";"AnyPath";"WildCardFilename";"RegPath";"KeyFormatted";"CustomSource";"Property";"Cabinet";"Shortcut";"URL";"DefaultDir"String category_ValidationColumnNIdentifierName of column_ValidationDescriptionYTextDescription of column_ValidationKeyColumnY132Column to which foreign key connects_ValidationKeyTableYIdentifierFor foreign key, Name of table to which data must link_ValidationMaxValueY-21474836472147483647Maximum value allowed_ValidationMinValueY-21474836472147483647Minimum value allowed_ValidationNullableNY;N;@Whether the column is nullable_ValidationSetYTextSet of values that are permitted_ValidationTableNIdentifierName of table
+
diff --git a/OpaqueMail.Net.ProxyInstaller/OpaqueMail.Net.ProxyInstaller.isproj b/OpaqueMail.Net.ProxyInstaller/OpaqueMail.Net.ProxyInstaller.isproj new file mode 100644 index 0000000..ce2fdee --- /dev/null +++ b/OpaqueMail.Net.ProxyInstaller/OpaqueMail.Net.ProxyInstaller.isproj @@ -0,0 +1,30 @@ + + + + Express + + Debug + $(Configuration) + + + + + + + + + + + + + + + + + + diff --git a/OpaqueMail.Net.ProxyService/Program.cs b/OpaqueMail.Net.ProxyService/Program.cs index f445f9d..edc3920 100644 --- a/OpaqueMail.Net.ProxyService/Program.cs +++ b/OpaqueMail.Net.ProxyService/Program.cs @@ -1,25 +1,25 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.ServiceProcess; -using System.Text; -using System.Threading.Tasks; - -namespace OpaqueMail -{ - static class Program - { - /// - /// The main entry point for the application. - /// - static void Main() - { - ServiceBase[] ServicesToRun; - ServicesToRun = new ServiceBase[] - { - new ProxyService() - }; - ServiceBase.Run(ServicesToRun); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.ServiceProcess; +using System.Text; +using System.Threading.Tasks; + +namespace OpaqueMail +{ + static class Program + { + /// + /// The main entry point for the application. + /// + static void Main() + { + ServiceBase[] ServicesToRun; + ServicesToRun = new ServiceBase[] + { + new ProxyService() + }; + ServiceBase.Run(ServicesToRun); + } + } +} diff --git a/OpaqueMail.Net.ProxyService/Properties/AssemblyInfo.cs b/OpaqueMail.Net.ProxyService/Properties/AssemblyInfo.cs index 65f9873..0d3fc33 100644 --- a/OpaqueMail.Net.ProxyService/Properties/AssemblyInfo.cs +++ b/OpaqueMail.Net.ProxyService/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpaqueMail.Net.ProxyService")] -[assembly: AssemblyDescription("SMTP proxy to add or remove S/MIME message signing, encryption, and authentication for outbound messages.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Bert Johnson")] -[assembly: AssemblyProduct("OpaqueMail")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("a9696089-4b6c-47c6-b2a1-cd692d45287b")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.2")] -[assembly: AssemblyFileVersion("1.4.2.2")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpaqueMail.Net.ProxyService")] +[assembly: AssemblyDescription("SMTP proxy to add or remove S/MIME message signing, encryption, and authentication for outbound messages.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Bert Johnson")] +[assembly: AssemblyProduct("OpaqueMail")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a9696089-4b6c-47c6-b2a1-cd692d45287b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.5.0")] +[assembly: AssemblyFileVersion("1.5.0.0")] diff --git a/OpaqueMail.Net.ProxyService/ProxyService.cs b/OpaqueMail.Net.ProxyService/ProxyService.cs index 214e20f..d8bceef 100644 --- a/OpaqueMail.Net.ProxyService/ProxyService.cs +++ b/OpaqueMail.Net.ProxyService/ProxyService.cs @@ -1,249 +1,249 @@ -using OpaqueMail.Net.Proxy; -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Configuration.Install; -using System.Data; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using System.ServiceProcess; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml.XPath; - -namespace OpaqueMail -{ - public partial class ProxyService : ServiceBase - { - #region Private Members - /// List of all proxies that have been started. - private List imapProxies = null; - private List pop3Proxies = null; - private List smtpProxies = null; - #endregion Private Members - - #region Constructors - /// - /// Default constructor. - /// - public ProxyService() - { - InitializeComponent(); - } - #endregion Constructors - - #region Protected Methods - /// - /// Handle the service start event by reading the settings file and starting all specified proxy instances. - /// - protected override void OnStart(string[] args) - { - imapProxies = ImapProxy.StartProxiesFromSettingsFile(GetSettingsFileName()); - pop3Proxies = Pop3Proxy.StartProxiesFromSettingsFile(GetSettingsFileName()); - smtpProxies = SmtpProxy.StartProxiesFromSettingsFile(GetSettingsFileName()); - } - - /// - /// Handle the service stop event by stopping all proxies. - /// - protected override void OnStop() - { - if (imapProxies != null) - { - foreach (ImapProxy imapProxy in imapProxies) - imapProxy.Stop(); - - imapProxies.Clear(); - } - if (pop3Proxies != null) - { - foreach (Pop3Proxy pop3Proxy in pop3Proxies) - pop3Proxy.Stop(); - - pop3Proxies.Clear(); - } - if (smtpProxies != null) - { - foreach (SmtpProxy smtpProxy in smtpProxies) - smtpProxy.Stop(); - - smtpProxies.Clear(); - } - } - - /// - /// Handle service continuations following pauses. - /// - protected override void OnContinue() - { - if (imapProxies != null) - { - foreach (ImapProxy imapProxy in imapProxies) - imapProxy.ProcessContinuation(); - - imapProxies.Clear(); - } - if (pop3Proxies != null) - { - foreach (Pop3Proxy pop3Proxy in pop3Proxies) - pop3Proxy.ProcessContinuation(); - - pop3Proxies.Clear(); - } - if (smtpProxies != null) - { - foreach (SmtpProxy smtpProxy in smtpProxies) - smtpProxy.ProcessContinuation(); - - smtpProxies.Clear(); - } - } - - /// - /// Handle pause event. - /// - protected override void OnPause() - { - if (imapProxies != null) - { - foreach (ImapProxy imapProxy in imapProxies) - imapProxy.ProcessPause(); - - imapProxies.Clear(); - } - if (pop3Proxies != null) - { - foreach (Pop3Proxy pop3Proxy in pop3Proxies) - pop3Proxy.ProcessPause(); - - pop3Proxies.Clear(); - } - if (smtpProxies != null) - { - foreach (SmtpProxy smtpProxy in smtpProxies) - smtpProxy.ProcessPause(); - - smtpProxies.Clear(); - } - } - - /// - /// Handle power events, such as hibernation. - /// - protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) - { - if (imapProxies != null) - { - foreach (ImapProxy imapProxy in imapProxies) - imapProxy.ProcessPowerEvent((int)powerStatus); - - imapProxies.Clear(); - } - if (pop3Proxies != null) - { - foreach (Pop3Proxy pop3Proxy in pop3Proxies) - pop3Proxy.ProcessPowerEvent((int)powerStatus); - - pop3Proxies.Clear(); - } - if (smtpProxies != null) - { - foreach (SmtpProxy smtpProxy in smtpProxies) - smtpProxy.ProcessPowerEvent((int)powerStatus); - - smtpProxies.Clear(); - } - - return base.OnPowerEvent(powerStatus); - } - #endregion Protected Methods - - #region Private Methods - /// - /// Return the path where the service's settings should be saved and loaded. - /// - private static string GetSettingsFileName() - { - return AppDomain.CurrentDomain.BaseDirectory + "\\OpaqueMail.Proxy.xml"; - } - #endregion Private Methods - } - - /// - /// Set the service account to the local system. - /// - [RunInstaller(true)] - public sealed class ProxyServiceProcessInstaller : ServiceProcessInstaller - { - public ProxyServiceProcessInstaller() - { - Account = ServiceAccount.LocalSystem; - } - } - - /// - /// Handle OpaqueMail Proxy service installation. - /// - [RunInstaller(true)] - public sealed class ProxyServiceInstaller : ServiceInstaller - { - /// - /// Default constructor. - /// - public ProxyServiceInstaller() - { - Description = "Serves as a local SMTP, IMAP, and POP3 proxy, optionally adding authentication and S/MIME signing or encryption to all outbound email."; - DisplayName = "OpaqueMail Proxy"; - ServiceName = "OpaqueMailProxy"; - StartType = ServiceStartMode.Automatic; - } - - /// - /// Handle installation and uninstallation. - /// - /// Whether we're uninstalling. False if installing, true if uninstalling - /// Any service installation arguments. - public void Install(bool uninstall, string[] args) - { - try - { - using (AssemblyInstaller installer = new AssemblyInstaller(typeof(Program).Assembly, args)) - { - IDictionary state = new Hashtable(); - installer.UseNewContext = true; - try - { - // Attempt to install or uninstall. - if (uninstall) - installer.Uninstall(state); - else - { - installer.Install(state); - installer.Commit(state); - } - } - catch - { - // If an error is encountered, attempt to roll back. - try - { - installer.Rollback(state); - } - catch { } - - throw; - } - } - } - catch (Exception ex) - { - Console.Error.WriteLine(ex.Message); - } - } - } -} +using OpaqueMail.Net.Proxy; +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Configuration.Install; +using System.Data; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Security.Cryptography.X509Certificates; +using System.ServiceProcess; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.XPath; + +namespace OpaqueMail +{ + public partial class ProxyService : ServiceBase + { + #region Private Members + /// List of all proxies that have been started. + private List imapProxies = null; + private List pop3Proxies = null; + private List smtpProxies = null; + #endregion Private Members + + #region Constructors + /// + /// Default constructor. + /// + public ProxyService() + { + InitializeComponent(); + } + #endregion Constructors + + #region Protected Methods + /// + /// Handle the service start event by reading the settings file and starting all specified proxy instances. + /// + protected override void OnStart(string[] args) + { + imapProxies = ImapProxy.StartProxiesFromSettingsFile(GetSettingsFileName()); + pop3Proxies = Pop3Proxy.StartProxiesFromSettingsFile(GetSettingsFileName()); + smtpProxies = SmtpProxy.StartProxiesFromSettingsFile(GetSettingsFileName()); + } + + /// + /// Handle the service stop event by stopping all proxies. + /// + protected override void OnStop() + { + if (imapProxies != null) + { + foreach (ImapProxy imapProxy in imapProxies) + imapProxy.Stop(); + + imapProxies.Clear(); + } + if (pop3Proxies != null) + { + foreach (Pop3Proxy pop3Proxy in pop3Proxies) + pop3Proxy.Stop(); + + pop3Proxies.Clear(); + } + if (smtpProxies != null) + { + foreach (SmtpProxy smtpProxy in smtpProxies) + smtpProxy.Stop(); + + smtpProxies.Clear(); + } + } + + /// + /// Handle service continuations following pauses. + /// + protected override void OnContinue() + { + if (imapProxies != null) + { + foreach (ImapProxy imapProxy in imapProxies) + imapProxy.ProcessContinuation(); + + imapProxies.Clear(); + } + if (pop3Proxies != null) + { + foreach (Pop3Proxy pop3Proxy in pop3Proxies) + pop3Proxy.ProcessContinuation(); + + pop3Proxies.Clear(); + } + if (smtpProxies != null) + { + foreach (SmtpProxy smtpProxy in smtpProxies) + smtpProxy.ProcessContinuation(); + + smtpProxies.Clear(); + } + } + + /// + /// Handle pause event. + /// + protected override void OnPause() + { + if (imapProxies != null) + { + foreach (ImapProxy imapProxy in imapProxies) + imapProxy.ProcessPause(); + + imapProxies.Clear(); + } + if (pop3Proxies != null) + { + foreach (Pop3Proxy pop3Proxy in pop3Proxies) + pop3Proxy.ProcessPause(); + + pop3Proxies.Clear(); + } + if (smtpProxies != null) + { + foreach (SmtpProxy smtpProxy in smtpProxies) + smtpProxy.ProcessPause(); + + smtpProxies.Clear(); + } + } + + /// + /// Handle power events, such as hibernation. + /// + protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) + { + if (imapProxies != null) + { + foreach (ImapProxy imapProxy in imapProxies) + imapProxy.ProcessPowerEvent((int)powerStatus); + + imapProxies.Clear(); + } + if (pop3Proxies != null) + { + foreach (Pop3Proxy pop3Proxy in pop3Proxies) + pop3Proxy.ProcessPowerEvent((int)powerStatus); + + pop3Proxies.Clear(); + } + if (smtpProxies != null) + { + foreach (SmtpProxy smtpProxy in smtpProxies) + smtpProxy.ProcessPowerEvent((int)powerStatus); + + smtpProxies.Clear(); + } + + return base.OnPowerEvent(powerStatus); + } + #endregion Protected Methods + + #region Private Methods + /// + /// Return the path where the service's settings should be saved and loaded. + /// + private static string GetSettingsFileName() + { + return AppDomain.CurrentDomain.BaseDirectory + "\\OpaqueMail.Proxy.xml"; + } + #endregion Private Methods + } + + /// + /// Set the service account to the local system. + /// + [RunInstaller(true)] + public sealed class ProxyServiceProcessInstaller : ServiceProcessInstaller + { + public ProxyServiceProcessInstaller() + { + Account = ServiceAccount.LocalSystem; + } + } + + /// + /// Handle OpaqueMail Proxy service installation. + /// + [RunInstaller(true)] + public sealed class ProxyServiceInstaller : ServiceInstaller + { + /// + /// Default constructor. + /// + public ProxyServiceInstaller() + { + Description = "Serves as a local SMTP, IMAP, and POP3 proxy, optionally adding authentication and S/MIME signing or encryption to all outbound email."; + DisplayName = "OpaqueMail Proxy"; + ServiceName = "OpaqueMailProxy"; + StartType = ServiceStartMode.Automatic; + } + + /// + /// Handle installation and uninstallation. + /// + /// Whether we're uninstalling. False if installing, true if uninstalling + /// Any service installation arguments. + public void Install(bool uninstall, string[] args) + { + try + { + using (AssemblyInstaller installer = new AssemblyInstaller(typeof(Program).Assembly, args)) + { + IDictionary state = new Hashtable(); + installer.UseNewContext = true; + try + { + // Attempt to install or uninstall. + if (uninstall) + installer.Uninstall(state); + else + { + installer.Install(state); + installer.Commit(state); + } + } + catch + { + // If an error is encountered, attempt to roll back. + try + { + installer.Rollback(state); + } + catch { } + + throw; + } + } + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.Message); + } + } + } +} diff --git a/OpaqueMail.Net.ProxySettings/Form1.Designer.cs b/OpaqueMail.Net.ProxySettings/Form1.Designer.cs index 0a09820..c1a20d0 100644 --- a/OpaqueMail.Net.ProxySettings/Form1.Designer.cs +++ b/OpaqueMail.Net.ProxySettings/Form1.Designer.cs @@ -1,236 +1,520 @@ -namespace OpaqueMail.Net.ProxySettings -{ - partial class Form1 - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (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(Form1)); - this.AboutGroupBox = new System.Windows.Forms.GroupBox(); - this.AboutLabel = new System.Windows.Forms.LinkLabel(); - this.AccountGroupBox = new System.Windows.Forms.GroupBox(); - this.SaveSettingsButton = new System.Windows.Forms.Button(); - this.AccountGrid = new System.Windows.Forms.DataGridView(); - this.CertificateGroupBox = new System.Windows.Forms.GroupBox(); - this.CertificateLabel = new System.Windows.Forms.LinkLabel(); - this.ClientColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.AccountColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.ProtectedColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn(); - this.CertificateColumn = new System.Windows.Forms.DataGridViewComboBoxColumn(); - this.RegistryKeyColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.SmimeSettingsLabel = new System.Windows.Forms.Label(); - this.SmimeSettingsMode = new System.Windows.Forms.ComboBox(); - this.AboutGroupBox.SuspendLayout(); - this.AccountGroupBox.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.AccountGrid)).BeginInit(); - this.CertificateGroupBox.SuspendLayout(); - this.SuspendLayout(); - // - // AboutGroupBox - // - this.AboutGroupBox.Controls.Add(this.AboutLabel); - this.AboutGroupBox.Location = new System.Drawing.Point(6, 6); - this.AboutGroupBox.Name = "AboutGroupBox"; - this.AboutGroupBox.Size = new System.Drawing.Size(570, 92); - this.AboutGroupBox.TabIndex = 0; - this.AboutGroupBox.TabStop = false; - this.AboutGroupBox.Text = "About OpaqueMail Proxy"; - // - // AboutLabel - // - this.AboutLabel.Location = new System.Drawing.Point(6, 28); - this.AboutLabel.Name = "AboutLabel"; - this.AboutLabel.Size = new System.Drawing.Size(558, 57); - this.AboutLabel.TabIndex = 0; - this.AboutLabel.TabStop = true; - this.AboutLabel.Text = "OpaqueMail Proxy protects e-mail by automatically encrypting messages using S/MIM" + - "E.\r\n\r\nInitial settings can be configured below or by editing the following XML f" + - "ile:\r\n[SETTINGSFILE]"; - // - // AccountGroupBox - // - this.AccountGroupBox.Controls.Add(this.SmimeSettingsMode); - this.AccountGroupBox.Controls.Add(this.SmimeSettingsLabel); - this.AccountGroupBox.Controls.Add(this.SaveSettingsButton); - this.AccountGroupBox.Controls.Add(this.AccountGrid); - this.AccountGroupBox.Location = new System.Drawing.Point(6, 104); - this.AccountGroupBox.Name = "AccountGroupBox"; - this.AccountGroupBox.Size = new System.Drawing.Size(570, 184); - this.AccountGroupBox.TabIndex = 1; - this.AccountGroupBox.TabStop = false; - this.AccountGroupBox.Text = "Select Accounts to Protect"; - // - // SaveSettingsButton - // - this.SaveSettingsButton.Location = new System.Drawing.Point(444, 155); - this.SaveSettingsButton.Name = "SaveSettingsButton"; - this.SaveSettingsButton.Size = new System.Drawing.Size(120, 24); - this.SaveSettingsButton.TabIndex = 4; - this.SaveSettingsButton.Text = "Save Settings"; - this.SaveSettingsButton.UseVisualStyleBackColor = true; - // - // AccountGrid - // - this.AccountGrid.AllowUserToAddRows = false; - this.AccountGrid.AllowUserToDeleteRows = false; - this.AccountGrid.AllowUserToResizeRows = false; - this.AccountGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.AccountGrid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.ClientColumn, - this.AccountColumn, - this.ProtectedColumn, - this.CertificateColumn, - this.RegistryKeyColumn}); - this.AccountGrid.Location = new System.Drawing.Point(6, 20); - this.AccountGrid.Name = "AccountGrid"; - this.AccountGrid.RowHeadersVisible = false; - this.AccountGrid.ShowCellErrors = false; - this.AccountGrid.ShowCellToolTips = false; - this.AccountGrid.ShowEditingIcon = false; - this.AccountGrid.ShowRowErrors = false; - this.AccountGrid.Size = new System.Drawing.Size(558, 129); - this.AccountGrid.TabIndex = 0; - // - // CertificateGroupBox - // - this.CertificateGroupBox.Controls.Add(this.CertificateLabel); - this.CertificateGroupBox.Location = new System.Drawing.Point(6, 294); - this.CertificateGroupBox.Name = "CertificateGroupBox"; - this.CertificateGroupBox.Size = new System.Drawing.Size(570, 154); - this.CertificateGroupBox.TabIndex = 2; - this.CertificateGroupBox.TabStop = false; - this.CertificateGroupBox.Text = "Confirm S/MIME Certificate(s)"; - // - // CertificateLabel - // - this.CertificateLabel.Location = new System.Drawing.Point(6, 28); - this.CertificateLabel.Name = "CertificateLabel"; - this.CertificateLabel.Size = new System.Drawing.Size(558, 123); - this.CertificateLabel.TabIndex = 0; - this.CertificateLabel.TabStop = true; - this.CertificateLabel.Text = resources.GetString("CertificateLabel.Text"); - // - // ClientColumn - // - this.ClientColumn.HeaderText = "Client"; - this.ClientColumn.Name = "ClientColumn"; - this.ClientColumn.ReadOnly = true; - this.ClientColumn.Width = 75; - // - // AccountColumn - // - this.AccountColumn.HeaderText = "Account"; - this.AccountColumn.Name = "AccountColumn"; - this.AccountColumn.ReadOnly = true; - this.AccountColumn.Width = 160; - // - // ProtectedColumn - // - this.ProtectedColumn.HeaderText = "Protected?"; - this.ProtectedColumn.Name = "ProtectedColumn"; - this.ProtectedColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True; - this.ProtectedColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; - this.ProtectedColumn.Width = 60; - // - // CertificateColumn - // - this.CertificateColumn.HeaderText = "S/MIME Certificate"; - this.CertificateColumn.Name = "CertificateColumn"; - this.CertificateColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True; - this.CertificateColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; - this.CertificateColumn.Width = 243; - // - // RegistryKeyColumn - // - this.RegistryKeyColumn.HeaderText = "Registry Key"; - this.RegistryKeyColumn.Name = "RegistryKeyColumn"; - this.RegistryKeyColumn.Visible = false; - // - // SmimeSettingsLabel - // - this.SmimeSettingsLabel.AutoSize = true; - this.SmimeSettingsLabel.Location = new System.Drawing.Point(6, 161); - this.SmimeSettingsLabel.Name = "SmimeSettingsLabel"; - this.SmimeSettingsLabel.Size = new System.Drawing.Size(132, 13); - this.SmimeSettingsLabel.TabIndex = 5; - this.SmimeSettingsLabel.Text = "S/MIME protection mode: "; - // - // SmimeSettingsMode - // - this.SmimeSettingsMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.SmimeSettingsMode.FormattingEnabled = true; - this.SmimeSettingsMode.Items.AddRange(new object[] { - "Best effort: try to encrypt, but allow unencrypted mail", - "Encrypted only"}); - this.SmimeSettingsMode.Location = new System.Drawing.Point(144, 157); - this.SmimeSettingsMode.Name = "SmimeSettingsMode"; - this.SmimeSettingsMode.Size = new System.Drawing.Size(272, 21); - this.SmimeSettingsMode.TabIndex = 6; - // - // Form1 - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(584, 455); - this.Controls.Add(this.CertificateGroupBox); - this.Controls.Add(this.AccountGroupBox); - this.Controls.Add(this.AboutGroupBox); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; - this.MaximumSize = new System.Drawing.Size(600, 494); - this.MinimizeBox = false; - this.MinimumSize = new System.Drawing.Size(600, 494); - this.Name = "Form1"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "OpaqueMail Proxy Settings"; - this.Load += new System.EventHandler(this.Form1_Load); - this.AboutGroupBox.ResumeLayout(false); - this.AccountGroupBox.ResumeLayout(false); - this.AccountGroupBox.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.AccountGrid)).EndInit(); - this.CertificateGroupBox.ResumeLayout(false); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.GroupBox AboutGroupBox; - private System.Windows.Forms.LinkLabel AboutLabel; - private System.Windows.Forms.GroupBox AccountGroupBox; - private System.Windows.Forms.DataGridView AccountGrid; - private System.Windows.Forms.GroupBox CertificateGroupBox; - private System.Windows.Forms.LinkLabel CertificateLabel; - private System.Windows.Forms.Button SaveSettingsButton; - private System.Windows.Forms.DataGridViewTextBoxColumn ClientColumn; - private System.Windows.Forms.DataGridViewTextBoxColumn AccountColumn; - private System.Windows.Forms.DataGridViewCheckBoxColumn ProtectedColumn; - private System.Windows.Forms.DataGridViewComboBoxColumn CertificateColumn; - private System.Windows.Forms.DataGridViewTextBoxColumn RegistryKeyColumn; - private System.Windows.Forms.Label SmimeSettingsLabel; - private System.Windows.Forms.ComboBox SmimeSettingsMode; - } +namespace OpaqueMail.Net.ProxySettings +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (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(Form1)); + this.Tabs = new System.Windows.Forms.TabControl(); + this.AboutTab = new System.Windows.Forms.TabPage(); + this.GettingStartedGroupBox = new System.Windows.Forms.GroupBox(); + this.GettingStartedLabel = new System.Windows.Forms.LinkLabel(); + this.AboutGroupBox = new System.Windows.Forms.GroupBox(); + this.AboutLabel = new System.Windows.Forms.LinkLabel(); + this.SelectAccountsTab = new System.Windows.Forms.TabPage(); + this.AccountGroupBox = new System.Windows.Forms.GroupBox(); + this.AccountsLabel = new System.Windows.Forms.LinkLabel(); + this.AccountGrid = new System.Windows.Forms.DataGridView(); + this.ConfirmCertificatesTab = new System.Windows.Forms.TabPage(); + this.ProtectionGroupBox = new System.Windows.Forms.GroupBox(); + this.SmimeOperations = new System.Windows.Forms.ComboBox(); + this.SmimeOperationsLabel = new System.Windows.Forms.Label(); + this.ProtectionModeLabel = new System.Windows.Forms.Label(); + this.SmimeSettingsMode = new System.Windows.Forms.ComboBox(); + this.SmimeSettingsModeLabel = new System.Windows.Forms.Label(); + this.CertificateGroupBox = new System.Windows.Forms.GroupBox(); + this.CertificateLabel = new System.Windows.Forms.LinkLabel(); + this.OtherOptionsTab = new System.Windows.Forms.TabPage(); + this.SpreadTheWordGroupBox = new System.Windows.Forms.GroupBox(); + this.UpdateOutlookSignature = new System.Windows.Forms.CheckBox(); + this.SpreadTheWordLabel = new System.Windows.Forms.Label(); + this.FirewallGroupBox = new System.Windows.Forms.GroupBox(); + this.UpdateFirewall = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.NetworkAccess = new System.Windows.Forms.ComboBox(); + this.NetworkAccessLabel = new System.Windows.Forms.Label(); + this.SaveSettingsButton = new System.Windows.Forms.Button(); + this.ClientColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.AccountColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ProtectedColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn(); + this.CertificateColumn = new System.Windows.Forms.DataGridViewComboBoxColumn(); + this.RegistryKeyColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ServiceStatusLabel = new System.Windows.Forms.Label(); + this.Tabs.SuspendLayout(); + this.AboutTab.SuspendLayout(); + this.GettingStartedGroupBox.SuspendLayout(); + this.AboutGroupBox.SuspendLayout(); + this.SelectAccountsTab.SuspendLayout(); + this.AccountGroupBox.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.AccountGrid)).BeginInit(); + this.ConfirmCertificatesTab.SuspendLayout(); + this.ProtectionGroupBox.SuspendLayout(); + this.CertificateGroupBox.SuspendLayout(); + this.OtherOptionsTab.SuspendLayout(); + this.SpreadTheWordGroupBox.SuspendLayout(); + this.FirewallGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // Tabs + // + this.Tabs.Controls.Add(this.AboutTab); + this.Tabs.Controls.Add(this.SelectAccountsTab); + this.Tabs.Controls.Add(this.ConfirmCertificatesTab); + this.Tabs.Controls.Add(this.OtherOptionsTab); + this.Tabs.Location = new System.Drawing.Point(3, 3); + this.Tabs.Name = "Tabs"; + this.Tabs.SelectedIndex = 0; + this.Tabs.Size = new System.Drawing.Size(578, 392); + this.Tabs.TabIndex = 3; + // + // AboutTab + // + this.AboutTab.Controls.Add(this.GettingStartedGroupBox); + this.AboutTab.Controls.Add(this.AboutGroupBox); + this.AboutTab.Location = new System.Drawing.Point(4, 22); + this.AboutTab.Name = "AboutTab"; + this.AboutTab.Padding = new System.Windows.Forms.Padding(3); + this.AboutTab.Size = new System.Drawing.Size(570, 366); + this.AboutTab.TabIndex = 0; + this.AboutTab.Text = "About OpaqueMail Proxy"; + this.AboutTab.UseVisualStyleBackColor = true; + // + // GettingStartedGroupBox + // + this.GettingStartedGroupBox.Controls.Add(this.GettingStartedLabel); + this.GettingStartedGroupBox.Location = new System.Drawing.Point(6, 176); + this.GettingStartedGroupBox.Name = "GettingStartedGroupBox"; + this.GettingStartedGroupBox.Size = new System.Drawing.Size(558, 141); + this.GettingStartedGroupBox.TabIndex = 2; + this.GettingStartedGroupBox.TabStop = false; + this.GettingStartedGroupBox.Text = "Getting Started"; + // + // GettingStartedLabel + // + this.GettingStartedLabel.Location = new System.Drawing.Point(6, 28); + this.GettingStartedLabel.Name = "GettingStartedLabel"; + this.GettingStartedLabel.Size = new System.Drawing.Size(546, 113); + this.GettingStartedLabel.TabIndex = 3; + this.GettingStartedLabel.TabStop = true; + this.GettingStartedLabel.Text = resources.GetString("GettingStartedLabel.Text"); + // + // AboutGroupBox + // + this.AboutGroupBox.Controls.Add(this.AboutLabel); + this.AboutGroupBox.Location = new System.Drawing.Point(6, 6); + this.AboutGroupBox.Name = "AboutGroupBox"; + this.AboutGroupBox.Size = new System.Drawing.Size(558, 167); + this.AboutGroupBox.TabIndex = 0; + this.AboutGroupBox.TabStop = false; + this.AboutGroupBox.Text = "About OpaqueMail Proxy"; + // + // AboutLabel + // + this.AboutLabel.Location = new System.Drawing.Point(6, 28); + this.AboutLabel.Name = "AboutLabel"; + this.AboutLabel.Size = new System.Drawing.Size(546, 139); + this.AboutLabel.TabIndex = 1; + this.AboutLabel.TabStop = true; + this.AboutLabel.Text = resources.GetString("AboutLabel.Text"); + // + // SelectAccountsTab + // + this.SelectAccountsTab.Controls.Add(this.AccountGroupBox); + this.SelectAccountsTab.Location = new System.Drawing.Point(4, 22); + this.SelectAccountsTab.Name = "SelectAccountsTab"; + this.SelectAccountsTab.Padding = new System.Windows.Forms.Padding(3); + this.SelectAccountsTab.Size = new System.Drawing.Size(570, 366); + this.SelectAccountsTab.TabIndex = 1; + this.SelectAccountsTab.Text = "1. Select E-mail Accounts"; + this.SelectAccountsTab.UseVisualStyleBackColor = true; + // + // AccountGroupBox + // + this.AccountGroupBox.Controls.Add(this.AccountsLabel); + this.AccountGroupBox.Controls.Add(this.AccountGrid); + this.AccountGroupBox.Location = new System.Drawing.Point(6, 6); + this.AccountGroupBox.Name = "AccountGroupBox"; + this.AccountGroupBox.Size = new System.Drawing.Size(558, 354); + this.AccountGroupBox.TabIndex = 0; + this.AccountGroupBox.TabStop = false; + this.AccountGroupBox.Text = "Select E-Mail Accounts to Protect"; + // + // AccountsLabel + // + this.AccountsLabel.Location = new System.Drawing.Point(6, 28); + this.AccountsLabel.Name = "AccountsLabel"; + this.AccountsLabel.Size = new System.Drawing.Size(546, 62); + this.AccountsLabel.TabIndex = 1; + this.AccountsLabel.TabStop = true; + this.AccountsLabel.Text = resources.GetString("AccountsLabel.Text"); + // + // AccountGrid + // + this.AccountGrid.AllowUserToAddRows = false; + this.AccountGrid.AllowUserToDeleteRows = false; + this.AccountGrid.AllowUserToResizeRows = false; + this.AccountGrid.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.AccountGrid.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.ClientColumn, + this.AccountColumn, + this.ProtectedColumn, + this.CertificateColumn, + this.RegistryKeyColumn}); + this.AccountGrid.Location = new System.Drawing.Point(6, 93); + this.AccountGrid.Name = "AccountGrid"; + this.AccountGrid.RowHeadersVisible = false; + this.AccountGrid.ShowCellErrors = false; + this.AccountGrid.ShowCellToolTips = false; + this.AccountGrid.ShowEditingIcon = false; + this.AccountGrid.ShowRowErrors = false; + this.AccountGrid.Size = new System.Drawing.Size(546, 255); + this.AccountGrid.TabIndex = 2; + // + // ConfirmCertificatesTab + // + this.ConfirmCertificatesTab.Controls.Add(this.ProtectionGroupBox); + this.ConfirmCertificatesTab.Controls.Add(this.CertificateGroupBox); + this.ConfirmCertificatesTab.Location = new System.Drawing.Point(4, 22); + this.ConfirmCertificatesTab.Name = "ConfirmCertificatesTab"; + this.ConfirmCertificatesTab.Size = new System.Drawing.Size(570, 366); + this.ConfirmCertificatesTab.TabIndex = 2; + this.ConfirmCertificatesTab.Text = "2. Confirm Certificates"; + this.ConfirmCertificatesTab.UseVisualStyleBackColor = true; + // + // ProtectionGroupBox + // + this.ProtectionGroupBox.Controls.Add(this.SmimeOperations); + this.ProtectionGroupBox.Controls.Add(this.SmimeOperationsLabel); + this.ProtectionGroupBox.Controls.Add(this.ProtectionModeLabel); + this.ProtectionGroupBox.Controls.Add(this.SmimeSettingsMode); + this.ProtectionGroupBox.Controls.Add(this.SmimeSettingsModeLabel); + this.ProtectionGroupBox.Location = new System.Drawing.Point(6, 168); + this.ProtectionGroupBox.Name = "ProtectionGroupBox"; + this.ProtectionGroupBox.Size = new System.Drawing.Size(559, 170); + this.ProtectionGroupBox.TabIndex = 2; + this.ProtectionGroupBox.TabStop = false; + this.ProtectionGroupBox.Text = "Choose S/MIME Protection Mode"; + // + // SmimeOperations + // + this.SmimeOperations.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.SmimeOperations.FormattingEnabled = true; + this.SmimeOperations.Items.AddRange(new object[] { + "Apply no S/MIME protection", + "Sign messages only", + "Encrypt messages only", + "Sign and encrypt messages (Recommended)"}); + this.SmimeOperations.Location = new System.Drawing.Point(145, 114); + this.SmimeOperations.Name = "SmimeOperations"; + this.SmimeOperations.Size = new System.Drawing.Size(408, 21); + this.SmimeOperations.TabIndex = 5; + // + // SmimeOperationsLabel + // + this.SmimeOperationsLabel.AutoSize = true; + this.SmimeOperationsLabel.Location = new System.Drawing.Point(7, 117); + this.SmimeOperationsLabel.Name = "SmimeOperationsLabel"; + this.SmimeOperationsLabel.Size = new System.Drawing.Size(102, 13); + this.SmimeOperationsLabel.TabIndex = 4; + this.SmimeOperationsLabel.Text = "S/MIME operations:"; + // + // ProtectionModeLabel + // + this.ProtectionModeLabel.Location = new System.Drawing.Point(6, 28); + this.ProtectionModeLabel.Name = "ProtectionModeLabel"; + this.ProtectionModeLabel.Size = new System.Drawing.Size(546, 72); + this.ProtectionModeLabel.TabIndex = 3; + this.ProtectionModeLabel.Text = resources.GetString("ProtectionModeLabel.Text"); + // + // SmimeSettingsMode + // + this.SmimeSettingsMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.SmimeSettingsMode.FormattingEnabled = true; + this.SmimeSettingsMode.Items.AddRange(new object[] { + "Best effort: try to protect, but allow unprotected mail", + "Require exact settings"}); + this.SmimeSettingsMode.Location = new System.Drawing.Point(145, 141); + this.SmimeSettingsMode.Name = "SmimeSettingsMode"; + this.SmimeSettingsMode.Size = new System.Drawing.Size(408, 21); + this.SmimeSettingsMode.TabIndex = 7; + // + // SmimeSettingsModeLabel + // + this.SmimeSettingsModeLabel.AutoSize = true; + this.SmimeSettingsModeLabel.Location = new System.Drawing.Point(7, 144); + this.SmimeSettingsModeLabel.Name = "SmimeSettingsModeLabel"; + this.SmimeSettingsModeLabel.Size = new System.Drawing.Size(132, 13); + this.SmimeSettingsModeLabel.TabIndex = 6; + this.SmimeSettingsModeLabel.Text = "S/MIME protection mode: "; + // + // CertificateGroupBox + // + this.CertificateGroupBox.Controls.Add(this.CertificateLabel); + this.CertificateGroupBox.Location = new System.Drawing.Point(6, 6); + this.CertificateGroupBox.Name = "CertificateGroupBox"; + this.CertificateGroupBox.Size = new System.Drawing.Size(559, 156); + this.CertificateGroupBox.TabIndex = 0; + this.CertificateGroupBox.TabStop = false; + this.CertificateGroupBox.Text = "Confirm S/MIME Certificate(s)"; + // + // CertificateLabel + // + this.CertificateLabel.Location = new System.Drawing.Point(6, 28); + this.CertificateLabel.Name = "CertificateLabel"; + this.CertificateLabel.Size = new System.Drawing.Size(547, 125); + this.CertificateLabel.TabIndex = 1; + this.CertificateLabel.TabStop = true; + this.CertificateLabel.Text = resources.GetString("CertificateLabel.Text"); + // + // OtherOptionsTab + // + this.OtherOptionsTab.Controls.Add(this.SpreadTheWordGroupBox); + this.OtherOptionsTab.Controls.Add(this.FirewallGroupBox); + this.OtherOptionsTab.Location = new System.Drawing.Point(4, 22); + this.OtherOptionsTab.Name = "OtherOptionsTab"; + this.OtherOptionsTab.Size = new System.Drawing.Size(570, 366); + this.OtherOptionsTab.TabIndex = 3; + this.OtherOptionsTab.Text = "3. Manage Other Options"; + this.OtherOptionsTab.UseVisualStyleBackColor = true; + // + // SpreadTheWordGroupBox + // + this.SpreadTheWordGroupBox.Controls.Add(this.UpdateOutlookSignature); + this.SpreadTheWordGroupBox.Controls.Add(this.SpreadTheWordLabel); + this.SpreadTheWordGroupBox.Location = new System.Drawing.Point(6, 162); + this.SpreadTheWordGroupBox.Name = "SpreadTheWordGroupBox"; + this.SpreadTheWordGroupBox.Size = new System.Drawing.Size(559, 150); + this.SpreadTheWordGroupBox.TabIndex = 5; + this.SpreadTheWordGroupBox.TabStop = false; + this.SpreadTheWordGroupBox.Text = "Spread the Word"; + // + // UpdateOutlookSignature + // + this.UpdateOutlookSignature.AutoSize = true; + this.UpdateOutlookSignature.Checked = true; + this.UpdateOutlookSignature.CheckState = System.Windows.Forms.CheckState.Checked; + this.UpdateOutlookSignature.Location = new System.Drawing.Point(103, 97); + this.UpdateOutlookSignature.Name = "UpdateOutlookSignature"; + this.UpdateOutlookSignature.Size = new System.Drawing.Size(256, 17); + this.UpdateOutlookSignature.TabIndex = 7; + this.UpdateOutlookSignature.Text = "Update Outlook Signature to Link to OpaqueMail"; + this.UpdateOutlookSignature.UseVisualStyleBackColor = true; + // + // SpreadTheWordLabel + // + this.SpreadTheWordLabel.Location = new System.Drawing.Point(6, 28); + this.SpreadTheWordLabel.Name = "SpreadTheWordLabel"; + this.SpreadTheWordLabel.Size = new System.Drawing.Size(546, 57); + this.SpreadTheWordLabel.TabIndex = 6; + this.SpreadTheWordLabel.Text = resources.GetString("SpreadTheWordLabel.Text"); + // + // FirewallGroupBox + // + this.FirewallGroupBox.Controls.Add(this.UpdateFirewall); + this.FirewallGroupBox.Controls.Add(this.label1); + this.FirewallGroupBox.Controls.Add(this.NetworkAccess); + this.FirewallGroupBox.Controls.Add(this.NetworkAccessLabel); + this.FirewallGroupBox.Location = new System.Drawing.Point(6, 6); + this.FirewallGroupBox.Name = "FirewallGroupBox"; + this.FirewallGroupBox.Size = new System.Drawing.Size(559, 150); + this.FirewallGroupBox.TabIndex = 0; + this.FirewallGroupBox.TabStop = false; + this.FirewallGroupBox.Text = "Configure Firewall"; + // + // UpdateFirewall + // + this.UpdateFirewall.AutoSize = true; + this.UpdateFirewall.Checked = true; + this.UpdateFirewall.CheckState = System.Windows.Forms.CheckState.Checked; + this.UpdateFirewall.Location = new System.Drawing.Point(103, 125); + this.UpdateFirewall.Name = "UpdateFirewall"; + this.UpdateFirewall.Size = new System.Drawing.Size(239, 17); + this.UpdateFirewall.TabIndex = 4; + this.UpdateFirewall.Text = "Automatically Configure the Windows Firewall"; + this.UpdateFirewall.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.Location = new System.Drawing.Point(6, 28); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(546, 57); + this.label1.TabIndex = 1; + this.label1.Text = resources.GetString("label1.Text"); + // + // NetworkAccess + // + this.NetworkAccess.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.NetworkAccess.FormattingEnabled = true; + this.NetworkAccess.Items.AddRange(new object[] { + "Only allow connections from the local computer (0.0.0.0)", + "Only allow connections from machines on the local network (192.168.*)", + "Allow connections from anyone (*)"}); + this.NetworkAccess.Location = new System.Drawing.Point(103, 98); + this.NetworkAccess.Name = "NetworkAccess"; + this.NetworkAccess.Size = new System.Drawing.Size(450, 21); + this.NetworkAccess.TabIndex = 3; + // + // NetworkAccessLabel + // + this.NetworkAccessLabel.AutoSize = true; + this.NetworkAccessLabel.Location = new System.Drawing.Point(6, 101); + this.NetworkAccessLabel.Name = "NetworkAccessLabel"; + this.NetworkAccessLabel.Size = new System.Drawing.Size(91, 13); + this.NetworkAccessLabel.TabIndex = 2; + this.NetworkAccessLabel.Text = "Network Access: "; + // + // SaveSettingsButton + // + this.SaveSettingsButton.Location = new System.Drawing.Point(440, 397); + this.SaveSettingsButton.Name = "SaveSettingsButton"; + this.SaveSettingsButton.Size = new System.Drawing.Size(138, 23); + this.SaveSettingsButton.TabIndex = 10; + this.SaveSettingsButton.Text = "Save Settings"; + this.SaveSettingsButton.UseVisualStyleBackColor = true; + this.SaveSettingsButton.Click += new System.EventHandler(this.SaveSettingsButton_Click); + // + // ClientColumn + // + this.ClientColumn.HeaderText = "Client"; + this.ClientColumn.Name = "ClientColumn"; + this.ClientColumn.ReadOnly = true; + this.ClientColumn.Width = 75; + // + // AccountColumn + // + this.AccountColumn.HeaderText = "Account"; + this.AccountColumn.Name = "AccountColumn"; + this.AccountColumn.ReadOnly = true; + this.AccountColumn.Width = 160; + // + // ProtectedColumn + // + this.ProtectedColumn.HeaderText = "Protected?"; + this.ProtectedColumn.Name = "ProtectedColumn"; + this.ProtectedColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.ProtectedColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + this.ProtectedColumn.Width = 60; + // + // CertificateColumn + // + this.CertificateColumn.HeaderText = "S/MIME Certificate"; + this.CertificateColumn.Name = "CertificateColumn"; + this.CertificateColumn.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.CertificateColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + this.CertificateColumn.Width = 248; + // + // RegistryKeyColumn + // + this.RegistryKeyColumn.HeaderText = "Registry Key"; + this.RegistryKeyColumn.Name = "RegistryKeyColumn"; + this.RegistryKeyColumn.Visible = false; + // + // ServiceStatusLabel + // + this.ServiceStatusLabel.Location = new System.Drawing.Point(4, 402); + this.ServiceStatusLabel.Name = "ServiceStatusLabel"; + this.ServiceStatusLabel.Size = new System.Drawing.Size(434, 23); + this.ServiceStatusLabel.TabIndex = 11; + this.ServiceStatusLabel.Text = "The OpaqueMail Proxy service is currently stopped."; + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(584, 421); + this.Controls.Add(this.ServiceStatusLabel); + this.Controls.Add(this.SaveSettingsButton); + this.Controls.Add(this.Tabs); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(600, 460); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(600, 460); + this.Name = "Form1"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "OpaqueMail Proxy Settings"; + this.Load += new System.EventHandler(this.Form1_Load); + this.Tabs.ResumeLayout(false); + this.AboutTab.ResumeLayout(false); + this.GettingStartedGroupBox.ResumeLayout(false); + this.AboutGroupBox.ResumeLayout(false); + this.SelectAccountsTab.ResumeLayout(false); + this.AccountGroupBox.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.AccountGrid)).EndInit(); + this.ConfirmCertificatesTab.ResumeLayout(false); + this.ProtectionGroupBox.ResumeLayout(false); + this.ProtectionGroupBox.PerformLayout(); + this.CertificateGroupBox.ResumeLayout(false); + this.OtherOptionsTab.ResumeLayout(false); + this.SpreadTheWordGroupBox.ResumeLayout(false); + this.SpreadTheWordGroupBox.PerformLayout(); + this.FirewallGroupBox.ResumeLayout(false); + this.FirewallGroupBox.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl Tabs; + private System.Windows.Forms.TabPage AboutTab; + private System.Windows.Forms.GroupBox AboutGroupBox; + private System.Windows.Forms.LinkLabel AboutLabel; + private System.Windows.Forms.TabPage SelectAccountsTab; + private System.Windows.Forms.GroupBox AccountGroupBox; + private System.Windows.Forms.DataGridView AccountGrid; + private System.Windows.Forms.TabPage ConfirmCertificatesTab; + private System.Windows.Forms.GroupBox CertificateGroupBox; + private System.Windows.Forms.LinkLabel CertificateLabel; + private System.Windows.Forms.TabPage OtherOptionsTab; + private System.Windows.Forms.Button SaveSettingsButton; + private System.Windows.Forms.GroupBox GettingStartedGroupBox; + private System.Windows.Forms.LinkLabel GettingStartedLabel; + private System.Windows.Forms.LinkLabel AccountsLabel; + private System.Windows.Forms.GroupBox ProtectionGroupBox; + private System.Windows.Forms.ComboBox SmimeSettingsMode; + private System.Windows.Forms.Label SmimeSettingsModeLabel; + private System.Windows.Forms.ComboBox SmimeOperations; + private System.Windows.Forms.Label SmimeOperationsLabel; + private System.Windows.Forms.Label ProtectionModeLabel; + private System.Windows.Forms.GroupBox FirewallGroupBox; + private System.Windows.Forms.ComboBox NetworkAccess; + private System.Windows.Forms.Label NetworkAccessLabel; + private System.Windows.Forms.GroupBox SpreadTheWordGroupBox; + private System.Windows.Forms.CheckBox UpdateOutlookSignature; + private System.Windows.Forms.Label SpreadTheWordLabel; + private System.Windows.Forms.CheckBox UpdateFirewall; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.DataGridViewTextBoxColumn ClientColumn; + private System.Windows.Forms.DataGridViewTextBoxColumn AccountColumn; + private System.Windows.Forms.DataGridViewCheckBoxColumn ProtectedColumn; + private System.Windows.Forms.DataGridViewComboBoxColumn CertificateColumn; + private System.Windows.Forms.DataGridViewTextBoxColumn RegistryKeyColumn; + private System.Windows.Forms.Label ServiceStatusLabel; + + } } \ No newline at end of file diff --git a/OpaqueMail.Net.ProxySettings/Form1.cs b/OpaqueMail.Net.ProxySettings/Form1.cs index 1bd0f0e..3146a0d 100644 --- a/OpaqueMail.Net.ProxySettings/Form1.cs +++ b/OpaqueMail.Net.ProxySettings/Form1.cs @@ -1,1465 +1,1713 @@ -using Microsoft.Win32; -using OpaqueMail.Net.Proxy; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Diagnostics; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Net.NetworkInformation; -using System.Security.Cryptography.X509Certificates; -using System.ServiceProcess; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Xml; -using System.Xml.XPath; - -namespace OpaqueMail.Net.ProxySettings -{ - public partial class Form1 : Form - { - #region Private Members - /// Outlook versions to check for in the registry. - Dictionary OutlookVersions = new Dictionary(); - /// The path where settings should be saved and loaded. - private string SettingsFileName = ""; - #endregion Private Members - - #region Constructors - /// - /// Default constructor. - /// - public Form1() - { - InitializeComponent(); - } - #endregion Constructors - - #region Protected Methods - /// - /// Handle the F5 keypress by reloading the e-mail accounts list and certificate choices. - /// - /// - /// - /// - protected override bool ProcessCmdKey(ref Message msg, Keys keyData) - { - if (keyData == Keys.F5) - PopulateAccounts(); - - return base.ProcessCmdKey(ref msg, keyData); - } - #endregion Protected Methods - - #region Private Methods - /// - /// Load event handler. - /// - private void Form1_Load(object sender, EventArgs e) - { - OutlookVersions.Add("8.0", "Outlook 97"); - OutlookVersions.Add("9.0", "Outlook 2000"); - OutlookVersions.Add("10.0", "Outlook XP"); - OutlookVersions.Add("11.0", "Outlook 2003"); - OutlookVersions.Add("12.0", "Outlook 2007"); - OutlookVersions.Add("14.0", "Outlook 2010"); - OutlookVersions.Add("15.0", "Outlook 2013"); - OutlookVersions.Add("16.0", "Outlook v16"); - OutlookVersions.Add("17.0", "Outlook v17"); - OutlookVersions.Add("18.0", "Outlook v18"); - OutlookVersions.Add("19.0", "Outlook v19"); - OutlookVersions.Add("20.0", "Outlook v20"); - - SettingsFileName = (AppDomain.CurrentDomain.BaseDirectory + "\\OpaqueMail.Proxy.xml").Replace("\\\\", "\\"); - - // Handle settings file linking. - int settingsFilePosition = AboutLabel.Text.IndexOf("[SETTINGSFILE]", StringComparison.Ordinal); - - AboutLabel.Links.Clear(); - AboutLabel.Text = AboutLabel.Text.Replace("[SETTINGSFILE]", SettingsFileName); - AboutLabel.Links.Add(AboutLabel.Text.IndexOf("S/MIME"), 6, "https://en.wikipedia.org/wiki/S/MIME"); - AboutLabel.Links.Add(settingsFilePosition, SettingsFileName.Length, SettingsFileName); - AboutLabel.LinkClicked += Label_LinkClicked; - - CertificateLabel.Links.Clear(); - CertificateLabel.Links.Add(CertificateLabel.Text.IndexOf("Comodo"), 6, "http://www.instantssl.com/ssl-certificate-products/free-email-certificate.html"); - CertificateLabel.Links.Add(CertificateLabel.Text.IndexOf("StartCom"), 8, "https://cert.startcom.org/"); - CertificateLabel.LinkClicked += Label_LinkClicked; - - this.SaveSettingsButton.Click += new System.EventHandler(this.SaveSettingsButton_Click); - - // Load the e-mail accounts list and certificate choices. - PopulateAccounts(); - - SmimeSettingsMode.SelectedIndex = 0; - } - - /// - /// Determine the next available port. - /// - /// The first port to check. - private int GetNextAvailablePort(int nextPortToTry) - { - IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); - TcpConnectionInformation[] tcpConnections = ipGlobalProperties.GetActiveTcpConnections(); - - while (true) - { - bool isAvailable = true; - foreach (TcpConnectionInformation tcpInfo in tcpConnections) - { - if (tcpInfo.LocalEndPoint.Port == nextPortToTry) - { - isAvailable = false; - break; - } - } - - if (isAvailable) - return nextPortToTry; - else - nextPortToTry++; - } - } - - /// - /// Install the OpaqueMail Proxy service. - /// - private void InstallService() - { - if (!ServiceExists("OpaqueMailProxy")) - { - ProxyServiceInstaller installer = new ProxyServiceInstaller(); - installer.Install(false, new string[] { }); - } - } - - /// - /// Open the default application to view clicked links. - /// - private void Label_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - Process.Start(e.Link.LinkData.ToString().Replace("\r\n", "")); - } - - /// - /// Populate the gridview with appropriate e-mail account and certificate choices. - /// - private void PopulateAccounts() - { - // Prepare the certificate choices. - List certChoices = new List(); - - X509Certificate2Collection certs = CertHelper.GetWindowsCertificates(StoreLocation.CurrentUser); - certs.AddRange(CertHelper.GetWindowsCertificates(StoreLocation.LocalMachine)); - - HashSet certificatesSeen = new HashSet(); - foreach (X509Certificate2 cert in certs) - { - // Avoid duplicate certificates. - if (!certificatesSeen.Contains(cert.SerialNumber)) - { - // Ensure that the certificate has a valid subject name. - if (cert.Subject.IndexOf("@") > -1 && (cert.Subject.StartsWith("E=") || (cert.Subject.StartsWith("CN=")))) - { - if (cert.Verify()) - { - certChoices.Add(new Choice(cert.Subject + " (SN: " + cert.SerialNumber + ")", cert.SerialNumber)); - certificatesSeen.Add(cert.SerialNumber); - } - } - } - } - - certChoices.Add(new Choice("New self-signed certificate", "self-signed")); - CertificateColumn.DataSource = certChoices; - CertificateColumn.DisplayMember = "Name"; - CertificateColumn.ValueMember = "Value"; - - // Check which Outlook registry keys and Thunderbird configs have proxies associated. - XPathDocument document; - HashSet outlookRegistryKeys = new HashSet(); - HashSet thunderbirdKeys = new HashSet(); - try - { - document = new XPathDocument(SettingsFileName); - XPathNavigator navigator = document.CreateNavigator(); - - int smtpServiceCount = GetXmlIntValue(navigator, "/Settings/SMTP/ServiceCount") ?? 0; - for (int i = 1; i <= smtpServiceCount; i++) - { - int? registryKeyCount = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/OutlookRegistryKeyCount") ?? 0; - for (int j = 1; j <= registryKeyCount; j++) - { - string registryKey = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/OutlookRegistryKey" + j); - if (!string.IsNullOrEmpty(registryKey)) - outlookRegistryKeys.Add(registryKey); - } - - int? thunderbirdKeyCount = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/ThunderbirdKeyCount") ?? 0; - for (int j = 1; j <= thunderbirdKeyCount; j++) - { - string thunderbirdKey = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/ThunderbirdKey" + j); - if (!string.IsNullOrEmpty(thunderbirdKey)) - thunderbirdKeys.Add(thunderbirdKey); - } - } - } - catch { } - - // Correlate Outlook registry keys with accounts. - AccountGrid.Rows.Clear(); - bool activeProxy = false; - foreach (string outlookVersion in OutlookVersions.Keys) - { - using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\" + outlookVersion + @"\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676", false)) - { - if (key != null) - { - string[] subkeyNames = key.GetSubKeyNames(); - if (subkeyNames != null) - { - foreach (string subkeyName in subkeyNames) - { - using (RegistryKey subKey = key.OpenSubKey(subkeyName, false)) - { - string smtpServer = GetOutlookRegistryValue(subKey, "SMTP Server"); - - if (!string.IsNullOrEmpty(smtpServer)) - { - string accountName = GetOutlookRegistryValue(subKey, "Account Name"); - - string matchingCert = "self-signed"; - - // Check if there's a matching certificate. - foreach (X509Certificate2 cert in certs) - { - string canonicalCertSubject = ""; - if (cert.Subject.StartsWith("E=")) - { - canonicalCertSubject = cert.Subject.Substring(2).ToUpper(); - int certSubjectComma = canonicalCertSubject.IndexOf(","); - if (certSubjectComma > -1) - canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma); - - if (accountName.ToUpper() == canonicalCertSubject) - matchingCert = cert.SerialNumber; - } - else if (cert.Subject.StartsWith("CN=")) - { - canonicalCertSubject = cert.Subject.Substring(3).ToUpper(); - int certSubjectComma = canonicalCertSubject.IndexOf(","); - if (certSubjectComma > -1) - canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma); - - if (accountName.ToUpper() == canonicalCertSubject) - matchingCert = cert.SerialNumber; - } - } - - if (!activeProxy && outlookRegistryKeys.Contains(subKey.Name)) - activeProxy = true; - - AccountGrid.Rows.Add(OutlookVersions[outlookVersion], accountName, outlookRegistryKeys.Contains(subKey.Name), matchingCert, subKey.Name); - } - } - } - } - } - } - } - - // Correlate Thunderbird config keys with accounts. - activeProxy = false; - if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) - { - foreach (string directory in Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) - { - if (File.Exists(directory + "\\prefs.js")) - { - string prefsFile = File.ReadAllText(directory + "\\prefs.js"); - - int keyCount; - int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.account.lastKey\", ", ")"), out keyCount); - for (int i = 1; i <= keyCount; i++) - { - string smtpServer = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".hostname\", \"", "\""); - string accountName = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".username\", \"", "\""); - - if (!string.IsNullOrEmpty(smtpServer) && !string.IsNullOrEmpty(accountName)) - { - string thunderbirdKey = directory + "~" + i.ToString(); - - string matchingCert = "self-signed"; - - // Check if there's a matching certificate. - foreach (X509Certificate2 cert in certs) - { - string canonicalCertSubject = ""; - if (cert.Subject.StartsWith("E=")) - { - canonicalCertSubject = cert.Subject.Substring(2).ToUpper(); - int certSubjectComma = canonicalCertSubject.IndexOf(","); - if (certSubjectComma > -1) - canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma); - - if (accountName.ToUpper() == canonicalCertSubject) - matchingCert = cert.SerialNumber; - } - else if (cert.Subject.StartsWith("CN=")) - { - canonicalCertSubject = cert.Subject.Substring(3).ToUpper(); - int certSubjectComma = canonicalCertSubject.IndexOf(","); - if (certSubjectComma > -1) - canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma); - - if (accountName.ToUpper() == canonicalCertSubject) - matchingCert = cert.SerialNumber; - } - } - - if (!activeProxy && thunderbirdKeys.Contains(thunderbirdKey)) - activeProxy = true; - - AccountGrid.Rows.Add("Thunderbird", accountName, thunderbirdKeys.Contains(thunderbirdKey), matchingCert, thunderbirdKey); - } - } - } - } - } - - // If there's at least one active proxy, ensure the service is running. - if (activeProxy) - StartService(); - } - - /// - /// Retrieve an Outlook registry setting. - /// - /// The registry key to read within. - /// The name of the value to read. - private string GetOutlookRegistryValue(RegistryKey key, string name) - { - object value = key.GetValue(name); - if (value is byte[]) - return Encoding.Unicode.GetString((byte[])value).Replace("\0", ""); - if (value != null) - return value.ToString(); - else - return null; - } - - /// - /// Return a boolean value from an XML document. - /// - /// An XPathNavigator within the current XmlDocument. - /// The XPath expression to evaluate. - private static bool? GetXmlBoolValue(XPathNavigator navigator, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - { - if (!string.IsNullOrEmpty(valueNavigator.Value)) - { - bool value; - bool.TryParse(valueNavigator.Value, out value); - return value; - } - else - return null; - } - else - return null; - } - - /// - /// Return an integer value from an XML document. - /// - /// An XPathNavigator within the current XmlDocument. - /// The XPath expression to evaluate. - private static int? GetXmlIntValue(XPathNavigator navigator, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - { - int value; - int.TryParse(valueNavigator.Value, out value); - return value; - } - else - return null; - } - - /// - /// Return a string value from an XML document. - /// - /// An XPathNavigator within the current XmlDocument. - /// The XPath expression to evaluate. - private static string GetXmlStringValue(XPathNavigator navigator, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - return valueNavigator.Value; - else - return null; - } - - /// - /// Handle the save event and update OpaqueMail Proxy's settings.. - /// - private void SaveSettingsButton_Click(object sender, EventArgs e) - { - XPathDocument document = null; - - if (File.Exists(SettingsFileName)) - { - DialogResult dr = MessageBox.Show("A settings file already exists for OpaqueMail Proxy. Overwrite with these settings?", "Overwrite OpaqueMail Proxy Settings?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); - if (dr != System.Windows.Forms.DialogResult.OK) - return; - - try - { - document = new XPathDocument(SettingsFileName); - } - catch { } - } - - // If the service is running, stop it before proceeding. - if (ServiceExists("OpaqueMailProxy")) - { - ServiceController serviceContoller = new ServiceController("OpaqueMailProxy"); - if (serviceContoller.Status != ServiceControllerStatus.Stopped && serviceContoller.Status != ServiceControllerStatus.StopPending) - serviceContoller.Stop(); - } - - List accounts = new List(); - - // First, account for any settings in the existing XML file. - string fqdn = Functions.GetLocalFQDN(); - - int smtpServiceCount = 0, imapServiceCount = 0, pop3ServiceCount = 0; - if (document != null) - { - XPathNavigator navigator = document.CreateNavigator(); - - smtpServiceCount = GetXmlIntValue(navigator, "/Settings/SMTP/ServiceCount") ?? 0; - for (int i = 1; i <= smtpServiceCount; i++) - { - ProxyAccount account = new ProxyAccount(); - account.LocalSmtpIPAddress = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/LocalIPAddress") ?? ""; - account.LocalSmtpPort = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/LocalPort") ?? 587; - account.LocalSmtpEnableSsl = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/LocalEnableSsl") ?? true; - account.RemoteSmtpServer = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerHostName"); - account.RemoteSmtpPort = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerPort") ?? 587; - account.RemoteSmtpEnableSsl = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerEnableSSL") ?? true; - account.RemoteSmtpUsername = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerUsername"); - account.RemoteSmtpPassword = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerPassword"); - account.SmtpAcceptedIPs = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/AcceptedIPs"); - account.SmtpCertificateLocation = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/Location"); - account.SmtpCertificateSerialNumber = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/SerialNumber"); - account.SmtpCertificateSubjectName = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/SubjectName"); - account.SmtpLogFile = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/LogFile"); - - string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/SMTP/Service" + i + "/LogLevel"); - switch (logLevel.ToUpper()) - { - case "NONE": - account.SmtpLogLevel = LogLevel.None; - break; - case "CRITICAL": - account.SmtpLogLevel = LogLevel.Critical; - break; - case "ERROR": - account.SmtpLogLevel = LogLevel.Error; - break; - case "RAW": - account.SmtpLogLevel = LogLevel.Raw; - break; - case "VERBOSE": - account.SmtpLogLevel = LogLevel.Verbose; - break; - case "WARNING": - account.SmtpLogLevel = LogLevel.Warning; - break; - case "INFORMATION": - default: - account.SmtpLogLevel = LogLevel.Information; - break; - } - - account.SendCertificateReminders = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SendCertificateReminders") ?? true; - account.SmimeEncrypt = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SMIMEEncrypt") ?? true; - account.SmimeRemovePreviousOperations = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SMIMERemovePreviousOperations") ?? true; - account.SmimeSign = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SMIMESign") ?? true; - account.SmimeTripleWrap = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SMIMETripleWrap") ?? true; - - int? registryKeyCount = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/OutlookRegistryKeyCount") ?? 0; - for (int j = 1; j <= registryKeyCount; j++) - { - string registryKey = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/OutlookRegistryKey" + j); - if (!account.OutlookRegistryKeys.Contains(registryKey)) - account.OutlookRegistryKeys.Add(registryKey); - } - - int? thunderbirdKeyCount = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/ThunderbirdKeyCount") ?? 0; - for (int j = 1; j <= registryKeyCount; j++) - { - string thunderbirdKey = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/ThunderbirdKey" + j); - if (!string.IsNullOrEmpty(thunderbirdKey) && !account.ThunderbirdKeys.Contains(thunderbirdKey)) - account.ThunderbirdKeys.Add(thunderbirdKey); - } - - accounts.Add(account); - } - - imapServiceCount = GetXmlIntValue(navigator, "/Settings/IMAP/ServiceCount") ?? 0; - for (int i = 1; i <= imapServiceCount; i++) - { - ProxyAccount account = new ProxyAccount(); - bool accountMatched = false; - - // Check if a matching Outlook account already exists. - int? registryKeyCount = GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/OutlookRegistryKeyCount") ?? 0; - for (int j = 1; j <= registryKeyCount; j++) - { - string registryKey = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/OutlookRegistryKey" + j); - if (!account.OutlookRegistryKeys.Contains(registryKey)) - account.OutlookRegistryKeys.Add(registryKey); - - foreach (ProxyAccount existingAccount in accounts) - { - if (existingAccount.OutlookRegistryKeys.Contains(registryKey) && !accountMatched) - { - account = existingAccount; - j = 0; - accountMatched = true; - } - } - } - - // Check if a matching Thunderbird account already exists. - int? thunderbirdKeyCount = GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/ThunderbirdKeyCount") ?? 0; - for (int j = 1; j <= registryKeyCount; j++) - { - string thunderbirdKey = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/ThunderbirdKey" + j); - if (!string.IsNullOrEmpty(thunderbirdKey) && !account.ThunderbirdKeys.Contains(thunderbirdKey)) - account.ThunderbirdKeys.Add(thunderbirdKey); - - foreach (ProxyAccount existingAccount in accounts) - { - if (existingAccount.ThunderbirdKeys.Contains(thunderbirdKey) && !accountMatched) - { - account = existingAccount; - j = 0; - accountMatched = true; - } - } - } - - account.LocalImapIPAddress = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/LocalIPAddress") ?? ""; - account.LocalImapPort = GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/LocalPort") ?? 587; - account.LocalImapEnableSsl = GetXmlBoolValue(navigator, "/Settings/IMAP/Service" + i + "/LocalEnableSsl") ?? true; - - account.RemoteImapServer = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerHostName"); - account.RemoteImapPort = GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerPort") ?? 993; - account.RemoteImapEnableSsl = GetXmlBoolValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerEnableSSL") ?? true; - account.ImapAcceptedIPs = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/AcceptedIPs"); - account.ImapCertificateLocation = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/Location"); - account.ImapCertificateSerialNumber = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/SerialNumber"); - account.ImapCertificateSubjectName = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/SubjectName"); - account.ImapLogFile = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/LogFile"); - - string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/IMAP/Service" + i + "/LogLevel"); - switch (logLevel.ToUpper()) - { - case "NONE": - account.ImapLogLevel = LogLevel.None; - break; - case "CRITICAL": - account.ImapLogLevel = LogLevel.Critical; - break; - case "ERROR": - account.ImapLogLevel = LogLevel.Error; - break; - case "RAW": - account.ImapLogLevel = LogLevel.Raw; - break; - case "VERBOSE": - account.ImapLogLevel = LogLevel.Verbose; - break; - case "WARNING": - account.ImapLogLevel = LogLevel.Warning; - break; - case "INFORMATION": - default: - account.ImapLogLevel = LogLevel.Information; - break; - } - } - - // Handle POP3 settings third. - pop3ServiceCount = GetXmlIntValue(navigator, "/Settings/POP3/ServiceCount") ?? 0; - for (int i = 1; i <= pop3ServiceCount; i++) - { - ProxyAccount account = new ProxyAccount(); - bool accountMatched = false; - - // Check if a matching Outlook account already exists. - int? registryKeyCount = GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/OutlookRegistryKeyCount") ?? 0; - for (int j = 1; j <= registryKeyCount; j++) - { - string registryKey = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/OutlookRegistryKey" + j); - if (!account.OutlookRegistryKeys.Contains(registryKey)) - account.OutlookRegistryKeys.Add(registryKey); - - foreach (ProxyAccount existingAccount in accounts) - { - if (existingAccount.OutlookRegistryKeys.Contains(registryKey) && !accountMatched) - { - account = existingAccount; - j = 0; - accountMatched = true; - } - } - } - - // Check if a matching Thunderbird account already exists. - int? thunderbirdKeyCount = GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/ThunderbirdKeyCount") ?? 0; - for (int j = 1; j <= registryKeyCount; j++) - { - string thunderbirdKey = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/ThunderbirdKey" + j); - if (!string.IsNullOrEmpty(thunderbirdKey) && !account.ThunderbirdKeys.Contains(thunderbirdKey)) - account.ThunderbirdKeys.Add(thunderbirdKey); - - foreach (ProxyAccount existingAccount in accounts) - { - if (existingAccount.ThunderbirdKeys.Contains(thunderbirdKey) && !accountMatched) - { - account = existingAccount; - j = 0; - accountMatched = true; - } - } - } - - account.LocalPop3IPAddress = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/LocalIPAddress") ?? ""; - account.LocalPop3Port = GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/LocalPort") ?? 995; - account.LocalPop3EnableSsl = GetXmlBoolValue(navigator, "/Settings/POP3/Service" + i + "/LocalEnableSsl") ?? true; - - account.RemotePop3Server = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerHostName"); - account.RemotePop3Port = GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerPort") ?? 995; - account.RemotePop3EnableSsl = GetXmlBoolValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerEnableSSL") ?? true; - account.Pop3AcceptedIPs = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/AcceptedIPs"); - account.Pop3CertificateLocation = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/Location"); - account.Pop3CertificateSerialNumber = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/SerialNumber"); - account.Pop3CertificateSubjectName = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/SubjectName"); - account.Pop3LogFile = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/LogFile"); - - string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/POP3/Service" + i + "/LogLevel"); - switch (logLevel.ToUpper()) - { - case "NONE": - account.Pop3LogLevel = LogLevel.None; - break; - case "CRITICAL": - account.Pop3LogLevel = LogLevel.Critical; - break; - case "ERROR": - account.Pop3LogLevel = LogLevel.Error; - break; - case "RAW": - account.Pop3LogLevel = LogLevel.Raw; - break; - case "VERBOSE": - account.Pop3LogLevel = LogLevel.Verbose; - break; - case "WARNING": - account.Pop3LogLevel = LogLevel.Warning; - break; - case "INFORMATION": - default: - account.Pop3LogLevel = LogLevel.Information; - break; - } - } - } - - // Second, gather existing Outlook account settings from the registry. - foreach (string outlookVersion in OutlookVersions.Keys) - { - using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\" + outlookVersion + @"\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676", false)) - { - if (key != null) - { - string[] subkeyNames = key.GetSubKeyNames(); - if (subkeyNames != null) - { - foreach (string subkeyName in subkeyNames) - { - using (RegistryKey subKey = key.OpenSubKey(subkeyName, false)) - { - bool matched = false; - foreach (ProxyAccount existingAccount in accounts) - { - if (existingAccount.OutlookRegistryKeys.Contains(subKey.Name)) - matched = true; - } - - if (!matched) - { - ProxyAccount account = new ProxyAccount(); - account.ClientType = "Outlook"; - account.ClientVersion = outlookVersion; - - account.RemoteImapEnableSsl = GetOutlookRegistryValue(subKey, "IMAP Use SSL") == "1"; - int.TryParse(GetOutlookRegistryValue(subKey, "IMAP Port"), out account.RemoteImapPort); - account.RemoteImapServer = GetOutlookRegistryValue(subKey, "IMAP Server") ?? ""; - - account.RemotePop3EnableSsl = GetOutlookRegistryValue(subKey, "POP3 Use SSL") == "1"; - int.TryParse(GetOutlookRegistryValue(subKey, "POP3 Port"), out account.RemotePop3Port); - account.RemotePop3Server = GetOutlookRegistryValue(subKey, "POP3 Server") ?? ""; - - account.RemoteSmtpEnableSsl = GetOutlookRegistryValue(subKey, "SMTP Use SSL") == "1"; - int.TryParse(GetOutlookRegistryValue(subKey, "SMTP Port"), out account.RemoteSmtpPort); - account.RemoteSmtpServer = GetOutlookRegistryValue(subKey, "SMTP Server") ?? ""; - - // Only proceed if a server is found. - if (!string.IsNullOrEmpty(account.RemoteImapServer) || !string.IsNullOrEmpty(account.RemotePop3Server) || !string.IsNullOrEmpty(account.RemoteSmtpServer)) - { - string username = GetOutlookRegistryValue(subKey, "IMAP User"); - if (string.IsNullOrEmpty(username)) - username = GetOutlookRegistryValue(subKey, "POP3 User"); - - if (!string.IsNullOrEmpty(username)) - { - account.Usernames.Add(username); - account.OutlookRegistryKeys.Add(subKey.Name); - - accounts.Add(account); - } - } - } - } - } - } - } - } - } - - // Third, gather existing Thunderbird account settings. - if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) - { - foreach (string directory in Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) - { - if (File.Exists(directory + "\\prefs.js")) - { - string prefsFile = File.ReadAllText(directory + "\\prefs.js"); - - int keyCount; - int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.account.lastKey\", ", ")"), out keyCount); - for (int i = 1; i <= keyCount; i++) - { - string thunderbirdKey = directory + "~" + i.ToString(); - - bool matched = false; - foreach (ProxyAccount existingAccount in accounts) - { - if (existingAccount.ThunderbirdKeys.Contains(thunderbirdKey)) - matched = true; - } - - if (!matched) - { - ProxyAccount account = new ProxyAccount(); - account.ClientType = "Thunderbird"; - - int sslValue = 0; - int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".try_ssl\", ", ")"), out sslValue); - account.RemoteSmtpEnableSsl = sslValue > 0; - int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".port\", ", ")"), out account.RemoteSmtpPort); - account.RemoteSmtpServer = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".hostname\", \"", "\"") ?? ""; - - if (Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".type\", \"", "\"") == "pop3") - { - int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")"), out account.RemotePop3Port); - account.RemotePop3EnableSsl = (account.RemotePop3Port == 995); - account.RemotePop3Server = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"") ?? ""; - } - else - { - int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")"), out account.RemoteImapPort); - account.RemoteImapEnableSsl = (account.RemoteImapPort == 993); - account.RemoteImapServer = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"") ?? ""; - } - - // Only proceed if a server is found. - if (!string.IsNullOrEmpty(account.RemoteImapServer) || !string.IsNullOrEmpty(account.RemotePop3Server) || !string.IsNullOrEmpty(account.RemoteSmtpServer)) - { - string username = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".username\", \"", "\""); - - if (!string.IsNullOrEmpty(username)) - { - account.Usernames.Add(username); - account.ThunderbirdKeys.Add(thunderbirdKey); - - accounts.Add(account); - } - } - } - } - } - } - } - - // Fourth, check which accounts the user chooses to encrypt. - smtpServiceCount = 0; - imapServiceCount = 0; - pop3ServiceCount = 0; - - HashSet portsReserved = new HashSet(); - int nextPortToTry = 1000; - - foreach (DataGridViewRow row in AccountGrid.Rows) - { - if ((bool)row.Cells[2].Value == true) - { - foreach (ProxyAccount account in accounts) - { - if ((account.OutlookRegistryKeys.Contains((string)row.Cells[4].Value) || account.ThunderbirdKeys.Contains((string)row.Cells[4].Value)) && !account.Matched) - { - account.Matched = true; - - // Ensure the SMTP proxy connection has a unique port. - if (!string.IsNullOrEmpty(account.RemoteSmtpServer)) - { - smtpServiceCount++; - - if (portsReserved.Contains(account.LocalSmtpPort)) - { - nextPortToTry = GetNextAvailablePort(++nextPortToTry); - account.LocalSmtpPort = nextPortToTry; - } - - portsReserved.Add(account.LocalSmtpPort); - } - - // Ensure the IMAP proxy connection has a unique port. - if (!string.IsNullOrEmpty(account.RemoteImapServer)) - { - imapServiceCount++; - - if (portsReserved.Contains(account.LocalImapPort)) - { - nextPortToTry = GetNextAvailablePort(++nextPortToTry); - account.LocalImapPort = nextPortToTry; - } - - portsReserved.Add(account.LocalImapPort); - } - - // Ensure the POP3 proxy connection has a unique port. - if (!string.IsNullOrEmpty(account.RemotePop3Server)) - { - pop3ServiceCount++; - - if (portsReserved.Contains(account.LocalPop3Port)) - { - nextPortToTry = GetNextAvailablePort(++nextPortToTry); - account.LocalPop3Port = nextPortToTry; - } - - portsReserved.Add(account.LocalPop3Port); - } - } - } - } - } - - // Fifth, write out the XML setting values. - XmlWriterSettings streamWriterSettings = new XmlWriterSettings(); - streamWriterSettings.Indent = true; - streamWriterSettings.IndentChars = " "; - streamWriterSettings.NewLineChars = "\r\n"; - streamWriterSettings.NewLineHandling = NewLineHandling.Replace; - - using (XmlWriter streamWriter = XmlWriter.Create(SettingsFileName, streamWriterSettings)) - { - streamWriter.WriteStartDocument(); - - streamWriter.WriteStartElement("Settings"); - - streamWriter.WriteStartElement("SMTP"); - - streamWriter.WriteComment("The number of SMTP proxy services to run. Each proxy's settings will be outlined in subsequent blocks."); - streamWriter.WriteElementString("ServiceCount", smtpServiceCount.ToString()); - - int smtpServiceId = 0; - foreach (ProxyAccount account in accounts) - { - if (!string.IsNullOrEmpty(account.RemoteSmtpServer) && account.Matched) - { - streamWriter.WriteStartElement("Service" + (++smtpServiceId).ToString()); - - streamWriter.WriteComment("IP addresses to accept connections from. Delete or set value to \"*\" to accept connections from any IP."); - streamWriter.WriteComment("Individual IPs can be specified, separated by commas, or ranges can be specified. The \"*\" wildcard character is supported."); - streamWriter.WriteComment("By default, connections are only accepted from the localhost."); - streamWriter.WriteElementString("AcceptedIPs", account.ImapAcceptedIPs ?? "0.0.0.0"); - - streamWriter.WriteComment("Local IP address to listen on. \"Any\" means listen on all IPs."); - streamWriter.WriteElementString("LocalIPAddress", account.LocalSmtpIPAddress ?? "Any"); - streamWriter.WriteComment("Local port to listen on."); - streamWriter.WriteElementString("LocalPort", account.LocalSmtpPort > 0 ? account.LocalSmtpPort.ToString() : "587"); - streamWriter.WriteComment("Whether local connections support TLS/SSL protection."); - streamWriter.WriteElementString("LocalEnableSSL", account.LocalSmtpEnableSsl.ToString()); - - streamWriter.WriteComment("Remote SMTP server hostname to connect to. Common values: smtp.gmail.com, smtp.live.com, smtp.mail.yahoo.com"); - streamWriter.WriteElementString("RemoteServerHostName", account.RemoteSmtpServer ?? "Any"); - streamWriter.WriteComment("Remote SMTP server port to connect to. 587 is recommended, but 465 or 25 may be required."); - streamWriter.WriteElementString("RemoteServerPort", account.RemoteSmtpPort > 0 ? account.RemoteSmtpPort.ToString() : "587"); - streamWriter.WriteComment("Whether the remote SMTP server supports TLS/SSL protection."); - streamWriter.WriteElementString("RemoteServerEnableSSL", account.RemoteSmtpEnableSsl.ToString()); - - streamWriter.WriteComment("(Optional) Username used when authenticating to the remote SMTP server. When supplied, it will override any values sent from the client."); - streamWriter.WriteElementString("RemoteServerUsername", account.RemoteSmtpUsername); - streamWriter.WriteComment("(Optional) Password used when authenticating to the remote SMTP server. When supplied, it will override any values sent from the client."); - streamWriter.WriteElementString("RemoteServerPassword", account.RemoteSmtpPassword); - - streamWriter.WriteStartElement("Certificate"); - streamWriter.WriteComment("Where certificates should be stored and retrieved from by default. \"LocalMachine\" or \"CurrentUser\" only."); - streamWriter.WriteElementString("Location", account.SmtpCertificateLocation ?? "LocalMachine"); - streamWriter.WriteComment("(Optional) The serial number of an X509 certificate to be used for server identification. If left blank, one will be autogenerated."); - streamWriter.WriteElementString("SerialNumber", account.SmtpCertificateSerialNumber); - streamWriter.WriteComment("(Optional) The subject name of an X509 certificate to be used for server identification. If left blank, one will be autogenerated."); - streamWriter.WriteElementString("SubjectName", account.SmtpCertificateSubjectName); - streamWriter.WriteEndElement(); - - streamWriter.WriteComment("Send e-mail reminders when a signing certificate is due to expire within 30 days."); - streamWriter.WriteElementString("SendCertificateReminders", account.SendCertificateReminders.ToString()); - - streamWriter.WriteComment("Whether all outgoing messages require the S/MIME settings specified below."); - streamWriter.WriteComment("When set to \"RequireExactSettings\", any messages that can't be signed or encrypted will be dropped, unsent."); - streamWriter.WriteComment("When set to \"BestEffort\", OpaqueMail Proxy will attempt to sign and/or encrypt messages but still forward any that can't be."); - streamWriter.WriteElementString("SMIMESettingsMode", SmimeSettingsMode.SelectedIndex > 0 ? "RequireExactSettings" : "BestEffort"); - - streamWriter.WriteComment("Whether to sign the e-mail. When true, signing is the first S/MIME operation."); - streamWriter.WriteElementString("SMIMESign", account.SmimeSign.ToString()); - streamWriter.WriteComment("Whether to encrypt the e-mail's envelope. When SmimeSign is true, encryption is the second S/MIME operation."); - streamWriter.WriteElementString("SMIMEEncrypt", account.SmimeEncrypt.ToString()); - streamWriter.WriteComment("Triple-wrap the e-mail by signing, then encrypting the envelope, then signing the encrypted envelope."); - streamWriter.WriteElementString("SMIMETripleWrap", account.SmimeTripleWrap.ToString()); - - streamWriter.WriteComment("Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied."); - streamWriter.WriteElementString("SMIMERemovePreviousOperations", account.SmimeRemovePreviousOperations.ToString()); - - streamWriter.WriteComment("Where log files should be stored, if any. Leave blank to avoid logging."); - streamWriter.WriteComment("Date and instance variables can be encased in angle braces. For example, \"Logs\\SMTPProxy{#}-{yyyy-MM-dd}.log\"."); - streamWriter.WriteElementString("LogFile", account.SmtpLogFile ?? "Logs\\SMTPProxy{#}-{yyyy-MM-dd}.log"); - - streamWriter.WriteComment("Proxy logging level, determining how much information is logged. Possible values: None, Critical, Error, Warning, Information, Verbose, Raw"); - streamWriter.WriteElementString("LogLevel", account.SmtpLogLevel.ToString()); - - if (account.OutlookRegistryKeys.Count > 0) - { - streamWriter.WriteComment("Outlook registry keys for accounts configured through the OpaqueMail Proxy settings app."); - streamWriter.WriteElementString("OutlookRegistryKeyCount", account.OutlookRegistryKeys.Count.ToString()); - - int registryKeyId = 0; - foreach (string registryKey in account.OutlookRegistryKeys) - streamWriter.WriteElementString("OutlookRegistryKey" + (++registryKeyId).ToString(), registryKey); - } - - if (account.ThunderbirdKeys.Count > 0) - { - streamWriter.WriteComment("Thunderbird keys for accounts configured through the OpaqueMail Proxy settings app."); - streamWriter.WriteElementString("ThunderbirdKeyCount", account.ThunderbirdKeys.Count.ToString()); - - int thunderbirdKeyId = 0; - foreach (string thunderbirdKey in account.ThunderbirdKeys) - streamWriter.WriteElementString("ThunderbirdKey" + (++thunderbirdKeyId).ToString(), thunderbirdKey); - } - - streamWriter.WriteEndElement(); - } - } - - streamWriter.WriteEndElement(); - - streamWriter.WriteStartElement("IMAP"); - - streamWriter.WriteComment("The number of IMAP proxy services to run. Each proxy's settings will be outlined in subsequent blocks."); - streamWriter.WriteElementString("ServiceCount", imapServiceCount.ToString()); - - int imapServiceId = 0; - foreach (ProxyAccount account in accounts) - { - if (!string.IsNullOrEmpty(account.RemoteImapServer) && account.Matched) - { - streamWriter.WriteStartElement("Service" + (++imapServiceId).ToString()); - - streamWriter.WriteComment("IP addresses to accept connections from. Delete or set value to \"*\" to accept connections from any IP."); - streamWriter.WriteComment("Individual IPs can be specified, separated by commas, or ranges can be specified. The \"*\" wildcard character is supported."); - streamWriter.WriteComment("By default, connections are only accepted from the localhost."); - streamWriter.WriteElementString("AcceptedIPs", account.ImapAcceptedIPs ?? "0.0.0.0"); - - streamWriter.WriteComment("Local IP address to listen on. \"Any\" means listen on all IPs."); - streamWriter.WriteElementString("LocalIPAddress", account.LocalImapIPAddress ?? "Any"); - streamWriter.WriteComment("Local port to listen on."); - streamWriter.WriteElementString("LocalPort", account.LocalImapPort > 0 ? account.LocalImapPort.ToString() : "993"); - streamWriter.WriteComment("Whether local connections support TLS/SSL protection."); - streamWriter.WriteElementString("LocalEnableSSL", account.LocalImapEnableSsl.ToString()); - - streamWriter.WriteComment("Remote IMAP server hostname to connect to. Common values: imap.gmail.com, imap.mail.yahoo.com"); - streamWriter.WriteElementString("RemoteServerHostName", account.RemoteImapServer ?? "Any"); - streamWriter.WriteComment("Remote IMAP server port to connect to. 993 is recommended, but 143 may be required."); - streamWriter.WriteElementString("RemoteServerPort", account.RemoteImapPort > 0 ? account.RemoteImapPort.ToString() : "993"); - streamWriter.WriteComment("Whether the remote IMAP server supports TLS/SSL protection."); - streamWriter.WriteElementString("RemoteServerEnableSSL", account.RemoteImapEnableSsl.ToString()); - - streamWriter.WriteComment("Where log files should be stored, if any. Leave blank to avoid logging."); - streamWriter.WriteComment("Date and instance variables can be encased in angle braces. For example, \"Logs\\IMAPProxy{#}-{yyyy-MM-dd}.log\"."); - streamWriter.WriteElementString("LogFile", account.ImapLogFile ?? "Logs\\IMAPProxy{#}-{yyyy-MM-dd}.log"); - - streamWriter.WriteComment("Proxy logging level, determining how much information is logged. Possible values: None, Critical, Error, Warning, Information, Verbose, Raw"); - streamWriter.WriteElementString("LogLevel", account.ImapLogLevel.ToString()); - - if (account.OutlookRegistryKeys.Count > 0) - { - streamWriter.WriteComment("Outlook registry keys for accounts configured through the OpaqueMail Proxy settings app."); - streamWriter.WriteElementString("OutlookRegistryKeyCount", account.OutlookRegistryKeys.Count.ToString()); - - int registryKeyId = 0; - foreach (string registryKey in account.OutlookRegistryKeys) - streamWriter.WriteElementString("OutlookRegistryKey" + (++registryKeyId).ToString(), registryKey); - } - - if (account.ThunderbirdKeys.Count > 0) - { - streamWriter.WriteComment("Thunderbird keys for accounts configured through the OpaqueMail Proxy settings app."); - streamWriter.WriteElementString("ThunderbirdKeyCount", account.ThunderbirdKeys.Count.ToString()); - - int thunderbirdKeyId = 0; - foreach (string thunderbirdKey in account.ThunderbirdKeys) - streamWriter.WriteElementString("ThunderbirdKey" + (++thunderbirdKeyId).ToString(), thunderbirdKey); - } - - streamWriter.WriteEndElement(); - } - } - - streamWriter.WriteEndElement(); - - streamWriter.WriteStartElement("POP3"); - - streamWriter.WriteComment("The number of POP3 proxy services to run. Each proxy's settings will be outlined in subsequent blocks."); - streamWriter.WriteElementString("ServiceCount", pop3ServiceCount.ToString()); - - int pop3ServiceId = 0; - foreach (ProxyAccount account in accounts) - { - if (!string.IsNullOrEmpty(account.RemotePop3Server) && account.Matched) - { - streamWriter.WriteStartElement("Service" + (++pop3ServiceId).ToString()); - - streamWriter.WriteComment("IP addresses to accept connections from. Delete or set value to \"*\" to accept connections from any IP."); - streamWriter.WriteComment("Individual IPs can be specified, separated by commas, or ranges can be specified. The \"*\" wildcard character is supported."); - streamWriter.WriteComment("By default, connections are only accepted from the localhost."); - streamWriter.WriteElementString("AcceptedIPs", account.Pop3AcceptedIPs ?? "0.0.0.0"); - - streamWriter.WriteComment("Local IP address to listen on. \"Any\" means listen on all IPs."); - streamWriter.WriteElementString("LocalIPAddress", account.LocalPop3IPAddress ?? "Any"); - streamWriter.WriteComment("Local port to listen on."); - streamWriter.WriteElementString("LocalPort", account.LocalPop3Port > 0 ? account.LocalPop3Port.ToString() : "995"); - streamWriter.WriteComment("Whether local connections support TLS/SSL protection."); - streamWriter.WriteElementString("LocalEnableSSL", account.LocalPop3EnableSsl.ToString()); - - streamWriter.WriteComment("Remote POP3 server hostname to connect to. Common values: pop.gmail.com, pop3.live.com, pop.mail.yahoo.com"); - streamWriter.WriteElementString("RemoteServerHostName", account.RemotePop3Server ?? "Any"); - streamWriter.WriteComment("Remote POP3 server port to connect to. 995 is recommended, but 110 may be required."); - streamWriter.WriteElementString("RemoteServerPort", account.RemotePop3Port > 0 ? account.RemotePop3Port.ToString() : "995"); - streamWriter.WriteComment("Whether the remote POP3 server supports TLS/SSL protection."); - streamWriter.WriteElementString("RemoteServerEnableSSL", account.RemotePop3EnableSsl.ToString()); - - streamWriter.WriteComment("Where log files should be stored, if any. Leave blank to avoid logging."); - streamWriter.WriteComment("Date and instance variables can be encased in angle braces. For example, \"Logs\\POP3Proxy{#}-{yyyy-MM-dd}.log\"."); - streamWriter.WriteElementString("LogFile", account.Pop3LogFile ?? "Logs\\POP3Proxy{#}-{yyyy-MM-dd}.log"); - - streamWriter.WriteComment("Proxy logging level, determining how much information is logged. Possible values: None, Critical, Error, Warning, Information, Verbose, Raw"); - streamWriter.WriteElementString("LogLevel", account.Pop3LogLevel.ToString()); - - if (account.OutlookRegistryKeys.Count > 0) - { - streamWriter.WriteComment("Outlook registry keys for accounts configured through the OpaqueMail Proxy settings app."); - streamWriter.WriteElementString("OutlookRegistryKeyCount", account.OutlookRegistryKeys.Count.ToString()); - - int registryKeyId = 0; - foreach (string registryKey in account.OutlookRegistryKeys) - streamWriter.WriteElementString("OutlookRegistryKey" + (++registryKeyId).ToString(), registryKey); - } - - if (account.ThunderbirdKeys.Count > 0) - { - streamWriter.WriteComment("Thunderbird keys for accounts configured through the OpaqueMail Proxy settings app."); - streamWriter.WriteElementString("ThunderbirdKeyCount", account.ThunderbirdKeys.Count.ToString()); - - int thunderbirdKeyId = 0; - foreach (string thunderbirdKey in account.ThunderbirdKeys) - streamWriter.WriteElementString("ThunderbirdKey" + (++thunderbirdKeyId).ToString(), thunderbirdKey); - } - - streamWriter.WriteEndElement(); - } - } - - streamWriter.WriteEndElement(); - - streamWriter.WriteEndElement(); - } - - // Sixth, restart the OpaqueMail service. - InstallService(); - StartService(); - - // Seventh, rewrite the Outlook registry values. - foreach (string outlookVersion in OutlookVersions.Keys) - { - using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\" + outlookVersion + @"\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676", false)) - { - if (key != null) - { - string[] subkeyNames = key.GetSubKeyNames(); - if (subkeyNames != null) - { - foreach (string subkeyName in subkeyNames) - { - using (RegistryKey subKey = key.OpenSubKey(subkeyName, true)) - { - string smtpServer = GetOutlookRegistryValue(subKey, "SMTP Server"); - if (!string.IsNullOrEmpty(smtpServer)) - { - foreach (ProxyAccount account in accounts) - { - // If matched, set to use the local proxy. If not matched and we previously used the local proxy, switch back to the original value. - if (account.OutlookRegistryKeys.Contains(subKey.Name)) - { - if (account.Matched) - { - subKey.SetValue("SMTP Server", Encoding.Unicode.GetBytes(fqdn + "\0")); - subKey.SetValue("SMTP Port", account.LocalSmtpPort); - subKey.SetValue("SMTP Use SSL", account.LocalSmtpEnableSsl ? 1 : 0); - } - else - { - subKey.SetValue("SMTP Server", Encoding.Unicode.GetBytes(account.RemoteSmtpServer)); - subKey.SetValue("SMTP Port", account.RemoteSmtpPort); - subKey.SetValue("SMTP Use SSL", account.RemoteSmtpEnableSsl ? 1 : 0); - } - } - } - } - - string imapServer = GetOutlookRegistryValue(subKey, "IMAP Server"); - if (!string.IsNullOrEmpty(imapServer)) - { - foreach (ProxyAccount account in accounts) - { - // If matched, set to use the local proxy. If not matched and we previously used the local proxy, switch back to the original value. - if (account.OutlookRegistryKeys.Contains(subKey.Name)) - { - if (account.Matched) - { - subKey.SetValue("IMAP Server", Encoding.Unicode.GetBytes(fqdn + "\0")); - subKey.SetValue("IMAP Port", account.LocalImapPort); - subKey.SetValue("IMAP Use SSL", account.LocalImapEnableSsl ? 1 : 0); - } - else - { - subKey.SetValue("IMAP Server", Encoding.Unicode.GetBytes(account.RemoteImapServer + "\0")); - subKey.SetValue("IMAP Port", account.RemoteImapPort); - subKey.SetValue("IMAP Use SSL", account.RemoteImapEnableSsl ? 1 : 0); - } - } - } - } - - string pop3Server = GetOutlookRegistryValue(subKey, "POP3 Server"); - if (!string.IsNullOrEmpty(pop3Server)) - { - foreach (ProxyAccount account in accounts) - { - // If matched, set to use the local proxy. If not matched and we previously used the local proxy, switch back to the original value. - if (account.OutlookRegistryKeys.Contains(subKey.Name)) - { - if (account.Matched) - { - subKey.SetValue("POP3 Server", Encoding.Unicode.GetBytes(fqdn + "\0")); - subKey.SetValue("POP3 Port", account.LocalPop3Port); - subKey.SetValue("POP3 Use SSL", account.LocalPop3EnableSsl ? 1 : 0); - } - else - { - subKey.SetValue("POP3 Server", Encoding.Unicode.GetBytes(account.RemotePop3Server + "\0")); - subKey.SetValue("POP3 Port", account.RemotePop3Port); - subKey.SetValue("POP3 Use SSL", account.RemotePop3EnableSsl ? 1 : 0); - } - } - } - } - } - } - } - } - } - } - - // Eighth, rewrite the Thunderbird registry values. - if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) - { - foreach (string directory in Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) - { - if (File.Exists(directory + "\\prefs.js")) - { - string prefsFile = File.ReadAllText(directory + "\\prefs.js"); - - int keyCount; - int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.account.lastKey\", ", ")"), out keyCount); - for (int i = 1; i <= keyCount; i++) - { - string thunderbirdKey = directory + "~" + i.ToString(); - - foreach (ProxyAccount account in accounts) - { - // If matched, set to use the local proxy. If not matched and we previously used the local proxy, switch back to the original value. - if (account.ThunderbirdKeys.Contains(thunderbirdKey)) - { - if (account.Matched) - { - if (Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".type\", \"", "\"").ToLower() == "pop3") - { - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"", fqdn); - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")", account.LocalPop3Port.ToString()); - } - else - { - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"", fqdn); - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")", account.LocalImapPort.ToString()); - } - - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".hostname\", \"", "\"", fqdn); - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".port\", ", ")", account.LocalSmtpPort.ToString()); - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".is_gmail\", ", ")", "false"); - } - else - { - if (Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".type\", \"", "\"").ToLower() == "pop3") - { - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"", account.RemotePop3Server); - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")", account.RemotePop3Port.ToString()); - } - else - { - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"", account.RemoteImapServer); - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")", account.RemoteImapPort.ToString()); - } - - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".hostname\", \"", "\"", account.RemoteSmtpServer); - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".port\", ", ")", account.RemoteSmtpPort.ToString()); - - bool isGmail = account.RemoteSmtpServer.ToUpper() == "SMTP.GMAIL.COM" || account.RemoteSmtpServer.ToUpper() == "SMTP.GOOGLEMAIL.COM"; - prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".is_gmail\", ", ")", isGmail ? "true" : "false"); - } - } - } - } - - // Write the settings file back. - File.WriteAllBytes(directory + "\\prefs.js", Encoding.UTF8.GetBytes(prefsFile)); - } - } - } - - // Finally, prompt to restart Outlook or Thunderbird. - Process[] processes = Process.GetProcessesByName("OUTLOOK"); - if (processes.Length > 0) - { - DialogResult dr = MessageBox.Show("Outlook is currently running and will need to be restarted before these changes will take effect. Would you like to restart Outlook now?", "Restart Outlook?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); - if (dr == System.Windows.Forms.DialogResult.OK) - { - // Stop Outlook. - foreach (Process process in processes) - process.Kill(); - - // Try to start Outlook. - for (int i = 15; i >= 8; i--) - { - if (File.Exists("C:\\Program Files (x86)\\Microsoft Office\\Office" + i.ToString() + "\\Outlook.exe")) - { - Process.Start("C:\\Program Files (x86)\\Microsoft Office\\Office" + i.ToString() + "\\Outlook.exe"); - break; - } - else if (File.Exists("C:\\Program Files\\Microsoft Office\\Office" + i.ToString() + "\\Outlook.exe")) - { - Process.Start("C:\\Program Files\\Microsoft Office\\Office" + i.ToString() + "\\Outlook.exe"); - break; - } - } - } - } - processes = Process.GetProcessesByName("THUNDERBIRD"); - if (processes.Length > 0) - { - DialogResult dr = MessageBox.Show("Thunderbird is currently running and will need to be restarted before these changes will take effect. Would you like to restart Thunderbird now?", "Restart Thunderbird?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); - if (dr == System.Windows.Forms.DialogResult.OK) - { - // Stop Thunderbird. - foreach (Process process in processes) - process.Kill(); - - // Try to start Thunderbird. - if (File.Exists("C:\\Program Files (x86)\\Mozilla Thunderbird\\Thunderbird.exe")) - Process.Start("C:\\Program Files (x86)\\Mozilla Thunderbird\\Thunderbird.exe"); - } - } - - MessageBox.Show("OpaqueMail Proxy has been successfully configured and the Windows Service is now running.\r\n\r\nYou may close this program and the proxy will continue to run in the background.", "Success.", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - - /// - /// Confirm the Windows service exists. - /// - /// Name of the WIndows service - private bool ServiceExists(string name) - { - ServiceController[] services = ServiceController.GetServices(); - return services.FirstOrDefault(s => s.ServiceName == name) != null; - } - - /// - /// Start the OpaqueMail Proxy Windows service. - /// - private void StartService() - { - if (ServiceExists("OpaqueMailProxy")) - { - ServiceController serviceContoller = new ServiceController("OpaqueMailProxy"); - if (serviceContoller.Status != ServiceControllerStatus.Running && serviceContoller.Status != ServiceControllerStatus.StartPending) - serviceContoller.Start(); - } - } - - /// - /// Stop the OpaqueMail Proxy Windows service. - /// - private void StopService() - { - if (ServiceExists("OpaqueMailProxy")) - { - ServiceController serviceContoller = new ServiceController("OpaqueMailProxy"); - if (serviceContoller.Status != ServiceControllerStatus.Stopped && serviceContoller.Status != ServiceControllerStatus.StopPending) - serviceContoller.Stop(); - } - } - - /// - /// Uninstall the OpaqueMail Proxy service. - /// - private void UninstallService() - { - if (ServiceExists("OpaqueMailProxy")) - { - ProxyServiceInstaller installer = new ProxyServiceInstaller(); - installer.Install(true, new string[] { }); - } - } - #endregion Private Methods - - /// - /// Represent certificate choices in a grid view. - /// - private class Choice - { - public string Name { get; private set; } - public string Value { get; private set; } - - public Choice(string name, string value) - { - Name = name; - Value = value; - } - } - - /// - /// Track e-mail account settings. - /// - private class ProxyAccount - { - public bool Matched = false; - - public string ClientType; - public string ClientVersion; - - public List OutlookRegistryKeys = new List(); - public List ThunderbirdKeys = new List(); - public List Usernames = new List(); - - public string ImapAcceptedIPs = "0.0.0.0"; - public string Pop3AcceptedIPs = "0.0.0.0"; - public string SmtpAcceptedIPs = "0.0.0.0"; - - public bool LocalImapEnableSsl = true; - public int LocalImapPort = 993; - public string LocalImapIPAddress = "Any"; - - public bool LocalPop3EnableSsl = true; - public int LocalPop3Port = 995; - public string LocalPop3IPAddress = "Any"; - - public bool LocalSmtpEnableSsl = true; - public int LocalSmtpPort = 587; - public string LocalSmtpIPAddress = "Any"; - - public bool RemoteImapEnableSsl; - public int RemoteImapPort; - public string RemoteImapServer = ""; - - public bool RemotePop3EnableSsl; - public int RemotePop3Port; - public string RemotePop3Server = ""; - - public bool RemoteSmtpEnableSsl; - public int RemoteSmtpPort; - public string RemoteSmtpServer = ""; - public string RemoteSmtpUsername; - public string RemoteSmtpPassword; - - public string ImapCertificateLocation; - public string ImapCertificateSerialNumber; - public string ImapCertificateSubjectName; - public string Pop3CertificateLocation; - public string Pop3CertificateSerialNumber; - public string Pop3CertificateSubjectName; - public string SmtpCertificateLocation; - public string SmtpCertificateSerialNumber; - public string SmtpCertificateSubjectName; - - public bool SendCertificateReminders = true; - public string SmimeSettingsModeValue = "BestEffort"; - - public bool SmimeSign = true; - public bool SmimeEncrypt = true; - public bool SmimeTripleWrap = true; - - public bool SmimeRemovePreviousOperations = true; - - public string ImapLogFile = "Logs\\IMAPProxy{#}-{yyyy-MM-dd}.log"; - public LogLevel ImapLogLevel = LogLevel.Verbose; - public string Pop3LogFile = "Logs\\POP3Proxy{#}-{yyyy-MM-dd}.log"; - public LogLevel Pop3LogLevel = LogLevel.Verbose; - public string SmtpLogFile = "Logs\\SMTPProxy{#}-{yyyy-MM-dd}.log"; - public LogLevel SmtpLogLevel = LogLevel.Verbose; - } - } -} +using Microsoft.Win32; +using OpaqueMail.Net.Proxy; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.ServiceProcess; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Xml; +using System.Xml.XPath; + +namespace OpaqueMail.Net.ProxySettings +{ + public partial class Form1 : Form + { + #region Private Members + /// Outlook versions to check for in the registry. + Dictionary OutlookVersions = new Dictionary(); + /// The path where settings should be saved and loaded. + private string SettingsFileName = ""; + /// Timer to check the service status. + private System.Threading.Timer StatusTimer; + #endregion Private Members + + #region Constructors + /// + /// Default constructor. + /// + public Form1() + { + InitializeComponent(); + } + #endregion Constructors + + #region Protected Methods + /// + /// Handle the F5 keypress by reloading the e-mail accounts list and certificate choices. + /// + /// + /// + /// + protected override bool ProcessCmdKey(ref Message msg, Keys keyData) + { + if (keyData == Keys.F5) + PopulateAccounts(); + + return base.ProcessCmdKey(ref msg, keyData); + } + #endregion Protected Methods + + #region Private Methods + /// + /// Load event handler. + /// + private void Form1_Load(object sender, EventArgs e) + { + OutlookVersions.Add("8.0", "Outlook 97"); + OutlookVersions.Add("9.0", "Outlook 2000"); + OutlookVersions.Add("10.0", "Outlook XP"); + OutlookVersions.Add("11.0", "Outlook 2003"); + OutlookVersions.Add("12.0", "Outlook 2007"); + OutlookVersions.Add("14.0", "Outlook 2010"); + OutlookVersions.Add("15.0", "Outlook 2013"); + OutlookVersions.Add("16.0", "Outlook v16"); + OutlookVersions.Add("17.0", "Outlook v17"); + OutlookVersions.Add("18.0", "Outlook v18"); + OutlookVersions.Add("19.0", "Outlook v19"); + OutlookVersions.Add("20.0", "Outlook v20"); + + SettingsFileName = (AppDomain.CurrentDomain.BaseDirectory + "\\OpaqueMail.Proxy.xml").Replace("\\\\", "\\"); + + AboutLabel.Links.Clear(); + AboutLabel.Links.Add(AboutLabel.Text.IndexOf("S/MIME"), 6, "https://en.wikipedia.org/wiki/S/MIME"); + AboutLabel.LinkClicked += Label_LinkClicked; + + GettingStartedLabel.Links.Clear(); + int settingsFileIndex = GettingStartedLabel.Text.IndexOf("[SETTINGSFILE]"); + GettingStartedLabel.Text = GettingStartedLabel.Text.Replace("[SETTINGSFILE]", SettingsFileName); + GettingStartedLabel.Links.Add(settingsFileIndex, SettingsFileName.Length, SettingsFileName); + GettingStartedLabel.Links.Add(GettingStartedLabel.Text.IndexOf("http://opaquemail.org/"), 22, "http://opaquemail.org/"); + GettingStartedLabel.LinkClicked += Label_LinkClicked; + + CertificateLabel.Links.Clear(); + CertificateLabel.Links.Add(CertificateLabel.Text.IndexOf("Comodo"), 6, "http://www.instantssl.com/ssl-certificate-products/free-email-certificate.html"); + CertificateLabel.Links.Add(CertificateLabel.Text.IndexOf("StartCom"), 8, "https://cert.startcom.org/"); + CertificateLabel.LinkClicked += Label_LinkClicked; + + AccountsLabel.Links.Clear(); + AccountsLabel.Links.Add(AboutLabel.Text.IndexOf("http://opaquemail.org/"), 22, "http://opaquemail.org/"); + AccountsLabel.LinkClicked += Label_LinkClicked; + + this.SaveSettingsButton.Click += new System.EventHandler(this.SaveSettingsButton_Click); + + // Load the e-mail accounts list and certificate choices. + PopulateAccounts(); + + SmimeOperations.SelectedIndex = 3; + SmimeOperations.SelectedIndexChanged += SmimeOperations_SelectedIndexChanged; + + SmimeSettingsMode.SelectedIndex = 0; + + string ip = "192.168.*"; + IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress hostIP in hostEntry.AddressList) + { + string[] ipParts = hostIP.ToString().Split('.'); + if (ipParts.Length > 2) + { + ip = ipParts[0] + "." + ipParts[1] + ".*"; + break; + } + } + NetworkAccess.Items[1] = ((string)NetworkAccess.Items[1]).Replace("192.168.*", ip); + + NetworkAccess.SelectedIndex = 0; + + UpdateServiceStatus(null); + + StatusTimer = new System.Threading.Timer(new TimerCallback(UpdateServiceStatus), null, 15000, 15000); + } + + /// + /// Determine the next available port. + /// + /// The first port to check. + private int GetNextAvailablePort(int nextPortToTry) + { + IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); + TcpConnectionInformation[] tcpConnections = ipGlobalProperties.GetActiveTcpConnections(); + + while (true) + { + bool isAvailable = true; + foreach (TcpConnectionInformation tcpInfo in tcpConnections) + { + if (tcpInfo.LocalEndPoint.Port == nextPortToTry) + { + isAvailable = false; + break; + } + } + + if (isAvailable) + return nextPortToTry; + else + nextPortToTry++; + } + } + + /// + /// Retrieve an Outlook registry setting. + /// + /// The registry key to read within. + /// The name of the value to read. + private string GetOutlookRegistryValue(RegistryKey key, string name) + { + object value = key.GetValue(name); + if (value is byte[]) + return Encoding.Unicode.GetString((byte[])value).Replace("\0", ""); + if (value != null) + return value.ToString(); + else + return null; + } + + /// + /// Return a boolean value from an XML document. + /// + /// An XPathNavigator within the current XmlDocument. + /// The XPath expression to evaluate. + private static bool? GetXmlBoolValue(XPathNavigator navigator, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + { + if (!string.IsNullOrEmpty(valueNavigator.Value)) + { + bool value; + bool.TryParse(valueNavigator.Value, out value); + return value; + } + else + return null; + } + else + return null; + } + + /// + /// Return an integer value from an XML document. + /// + /// An XPathNavigator within the current XmlDocument. + /// The XPath expression to evaluate. + private static int? GetXmlIntValue(XPathNavigator navigator, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + { + int value; + int.TryParse(valueNavigator.Value, out value); + return value; + } + else + return null; + } + + /// + /// Return a string value from an XML document. + /// + /// An XPathNavigator within the current XmlDocument. + /// The XPath expression to evaluate. + private static string GetXmlStringValue(XPathNavigator navigator, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + return valueNavigator.Value; + else + return null; + } + + /// + /// Install the OpaqueMail Proxy service. + /// + private void InstallService() + { + if (!ServiceExists("OpaqueMailProxy")) + { + ProxyServiceInstaller installer = new ProxyServiceInstaller(); + installer.Install(false, new string[] { }); + } + } + + /// + /// Open the default application to view clicked links. + /// + private void Label_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Process.Start(e.Link.LinkData.ToString().Replace("\r\n", "")); + } + + /// + /// Populate the gridview with appropriate e-mail account and certificate choices. + /// + private void PopulateAccounts() + { + // Prepare the certificate choices. + List certChoices = new List(); + + X509Certificate2Collection certs = CertHelper.GetWindowsCertificates(StoreLocation.CurrentUser); + certs.AddRange(CertHelper.GetWindowsCertificates(StoreLocation.LocalMachine)); + + HashSet certificatesSeen = new HashSet(); + foreach (X509Certificate2 cert in certs) + { + // Avoid duplicate certificates. + if (!certificatesSeen.Contains(cert.SerialNumber)) + { + // Ensure that the certificate has a valid subject name. + if (cert.Subject.IndexOf("@") > -1 && (cert.Subject.StartsWith("E=") || (cert.Subject.StartsWith("CN=")))) + { + if (cert.Verify()) + { + certChoices.Add(new Choice(cert.Subject + " (SN: " + cert.SerialNumber + ")", cert.SerialNumber)); + certificatesSeen.Add(cert.SerialNumber); + } + } + } + } + + certChoices.Add(new Choice("New self-signed certificate", "self-signed")); + CertificateColumn.DataSource = certChoices; + CertificateColumn.DisplayMember = "Name"; + CertificateColumn.ValueMember = "Value"; + + // Check which Outlook registry keys and Thunderbird configs have proxies associated. + XPathDocument document; + HashSet outlookRegistryKeys = new HashSet(); + HashSet thunderbirdKeys = new HashSet(); + try + { + document = new XPathDocument(SettingsFileName); + XPathNavigator navigator = document.CreateNavigator(); + + int smtpServiceCount = GetXmlIntValue(navigator, "/Settings/SMTP/ServiceCount") ?? 0; + for (int i = 1; i <= smtpServiceCount; i++) + { + int? registryKeyCount = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/OutlookRegistryKeyCount") ?? 0; + for (int j = 1; j <= registryKeyCount; j++) + { + string registryKey = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/OutlookRegistryKey" + j); + if (!string.IsNullOrEmpty(registryKey)) + outlookRegistryKeys.Add(registryKey); + } + + int? thunderbirdKeyCount = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/ThunderbirdKeyCount") ?? 0; + for (int j = 1; j <= thunderbirdKeyCount; j++) + { + string thunderbirdKey = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/ThunderbirdKey" + j); + if (!string.IsNullOrEmpty(thunderbirdKey)) + thunderbirdKeys.Add(thunderbirdKey); + } + } + } + catch { } + + // Correlate Outlook registry keys with accounts. + AccountGrid.Rows.Clear(); + bool activeProxy = false; + foreach (string outlookVersion in OutlookVersions.Keys) + { + using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\" + outlookVersion + @"\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676", false)) + { + if (key != null) + { + string[] subkeyNames = key.GetSubKeyNames(); + if (subkeyNames != null) + { + foreach (string subkeyName in subkeyNames) + { + using (RegistryKey subKey = key.OpenSubKey(subkeyName, false)) + { + string smtpServer = GetOutlookRegistryValue(subKey, "SMTP Server"); + + if (!string.IsNullOrEmpty(smtpServer)) + { + string accountName = GetOutlookRegistryValue(subKey, "Account Name"); + + string matchingCert = "self-signed"; + + // Check if there's a matching certificate. + foreach (X509Certificate2 cert in certs) + { + string canonicalCertSubject = ""; + if (cert.Subject.StartsWith("E=")) + { + canonicalCertSubject = cert.Subject.Substring(2).ToUpper(); + int certSubjectComma = canonicalCertSubject.IndexOf(","); + if (certSubjectComma > -1) + canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma); + + if (accountName.ToUpper() == canonicalCertSubject) + matchingCert = cert.SerialNumber; + } + else if (cert.Subject.StartsWith("CN=")) + { + canonicalCertSubject = cert.Subject.Substring(3).ToUpper(); + int certSubjectComma = canonicalCertSubject.IndexOf(","); + if (certSubjectComma > -1) + canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma); + + if (accountName.ToUpper() == canonicalCertSubject) + matchingCert = cert.SerialNumber; + } + } + + if (!activeProxy && outlookRegistryKeys.Contains(subKey.Name)) + activeProxy = true; + + AccountGrid.Rows.Add(OutlookVersions[outlookVersion], accountName, outlookRegistryKeys.Contains(subKey.Name), matchingCert, subKey.Name); + } + } + } + } + } + } + } + + // Correlate Thunderbird config keys with accounts. + activeProxy = false; + if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) + { + foreach (string directory in Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) + { + if (File.Exists(directory + "\\prefs.js")) + { + string prefsFile = File.ReadAllText(directory + "\\prefs.js"); + + int keyCount; + int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.account.lastKey\", ", ")"), out keyCount); + for (int i = 1; i <= keyCount; i++) + { + string smtpServer = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".hostname\", \"", "\""); + string accountName = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".username\", \"", "\""); + + if (!string.IsNullOrEmpty(smtpServer) && !string.IsNullOrEmpty(accountName)) + { + string thunderbirdKey = directory + "~" + i.ToString(); + + string matchingCert = "self-signed"; + + // Check if there's a matching certificate. + foreach (X509Certificate2 cert in certs) + { + string canonicalCertSubject = ""; + if (cert.Subject.StartsWith("E=")) + { + canonicalCertSubject = cert.Subject.Substring(2).ToUpper(); + int certSubjectComma = canonicalCertSubject.IndexOf(","); + if (certSubjectComma > -1) + canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma); + + if (accountName.ToUpper() == canonicalCertSubject) + matchingCert = cert.SerialNumber; + } + else if (cert.Subject.StartsWith("CN=")) + { + canonicalCertSubject = cert.Subject.Substring(3).ToUpper(); + int certSubjectComma = canonicalCertSubject.IndexOf(","); + if (certSubjectComma > -1) + canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma); + + if (accountName.ToUpper() == canonicalCertSubject) + matchingCert = cert.SerialNumber; + } + } + + if (!activeProxy && thunderbirdKeys.Contains(thunderbirdKey)) + activeProxy = true; + + AccountGrid.Rows.Add("Thunderbird", accountName, thunderbirdKeys.Contains(thunderbirdKey), matchingCert, thunderbirdKey); + } + } + } + } + } + + // If there's at least one active proxy, ensure the service is running. + if (activeProxy) + StartService(); + } + + /// + /// Save an embedded resource file to the file system. + /// + /// Identifier of the embedded resource. + /// Full path to the embedded resource. + /// File system location to save the file. + private async void SaveResourceFile(string resourceFileName, string resourcePath, string filePath) + { + if (!File.Exists(filePath + "\\" + resourceFileName)) + { + using (StreamReader resourceReader = new StreamReader(Assembly.GetAssembly(GetType()).GetManifestResourceStream(resourcePath + "." + resourceFileName))) + { + using (StreamWriter fileWriter = new StreamWriter(filePath + "\\" + resourceFileName)) + { + char[] buffer = new char[Constants.SMALLBUFFERSIZE]; + + int bytesRead; + while ((bytesRead = await resourceReader.ReadAsync(buffer, 0, buffer.Length)) > 0) + await fileWriter.WriteAsync(buffer, 0, bytesRead); + } + } + } + } + + /// + /// Handle the save event and update OpaqueMail Proxy's settings.. + /// + private void SaveSettingsButton_Click(object sender, EventArgs e) + { + XPathDocument document = null; + + if (File.Exists(SettingsFileName)) + { + DialogResult dr = MessageBox.Show("A settings file already exists for OpaqueMail Proxy. Overwrite with these settings?", "Overwrite OpaqueMail Proxy Settings?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); + if (dr != System.Windows.Forms.DialogResult.OK) + return; + + try + { + document = new XPathDocument(SettingsFileName); + } + catch { } + } + + // If the service is running, stop it before proceeding. + if (ServiceExists("OpaqueMailProxy")) + { + ServiceController serviceContoller = new ServiceController("OpaqueMailProxy"); + if (serviceContoller.Status != ServiceControllerStatus.Stopped && serviceContoller.Status != ServiceControllerStatus.StopPending) + serviceContoller.Stop(); + } + + List accounts = new List(); + + // First, account for any settings in the existing XML file. + string fqdn = Functions.GetLocalFQDN(); + + int smtpServiceCount = 0, imapServiceCount = 0, pop3ServiceCount = 0; + if (document != null) + { + XPathNavigator navigator = document.CreateNavigator(); + + smtpServiceCount = GetXmlIntValue(navigator, "/Settings/SMTP/ServiceCount") ?? 0; + for (int i = 1; i <= smtpServiceCount; i++) + { + ProxyAccount account = new ProxyAccount(); + account.LocalSmtpIPAddress = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/LocalIPAddress") ?? ""; + account.LocalSmtpPort = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/LocalPort") ?? 587; + account.LocalSmtpEnableSsl = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/LocalEnableSsl") ?? true; + account.RemoteSmtpServer = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerHostName"); + account.RemoteSmtpPort = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerPort") ?? 587; + account.RemoteSmtpEnableSsl = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerEnableSSL") ?? true; + account.RemoteSmtpUsername = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerUsername"); + account.RemoteSmtpPassword = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerPassword"); + account.RemoteSmtpFrom = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/RemoteServerFrom"); + account.SmtpAcceptedIPs = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/AcceptedIPs"); + account.SmtpCertificateLocation = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/Location"); + account.SmtpCertificateSerialNumber = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/SerialNumber"); + account.SmtpCertificateSubjectName = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/Certificate/SubjectName"); + account.SmtpLogFile = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/LogFile"); + + string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/SMTP/Service" + i + "/LogLevel"); + switch (logLevel.ToUpper()) + { + case "NONE": + account.SmtpLogLevel = LogLevel.None; + break; + case "CRITICAL": + account.SmtpLogLevel = LogLevel.Critical; + break; + case "ERROR": + account.SmtpLogLevel = LogLevel.Error; + break; + case "RAW": + account.SmtpLogLevel = LogLevel.Raw; + break; + case "VERBOSE": + account.SmtpLogLevel = LogLevel.Verbose; + break; + case "WARNING": + account.SmtpLogLevel = LogLevel.Warning; + break; + case "INFORMATION": + default: + account.SmtpLogLevel = LogLevel.Information; + break; + } + + account.SendCertificateReminders = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SendCertificateReminders") ?? true; + account.SmimeEncrypt = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SMIMEEncrypt") ?? true; + account.SmimeRemovePreviousOperations = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SMIMERemovePreviousOperations") ?? true; + account.SmimeSign = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SMIMESign") ?? true; + account.SmimeTripleWrap = GetXmlBoolValue(navigator, "/Settings/SMTP/Service" + i + "/SMIMETripleWrap") ?? true; + + int? registryKeyCount = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/OutlookRegistryKeyCount") ?? 0; + for (int j = 1; j <= registryKeyCount; j++) + { + string registryKey = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/OutlookRegistryKey" + j); + if (!account.OutlookRegistryKeys.Contains(registryKey)) + account.OutlookRegistryKeys.Add(registryKey); + } + + int? thunderbirdKeyCount = GetXmlIntValue(navigator, "/Settings/SMTP/Service" + i + "/ThunderbirdKeyCount") ?? 0; + for (int j = 1; j <= registryKeyCount; j++) + { + string thunderbirdKey = GetXmlStringValue(navigator, "/Settings/SMTP/Service" + i + "/ThunderbirdKey" + j); + if (!string.IsNullOrEmpty(thunderbirdKey) && !account.ThunderbirdKeys.Contains(thunderbirdKey)) + account.ThunderbirdKeys.Add(thunderbirdKey); + } + + accounts.Add(account); + } + + imapServiceCount = GetXmlIntValue(navigator, "/Settings/IMAP/ServiceCount") ?? 0; + for (int i = 1; i <= imapServiceCount; i++) + { + ProxyAccount account = new ProxyAccount(); + bool accountMatched = false; + + // Check if a matching Outlook account already exists. + int? registryKeyCount = GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/OutlookRegistryKeyCount") ?? 0; + for (int j = 1; j <= registryKeyCount; j++) + { + string registryKey = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/OutlookRegistryKey" + j); + if (!account.OutlookRegistryKeys.Contains(registryKey)) + account.OutlookRegistryKeys.Add(registryKey); + + foreach (ProxyAccount existingAccount in accounts) + { + if (existingAccount.OutlookRegistryKeys.Contains(registryKey) && !accountMatched) + { + account = existingAccount; + j = 0; + accountMatched = true; + } + } + } + + // Check if a matching Thunderbird account already exists. + int? thunderbirdKeyCount = GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/ThunderbirdKeyCount") ?? 0; + for (int j = 1; j <= registryKeyCount; j++) + { + string thunderbirdKey = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/ThunderbirdKey" + j); + if (!string.IsNullOrEmpty(thunderbirdKey) && !account.ThunderbirdKeys.Contains(thunderbirdKey)) + account.ThunderbirdKeys.Add(thunderbirdKey); + + foreach (ProxyAccount existingAccount in accounts) + { + if (existingAccount.ThunderbirdKeys.Contains(thunderbirdKey) && !accountMatched) + { + account = existingAccount; + j = 0; + accountMatched = true; + } + } + } + + account.LocalImapIPAddress = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/LocalIPAddress") ?? ""; + account.LocalImapPort = GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/LocalPort") ?? 587; + account.LocalImapEnableSsl = GetXmlBoolValue(navigator, "/Settings/IMAP/Service" + i + "/LocalEnableSsl") ?? true; + + account.RemoteImapServer = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerHostName"); + account.RemoteImapPort = GetXmlIntValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerPort") ?? 993; + account.RemoteImapEnableSsl = GetXmlBoolValue(navigator, "/Settings/IMAP/Service" + i + "/RemoteServerEnableSSL") ?? true; + account.ImapAcceptedIPs = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/AcceptedIPs"); + account.ImapCertificateLocation = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/Location"); + account.ImapCertificateSerialNumber = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/SerialNumber"); + account.ImapCertificateSubjectName = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/Certificate/SubjectName"); + account.ImapLogFile = GetXmlStringValue(navigator, "/Settings/IMAP/Service" + i + "/LogFile"); + + string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/IMAP/Service" + i + "/LogLevel"); + switch (logLevel.ToUpper()) + { + case "NONE": + account.ImapLogLevel = LogLevel.None; + break; + case "CRITICAL": + account.ImapLogLevel = LogLevel.Critical; + break; + case "ERROR": + account.ImapLogLevel = LogLevel.Error; + break; + case "RAW": + account.ImapLogLevel = LogLevel.Raw; + break; + case "VERBOSE": + account.ImapLogLevel = LogLevel.Verbose; + break; + case "WARNING": + account.ImapLogLevel = LogLevel.Warning; + break; + case "INFORMATION": + default: + account.ImapLogLevel = LogLevel.Information; + break; + } + } + + // Handle POP3 settings third. + pop3ServiceCount = GetXmlIntValue(navigator, "/Settings/POP3/ServiceCount") ?? 0; + for (int i = 1; i <= pop3ServiceCount; i++) + { + ProxyAccount account = new ProxyAccount(); + bool accountMatched = false; + + // Check if a matching Outlook account already exists. + int? registryKeyCount = GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/OutlookRegistryKeyCount") ?? 0; + for (int j = 1; j <= registryKeyCount; j++) + { + string registryKey = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/OutlookRegistryKey" + j); + if (!account.OutlookRegistryKeys.Contains(registryKey)) + account.OutlookRegistryKeys.Add(registryKey); + + foreach (ProxyAccount existingAccount in accounts) + { + if (existingAccount.OutlookRegistryKeys.Contains(registryKey) && !accountMatched) + { + account = existingAccount; + j = 0; + accountMatched = true; + } + } + } + + // Check if a matching Thunderbird account already exists. + int? thunderbirdKeyCount = GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/ThunderbirdKeyCount") ?? 0; + for (int j = 1; j <= registryKeyCount; j++) + { + string thunderbirdKey = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/ThunderbirdKey" + j); + if (!string.IsNullOrEmpty(thunderbirdKey) && !account.ThunderbirdKeys.Contains(thunderbirdKey)) + account.ThunderbirdKeys.Add(thunderbirdKey); + + foreach (ProxyAccount existingAccount in accounts) + { + if (existingAccount.ThunderbirdKeys.Contains(thunderbirdKey) && !accountMatched) + { + account = existingAccount; + j = 0; + accountMatched = true; + } + } + } + + account.LocalPop3IPAddress = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/LocalIPAddress") ?? ""; + account.LocalPop3Port = GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/LocalPort") ?? 995; + account.LocalPop3EnableSsl = GetXmlBoolValue(navigator, "/Settings/POP3/Service" + i + "/LocalEnableSsl") ?? true; + + account.RemotePop3Server = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerHostName"); + account.RemotePop3Port = GetXmlIntValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerPort") ?? 995; + account.RemotePop3EnableSsl = GetXmlBoolValue(navigator, "/Settings/POP3/Service" + i + "/RemoteServerEnableSSL") ?? true; + account.Pop3AcceptedIPs = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/AcceptedIPs"); + account.Pop3CertificateLocation = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/Location"); + account.Pop3CertificateSerialNumber = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/SerialNumber"); + account.Pop3CertificateSubjectName = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/Certificate/SubjectName"); + account.Pop3LogFile = GetXmlStringValue(navigator, "/Settings/POP3/Service" + i + "/LogFile"); + + string logLevel = ProxyFunctions.GetXmlStringValue(navigator, "Settings/POP3/Service" + i + "/LogLevel"); + switch (logLevel.ToUpper()) + { + case "NONE": + account.Pop3LogLevel = LogLevel.None; + break; + case "CRITICAL": + account.Pop3LogLevel = LogLevel.Critical; + break; + case "ERROR": + account.Pop3LogLevel = LogLevel.Error; + break; + case "RAW": + account.Pop3LogLevel = LogLevel.Raw; + break; + case "VERBOSE": + account.Pop3LogLevel = LogLevel.Verbose; + break; + case "WARNING": + account.Pop3LogLevel = LogLevel.Warning; + break; + case "INFORMATION": + default: + account.Pop3LogLevel = LogLevel.Information; + break; + } + } + } + + // Second, gather existing Outlook account settings from the registry. + foreach (string outlookVersion in OutlookVersions.Keys) + { + using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\" + outlookVersion + @"\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676", false)) + { + if (key != null) + { + string[] subkeyNames = key.GetSubKeyNames(); + if (subkeyNames != null) + { + foreach (string subkeyName in subkeyNames) + { + using (RegistryKey subKey = key.OpenSubKey(subkeyName, false)) + { + bool matched = false; + foreach (ProxyAccount existingAccount in accounts) + { + if (existingAccount.OutlookRegistryKeys.Contains(subKey.Name)) + matched = true; + } + + if (!matched) + { + ProxyAccount account = new ProxyAccount(); + account.ClientType = "Outlook"; + account.ClientVersion = outlookVersion; + + account.RemoteImapEnableSsl = GetOutlookRegistryValue(subKey, "IMAP Use SSL") == "1"; + int.TryParse(GetOutlookRegistryValue(subKey, "IMAP Port"), out account.RemoteImapPort); + account.RemoteImapServer = GetOutlookRegistryValue(subKey, "IMAP Server") ?? ""; + + account.RemotePop3EnableSsl = GetOutlookRegistryValue(subKey, "POP3 Use SSL") == "1"; + int.TryParse(GetOutlookRegistryValue(subKey, "POP3 Port"), out account.RemotePop3Port); + account.RemotePop3Server = GetOutlookRegistryValue(subKey, "POP3 Server") ?? ""; + + account.RemoteSmtpEnableSsl = GetOutlookRegistryValue(subKey, "SMTP Use SSL") == "1"; + int.TryParse(GetOutlookRegistryValue(subKey, "SMTP Port"), out account.RemoteSmtpPort); + account.RemoteSmtpServer = GetOutlookRegistryValue(subKey, "SMTP Server") ?? ""; + + // Only proceed if a server is found. + if (!string.IsNullOrEmpty(account.RemoteImapServer) || !string.IsNullOrEmpty(account.RemotePop3Server) || !string.IsNullOrEmpty(account.RemoteSmtpServer)) + { + string username = GetOutlookRegistryValue(subKey, "IMAP User"); + if (string.IsNullOrEmpty(username)) + username = GetOutlookRegistryValue(subKey, "POP3 User"); + + if (!string.IsNullOrEmpty(username)) + { + account.Usernames.Add(username); + account.OutlookRegistryKeys.Add(subKey.Name); + + accounts.Add(account); + } + } + } + } + } + } + } + } + } + + // Third, gather existing Thunderbird account settings. + if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) + { + foreach (string directory in Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) + { + if (File.Exists(directory + "\\prefs.js")) + { + string prefsFile = File.ReadAllText(directory + "\\prefs.js"); + + int keyCount; + int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.account.lastKey\", ", ")"), out keyCount); + for (int i = 1; i <= keyCount; i++) + { + string thunderbirdKey = directory + "~" + i.ToString(); + + bool matched = false; + foreach (ProxyAccount existingAccount in accounts) + { + if (existingAccount.ThunderbirdKeys.Contains(thunderbirdKey)) + matched = true; + } + + if (!matched) + { + ProxyAccount account = new ProxyAccount(); + account.ClientType = "Thunderbird"; + + int sslValue = 0; + int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".try_ssl\", ", ")"), out sslValue); + account.RemoteSmtpEnableSsl = sslValue > 0; + int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".port\", ", ")"), out account.RemoteSmtpPort); + account.RemoteSmtpServer = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".hostname\", \"", "\"") ?? ""; + + if (Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".type\", \"", "\"") == "pop3") + { + int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")"), out account.RemotePop3Port); + account.RemotePop3EnableSsl = (account.RemotePop3Port == 995); + account.RemotePop3Server = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"") ?? ""; + } + else + { + int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")"), out account.RemoteImapPort); + account.RemoteImapEnableSsl = (account.RemoteImapPort == 993); + account.RemoteImapServer = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"") ?? ""; + } + + // Only proceed if a server is found. + if (!string.IsNullOrEmpty(account.RemoteImapServer) || !string.IsNullOrEmpty(account.RemotePop3Server) || !string.IsNullOrEmpty(account.RemoteSmtpServer)) + { + string username = Functions.ReturnBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".username\", \"", "\""); + + if (!string.IsNullOrEmpty(username)) + { + account.Usernames.Add(username); + account.ThunderbirdKeys.Add(thunderbirdKey); + + accounts.Add(account); + } + } + } + } + } + } + } + + // Fourth, check which accounts the user chooses to encrypt. + smtpServiceCount = 0; + imapServiceCount = 0; + pop3ServiceCount = 0; + + HashSet portsReserved = new HashSet(); + int nextPortToTry = 1000; + + foreach (DataGridViewRow row in AccountGrid.Rows) + { + if ((bool)row.Cells[2].Value == true) + { + foreach (ProxyAccount account in accounts) + { + if ((account.OutlookRegistryKeys.Contains((string)row.Cells[4].Value) || account.ThunderbirdKeys.Contains((string)row.Cells[4].Value)) && !account.Matched) + { + account.Matched = true; + + // Ensure the SMTP proxy connection has a unique port. + if (!string.IsNullOrEmpty(account.RemoteSmtpServer)) + { + smtpServiceCount++; + + if (portsReserved.Contains(account.LocalSmtpPort)) + { + nextPortToTry = GetNextAvailablePort(++nextPortToTry); + account.LocalSmtpPort = nextPortToTry; + } + + portsReserved.Add(account.LocalSmtpPort); + } + + // Ensure the IMAP proxy connection has a unique port. + if (!string.IsNullOrEmpty(account.RemoteImapServer)) + { + imapServiceCount++; + + if (portsReserved.Contains(account.LocalImapPort)) + { + nextPortToTry = GetNextAvailablePort(++nextPortToTry); + account.LocalImapPort = nextPortToTry; + } + + portsReserved.Add(account.LocalImapPort); + } + + // Ensure the POP3 proxy connection has a unique port. + if (!string.IsNullOrEmpty(account.RemotePop3Server)) + { + pop3ServiceCount++; + + if (portsReserved.Contains(account.LocalPop3Port)) + { + nextPortToTry = GetNextAvailablePort(++nextPortToTry); + account.LocalPop3Port = nextPortToTry; + } + + portsReserved.Add(account.LocalPop3Port); + } + } + } + } + } + + // Fifth, write out the XML setting values. + XmlWriterSettings streamWriterSettings = new XmlWriterSettings(); + streamWriterSettings.Indent = true; + streamWriterSettings.IndentChars = " "; + streamWriterSettings.NewLineChars = "\r\n"; + streamWriterSettings.NewLineHandling = NewLineHandling.Replace; + + // Determine default accepted IPs. + string defaultAcceptedIPs = "0.0.0.0"; + if (NetworkAccess.SelectedIndex == 1) + { + IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName()); + foreach (IPAddress hostIP in hostEntry.AddressList) + { + string[] ipParts = hostIP.ToString().Split('.'); + if (ipParts.Length > 2) + { + defaultAcceptedIPs = ipParts[0] + "." + ipParts[1] + ".*"; + break; + } + } + } + else if (NetworkAccess.SelectedIndex == 2) + defaultAcceptedIPs = "*"; + + using (XmlWriter streamWriter = XmlWriter.Create(SettingsFileName, streamWriterSettings)) + { + streamWriter.WriteStartDocument(); + + streamWriter.WriteStartElement("Settings"); + + streamWriter.WriteStartElement("SMTP"); + + streamWriter.WriteComment("The number of SMTP proxy services to run. Each proxy's settings will be outlined in subsequent blocks."); + streamWriter.WriteElementString("ServiceCount", smtpServiceCount.ToString()); + + int smtpServiceId = 0; + foreach (ProxyAccount account in accounts) + { + if (!string.IsNullOrEmpty(account.RemoteSmtpServer) && account.Matched) + { + streamWriter.WriteStartElement("Service" + (++smtpServiceId).ToString()); + + streamWriter.WriteComment("IP addresses to accept connections from. Delete or set value to \"*\" to accept connections from any IP."); + streamWriter.WriteComment("Individual IPs can be specified, separated by commas, or ranges can be specified. The \"*\" wildcard character is supported."); + streamWriter.WriteComment("By default, connections are only accepted from the localhost."); + streamWriter.WriteElementString("AcceptedIPs", account.ImapAcceptedIPs ?? defaultAcceptedIPs); + + streamWriter.WriteComment("Local IP address to listen on. \"Any\" means listen on all IPs."); + streamWriter.WriteElementString("LocalIPAddress", account.LocalSmtpIPAddress ?? "Any"); + streamWriter.WriteComment("Local port to listen on."); + streamWriter.WriteElementString("LocalPort", account.LocalSmtpPort > 0 ? account.LocalSmtpPort.ToString() : "587"); + streamWriter.WriteComment("Whether local connections support TLS/SSL protection."); + streamWriter.WriteElementString("LocalEnableSSL", account.LocalSmtpEnableSsl.ToString()); + + streamWriter.WriteComment("Remote SMTP server hostname to connect to. Common values: smtp.gmail.com, smtp.live.com, smtp.mail.yahoo.com"); + streamWriter.WriteElementString("RemoteServerHostName", account.RemoteSmtpServer ?? "Any"); + streamWriter.WriteComment("Remote SMTP server port to connect to. 587 is recommended, but 465 or 25 may be required."); + streamWriter.WriteElementString("RemoteServerPort", account.RemoteSmtpPort > 0 ? account.RemoteSmtpPort.ToString() : "587"); + streamWriter.WriteComment("Whether the remote SMTP server supports TLS/SSL protection."); + streamWriter.WriteElementString("RemoteServerEnableSSL", account.RemoteSmtpEnableSsl.ToString()); + + streamWriter.WriteComment("(Optional) Username used when authenticating to the remote SMTP server. When supplied, it will override any values sent from the client."); + streamWriter.WriteElementString("RemoteServerUsername", account.RemoteSmtpUsername); + streamWriter.WriteComment("(Optional) Password used when authenticating to the remote SMTP server. When supplied, it will override any values sent from the client."); + streamWriter.WriteElementString("RemoteServerPassword", account.RemoteSmtpPassword); + streamWriter.WriteComment("(Optional) \"From\" address for all sent messages. When supplied, it will override any values sent from the client."); + streamWriter.WriteElementString("RemoteServerFrom", account.RemoteSmtpFrom); + + streamWriter.WriteStartElement("Certificate"); + streamWriter.WriteComment("Where certificates should be stored and retrieved from by default. \"LocalMachine\" or \"CurrentUser\" only."); + streamWriter.WriteElementString("Location", account.SmtpCertificateLocation ?? "LocalMachine"); + streamWriter.WriteComment("(Optional) The serial number of an X509 certificate to be used for server identification. If left blank, one will be autogenerated."); + streamWriter.WriteElementString("SerialNumber", account.SmtpCertificateSerialNumber); + streamWriter.WriteComment("(Optional) The subject name of an X509 certificate to be used for server identification. If left blank, one will be autogenerated."); + streamWriter.WriteElementString("SubjectName", account.SmtpCertificateSubjectName); + streamWriter.WriteEndElement(); + + streamWriter.WriteComment("Send e-mail reminders when a signing certificate is due to expire within 30 days."); + streamWriter.WriteElementString("SendCertificateReminders", account.SendCertificateReminders.ToString()); + + streamWriter.WriteComment("Whether all outgoing messages require the S/MIME settings specified below."); + streamWriter.WriteComment("When set to \"RequireExactSettings\", any messages that can't be signed or encrypted will be dropped, unsent."); + streamWriter.WriteComment("When set to \"BestEffort\", OpaqueMail Proxy will attempt to sign and/or encrypt messages but still forward any that can't be."); + streamWriter.WriteElementString("SMIMESettingsMode", SmimeSettingsMode.SelectedIndex > 0 ? "RequireExactSettings" : "BestEffort"); + + streamWriter.WriteComment("Whether to sign the e-mail. When true, signing is the first S/MIME operation."); + streamWriter.WriteElementString("SMIMESign", SmimeOperations.SelectedIndex > 0 ? "True" : "False"); + streamWriter.WriteComment("Whether to encrypt the e-mail's envelope. When SmimeSign is true, encryption is the second S/MIME operation."); + streamWriter.WriteElementString("SMIMEEncrypt", SmimeOperations.SelectedIndex > 1 ? "True" : "False"); + streamWriter.WriteComment("Triple-wrap the e-mail by signing, then encrypting the envelope, then signing the encrypted envelope."); + streamWriter.WriteElementString("SMIMETripleWrap", SmimeOperations.SelectedIndex > 2 ? "True" : "False"); + + streamWriter.WriteComment("Remove envelope encryption and signatures from passed-in messages. If true and SmimeSigned or SmimeEncryptEnvelope is also true, new S/MIME operations will be applied."); + streamWriter.WriteElementString("SMIMERemovePreviousOperations", account.SmimeRemovePreviousOperations.ToString()); + + streamWriter.WriteComment("Where log files should be stored, if any. Leave blank to avoid logging."); + streamWriter.WriteComment("Date and instance variables can be encased in angle braces. For example, \"Logs\\SMTPProxy{#}-{yyyy-MM-dd}.log\"."); + streamWriter.WriteElementString("LogFile", account.SmtpLogFile ?? "Logs\\SMTPProxy{#}-{yyyy-MM-dd}.log"); + + streamWriter.WriteComment("Proxy logging level, determining how much information is logged. Possible values: None, Critical, Error, Warning, Information, Verbose, Raw"); + streamWriter.WriteElementString("LogLevel", account.SmtpLogLevel.ToString()); + + if (account.OutlookRegistryKeys.Count > 0) + { + streamWriter.WriteComment("Outlook registry keys for accounts configured through the OpaqueMail Proxy settings app."); + streamWriter.WriteElementString("OutlookRegistryKeyCount", account.OutlookRegistryKeys.Count.ToString()); + + int registryKeyId = 0; + foreach (string registryKey in account.OutlookRegistryKeys) + streamWriter.WriteElementString("OutlookRegistryKey" + (++registryKeyId).ToString(), registryKey); + } + + if (account.ThunderbirdKeys.Count > 0) + { + streamWriter.WriteComment("Thunderbird keys for accounts configured through the OpaqueMail Proxy settings app."); + streamWriter.WriteElementString("ThunderbirdKeyCount", account.ThunderbirdKeys.Count.ToString()); + + int thunderbirdKeyId = 0; + foreach (string thunderbirdKey in account.ThunderbirdKeys) + streamWriter.WriteElementString("ThunderbirdKey" + (++thunderbirdKeyId).ToString(), thunderbirdKey); + } + + streamWriter.WriteEndElement(); + } + } + + streamWriter.WriteEndElement(); + + streamWriter.WriteStartElement("IMAP"); + + streamWriter.WriteComment("The number of IMAP proxy services to run. Each proxy's settings will be outlined in subsequent blocks."); + streamWriter.WriteElementString("ServiceCount", imapServiceCount.ToString()); + + int imapServiceId = 0; + foreach (ProxyAccount account in accounts) + { + if (!string.IsNullOrEmpty(account.RemoteImapServer) && account.Matched) + { + streamWriter.WriteStartElement("Service" + (++imapServiceId).ToString()); + + streamWriter.WriteComment("IP addresses to accept connections from. Delete or set value to \"*\" to accept connections from any IP."); + streamWriter.WriteComment("Individual IPs can be specified, separated by commas, or ranges can be specified. The \"*\" wildcard character is supported."); + streamWriter.WriteComment("By default, connections are only accepted from the localhost."); + streamWriter.WriteElementString("AcceptedIPs", account.ImapAcceptedIPs ?? defaultAcceptedIPs); + + streamWriter.WriteComment("Local IP address to listen on. \"Any\" means listen on all IPs."); + streamWriter.WriteElementString("LocalIPAddress", account.LocalImapIPAddress ?? "Any"); + streamWriter.WriteComment("Local port to listen on."); + streamWriter.WriteElementString("LocalPort", account.LocalImapPort > 0 ? account.LocalImapPort.ToString() : "993"); + streamWriter.WriteComment("Whether local connections support TLS/SSL protection."); + streamWriter.WriteElementString("LocalEnableSSL", account.LocalImapEnableSsl.ToString()); + + streamWriter.WriteComment("Remote IMAP server hostname to connect to. Common values: imap.gmail.com, imap.mail.yahoo.com"); + streamWriter.WriteElementString("RemoteServerHostName", account.RemoteImapServer ?? "Any"); + streamWriter.WriteComment("Remote IMAP server port to connect to. 993 is recommended, but 143 may be required."); + streamWriter.WriteElementString("RemoteServerPort", account.RemoteImapPort > 0 ? account.RemoteImapPort.ToString() : "993"); + streamWriter.WriteComment("Whether the remote IMAP server supports TLS/SSL protection."); + streamWriter.WriteElementString("RemoteServerEnableSSL", account.RemoteImapEnableSsl.ToString()); + + streamWriter.WriteComment("Where log files should be stored, if any. Leave blank to avoid logging."); + streamWriter.WriteComment("Date and instance variables can be encased in angle braces. For example, \"Logs\\IMAPProxy{#}-{yyyy-MM-dd}.log\"."); + streamWriter.WriteElementString("LogFile", account.ImapLogFile ?? "Logs\\IMAPProxy{#}-{yyyy-MM-dd}.log"); + + streamWriter.WriteComment("Proxy logging level, determining how much information is logged. Possible values: None, Critical, Error, Warning, Information, Verbose, Raw"); + streamWriter.WriteElementString("LogLevel", account.ImapLogLevel.ToString()); + + if (account.OutlookRegistryKeys.Count > 0) + { + streamWriter.WriteComment("Outlook registry keys for accounts configured through the OpaqueMail Proxy settings app."); + streamWriter.WriteElementString("OutlookRegistryKeyCount", account.OutlookRegistryKeys.Count.ToString()); + + int registryKeyId = 0; + foreach (string registryKey in account.OutlookRegistryKeys) + streamWriter.WriteElementString("OutlookRegistryKey" + (++registryKeyId).ToString(), registryKey); + } + + if (account.ThunderbirdKeys.Count > 0) + { + streamWriter.WriteComment("Thunderbird keys for accounts configured through the OpaqueMail Proxy settings app."); + streamWriter.WriteElementString("ThunderbirdKeyCount", account.ThunderbirdKeys.Count.ToString()); + + int thunderbirdKeyId = 0; + foreach (string thunderbirdKey in account.ThunderbirdKeys) + streamWriter.WriteElementString("ThunderbirdKey" + (++thunderbirdKeyId).ToString(), thunderbirdKey); + } + + streamWriter.WriteEndElement(); + } + } + + streamWriter.WriteEndElement(); + + streamWriter.WriteStartElement("POP3"); + + streamWriter.WriteComment("The number of POP3 proxy services to run. Each proxy's settings will be outlined in subsequent blocks."); + streamWriter.WriteElementString("ServiceCount", pop3ServiceCount.ToString()); + + int pop3ServiceId = 0; + foreach (ProxyAccount account in accounts) + { + if (!string.IsNullOrEmpty(account.RemotePop3Server) && account.Matched) + { + streamWriter.WriteStartElement("Service" + (++pop3ServiceId).ToString()); + + streamWriter.WriteComment("IP addresses to accept connections from. Delete or set value to \"*\" to accept connections from any IP."); + streamWriter.WriteComment("Individual IPs can be specified, separated by commas, or ranges can be specified. The \"*\" wildcard character is supported."); + streamWriter.WriteComment("By default, connections are only accepted from the localhost."); + streamWriter.WriteElementString("AcceptedIPs", account.Pop3AcceptedIPs ?? defaultAcceptedIPs); + + streamWriter.WriteComment("Local IP address to listen on. \"Any\" means listen on all IPs."); + streamWriter.WriteElementString("LocalIPAddress", account.LocalPop3IPAddress ?? "Any"); + streamWriter.WriteComment("Local port to listen on."); + streamWriter.WriteElementString("LocalPort", account.LocalPop3Port > 0 ? account.LocalPop3Port.ToString() : "995"); + streamWriter.WriteComment("Whether local connections support TLS/SSL protection."); + streamWriter.WriteElementString("LocalEnableSSL", account.LocalPop3EnableSsl.ToString()); + + streamWriter.WriteComment("Remote POP3 server hostname to connect to. Common values: pop.gmail.com, pop3.live.com, pop.mail.yahoo.com"); + streamWriter.WriteElementString("RemoteServerHostName", account.RemotePop3Server ?? "Any"); + streamWriter.WriteComment("Remote POP3 server port to connect to. 995 is recommended, but 110 may be required."); + streamWriter.WriteElementString("RemoteServerPort", account.RemotePop3Port > 0 ? account.RemotePop3Port.ToString() : "995"); + streamWriter.WriteComment("Whether the remote POP3 server supports TLS/SSL protection."); + streamWriter.WriteElementString("RemoteServerEnableSSL", account.RemotePop3EnableSsl.ToString()); + + streamWriter.WriteComment("Where log files should be stored, if any. Leave blank to avoid logging."); + streamWriter.WriteComment("Date and instance variables can be encased in angle braces. For example, \"Logs\\POP3Proxy{#}-{yyyy-MM-dd}.log\"."); + streamWriter.WriteElementString("LogFile", account.Pop3LogFile ?? "Logs\\POP3Proxy{#}-{yyyy-MM-dd}.log"); + + streamWriter.WriteComment("Proxy logging level, determining how much information is logged. Possible values: None, Critical, Error, Warning, Information, Verbose, Raw"); + streamWriter.WriteElementString("LogLevel", account.Pop3LogLevel.ToString()); + + if (account.OutlookRegistryKeys.Count > 0) + { + streamWriter.WriteComment("Outlook registry keys for accounts configured through the OpaqueMail Proxy settings app."); + streamWriter.WriteElementString("OutlookRegistryKeyCount", account.OutlookRegistryKeys.Count.ToString()); + + int registryKeyId = 0; + foreach (string registryKey in account.OutlookRegistryKeys) + streamWriter.WriteElementString("OutlookRegistryKey" + (++registryKeyId).ToString(), registryKey); + } + + if (account.ThunderbirdKeys.Count > 0) + { + streamWriter.WriteComment("Thunderbird keys for accounts configured through the OpaqueMail Proxy settings app."); + streamWriter.WriteElementString("ThunderbirdKeyCount", account.ThunderbirdKeys.Count.ToString()); + + int thunderbirdKeyId = 0; + foreach (string thunderbirdKey in account.ThunderbirdKeys) + streamWriter.WriteElementString("ThunderbirdKey" + (++thunderbirdKeyId).ToString(), thunderbirdKey); + } + + streamWriter.WriteEndElement(); + } + } + + streamWriter.WriteEndElement(); + + streamWriter.WriteEndElement(); + } + + // Sixth, address loopback firewall settings. + if (UpdateFirewall.Checked) + { + // Enable the back connection hostname to avoid loopback checks. + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0", true)) + { + object loopbackObject = key.GetValue("BackConnectionHostNames"); + if (loopbackObject != null) + { + string[] loopbackValue = (string[])loopbackObject; + + bool loopbackFound = false; + string[] newLoopbackValue = new string[loopbackValue.Length + 1]; + for (int i = 0; i < loopbackValue.Length; i++) + { + if (loopbackValue[i].ToUpper() == fqdn.ToUpper()) + { + loopbackFound = true; + break; + } + newLoopbackValue[i] = loopbackValue[i]; + } + + if (!loopbackFound) + { + newLoopbackValue[newLoopbackValue.Length - 1] = Functions.GetLocalFQDN(); + key.SetValue("BackConnectionHostNames", newLoopbackValue); + + StopService("IISAdmin"); + StartService("IISAdmin"); + } + } + else + { + key.SetValue("BackConnectionHostNames", new string[] { fqdn }); + + StopService("IISAdmin"); + StartService("IISAdmin"); + } + } + + // Open up Windows 8 Mail loopback. + try + { + Windows8MailHelper windows8MailLoopbackHelper = new Windows8MailHelper(); + windows8MailLoopbackHelper.EnableWindows8MailLoopback(); + } + catch { } + } + + // Seventh, restart the OpaqueMail service. + InstallService(); + StartService(); + + // Eighth, rewrite the Outlook registry values. + foreach (string outlookVersion in OutlookVersions.Keys) + { + using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Office\" + outlookVersion + @"\Outlook\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676", false)) + { + if (key != null) + { + string[] subkeyNames = key.GetSubKeyNames(); + if (subkeyNames != null) + { + foreach (string subkeyName in subkeyNames) + { + using (RegistryKey subKey = key.OpenSubKey(subkeyName, true)) + { + string smtpServer = GetOutlookRegistryValue(subKey, "SMTP Server"); + if (!string.IsNullOrEmpty(smtpServer)) + { + foreach (ProxyAccount account in accounts) + { + // If matched, set to use the local proxy. If not matched and we previously used the local proxy, switch back to the original value. + if (account.OutlookRegistryKeys.Contains(subKey.Name)) + { + if (account.Matched) + { + subKey.SetValue("SMTP Server", Encoding.Unicode.GetBytes(fqdn + "\0")); + subKey.SetValue("SMTP Port", account.LocalSmtpPort); + subKey.SetValue("SMTP Use SSL", account.LocalSmtpEnableSsl ? 1 : 0); + } + else + { + subKey.SetValue("SMTP Server", Encoding.Unicode.GetBytes(account.RemoteSmtpServer)); + subKey.SetValue("SMTP Port", account.RemoteSmtpPort); + subKey.SetValue("SMTP Use SSL", account.RemoteSmtpEnableSsl ? 1 : 0); + } + } + } + } + + string imapServer = GetOutlookRegistryValue(subKey, "IMAP Server"); + if (!string.IsNullOrEmpty(imapServer)) + { + foreach (ProxyAccount account in accounts) + { + // If matched, set to use the local proxy. If not matched and we previously used the local proxy, switch back to the original value. + if (account.OutlookRegistryKeys.Contains(subKey.Name)) + { + if (account.Matched) + { + subKey.SetValue("IMAP Server", Encoding.Unicode.GetBytes(fqdn + "\0")); + subKey.SetValue("IMAP Port", account.LocalImapPort); + subKey.SetValue("IMAP Use SSL", account.LocalImapEnableSsl ? 1 : 0); + } + else + { + subKey.SetValue("IMAP Server", Encoding.Unicode.GetBytes(account.RemoteImapServer + "\0")); + subKey.SetValue("IMAP Port", account.RemoteImapPort); + subKey.SetValue("IMAP Use SSL", account.RemoteImapEnableSsl ? 1 : 0); + } + } + } + } + + string pop3Server = GetOutlookRegistryValue(subKey, "POP3 Server"); + if (!string.IsNullOrEmpty(pop3Server)) + { + foreach (ProxyAccount account in accounts) + { + // If matched, set to use the local proxy. If not matched and we previously used the local proxy, switch back to the original value. + if (account.OutlookRegistryKeys.Contains(subKey.Name)) + { + if (account.Matched) + { + subKey.SetValue("POP3 Server", Encoding.Unicode.GetBytes(fqdn + "\0")); + subKey.SetValue("POP3 Port", account.LocalPop3Port); + subKey.SetValue("POP3 Use SSL", account.LocalPop3EnableSsl ? 1 : 0); + } + else + { + subKey.SetValue("POP3 Server", Encoding.Unicode.GetBytes(account.RemotePop3Server + "\0")); + subKey.SetValue("POP3 Port", account.RemotePop3Port); + subKey.SetValue("POP3 Use SSL", account.RemotePop3EnableSsl ? 1 : 0); + } + } + } + } + } + } + } + } + } + } + + // Ninth, rewrite the Thunderbird registry values. + if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) + { + foreach (string directory in Directory.GetDirectories(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Thunderbird\\Profiles")) + { + if (File.Exists(directory + "\\prefs.js")) + { + string prefsFile = File.ReadAllText(directory + "\\prefs.js"); + + int keyCount; + int.TryParse(Functions.ReturnBetween(prefsFile, "user_pref(\"mail.account.lastKey\", ", ")"), out keyCount); + for (int i = 1; i <= keyCount; i++) + { + string thunderbirdKey = directory + "~" + i.ToString(); + + foreach (ProxyAccount account in accounts) + { + // If matched, set to use the local proxy. If not matched and we previously used the local proxy, switch back to the original value. + if (account.ThunderbirdKeys.Contains(thunderbirdKey)) + { + if (account.Matched) + { + if (Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".type\", \"", "\"").ToLower() == "pop3") + { + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"", fqdn); + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")", account.LocalPop3Port.ToString()); + } + else + { + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"", fqdn); + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")", account.LocalImapPort.ToString()); + } + + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".hostname\", \"", "\"", fqdn); + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".port\", ", ")", account.LocalSmtpPort.ToString()); + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".is_gmail\", ", ")", "false"); + } + else + { + if (Functions.ReturnBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".type\", \"", "\"").ToLower() == "pop3") + { + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"", account.RemotePop3Server); + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")", account.RemotePop3Port.ToString()); + } + else + { + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".hostname\", \"", "\"", account.RemoteImapServer); + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".port\", ", ")", account.RemoteImapPort.ToString()); + } + + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".hostname\", \"", "\"", account.RemoteSmtpServer); + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.smtpserver.smtp" + i.ToString() + ".port\", ", ")", account.RemoteSmtpPort.ToString()); + + bool isGmail = account.RemoteSmtpServer.ToUpper() == "SMTP.GMAIL.COM" || account.RemoteSmtpServer.ToUpper() == "SMTP.GOOGLEMAIL.COM"; + prefsFile = Functions.ReplaceBetween(prefsFile, "user_pref(\"mail.server.server" + i.ToString() + ".is_gmail\", ", ")", isGmail ? "true" : "false"); + } + } + } + } + + // Write the settings file back. + File.WriteAllBytes(directory + "\\prefs.js", Encoding.UTF8.GetBytes(prefsFile)); + } + } + } + + // Tenth, save Outlook signatures. + if (UpdateOutlookSignature.Checked) + { + List finalOutlookKeyLocations = new List(); + foreach (ProxyAccount account in accounts) + { + if (account.Matched) + { + foreach (string keyLocation in account.OutlookRegistryKeys) + finalOutlookKeyLocations.Add(keyLocation); + } + } + SetOutlookSignatures(finalOutlookKeyLocations); + + } + // Finally, prompt to restart Outlook or Thunderbird. + Process[] processes = Process.GetProcessesByName("OUTLOOK"); + if (processes.Length > 0) + { + DialogResult dr = MessageBox.Show("Outlook is currently running and will need to be restarted before these changes will take effect. Would you like to restart Outlook now?", "Restart Outlook?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); + if (dr == System.Windows.Forms.DialogResult.OK) + { + // Stop Outlook. + foreach (Process process in processes) + process.Kill(); + + // Try to start Outlook. + for (int i = 15; i >= 8; i--) + { + if (File.Exists("C:\\Program Files (x86)\\Microsoft Office\\Office" + i.ToString() + "\\Outlook.exe")) + { + Process.Start("C:\\Program Files (x86)\\Microsoft Office\\Office" + i.ToString() + "\\Outlook.exe"); + break; + } + else if (File.Exists("C:\\Program Files\\Microsoft Office\\Office" + i.ToString() + "\\Outlook.exe")) + { + Process.Start("C:\\Program Files\\Microsoft Office\\Office" + i.ToString() + "\\Outlook.exe"); + break; + } + } + } + } + processes = Process.GetProcessesByName("THUNDERBIRD"); + if (processes.Length > 0) + { + DialogResult dr = MessageBox.Show("Thunderbird is currently running and will need to be restarted before these changes will take effect. Would you like to restart Thunderbird now?", "Restart Thunderbird?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); + if (dr == System.Windows.Forms.DialogResult.OK) + { + // Stop Thunderbird. + foreach (Process process in processes) + process.Kill(); + + // Try to start Thunderbird. + if (File.Exists("C:\\Program Files (x86)\\Mozilla Thunderbird\\Thunderbird.exe")) + Process.Start("C:\\Program Files (x86)\\Mozilla Thunderbird\\Thunderbird.exe"); + } + } + + UpdateServiceStatus(null); + + MessageBox.Show("OpaqueMail Proxy has been successfully configured and the Windows Service is now running.\r\n\r\nYou may close this program and the proxy will continue to run in the background.", "Success.", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + /// + /// Confirm the Windows service exists. + /// + /// Name of the WIndows service + private bool ServiceExists(string serviceName) + { + ServiceController[] services = ServiceController.GetServices(); + return services.FirstOrDefault(s => s.ServiceName == serviceName) != null; + } + + /// + /// Update Outlook signatures to reference OpaqueMail. + /// + /// List of registry entries for Outlook accounts to update. + private void SetOutlookSignatures(List registryKeyLocations) + { + string microsoftFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Microsoft"; + if (Directory.Exists(microsoftFolder)) + { + if (!Directory.Exists(microsoftFolder + "\\Signatures")) + Directory.CreateDirectory(microsoftFolder + "\\Signatures"); + if (!Directory.Exists(microsoftFolder + "\\Signatures\\OpaqueMail_files")) + Directory.CreateDirectory(microsoftFolder + "\\Signatures\\OpaqueMail_files"); + + SaveResourceFile("OpaqueMail.htm", "OpaqueMail.Net.ProxySettings.Signature.Resources", microsoftFolder + "\\Signatures"); + SaveResourceFile("OpaqueMail.rtf", "OpaqueMail.Net.ProxySettings.Signature.Resources", microsoftFolder + "\\Signatures"); + SaveResourceFile("OpaqueMail.txt", "OpaqueMail.Net.ProxySettings.Signature.Resources", microsoftFolder + "\\Signatures"); + SaveResourceFile("colorschememapping.xml", "OpaqueMail.Net.ProxySettings.Signature.Resources", microsoftFolder + "\\Signatures\\OpaqueMail_files"); + SaveResourceFile("filelist.xml", "OpaqueMail.Net.ProxySettings.Signature.Resources", microsoftFolder + "\\Signatures\\OpaqueMail_files"); + SaveResourceFile("themedata.thmx", "OpaqueMail.Net.ProxySettings.Signature.Resources", microsoftFolder + "\\Signatures\\OpaqueMail_files"); + + foreach (string keyLocation in registryKeyLocations) + { + using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyLocation.Replace("HKEY_CURRENT_USER\\", ""), true)) + { + if (key != null) + { + string signatureValue = GetOutlookRegistryValue(key, "New Signature"); + + // If there's no signature for this account, set it to OpaqueMail. + if (string.IsNullOrEmpty(signatureValue)) + key.SetValue("New Signature", Encoding.Unicode.GetBytes("OpaqueMail\0")); + } + } + } + } + } + + /// + /// Handle S/MIME operation option changes. + /// + private void SmimeOperations_SelectedIndexChanged(object sender, EventArgs e) + { + SmimeSettingsMode.Enabled = SmimeOperations.SelectedIndex > 0; + } + + /// + /// Start the OpaqueMail Proxy Windows service. + /// + private void StartService() + { + StartService("OpaqueMailProxy"); + } + + /// + /// Start a Windows service. + /// + /// Name of the service to start. + private void StartService(string serviceName) + { + if (ServiceExists(serviceName)) + { + ServiceController serviceContoller = new ServiceController(serviceName); + if (serviceContoller.Status != ServiceControllerStatus.Running && serviceContoller.Status != ServiceControllerStatus.StartPending) + serviceContoller.Start(); + } + } + + /// + /// Stop the OpaqueMail Proxy Windows service. + /// + private void StopService() + { + StopService("OpaqueMailProxy"); + } + + /// + /// Stop a Windows service. + /// + /// Name of the service to start. + private void StopService(string serviceName) + { + if (ServiceExists(serviceName)) + { + ServiceController serviceContoller = new ServiceController(serviceName); + if (serviceContoller.Status != ServiceControllerStatus.Stopped && serviceContoller.Status != ServiceControllerStatus.StopPending) + serviceContoller.Stop(); + } + } + + /// + /// Uninstall the OpaqueMail Proxy service. + /// + private void UninstallService() + { + if (ServiceExists("OpaqueMailProxy")) + { + ProxyServiceInstaller installer = new ProxyServiceInstaller(); + installer.Install(true, new string[] { }); + } + } + + /// + /// Update the service status message. + /// + private void UpdateServiceStatus(object o) + { + if (ServiceExists("OpaqueMailProxy")) + { + ServiceController serviceContoller = new ServiceController("OpaqueMailProxy"); + switch (serviceContoller.Status) + { + case ServiceControllerStatus.ContinuePending: + case ServiceControllerStatus.Running: + ServiceStatusLabel.Text = "OpaqueMailProxy service running successfully."; + ServiceStatusLabel.ForeColor = Color.DarkGreen; + break; + case ServiceControllerStatus.Paused: + case ServiceControllerStatus.PausePending: + ServiceStatusLabel.Text = "OpaqueMailProxy service paused."; + ServiceStatusLabel.ForeColor = Color.Black; + break; + case ServiceControllerStatus.StartPending: + ServiceStatusLabel.Text = "OpaqueMailProxy service starting."; + ServiceStatusLabel.ForeColor = Color.Black; + break; + case ServiceControllerStatus.Stopped: + case ServiceControllerStatus.StopPending: + ServiceStatusLabel.Text = "OpaqueMailProxy service stopped."; + ServiceStatusLabel.ForeColor = Color.Black; + break; + } + } + else + { + ServiceStatusLabel.Text = "OpaqueMailProxy service not installed."; + ServiceStatusLabel.ForeColor = Color.DarkRed; + } + } + #endregion Private Methods + + /// + /// Represent certificate choices in a grid view. + /// + private class Choice + { + public string Name { get; private set; } + public string Value { get; private set; } + + public Choice(string name, string value) + { + Name = name; + Value = value; + } + } + + /// + /// Track e-mail account settings. + /// + private class ProxyAccount + { + public bool Matched = false; + + public string ClientType; + public string ClientVersion; + + public List OutlookRegistryKeys = new List(); + public List ThunderbirdKeys = new List(); + public List Usernames = new List(); + + public string ImapAcceptedIPs; + public string Pop3AcceptedIPs; + public string SmtpAcceptedIPs; + + public bool LocalImapEnableSsl = true; + public int LocalImapPort = 993; + public string LocalImapIPAddress = "Any"; + + public bool LocalPop3EnableSsl = true; + public int LocalPop3Port = 995; + public string LocalPop3IPAddress = "Any"; + + public bool LocalSmtpEnableSsl = true; + public int LocalSmtpPort = 587; + public string LocalSmtpIPAddress = "Any"; + + public bool RemoteImapEnableSsl; + public int RemoteImapPort; + public string RemoteImapServer = ""; + + public bool RemotePop3EnableSsl; + public int RemotePop3Port; + public string RemotePop3Server = ""; + + public bool RemoteSmtpEnableSsl; + public int RemoteSmtpPort; + public string RemoteSmtpServer = ""; + public string RemoteSmtpUsername; + public string RemoteSmtpPassword; + public string RemoteSmtpFrom; + + public string ImapCertificateLocation; + public string ImapCertificateSerialNumber; + public string ImapCertificateSubjectName; + public string Pop3CertificateLocation; + public string Pop3CertificateSerialNumber; + public string Pop3CertificateSubjectName; + public string SmtpCertificateLocation; + public string SmtpCertificateSerialNumber; + public string SmtpCertificateSubjectName; + + public bool SendCertificateReminders = true; + public string SmimeSettingsModeValue = "BestEffort"; + + public bool SmimeSign = true; + public bool SmimeEncrypt = true; + public bool SmimeTripleWrap = true; + + public bool SmimeRemovePreviousOperations = true; + + public string ImapLogFile = "Logs\\IMAPProxy{#}-{yyyy-MM-dd}.log"; + public LogLevel ImapLogLevel = LogLevel.Verbose; + public string Pop3LogFile = "Logs\\POP3Proxy{#}-{yyyy-MM-dd}.log"; + public LogLevel Pop3LogLevel = LogLevel.Verbose; + public string SmtpLogFile = "Logs\\SMTPProxy{#}-{yyyy-MM-dd}.log"; + public LogLevel SmtpLogLevel = LogLevel.Verbose; + } + } +} diff --git a/OpaqueMail.Net.ProxySettings/Form1.resx b/OpaqueMail.Net.ProxySettings/Form1.resx index 96e89aa..704d28a 100644 --- a/OpaqueMail.Net.ProxySettings/Form1.resx +++ b/OpaqueMail.Net.ProxySettings/Form1.resx @@ -1,255 +1,293 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - True - - - True - - - True - - - True - - - True - - - Each e-mail account should have a personal S/MIME certificate for signing and encrypting messages. Certificates can be obtained in three ways: - -1. Trusted certificates can be procured for free from Comodo or StartCom. -2. Other trusted certificates can be purchased from other sources. -3. "Self-signed" certificates can be generated by OpaqueMail automatically. - -While other OpaqueMail Proxy users will trust all certificates, some e-mail clients have trouble with self-signed certificates, so we recommend downloading free trusted certificates from Comodo or StartCom. - - - True - - - True - - - True - - - True - - - True - - - - - AAABAAIAEBAAAAAAIABoBAAAJgAAACAgAAAAACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAEAE - AAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wEHx2aDDchpgwnHZ4MBxmKDAcZigwGuVoMBo1GDAaNRgwF1 - OoMBdTqDAWYygwFKJIMBSiSDAUokgwFKJIMBSiSDOdOF/2bdof9E1Yz/AcZi/wHGYv8Arlb/AaNR/wGj - Uf8BdTr/AXU6/wFmMv8BSiT/AUok/wFKJP8BSiT/AUok/znThf9n3aL/RNWM/wHGYv8BxmL/AK5W/wGj - Uf8Bo1H/AXU6/wF1Ov8BZjL/AUok/wFKJP8BSiT/AUok/wFKJP9c25r/puvI/4vluP9X2Zf/V9mX/x3J - cv8BwF//AcBf/wGcTf8BnE3/AY1F/wFuNv8Bbjb/AVYq/wFKJP8BSiT/Y9yf/7Lu0P+Z6MD/Z92i/2fd - ov8iznf/AcZi/wHGYv8Bo1H/AaNR/wGUSf8BdTr/AXU6/wFYK/8BSiT/AUok/2reo/+/8dj/puvJ/3Tg - qv904Kr/M9GB/wrIaP+L5bf/P8yE/wGqVP8Bm0z/AX0+/wF9Pv8BYC//AVEo/wFRKP+O5rn//////+X5 - 7/+y7tD/q+zM/1jamP/L89////////z+/f+D47L/Bbxf/wGjUf8Bo1H/AIRB/wF1Ov8BdTr/jua5//// - ///l+e//mejA/23fpf/t+/T//////////////////////8Xy2/8hwG//AaRR/wCEQf8BdTr/AXU6/47m - uf//////p+vJ/5Dmuv/9/v7/////////////////////////////////8Pv1/1nVlv8AnE3/AYxF/wGM - Rf+J5bb/i+W3/73w1//////////////////////////////////////////////////+////oOnE/w22 - YP8Bo1H/HM1z/5vowf+17tH/te7R/7Xu0f+17tH/te7R/7Xu0f+17tH/te7R/7Xu0f+17tH/te7R/7Xu - 0f+R5rv/DL9k/wHGYicBxmInAcZiJwHGYicBxmInAcZiJwHGYicBxmInAcZiJwHGYicBxmInAcZiJwHG - YicBxmInAcZiJwHGYif///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AQAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA - //8AAP//AAD//wAA//8AAP//AAD//wAA//8oAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAA - AAAAAAAA////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wEBxmIHAcZiBwHGYgcBxmIHAcZiBwHG - YgcBxmIHAcZiBwHGYgcBxmIHAbpcBwGjUQcBo1EHAaNRBwGjUQcBo1EHAXU6BwF1OgcBdToHAXU6BwF1 - OgcBWCwHAUokBwFKJAcBSiQHAUokBwFKJAcBSiQHAUokBwFKJAcBSiQHAUokBwLHY/8OyWr/Dslq/w7J - av8OyWr/Bcdl/wHGYv8BxmL/AcZi/wHGYv8Bulz/AaNR/wGjUf8Bo1H/AaNR/wGjUf8BdTr/AXU6/wF1 - Ov8BdTr/AXU6/wFYK/8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/DMlp/2bd - of9m3aH/Zt2h/2bdof8iznf/AcZi/wHGYv8BxmL/AcZi/wC6XP8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wF1 - Ov8BdTr/AXU6/wF1Ov8BdTr/AVgr/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFK - JP8MyWn/Z92i/2fdov9n3aL/Z92i/yLOd/8BxmL/AcZi/wHGYv8BxmL/Abpc/wGjUf8Bo1H/AaNR/wGj - Uf8Bo1H/AXU6/wF1Ov8BdTr/AXU6/wF1Ov8BWCv/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFK - JP8BSiT/AUok/wzJaf9m3aL/Z92i/2fdov9n3aH/Is53/wHGYv8BxmL/AcZi/wHGYv8Aulz/AaNR/wGj - Uf8Bo1H/AaNR/wGjUf8BdTr/AXU6/wF1Ov8BdTr/AXU6/wFYK/8BSiT/AUok/wFKJP8BSiT/AUok/wFK - JP8BSiT/AUok/wFKJP8BSiT/DMlp/2fdov9n3aL/Z92i/2fdov8iznf/AcZi/wHGYv8BxmL/AcZi/wG6 - XP8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wF1Ov8BdTr/AXU6/wF1Ov8BdTr/AVgr/wFKJP8BSiT/AUok/wFK - JP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8Rymz/munB/5rpwf+a6cH/munB/2Pcn/9H1o3/R9aN/0fW - jf9H1o3/L819/wG8Xf8Bu13/Abtd/wG7Xf8Bu1z/AZVK/wGVSv8BlUr/AZVK/wGVSv8Bdzr/AWgz/wFo - M/8BaDP/AWgz/wFdLv8BSiT/AUok/wFKJP8BSiT/AUok/xTLbv+y7tD/su7Q/7Lu0P+y7tD/gOKx/2fd - ov9n3aL/Z92i/2fdov9F1Yz/Acdi/wHGYv8BxmL/AcZi/wHGYv8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wGF - Qf8BdTr/AXU6/wF1Ov8BdTr/AWcy/wFKJP8BSiT/AUok/wFKJP8BSiT/FMtu/7Lu0P+y7tD/su7Q/7Lu - 0P+A4rH/Z92i/2fdov9n3aL/Z92i/0XWjP8BxmL/AcZi/wHGYv8BxmL/AcZi/wGjUf8Bo1H/AaNR/wGj - Uf8Bo1H/AYVB/wF1Ov8BdTr/AXU6/wF1Ov8BZzL/AUok/wFKJP8BSiT/AUok/wFKJP8Uy27/su7Q/7Lu - 0P+y7tD/su7Q/4Disf9n3aL/Z92i/2fdov9n3aL/RNWM/wDHYv8BxmL/AcZi/wHGYv8BxmL/AaVS/wGj - Uf8Bo1H/AaNR/wGjUf8BhUH/AXU6/wF1Ov8BdTr/AXU6/wFnMv8BSiT/AUok/wFKJP8BSiT/AUok/xTL - bv+y7tD/su7Q/7Lu0P+y7tD/gOKx/2fdov9n3aL/Z92i/2fdov9F1oz/AcZi/wHGYv8BxmL/B8hm/2Hc - nf8IxGT/AatU/wGjUf8Bo1H/AaNR/wGFQf8BdTr/AXU6/wF1Ov8BdTr/AWcy/wFKJP8BSiT/AUok/wFK - JP8BSiT/F8xw/8304f/N9OH/zfTh/8304f+b6cL/geOy/4Hjsv+B47L/geOy/2Lcn/8lznj/Cchn/x/N - df/I893//////8304P8oz3r/AbVZ/wGwV/8BsFf/AZRJ/wGGQv8BhkL/AYZC/wGGQv8Bdzr/AVks/wFZ - LP8BWSz/AVks/wFZLP8dzXP//////////////////////8v04P+y7tD/su7Q/7Lu0P+y7tD/lei+/yfP - ev9I147/6/vz//////////////////T8+P9k3Z//AcZi/wHGYv8Ar1b/AaNR/wGjUf8Bo1H/AaNR/wCU - Sf8BdTr/AXU6/wF1Ov8BdTr/AXU6/x3Nc///////////////////////y/Tg/7Lu0P+y7tD/su7Q/5no - wP8lz3j/geOx//z+/f////////////////////////////////+q7Mv/Esps/wGzWP8Bo1H/AaNR/wGj - Uf8Bo1H/AZRJ/wF1Ov8BdTr/AXU6/wF1Ov8BdTr/Hc1z///////////////////////L9OD/su7Q/7Lu - 0P964q3/Ic52/7nv1P/////////////////////////////////////////////////g+Oz/PdSH/wG4 - W/8BpFH/AaNR/wGjUf8AlEn/AXU6/wF1Ov8BdTr/AXU6/wF1Ov8dzXP//////////////////////8v0 - 4P+v7c7/UdmU/zrUhf/h+O3///////////////////////////////////////////////////////// - ///7/v3/gOOx/wXCYf8BqVT/AaNR/wCUSf8BdTr/AXU6/wF1Ov8BdTr/AXU6/x3Nc/////////////// - ////////uvDV/yzQff9u36X/+f77//////////////////////////////////////////////////// - ////////////////////////w/La/yDNdf8Bslj/AJVJ/wF2Ov8Bdjr/AXY6/wF2Ov8Bdjr/Hc1z//// - /////////////77x1/8lz3n/qOzJ//////////////////////////////////////////////////// - ////////////////////////////////////////7/v1/1fal/8BwF//AaZS/wGjUf8Bo1H/AaNR/wGj - Uf8dzXP///////3//v+H5bX/L9F//9b25v////////////////////////////////////////////// - /////////////////////////////////////////////////////////v///53pw/8Mxmf/Aa1V/wGj - Uf8Bo1H/AaNR/x3Nc//u+/T/TtiS/1vbmv/z/Pj///////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////9j2 - 5/8z0oH/AbZa/wGkUf8Bo1H/Dspq/yrQe/+W6L7//v////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////n9+/9z4Kn/A8Bf/wGoU/8Ax2L/OtOG/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2ze - pP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2ze - pP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP8t0H3/AcVh/wHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHG - Yk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHG - Yk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJP////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Common settings can be configured in this tool by accessing the tabs above. When done making changes click "Save Settings" below. + +Advanced settings can be managed by editing: + +[SETTINGSFILE] + +For more information, visit http://opaquemail.org/. + + + OpaqueMail is a flexible e-mail proxy designed for security, performance, and flexibility. + +OpaqueMail can protect e-mail by automatically signing and encrypting messages using S/MIME. It operates as an SMTP proxy to wrap outgoing messages, and also serves as an IMAP and POP3 proxy to import certificates from inbound messages. + +The proxy can also add TLS/SSL protection and authentication credentials to wrap anonymous connections. + +By default, OpaqueMail only accepts connections from the localhost, but accepted IP ranges can be specified. Logging is configurable to help troubleshoot and optionally serve as an audit trail. + + + The following Outlook and Thunderbird accounts have been identified. Select which you'd like to be protected using S/MIME encryption. + +Other account types can be configured manually. See http://opaquemail.org/ for more information. + + + True + + + True + + + True + + + True + + + True + + + OpaqueMail Proxy will attempt to apply S/MIME operations to every message, but may not always be able to. For example, a recipient without a public key won't be able to receive encrypted messages. + +In "Best Effort" mode, the proxy will attempt to apply S/MIME protection, but still allow messages through if they can't be processed. With "Require Exact Settings", any message that can't be protected will be blocked. + + + Each e-mail account should have a personal S/MIME certificate for signing and encrypting messages. Certificates can be obtained in three ways: + +1. Trusted certificates can be procured for free from Comodo or StartCom. +2. Other trusted certificates can be purchased from other sources. +3. "Self-signed" certificates can be generated by OpaqueMail automatically. + +If no certificate is assigned for an account using OpaqueMail, a self-signed certificate will automatically be generated. We recommend downloading free trusted certificates from Comodo or StartCom if possible. + + + OpaqueMail is 100% free and open source, relying on the community to spread the word. We believe that everybody deserves secure and private e-mail. + +Please help spread the word by updating your Outlook e-mail signature with a link to OpaqueMail. + + + Choose whether other computers will be able to access this instance of the OpaqueMail proxy. + +Optionally, allow OpaqueMail to automatically configure the firewall. If true and allowing local access, a loopback exemption will be made to support the Windows 8 Mail client. + + + True + + + True + + + True + + + True + + + True + + + + + AAABAAIAEBAAAAAAIABoBAAAJgAAACAgAAAAACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAEAE + AAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wEHx2aDDchpgwnHZ4MBxmKDAcZigwGuVoMBo1GDAaNRgwF1 + OoMBdTqDAWYygwFKJIMBSiSDAUokgwFKJIMBSiSDOdOF/2bdof9E1Yz/AcZi/wHGYv8Arlb/AaNR/wGj + Uf8BdTr/AXU6/wFmMv8BSiT/AUok/wFKJP8BSiT/AUok/znThf9n3aL/RNWM/wHGYv8BxmL/AK5W/wGj + Uf8Bo1H/AXU6/wF1Ov8BZjL/AUok/wFKJP8BSiT/AUok/wFKJP9c25r/puvI/4vluP9X2Zf/V9mX/x3J + cv8BwF//AcBf/wGcTf8BnE3/AY1F/wFuNv8Bbjb/AVYq/wFKJP8BSiT/Y9yf/7Lu0P+Z6MD/Z92i/2fd + ov8iznf/AcZi/wHGYv8Bo1H/AaNR/wGUSf8BdTr/AXU6/wFYK/8BSiT/AUok/2reo/+/8dj/puvJ/3Tg + qv904Kr/M9GB/wrIaP+L5bf/P8yE/wGqVP8Bm0z/AX0+/wF9Pv8BYC//AVEo/wFRKP+O5rn//////+X5 + 7/+y7tD/q+zM/1jamP/L89////////z+/f+D47L/Bbxf/wGjUf8Bo1H/AIRB/wF1Ov8BdTr/jua5//// + ///l+e//mejA/23fpf/t+/T//////////////////////8Xy2/8hwG//AaRR/wCEQf8BdTr/AXU6/47m + uf//////p+vJ/5Dmuv/9/v7/////////////////////////////////8Pv1/1nVlv8AnE3/AYxF/wGM + Rf+J5bb/i+W3/73w1//////////////////////////////////////////////////+////oOnE/w22 + YP8Bo1H/HM1z/5vowf+17tH/te7R/7Xu0f+17tH/te7R/7Xu0f+17tH/te7R/7Xu0f+17tH/te7R/7Xu + 0f+R5rv/DL9k/wHGYicBxmInAcZiJwHGYicBxmInAcZiJwHGYicBxmInAcZiJwHGYicBxmInAcZiJwHG + YicBxmInAcZiJwHGYif///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AQAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA + //8AAP//AAD//wAA//8AAP//AAD//wAA//8oAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAA + AAAAAAAA////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wEBxmIHAcZiBwHGYgcBxmIHAcZiBwHG + YgcBxmIHAcZiBwHGYgcBxmIHAbpcBwGjUQcBo1EHAaNRBwGjUQcBo1EHAXU6BwF1OgcBdToHAXU6BwF1 + OgcBWCwHAUokBwFKJAcBSiQHAUokBwFKJAcBSiQHAUokBwFKJAcBSiQHAUokBwLHY/8OyWr/Dslq/w7J + av8OyWr/Bcdl/wHGYv8BxmL/AcZi/wHGYv8Bulz/AaNR/wGjUf8Bo1H/AaNR/wGjUf8BdTr/AXU6/wF1 + Ov8BdTr/AXU6/wFYK/8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/DMlp/2bd + of9m3aH/Zt2h/2bdof8iznf/AcZi/wHGYv8BxmL/AcZi/wC6XP8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wF1 + Ov8BdTr/AXU6/wF1Ov8BdTr/AVgr/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFK + JP8MyWn/Z92i/2fdov9n3aL/Z92i/yLOd/8BxmL/AcZi/wHGYv8BxmL/Abpc/wGjUf8Bo1H/AaNR/wGj + Uf8Bo1H/AXU6/wF1Ov8BdTr/AXU6/wF1Ov8BWCv/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFK + JP8BSiT/AUok/wzJaf9m3aL/Z92i/2fdov9n3aH/Is53/wHGYv8BxmL/AcZi/wHGYv8Aulz/AaNR/wGj + Uf8Bo1H/AaNR/wGjUf8BdTr/AXU6/wF1Ov8BdTr/AXU6/wFYK/8BSiT/AUok/wFKJP8BSiT/AUok/wFK + JP8BSiT/AUok/wFKJP8BSiT/DMlp/2fdov9n3aL/Z92i/2fdov8iznf/AcZi/wHGYv8BxmL/AcZi/wG6 + XP8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wF1Ov8BdTr/AXU6/wF1Ov8BdTr/AVgr/wFKJP8BSiT/AUok/wFK + JP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8Rymz/munB/5rpwf+a6cH/munB/2Pcn/9H1o3/R9aN/0fW + jf9H1o3/L819/wG8Xf8Bu13/Abtd/wG7Xf8Bu1z/AZVK/wGVSv8BlUr/AZVK/wGVSv8Bdzr/AWgz/wFo + M/8BaDP/AWgz/wFdLv8BSiT/AUok/wFKJP8BSiT/AUok/xTLbv+y7tD/su7Q/7Lu0P+y7tD/gOKx/2fd + ov9n3aL/Z92i/2fdov9F1Yz/Acdi/wHGYv8BxmL/AcZi/wHGYv8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wGF + Qf8BdTr/AXU6/wF1Ov8BdTr/AWcy/wFKJP8BSiT/AUok/wFKJP8BSiT/FMtu/7Lu0P+y7tD/su7Q/7Lu + 0P+A4rH/Z92i/2fdov9n3aL/Z92i/0XWjP8BxmL/AcZi/wHGYv8BxmL/AcZi/wGjUf8Bo1H/AaNR/wGj + Uf8Bo1H/AYVB/wF1Ov8BdTr/AXU6/wF1Ov8BZzL/AUok/wFKJP8BSiT/AUok/wFKJP8Uy27/su7Q/7Lu + 0P+y7tD/su7Q/4Disf9n3aL/Z92i/2fdov9n3aL/RNWM/wDHYv8BxmL/AcZi/wHGYv8BxmL/AaVS/wGj + Uf8Bo1H/AaNR/wGjUf8BhUH/AXU6/wF1Ov8BdTr/AXU6/wFnMv8BSiT/AUok/wFKJP8BSiT/AUok/xTL + bv+y7tD/su7Q/7Lu0P+y7tD/gOKx/2fdov9n3aL/Z92i/2fdov9F1oz/AcZi/wHGYv8BxmL/B8hm/2Hc + nf8IxGT/AatU/wGjUf8Bo1H/AaNR/wGFQf8BdTr/AXU6/wF1Ov8BdTr/AWcy/wFKJP8BSiT/AUok/wFK + JP8BSiT/F8xw/8304f/N9OH/zfTh/8304f+b6cL/geOy/4Hjsv+B47L/geOy/2Lcn/8lznj/Cchn/x/N + df/I893//////8304P8oz3r/AbVZ/wGwV/8BsFf/AZRJ/wGGQv8BhkL/AYZC/wGGQv8Bdzr/AVks/wFZ + LP8BWSz/AVks/wFZLP8dzXP//////////////////////8v04P+y7tD/su7Q/7Lu0P+y7tD/lei+/yfP + ev9I147/6/vz//////////////////T8+P9k3Z//AcZi/wHGYv8Ar1b/AaNR/wGjUf8Bo1H/AaNR/wCU + Sf8BdTr/AXU6/wF1Ov8BdTr/AXU6/x3Nc///////////////////////y/Tg/7Lu0P+y7tD/su7Q/5no + wP8lz3j/geOx//z+/f////////////////////////////////+q7Mv/Esps/wGzWP8Bo1H/AaNR/wGj + Uf8Bo1H/AZRJ/wF1Ov8BdTr/AXU6/wF1Ov8BdTr/Hc1z///////////////////////L9OD/su7Q/7Lu + 0P964q3/Ic52/7nv1P/////////////////////////////////////////////////g+Oz/PdSH/wG4 + W/8BpFH/AaNR/wGjUf8AlEn/AXU6/wF1Ov8BdTr/AXU6/wF1Ov8dzXP//////////////////////8v0 + 4P+v7c7/UdmU/zrUhf/h+O3///////////////////////////////////////////////////////// + ///7/v3/gOOx/wXCYf8BqVT/AaNR/wCUSf8BdTr/AXU6/wF1Ov8BdTr/AXU6/x3Nc/////////////// + ////////uvDV/yzQff9u36X/+f77//////////////////////////////////////////////////// + ////////////////////////w/La/yDNdf8Bslj/AJVJ/wF2Ov8Bdjr/AXY6/wF2Ov8Bdjr/Hc1z//// + /////////////77x1/8lz3n/qOzJ//////////////////////////////////////////////////// + ////////////////////////////////////////7/v1/1fal/8BwF//AaZS/wGjUf8Bo1H/AaNR/wGj + Uf8dzXP///////3//v+H5bX/L9F//9b25v////////////////////////////////////////////// + /////////////////////////////////////////////////////////v///53pw/8Mxmf/Aa1V/wGj + Uf8Bo1H/AaNR/x3Nc//u+/T/TtiS/1vbmv/z/Pj///////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////9j2 + 5/8z0oH/AbZa/wGkUf8Bo1H/Dspq/yrQe/+W6L7//v////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////n9+/9z4Kn/A8Bf/wGoU/8Ax2L/OtOG/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2ze + pP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2ze + pP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP8t0H3/AcVh/wHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHG + Yk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHG + Yk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJP////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + \ No newline at end of file diff --git a/OpaqueMail.Net.ProxySettings/OpaqueMail.Net.ProxySettings.csproj b/OpaqueMail.Net.ProxySettings/OpaqueMail.Net.ProxySettings.csproj index 2d77e4a..5ac02a5 100644 --- a/OpaqueMail.Net.ProxySettings/OpaqueMail.Net.ProxySettings.csproj +++ b/OpaqueMail.Net.ProxySettings/OpaqueMail.Net.ProxySettings.csproj @@ -1,124 +1,132 @@ - - - - - Debug - AnyCPU - {EE9F6184-F918-4FC0-9018-3019DFB8A408} - WinExe - Properties - OpaqueMail.Net.ProxySettings - OpaqueMail.Net.ProxySettings - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - OpaqueMail32.ico - - - app.manifest - - - - - - - - - - - - - - - - - - Form - - - Form1.cs - - - - - Form1.cs - Designer - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - {9f1402f2-d701-43a5-9ad6-cfa926c31dce} - OpaqueMail.Net.CertHelper - - - {c8f12621-2e22-48fd-8e52-71f1b059179e} - OpaqueMail.Net.ProxyService - - - {fbdcf97d-7ef1-470d-a13f-04cab5911d6b} - OpaqueMail.Net.Proxy - - - {bf21d27f-72ad-492e-8219-6c82c2330c72} - OpaqueMail.Net - - - - - Always - - - - - - - + + + + + Debug + AnyCPU + {EE9F6184-F918-4FC0-9018-3019DFB8A408} + WinExe + Properties + OpaqueMail.Net.ProxySettings + OpaqueMail.Net.ProxySettings + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + OpaqueMail32.ico + + + app.manifest + + + + + + + + + + + + + + + + + + Form + + + Form1.cs + + + + + + Form1.cs + Designer + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + {9f1402f2-d701-43a5-9ad6-cfa926c31dce} + OpaqueMail.Net.CertHelper + + + {c8f12621-2e22-48fd-8e52-71f1b059179e} + OpaqueMail.Net.ProxyService + + + {fbdcf97d-7ef1-470d-a13f-04cab5911d6b} + OpaqueMail.Net.Proxy + + + {bf21d27f-72ad-492e-8219-6c82c2330c72} + OpaqueMail.Net + + + + + Always + Designer + + + + + + + + + + + \ No newline at end of file diff --git a/OpaqueMail.Net.ProxySettings/OpaqueMail.Proxy.Sample.xml b/OpaqueMail.Net.ProxySettings/OpaqueMail.Proxy.Sample.xml index 068c8f9..60037d3 100644 --- a/OpaqueMail.Net.ProxySettings/OpaqueMail.Proxy.Sample.xml +++ b/OpaqueMail.Net.ProxySettings/OpaqueMail.Proxy.Sample.xml @@ -1,125 +1,127 @@ - - - - - 1 - - - - - 0.0.0.0 - - - Any - - 587 - - True - - - smtp.gmail.com - - 587 - - True - - - - - - - - - LocalMachine - - - - - - - - True - - - - - BestEffort - - - True - - True - - True - - - True - - - - Logs\SMTPProxy{#}-{yyyy-MM-dd}.log - - Verbose - - - - - 1 - - - - - 0.0.0.0 - - - Any - - 993 - - True - - - imap.gmail.com - - 993 - - True - - - - Logs\IMAPProxy{#}-{yyyy-MM-dd}.log - - Verbose - - - - - 1 - - - - - 0.0.0.0 - - - Any - - 995 - - True - - - pop.gmail.com - - 995 - - True - - - - Logs\POP3Proxy{#}-{yyyy-MM-dd}.log - - Verbose - - + + + + + 1 + + + + + 0.0.0.0 + + + Any + + 587 + + True + + + smtp.gmail.com + + 587 + + True + + + + + + + + + + + LocalMachine + + + + + + + + True + + + + + BestEffort + + + True + + True + + True + + + True + + + + Logs\SMTPProxy{#}-{yyyy-MM-dd}.log + + Verbose + + + + + 1 + + + + + 0.0.0.0 + + + Any + + 993 + + True + + + imap.gmail.com + + 993 + + True + + + + Logs\IMAPProxy{#}-{yyyy-MM-dd}.log + + Verbose + + + + + 1 + + + + + 0.0.0.0 + + + Any + + 995 + + True + + + pop.gmail.com + + 995 + + True + + + + Logs\POP3Proxy{#}-{yyyy-MM-dd}.log + + Verbose + + \ No newline at end of file diff --git a/OpaqueMail.Net.ProxySettings/Program.cs b/OpaqueMail.Net.ProxySettings/Program.cs index b44552b..ab220b5 100644 --- a/OpaqueMail.Net.ProxySettings/Program.cs +++ b/OpaqueMail.Net.ProxySettings/Program.cs @@ -1,22 +1,22 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace OpaqueMail.Net.ProxySettings -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new Form1()); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace OpaqueMail.Net.ProxySettings +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/OpaqueMail.Net.ProxySettings/Properties/AssemblyInfo.cs b/OpaqueMail.Net.ProxySettings/Properties/AssemblyInfo.cs index 38d1f11..2db3012 100644 --- a/OpaqueMail.Net.ProxySettings/Properties/AssemblyInfo.cs +++ b/OpaqueMail.Net.ProxySettings/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpaqueMail.Net.ProxySettings")] -[assembly: AssemblyDescription("SMTP proxy to add or remove S/MIME message signing, encryption, and authentication for outbound messages.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Bert Johnson")] -[assembly: AssemblyProduct("OpaqueMail")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("6d67b499-de07-498e-8853-51c0b4d01253")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.2")] -[assembly: AssemblyFileVersion("1.4.2.2")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpaqueMail.Net.ProxySettings")] +[assembly: AssemblyDescription("SMTP proxy to add or remove S/MIME message signing, encryption, and authentication for outbound messages.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Bert Johnson")] +[assembly: AssemblyProduct("OpaqueMail")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6d67b499-de07-498e-8853-51c0b4d01253")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.5.0")] +[assembly: AssemblyFileVersion("1.5.0.0")] diff --git a/OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.htm b/OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.htm new file mode 100644 index 0000000..ab8eb32 --- /dev/null +++ b/OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.htm @@ -0,0 +1,745 @@ + + + + + + + + + + + + + + + + + + +
+ +

-----

+ +

This message is digitally signed +with OpaqueMail.  Start securing your e-mail for free by visiting http://opaquemail.org/.

+ +

 

+ +
+ + + + diff --git a/OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.rtf b/OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.rtf new file mode 100644 index 0000000..5914961 --- /dev/null +++ b/OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.rtf @@ -0,0 +1,195 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31505\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f34\fbidi \froman\fcharset1\fprq2{\*\panose 02040503050406030204}Cambria Math;} +{\f37\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;} +{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} +{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} +{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f45\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f46\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\f48\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f49\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f50\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f51\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\f52\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f53\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f415\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f416\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\f418\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f419\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f422\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f423\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);} +{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;} +{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;} +{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} +{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} +{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} +{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} +{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} +{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; +\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red5\green99\blue193;\cfollowedhyperlink\ctint255\cshade255\red149\green79\blue114;\red118\green113\blue113;\red58\green117\blue1;} +{\*\defchp \fs22\loch\af31506\hich\af31506\dbch\af31505 }{\*\defpap \ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ +\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 +\snext0 \sqformat \spriority0 Normal;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 +\snext11 \ssemihidden \sunhideused Normal Table;}{\*\cs15 \additive \ul\cf17 \ssemihidden \sunhideused \styrsid14834849 Hyperlink;}{\*\cs16 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf18 \sbasedon10 \ssemihidden \sunhideused \styrsid9921474 +FollowedHyperlink;}}{\*\rsidtbl \rsid9921474\rsid14834849}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\version0}{\edmins0}{\nofpages1}{\nofwords25}{\nofchars145} +{\nofcharsws169}{\vern57435}}{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen +\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1701\dgvorigin1984\dghshow1\dgvshow1 +\jexpand\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct\asianbrkrule\rsidroot14834849 +\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \donotshowmarkup1\fet0{\*\wgrffmtfilter 2450} +\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\endnhere\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang +{\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7 +\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar +\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\af31506\hich\af31506\dbch\af31505\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af31507 +\ltrch\fcs0 \cf19\insrsid14834849 ----- +\par \hich\af31506\dbch\af31505\loch\f31506 This message is digitally signed with OpaqueMail.\~ }{\rtlch\fcs1 \af31507 \ltrch\fcs0 \cf19\insrsid14834849 \hich\af31506\dbch\af31505\loch\f31506 S\hich\af31506\dbch\af31505\loch\f31506 tart securing}{\rtlch\fcs1 +\af31507 \ltrch\fcs0 \cf19\insrsid14834849 \hich\af31506\dbch\af31505\loch\f31506 your e-mail for free \hich\af31506\dbch\af31505\loch\f31506 by visiting }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af31507 \ltrch\fcs0 \cf20\insrsid14834849 +\hich\af31506\dbch\af31505\loch\f31506 \hich\af31506\dbch\af31505\loch\f31506 HYPERLINK "http://opaquemail.org/"\hich\af31506\dbch\af31505\loch\f31506 }{\rtlch\fcs1 \af31507 \ltrch\fcs0 \cf20\insrsid14834849 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b4600000068007400740070003a002f002f006f00700061007100750065006d00610069006c002e006f00720067002f000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt { +\rtlch\fcs1 \af31507 \ltrch\fcs0 \cs15\ul\cf20\insrsid14834849 \hich\af31506\dbch\af31505\loch\f31506 http://opaquemail.org/}}}\sectd \ltrsect\linex0\endnhere\sectdefaultcl\sftnbj {\rtlch\fcs1 \af31507 \ltrch\fcs0 \cf19\insrsid14834849 .}{\rtlch\fcs1 +\af31507 \ltrch\fcs0 \insrsid14834849 +\par }\pard \ltrpar\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid9921474 +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b030414000600080000002100aa5225dfc60600008b1a0000160000007468656d652f7468656d652f +7468656d65312e786d6cec595d8bdb46147d2ff43f08bd3bfe92fcb1c41b6cd9ceb6d94d42eca4e4716c8fadc98e344633de8d0981923c160aa569e943037deb +43691b48a02fe9afd936a54d217fa17746b63c638fbb9b2585a5640d8b343af7ce997bafce1d4997afdc8fa87384134e58dc708b970aae83e3211b9178d2706f +f7bbb99aeb7081e211a22cc60d778eb97b65f7c30f2ea31d11e2083b601ff31dd4704321a63bf93c1fc230e297d814c7706dcc920809384d26f951828ec16f44 +f3a542a1928f10895d274611b8bd311e932176fad2a5bbbb74dea1701a0b2e078634e949d7d8b050d8d1615122f89c0734718e106db830cf881df7f17de13a14 +7101171a6e41fdb9f9ddcb79b4b330a2628bad66d7557f0bbb85c1e8b0a4e64c26836c52cff3bd4a33f3af00546ce23ad54ea553c9fc29001a0e61a52917dda7 +dfaab7dafe02ab81d2438bef76b55d2e1a78cd7f798373d3973f03af40a97f6f03dfed06104503af4029dedfc07b5eb51478065e81527c65035f2d34db5ed5c0 +2b5048497cb8812ef89572b05c6d061933ba6785d77daf5b2d2d9caf50500d5975c929c62c16db6a2d42f758d2058004522448ec88f9148fd110aa3840940c12 +e2ec93490885374531e3305c2815ba8532fc973f4f1da988a01d8c346bc90b98f08d21c9c7e1c3844c45c3fd18bcba1ae4cdcb1fdfbc7cee9c3c7a71f2e89793 +c78f4f1efd9c3a32acf6503cd1ad5e7fffc5df4f3f75fe7afeddeb275fd9f15cc7fffed367bffdfaa51d082b5d85e0d5d7cffe78f1ecd5379ffff9c3130bbc99 +a0810eef930873e73a3e766eb10816a6426032c783e4ed2cfa2122ba45339e701423398bc57f478406fafa1c5164c1b5b019c13b09488c0d787576cf20dc0b93 +9920168fd7c2c8001e30465b2cb146e19a9c4b0b737f164fec9327331d770ba123dbdc018a8dfc766653d05662731984d8a07993a258a0098eb170e4357688b1 +6575770931e27a408609e36c2c9cbbc46921620d499f0c8c6a5a19ed9108f232b711847c1bb139b8e3b418b5adba8d8f4c24dc15885ac8f73135c27815cd048a +6c2efb28a27ac0f791086d247bf364a8e33a5c40a6279832a733c29cdb6c6e24b05e2de9d7405eec693fa0f3c84426821cda7cee23c674649b1d06218aa6366c +8fc4a18efd881f428922e7261336f80133ef10790e7940f1d674df21d848f7e96a701b9455a7b42a107965965872791533a37e7b733a4658490d08bfa1e71189 +4f15f73559f7ff5b5907217df5ed53cbaa2eaaa0371362bda3f6d6647c1b6e5dbc03968cc8c5d7ee369ac53731dc2e9b0decbd74bf976ef77f2fdddbeee7772f +d82b8d06f9965bc574abae36eed1d67dfb9850da13738af7b9daba73e84ca32e0c4a3bf5cc8ab3e7b8690887f24e86090cdc2441cac64998f88488b017a229ec +ef8bae7432e10bd713ee4c19876dbf1ab6fa96783a8b0ed8287d5c2d16e5a3692a1e1c89d578c1cfc6e15143a4e84a75f50896b9576c27ea51794940dabe0d09 +6d329344d942a2ba1c9441520fe610340b09b5b277c2a26e615193ee97a9da6001d4b2acc0d6c9810d57c3f53d30012378a242148f649ed2542fb3ab92f92e33 +bd2d984605c03e625901ab4cd725d7adcb93ab4b4bed0c99364868e566925091513d8c87688417d52947cf42e36d735d5fa5d4a02743a1e683d25ad1a8d6fe8d +c579730d76ebda40635d2968ec1c37dc4ad9879219a269c31dc3633f1c4653a81d2eb7bc884ee0ddd95024e90d7f1e6599265cb4110fd3802bd149d520220227 +0e2551c395cbcfd24063a5218a5bb104827061c9d541562e1a3948ba99643c1ee3a1d0d3ae8dc848a7a7a0f0a95658af2af3f383a5259b41ba7be1e8d819d059 +720b4189f9d5a20ce0887078fb534ca33922f03a3313b255fdad35a685eceaef13550da5e3884e43b4e828ba98a77025e5191d7596c5403b5bac1902aa8564d1 +080713d960f5a01add34eb1a2987ad5df7742319394d34573dd35015d935ed2a66ccb06c036bb13c5f93d7582d430c9aa677f854bad725b7bed4bab57d42d625 +20e059fc2c5df70c0d41a3b69acca026196fcab0d4ecc5a8d93b960b3c85da599a84a6fa95a5dbb5b8653dc23a1d0c9eabf383dd7ad5c2d078b9af549156df3d +f44f136c700fc4a30d2f81675470954af8f09020d810f5d49e24950db845ee8bc5ad0147ce2c210df741c16f7a41c90f72859adfc97965af90abf9cd72aee9fb +e562c72f16daadd243682c228c8a7efacda50bafa2e87cf1e5458d6f7c7d89966fdb2e0d599467eaeb4a5e11575f5f8aa5ed5f5f1c02a2f3a052ead6cbf55625 +572f37bb39afddaae5ea41a5956b57826abbdb0efc5abdfbd0758e14d86b9603afd2a9e52ac520c8799582a45fabe7aa5ea9d4f4aacd5ac76b3e5c6c6360e5a9 +7c2c6201e155bc76ff010000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f7468656d652f5f72656c732f +7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c082e8761be +9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd08a54f980 +ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa4c04ca5b +babac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000000000000000000000000000005b436f6e74656e +745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000000000300100005f72656c732f +2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000190200007468656d652f7468656d652f74 +68656d654d616e616765722e786d6c504b01022d0014000600080000002100aa5225dfc60600008b1a00001600000000000000000000000000d6020000746865 +6d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b0100002700000000000000000000000000d00900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000cb0a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax371\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6; +\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature;\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong; +\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Table;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 2; +\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Theme;\lsdsemihidden1 \lsdlocked0 Placeholder Text; +\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2; +\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List; +\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1; +\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision; +\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1; +\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1; +\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2; +\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2; +\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3; +\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4; +\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4; +\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5; +\lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; +\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; +\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; +\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; +\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; +\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; +\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; +\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; +\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; +\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; +\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; +\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; +\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; +\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; +\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; +\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; +\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; +\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; +\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; +\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; +\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; +\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;}}} \ No newline at end of file diff --git a/OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.txt b/OpaqueMail.Net.ProxySettings/Signature.Resources/OpaqueMail.txt new file mode 100644 index 0000000000000000000000000000000000000000..e89706fc6c7c3d8812f47bb0284cb60a01478587 GIT binary patch literal 268 zcmYk1-3`Jp4216)i5;>)!#g`59$*2WX;KMVNJ>ruTHw-_8Kt8a2sRh>m-7%@zqUAxY^1rjc E0X{M?4gdfE literal 0 HcmV?d00001 diff --git a/OpaqueMail.Net.ProxySettings/Signature.Resources/colorschememapping.xml b/OpaqueMail.Net.ProxySettings/Signature.Resources/colorschememapping.xml new file mode 100644 index 0000000..6a0069c --- /dev/null +++ b/OpaqueMail.Net.ProxySettings/Signature.Resources/colorschememapping.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/OpaqueMail.Net.ProxySettings/Signature.Resources/filelist.xml b/OpaqueMail.Net.ProxySettings/Signature.Resources/filelist.xml new file mode 100644 index 0000000..3e6dd5a --- /dev/null +++ b/OpaqueMail.Net.ProxySettings/Signature.Resources/filelist.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/OpaqueMail.Net.ProxySettings/Signature.Resources/themedata.thmx b/OpaqueMail.Net.ProxySettings/Signature.Resources/themedata.thmx new file mode 100644 index 0000000000000000000000000000000000000000..f7418b305e4db9d41db5d9662d76d13e051254c3 GIT binary patch literal 3134 zcmaKu2{hDu8^;Hw5rZzK#*(G6WsIG&T+3Xt8&Z+1nNWktF5JnMEqm6yvW+zcr5F?@ zF(JD#hJ=I+D!J1rywmMHr>?j6{r%5xJHP+)J>-%m=yrvW9|+JU!MS!Pk^&+C4Odh?)Q(qv=Bxi56{5nF>wF)$iNQK1)VE%>#I9%>@y7F3wxKN`qESB1_v;$}6h zD7EJkA_b3WyW)6qKM|g^$!NsgG)c&}!G*oWc$hcpq@?+Yrj?CspJMG(3zE9IbCY`@ z(0cmVYC`&{1rmb1$o8mS#7<{@k&z3XPkTtvRkyrviU3AG=*SqORWgi7U<3Gz7U?Z` znOs8gfPI%O15&^2gi;$;t9&K)k@NO%#Com-=RprKi2U29T7?NT>V!SIZ6>8LC0yO9 z-?DR$-(^d8nQyco>wsluWN(iRfc!_ka1#|v9n3tbvj6~`%-z`!h4xo~e_LZx2d@$p zx#Bir+oOjgtVf8)B!d)iX$XbV2*8{S)>A@=Dzr+q>R~_J3XCb+pjYYa)+I6K?ktIc z^Q+m3^9bEO&$%F-r`RY`Y*~dQ&>;kKt2B(-aV2 zK1|{rF*T&BxDQgfqPc~T*Xrh@TVNZqhD%*)C28*gQL(?!suPQcEgBy1(YRyo z^~SBZQeok2ds-!`dhGa6Q!`^?hZ)L$2YM|u8yw9HFp)WhnKQr><&9GKX|-_marHp? zeX9_zev89IFFTBzS>4|_BlHMkR%8IfPdw@(l@th_KrzZBmM}5 zPrAT%t=){T4bOzol$Vbv76!DkH`mdrwWJhPBb8Ul9FZB?;KMkt6$D$vD#A<1xJY3I zPf^ENh+~U`jUH)VPHnv_8H;lVC8=uCU*?73Uy7LI;HOh7{bTqn?WQqFIfv__7sQ3q zLq;&Z^wyUARZQgkkrLQ*P;g317>Z8hftQLLnFngQh;E9EVG!cwS`2N`F2(trN&X&; zcYg+u}uL$ibTjY@o)pcm{v! zFmLrKy*pgL&C@bXfUz2eN(+jYWCRGY%3pJItwgHgi$rd#UL$elzE2h>^~@1t%ngY< z$Jz*jZwC5AXWPQp3$$-s|G1o!*^_E@e5XKD`Tk!>?crJ{>|Xb*m2Tkfjoq15X=loZ zOFetLqaIIpws9grImg)72Dz#5F^@(av3{jp#xTBfv$NHAyd3FWpCx{_K~h`_*sL^S-1Ov#N7dOFY>8n)?PxuAJTuUtA;xk9C@fQt5k);r0s zkoBBsUb;n_y?;nW2~UNupLibNepiHDJh5CIo%*aeWhY+LAVFeDickGBi^>LXD0isd zhlzk$@kwL)%2kZeopTirPx6MMvhDmr1yoA^4D;7CvNHhzpB672Nw5;2tJ%}{9PAH? zM{KQ~?W=}Y7isXhwU_=i;T|sJ}tysn<#t)VmxzYC(Z2RzO3p<@+hsOw9? z3X^il?2K%O?tq#)_4VP2h%dRwDL(&1ddn2Sf7!eg&Sk2#)t~raso4t{_tEke2iG)I z|HX5Y+|M!b^+!u&X&Xs(0m^fnWWIHCNSyam!AINKA(~0tlQI#P!s<-#)7c)w$U6bZUO+|p5GIT2D~z@O?~IAW{tpeBO>S812eH9FYwyPG{rjX7D8 zT-%pfV`jF-m8Wj(xp*Z5nQpCfJI*ua=(y~eL4yTve@AEBSdlcM_-*va#o@|{-Q@02 ze^Aisl-^}WSx=g<#W9MQXs6Hjz#I1q(#C5`K!hYOxICgizmA73aQ8m&w2HLj>A zdS7+k4u5iUGa4s(SDz3zw>$|MviIZEPhgCfaJ`PlgnT(`UV2*My`~CZ3C!-+({i{EVXcmJ9|j)cqquAp3C^gl3d+XSI?@A9 zKu%kzUeFmu3Q*M6PG0G+N3&gP(00xw+sPxi^2>ui+Y-oc?o^l@zUPPVLw!`}~;l?JOL%sTrlyr76NyShE>Db7Q<~D5M2% zn|^*)=aUs58pHFf6r>R8VT;K!{ro;nY?60ttPq+7YBpR?>aJxu^AaWwqU(41-q0uU z_{HYWl0s4Vv>L{MU){==MYkRW@Ko&x!V@NOJMqu$0URg3WI6o9qG7Ucctk?BFBJ~5 zzvJ<4)zs<81!w0(+?unqFl)(Xkm8Y-q5V5GUuA5YNx8PPt^k#d#?R{34PgRu__$q#xi%VEokqT zZ5uS4cJC3h;|VcerT2Bd@2v873;(Hk|A$>H4tFW?$IzpKfxf<@1m1M1nfRgFF}9dD zY9Mu8s5Na3R0jjX<6NKQd9~*Ji^O+X`IVAqwIEP)CF^>Ar*|_gMb8FonLyEUuXl+` z@zq!p@JKh4v5?po{nQ(_OwlsxZ$ed2Dl`GFS>1j4gfW?gN3II6B{2ZCl_Z|Lr`#10TT-5*o literal 0 HcmV?d00001 diff --git a/OpaqueMail.Net.ProxySettings/Windows8Mail.cs b/OpaqueMail.Net.ProxySettings/Windows8Mail.cs new file mode 100644 index 0000000..3e21662 --- /dev/null +++ b/OpaqueMail.Net.ProxySettings/Windows8Mail.cs @@ -0,0 +1,268 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace OpaqueMail.Net.ProxySettings +{ + /// + /// Helper class to create a loopback exception for the Windows 8 Mail app. + /// + /// See http://blogs.msdn.com/b/fiddler/archive/2011/09/14/fiddler-and-windows-8-metro-style-applications-https-and-private-network-capabilities.aspx for more information. + public class Windows8MailHelper + { + #region Structs + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct SidAndAttributes + { + public IntPtr Sid; + public uint Attributes; + } + + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct InetFirewallAcCapabilities + { + public uint Count; + public IntPtr Capabilities; + } + + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct InetFirewallAcBinaries + { + public uint Count; + public IntPtr Binaries; + } + + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct InetFirewallAppContainer + { + internal IntPtr AppContainerSid; + internal IntPtr UserSid; + [MarshalAs(UnmanagedType.LPWStr)] + public string AppContainerName; + [MarshalAs(UnmanagedType.LPWStr)] + public string DisplayName; + [MarshalAs(UnmanagedType.LPWStr)] + public string Description; + internal InetFirewallAcCapabilities Capabilities; + internal InetFirewallAcBinaries Binaries; + [MarshalAs(UnmanagedType.LPWStr)] + public string WorkingDirectory; + [MarshalAs(UnmanagedType.LPWStr)] + public string PackageFullName; + } + #endregion Structs + + #region Externs + [DllImport("FirewallAPI.dll")] + internal static extern void NetworkIsolationFreeAppContainers(IntPtr pACs); + + [DllImport("FirewallAPI.dll")] + internal static extern uint NetworkIsolationGetAppContainerConfig(out uint pdwCntACs, out IntPtr appContainerSids); + + [DllImport("FirewallAPI.dll")] + private static extern uint NetworkIsolationSetAppContainerConfig(uint pdwCntACs, SidAndAttributes[] appContainerSids); + + [DllImport("advapi32.dll", SetLastError = true)] + internal static extern bool ConvertStringSidToSid(string strSid, out IntPtr pSid); + + [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)] + static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid); + + [DllImport("FirewallAPI.dll")] + internal static extern uint NetworkIsolationEnumAppContainers(uint Flags, out uint pdwCntPublicACs, out IntPtr ppACs); + #endregion Externs + + #region Private Enums + private enum NETISO_FLAG + { + NETISO_FLAG_FORCE_COMPUTE_BINARIES = 0x1, + NETISO_FLAG_MAX = 0x2 + } + #endregion Private Enums + + /// + /// Object to track app container information. + /// + public class AppContainer + { + public String AppContainerName { get; set; } + public String DisplayName { get; set; } + public String WorkingDirectory { get; set; } + public String StringSid { get; set; } + public List Capabilities { get; set; } + public bool LoopbackEnabled { get; set; } + + public AppContainer(String _appContainerName, String _displayName, String _workingDirectory, IntPtr _sid) + { + this.AppContainerName = _appContainerName; + this.DisplayName = _displayName; + this.WorkingDirectory = _workingDirectory; + String tempSid; + ConvertSidToStringSid(_sid, out tempSid); + this.StringSid = tempSid; + } + } + + #region Internal Members + internal List Apps = new List(); + internal List AppList; + internal List AppListConfig; + internal IntPtr ACs; + #endregion Internal Members + + #region Constructors + /// + /// Default constructor. + /// + public Windows8MailHelper() + { + AppList = PINetworkIsolationEnumAppContainers(); + AppListConfig = PINetworkIsolationGetAppContainerConfig(); + foreach (InetFirewallAppContainer PIApp in AppList) + { + AppContainer app = new AppContainer(PIApp.AppContainerName, PIApp.DisplayName, PIApp.WorkingDirectory, PIApp.AppContainerSid); + + List AppCapabilities = GetCapabilites(PIApp.Capabilities); + + app.LoopbackEnabled = CheckLoopback(PIApp.AppContainerSid); + Apps.Add(app); + } + } + #endregion Constructors + + /// + /// Check if an application is set up for the loopback exemption. + /// + /// Pointer to the application container. + private bool CheckLoopback(IntPtr intPtr) + { + foreach (SidAndAttributes item in AppListConfig) + { + string before, after; + ConvertSidToStringSid(item.Sid, out before); + ConvertSidToStringSid(intPtr, out after); + + return before == after; + } + + return false; + } + + /// + /// Return a list of capabilities of a Windows 8 application. + /// + private static List GetCapabilites(InetFirewallAcCapabilities cap) + { + List myCapabilities = new List(); + + IntPtr arrayValue = cap.Capabilities; + + int structSize = Marshal.SizeOf(typeof(SidAndAttributes)); + for (var i = 0; i < cap.Count; i++) + { + SidAndAttributes currentSidAndAttributes = (SidAndAttributes)Marshal.PtrToStructure(arrayValue, typeof(SidAndAttributes)); + myCapabilities.Add(currentSidAndAttributes); + arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); + } + + return myCapabilities; + } + + /// + /// Return a list of Windows 8 app container configuration settings. + /// + private static List PINetworkIsolationGetAppContainerConfig() + { + IntPtr arrayValue = IntPtr.Zero; + uint size = 0; + List myCapabilities = new List(); + + // Pin down variables + GCHandle handlePdwCntPublicACs = GCHandle.Alloc(size, GCHandleType.Pinned); + GCHandle handlePpACs = GCHandle.Alloc(arrayValue, GCHandleType.Pinned); + + uint retVal = NetworkIsolationGetAppContainerConfig(out size, out arrayValue); + + int structSize = Marshal.SizeOf(typeof(SidAndAttributes)); + for (var i = 0; i < size; i++) + { + SidAndAttributes currentSidAndAttributes = (SidAndAttributes)Marshal.PtrToStructure(arrayValue, typeof(SidAndAttributes)); + myCapabilities.Add(currentSidAndAttributes); + arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); + } + + handlePdwCntPublicACs.Free(); + handlePpACs.Free(); + + return myCapabilities; + } + + /// + /// Return a list of Windows 8 app containers. + /// + private List PINetworkIsolationEnumAppContainers() + { + IntPtr arrayValue = IntPtr.Zero; + uint size = 0; + List list = new List(); + + GCHandle handlePdwCntPublicACs = GCHandle.Alloc(size, GCHandleType.Pinned); + GCHandle handlePpACs = GCHandle.Alloc(arrayValue, GCHandleType.Pinned); + + uint retVal = NetworkIsolationEnumAppContainers((Int32)NETISO_FLAG.NETISO_FLAG_MAX, out size, out arrayValue); + ACs = arrayValue; //store the pointer so it can be freed when we close the form + + int structSize = Marshal.SizeOf(typeof(InetFirewallAppContainer)); + for (var i = 0; i < size; i++) + { + InetFirewallAppContainer cur = (InetFirewallAppContainer)Marshal.PtrToStructure(arrayValue, typeof(InetFirewallAppContainer)); + list.Add(cur); + arrayValue = new IntPtr((long)(arrayValue) + (long)(structSize)); + } + + handlePdwCntPublicACs.Free(); + handlePpACs.Free(); + + return list; + } + + /// + /// Enable Windows 8 Mail loopback exemptions. + /// + public void EnableWindows8MailLoopback() + { + // Loop through and enable loopbacks for any Windows 8 Mail applications. + for (int i = 0; i < Apps.Count; i++) + { + // Strings relevant to the Windows 8 Mail app. + if (Apps[i].AppContainerName.Contains("microsoft.windowscommunicationsapps_") || Apps[i].AppContainerName.Contains("microsoft.winjs.")) + Apps[i].LoopbackEnabled = true; + } + + // Count the number of exemptions. + int countEnabled = Apps.Where(_ => _.LoopbackEnabled == true).Count(); + SidAndAttributes[] sidAndAttributesArray = new SidAndAttributes[countEnabled]; + int count = 0; + + // Save our exemptions. + for (int i = 0; i < Apps.Count; i++) + { + if (Apps[i].LoopbackEnabled) + { + sidAndAttributesArray[count].Attributes = 0; + IntPtr ptr; + ConvertStringSidToSid(Apps[i].StringSid, out ptr); + sidAndAttributesArray[count].Sid = ptr; + count++; + } + } + NetworkIsolationSetAppContainerConfig((uint)countEnabled, sidAndAttributesArray); + + // Free our app container handles. + NetworkIsolationFreeAppContainers(ACs); + } + } +} diff --git a/OpaqueMail.Net.TestClient/Form1.cs b/OpaqueMail.Net.TestClient/Form1.cs index c492046..f4fa24e 100644 --- a/OpaqueMail.Net.TestClient/Form1.cs +++ b/OpaqueMail.Net.TestClient/Form1.cs @@ -1,1075 +1,1076 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Mail; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; -using System.Xml; -using System.Xml.XPath; - -namespace OpaqueMail.TestClient -{ - public partial class Form1 : Form - { - #region Private Members - /// Collection of mailbox/UID pairs for messages loaded over IMAP. - private Tuple[] ImapMessageIDs; - /// List of message IDs for messages loaded over POP3. - private List Pop3MessageIDs; - /// An OpaqueMail.ImapClient to be reused across operations. - ImapClient myImapClient; - /// An OpaqueMail.Pop3Client to be reused across operations. - Pop3Client myPop3Client; - /// A file dialog box to be used when loading or saving *.eml files. - OpenFileDialog FileDialog = new OpenFileDialog(); - #endregion Private Members - - #region Event Handlers - /// - /// Test Client constructor. - /// - public Form1() - { - InitializeComponent(); - } - - /// - /// Test Client load event handler. - /// - private void Form1_Load(object sender, EventArgs e) - { - // Attempt to initialize settings based on values from a previous session. - LoadSettings(); - // Resize the form in case loaded settings changed the window size. - ResizeForm(); - - // Handle the resize event. - this.Resize += Form1_Resize; - this.FormClosing += Form1_FormClosing; - ImapSearchText.GotFocus += ImapSearchText_GotFocus; - ImapSearchText.KeyDown += ImapSearchText_KeyDown; - ImapSearchText.LostFocus += ImapSearchText_LostFocus; - } - - /// - /// Clean up when the Test Client is unloading. - /// - void Form1_FormClosing(object sender, FormClosingEventArgs e) - { - if (myImapClient != null) - myImapClient.Dispose(); - if (myPop3Client != null) - myPop3Client.Dispose(); - } - - /// - /// Handle the window resized event. - /// - void Form1_Resize(object sender, System.EventArgs e) - { - ResizeForm(); - } - - /// - /// Append a sample message to the Inbox. - /// - private void ImapAppendMessageButton_Click(object sender, EventArgs e) - { - // If we're not currently connected to the IMAP server, connect and authenticate. - if (myImapClient == null) - { - int imapPort = 993; - int.TryParse(ImapPort.Text, out imapPort); - - myImapClient = new ImapClient(ImapHost.Text, imapPort, ImapUsername.Text, ImapPassword.Text, ImapSsl.Checked); - } - - // If our connection has timed out or been closed, reconnect. - if (!myImapClient.IsConnected) - { - myImapClient.Connect(); - myImapClient.Authenticate(); - - // Ensure we connected successfully. - if (!myImapClient.IsConnected) - { - MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - } - - myImapClient.AppendMessage("INBOX", -@"Date: " + DateTime.Now.ToString("dd-MM-yyyy hh:mm:ss zzzz") + @" -From: Test User -To: Me <" + ImapUsername.Text + @"> -Message-Id: <" + Guid.NewGuid().ToString().Replace("{", "").Replace("}", "") + "@" + ImapHost.Text + @"> -Subject: Test Append Message -MIME-Version: 1.0 -Content-Type: Text/Plain; Charset=US-ASCII - -This is a test of the APPEND command.", new string[] { @"\Seen" }, DateTime.Now); - } - - /// - /// Copy server settings from POP3 to IMAP. - /// - private void ImapCopyPop3Button_Click(object sender, EventArgs e) - { - ImapHost.Text = Pop3Host.Text; - ImapUsername.Text = Pop3Username.Text; - ImapPassword.Text = Pop3Password.Text; - ImapSsl.Checked = Pop3Ssl.Checked; - } - - /// - /// Copy server settings from SMTP to IMAP. - /// - private void ImapCopySmtpButton_Click(object sender, EventArgs e) - { - ImapHost.Text = SmtpHost.Text; - ImapUsername.Text = SmtpUsername.Text; - ImapPassword.Text = SmtpPassword.Text; - ImapSsl.Checked = SmtpSsl.Checked; - } - - /// - /// Delete the selected IMAP message. - /// - private async void ImapDeleteMessageButton_Click(object sender, EventArgs e) - { - await myImapClient.DeleteMessageUidAsync(ImapMessageIDs[ImapMessageList.SelectedIndex].Item1, ImapMessageIDs[ImapMessageList.SelectedIndex].Item2); - ImapDeleteMessageButton.Enabled = false; - await RefreshImapMessages(); - } - - /// - /// Retrieve the overall quota and the quota for the users Inbox. - /// - private async void ImapGetQuotaButton_Click(object sender, EventArgs e) - { - // If we're not currently connected to the IMAP server, connect and authenticate. - if (myImapClient == null) - { - int imapPort = 993; - int.TryParse(ImapPort.Text, out imapPort); - - myImapClient = new ImapClient(ImapHost.Text, imapPort, ImapUsername.Text, ImapPassword.Text, ImapSsl.Checked); - } - - // If our connection has timed out or been closed, reconnect. - if (!myImapClient.IsConnected) - { - myImapClient.Connect(); - myImapClient.Authenticate(); - - // Ensure we connected successfully. - if (!myImapClient.IsConnected) - { - MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - } - - StringBuilder messageBuilder = new StringBuilder(); - - QuotaUsage totalUsage = await myImapClient.GetQuotaAsync(""); - QuotaUsage inboxUsage = await myImapClient.GetQuotaRootAsync("INBOX"); - - messageBuilder.Append("Overall quota information:\r\n\r\nUsed: " + totalUsage.Usage + "\r\nTotal: " + totalUsage.QuotaMaximum + "\r\n\r\nINBOX quota information:\r\n\r\nUsed: " + inboxUsage.Usage + "\r\nTotal: " + inboxUsage.QuotaMaximum); - - string[] mailboxes = await myImapClient.ListMailboxNamesAsync(true); - messageBuilder.Append("\r\n\r\nFolders (" + mailboxes.Length.ToString() + " Total):\r\n"); - - foreach (string mailbox in mailboxes) - messageBuilder.Append("\r\n" + mailbox); - - MessageBox.Show(messageBuilder.ToString(), "Quota and Mailbox Information", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - - /// - /// Load and preview an *.eml file. - /// - private void ImapLoadFileButton_Click(object sender, EventArgs e) - { - FileDialog.CheckFileExists = true; - FileDialog.Filter = "Email messages (*.eml)|*.eml|All files|*.*"; - FileDialog.Multiselect = false; - - if (FileDialog.ShowDialog() == DialogResult.OK) - { - ReadOnlyMailMessage message = ReadOnlyMailMessage.LoadFile(FileDialog.FileName); - RenderMessage(message, ImapHeaders, ImapWebPreview, ImapWebPreviewPanel); - } - } - - /// - /// When a message has been selected, attempt to load its preview. - /// - private async void ImapMessageList_SelectedIndexChanged(object sender, EventArgs e) - { - ImapDeleteMessageButton.Enabled = ImapMessageList.SelectedIndex > -1; - if (ImapMessageList.SelectedIndex > -1) - { - if (myImapClient != null && ImapMessageIDs != null) - { - if (ImapMessageList.SelectedIndex < ImapMessageIDs.Length) - { - if (ImapMessageIDs[ImapMessageList.SelectedIndex].Item2 > 0) - { - // If our connection has timed out or been closed, reconnect. - if (!myImapClient.IsConnected) - { - myImapClient.Connect(); - myImapClient.Authenticate(); - } - - if (myImapClient.IsConnected) - { - if (myImapClient.IsIdle) - await myImapClient.IdleStopAsync(); - - // Retrieve the selected message. - ReadOnlyMailMessage message = myImapClient.GetMessageUid(ImapMessageIDs[ImapMessageList.SelectedIndex].Item1, ImapMessageIDs[ImapMessageList.SelectedIndex].Item2); - - if (message != null) - { - // Populate the IMAP viewport with this message's headers and body. - RenderMessage(message, ImapHeaders, ImapWebPreview, ImapWebPreviewPanel); - } - else - { - // If the message was deleted, null will be returned. - ImapHeaders.BackColor = Color.White; - ImapHeaders.Text = ""; - ImapWebPreview.Document.Write("Message not found."); - ImapWebPreviewPanel.Refresh(); - } - - await myImapClient.IdleStartAsync(); - } - } - } - } - } - else - { - // If no message was retrieved, clear the viewport. - ImapWebPreview.DocumentText = ""; - ImapWebPreviewPanel.Refresh(); - } - } - - /// - /// Attempt to retrieve up to 25 IMAP messages and populate the list of messages. - /// - private async void ImapRetrieveMessagesButton_Click(object sender, EventArgs e) - { - ImapRetrieveMessagesButton.Enabled = false; - ImapMessageList.Enabled = false; - await RefreshImapMessages(); - ImapMessageList.Enabled = true; - ImapRetrieveMessagesButton.Enabled = true; - } - - /// - /// Clear the default search message when clicked. - /// - private void ImapSearchText_GotFocus(object sender, System.EventArgs e) - { - if (ImapSearchText.Text == "Search...") - ImapSearchText.Text = ""; - } - - /// - /// Process a search when clicked. - /// - private async void ImapSearchText_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter) - { - // If we're not currently connected to the IMAP server, connect and authenticate. - if (myImapClient == null) - { - int imapPort = 993; - int.TryParse(ImapPort.Text, out imapPort); - - myImapClient = new ImapClient(ImapHost.Text, imapPort, ImapUsername.Text, ImapPassword.Text, ImapSsl.Checked); - } - - // If our connection has timed out or been closed, reconnect. - if (!myImapClient.IsConnected) - { - myImapClient.Connect(); - myImapClient.Authenticate(); - - // Ensure we connected successfully. - if (!myImapClient.IsConnected) - { - MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - } - - if (myImapClient != null) - { - if (myImapClient.IsConnected) - { - await myImapClient.SelectMailboxAsync("INBOX"); - List messages = await myImapClient.SearchAsync("TEXT \"" + ImapSearchText.Text + "\""); - - ImapMessageIDs = new Tuple[messages.Count]; - - // Repopulate the message list with the subjects of messages retrieved. - ImapMessageList.Items.Clear(); - for (int i = 0; i < messages.Count; i++) - { - ImapMessageList.Items.Add(messages[i].Subject); - ImapMessageIDs[i] = new Tuple(messages[i].Mailbox, messages[i].ImapUid); - } - - // Reset the preview viewport. - ImapHeaders.BackColor = Color.White; - ImapHeaders.Text = ""; - ImapWebPreview.DocumentText = "Please select a message from the left-hand panel."; - ImapWebPreviewPanel.Refresh(); - } - } - } - } - - /// - /// Add the default search message if the query is blank. - /// - private void ImapSearchText_LostFocus(object sender, System.EventArgs e) - { - if (string.IsNullOrEmpty(ImapSearchText.Text)) - ImapSearchText.Text = "Search..."; - } - - /// - /// Load settings for this Test Client from an XML file. - /// - private void LoadSettingsButton_Click(object sender, EventArgs e) - { - MailAddressCollection mac = Functions.FromMailAddressString("'Toni Johnson' "); - - DialogResult result = MessageBox.Show("Are you sure? All settings will be overwritten by the contents of \"OpaqueMail.TestClient.xml\".", "Confirm Load", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); - if (result == DialogResult.OK) - LoadSettings(); - } - - /// - /// Copy server settings from IMAP to POP3. - /// - private void Pop3CopyImapButton_Click(object sender, EventArgs e) - { - Pop3Host.Text = ImapHost.Text; - Pop3Username.Text = ImapUsername.Text; - Pop3Password.Text = ImapPassword.Text; - Pop3Ssl.Checked = ImapSsl.Checked; - } - - /// - /// Copy server settings from SMTP to POP3. - /// - private void Pop3CopySmtpButton_Click(object sender, EventArgs e) - { - Pop3Host.Text = SmtpHost.Text; - Pop3Username.Text = SmtpUsername.Text; - Pop3Password.Text = SmtpPassword.Text; - Pop3Ssl.Checked = SmtpSsl.Checked; - } - - /// - /// Delete the selected IMAP message. - /// - private async void Pop3DeleteMessageButton_Click(object sender, EventArgs e) - { - await myPop3Client.DeleteMessageAsync(Pop3MessageIDs[Pop3MessageList.SelectedIndex]); - Pop3DeleteMessageButton.Enabled = false; - await RefreshPop3Messages(); - } - - /// - /// Attempt to retrieve up to 25 POP3 messages and populate the list of messages. - /// - private async void Pop3RetrieveMessageButton_Click(object sender, EventArgs e) - { - Pop3RetrieveMessageButton.Enabled = false; - Pop3MessageList.Enabled = false; - await RefreshPop3Messages(); - Pop3MessageList.Enabled = true; - Pop3RetrieveMessageButton.Enabled = true; - } - - /// - /// When a message has been selected, attempt to load its preview. - /// - private void Pop3MessageList_SelectedIndexChanged(object sender, EventArgs e) - { - Pop3DeleteMessageButton.Enabled = Pop3MessageList.SelectedIndex > -1; - if (Pop3MessageList.SelectedIndex > -1) - { - if (myPop3Client != null && Pop3MessageIDs != null) - { - if (Pop3MessageList.SelectedIndex < Pop3MessageIDs.Count) - { - if (Pop3MessageIDs[Pop3MessageList.SelectedIndex] > -1) - { - // If our connection has timed out or been closed, reconnect. - if (!myPop3Client.IsConnected) - { - myPop3Client.Connect(); - myPop3Client.Authenticate(); - } - - if (myPop3Client.IsConnected) - { - // Retrieve the selected message. - ReadOnlyMailMessage message = myPop3Client.GetMessage(Pop3MessageIDs[Pop3MessageList.SelectedIndex]); - - if (message != null) - { - // Populate the IMAP viewport with this message's headers and body. - RenderMessage(message, Pop3Headers, Pop3WebPreview, Pop3WebPreviewPanel); - } - else - { - // If the message was deleted, null will be returned. - Pop3Headers.BackColor = Color.White; - Pop3Headers.Text = ""; - Pop3WebPreview.Document.Write("Message not found."); - Pop3WebPreviewPanel.Refresh(); - } - } - } - } - } - } - else - { - // If no message was retrieved, clear the viewport. - Pop3WebPreview.DocumentText = ""; - Pop3WebPreviewPanel.Refresh(); - } - } - - /// - /// Save all current Test Client settings to an XML file. - /// - private void SaveSettingsButton_Click(object sender, EventArgs e) - { - SaveSettings(); - } - - /// - /// Copy server settings from IMAP to SMTP. - /// - private void SmtpCopyImapButton_Click(object sender, EventArgs e) - { - SmtpHost.Text = ImapHost.Text; - SmtpUsername.Text = ImapUsername.Text; - SmtpPassword.Text = ImapPassword.Text; - SmtpSsl.Checked = ImapSsl.Checked; - } - - /// - /// Copy server settings from POP3 to SMTP. - /// - private void SmtpCopyPop3Button_Click(object sender, EventArgs e) - { - SmtpHost.Text = Pop3Host.Text; - SmtpUsername.Text = Pop3Username.Text; - SmtpPassword.Text = Pop3Password.Text; - SmtpSsl.Checked = Pop3Ssl.Checked; - } - - /// - /// Send an e-mail using the SMTP settings specified. - /// - private async void SmtpSendButton_Click(object sender, EventArgs e) - { - X509Certificate2 signingCertificate = null; - if (SmtpSmimeSign.Checked || SmtpSmimeTripleWrap.Checked) - { - // If S/MIME signing the message, attempt to look up a certificate from the Windows certificate store matching the serial number specified. - if (SmtpSmimeSerialNumber.Text.Length < 1) - { - MessageBox.Show("SMTP send exception:\r\n\r\nA signing certificate must be passed prior to signing.", "SMTP send exception", MessageBoxButtons.OK, MessageBoxIcon.Error); - SmtpSmimeSerialNumber.Focus(); - return; - } - - // Try first looking the certificate up by its serial number, falling back to finding it by its subject name. - signingCertificate = CertHelper.GetCertificateBySerialNumber(StoreLocation.CurrentUser, SmtpSmimeSerialNumber.Text); - if (signingCertificate == null) - signingCertificate = CertHelper.GetCertificateBySerialNumber(StoreLocation.LocalMachine, SmtpSmimeSerialNumber.Text); - if (signingCertificate == null) - signingCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, SmtpSmimeSerialNumber.Text); - if (signingCertificate == null) - signingCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, SmtpSmimeSerialNumber.Text); - - if (signingCertificate == null) - { - MessageBox.Show("Certificate with serial # \"" + SmtpSmimeSerialNumber.Text + "\" not found."); - SmtpSmimeSerialNumber.Focus(); - return; - } - } - - try - { - int smtpPort = 25; - int.TryParse(SmtpPort.Text, out smtpPort); - - SmtpClient smtpClient = new SmtpClient(SmtpHost.Text, smtpPort); - smtpClient.Credentials = new NetworkCredential(SmtpUsername.Text, SmtpPassword.Text); - smtpClient.EnableSsl = true; - - MailMessage message = new MailMessage(); - message.From = new MailAddress(SmtpFrom.Text); - - // Parse all addresses provided into MailAddress objects. - if (SmtpTo.Text.Length > 0) - { - MailAddressCollection toAddresses = Functions.FromMailAddressString(SmtpTo.Text); - foreach (MailAddress toAddress in toAddresses) - message.To.Add(toAddress); - } - - if (SmtpCC.Text.Length > 0) - { - MailAddressCollection ccAddresses = Functions.FromMailAddressString(SmtpCC.Text); - foreach (MailAddress ccAddress in ccAddresses) - message.CC.Add(ccAddress); - } - - if (SmtpBcc.Text.Length > 0) - { - MailAddressCollection bccAddresses = Functions.FromMailAddressString(SmtpBcc.Text); - foreach (MailAddress bccAddress in bccAddresses) - message.Bcc.Add(bccAddress); - } - - message.Subject = SmtpSubject.Text; - message.Body = SmtpBody.Text; - - // Process attachments. - string[] attachmentLines = SmtpAttachments.Text.Replace("\r", "").Split('\n'); - foreach (string attachmentLine in attachmentLines) - { - if (attachmentLine.Trim().Length > 0) - message.Attachments.Add(new Attachment(attachmentLine.Trim())); - } - - message.IsBodyHtml = SmtpIsHtml.Checked; - message.SmimeSigningCertificate = signingCertificate; - message.SmimeSigned = SmtpSmimeSign.Checked; - message.SmimeEncryptedEnvelope = SmtpSmimeEncrypt.Checked; - message.SmimeTripleWrapped = SmtpSmimeTripleWrap.Checked; - - await smtpClient.SendAsync(message); - MessageBox.Show("Message successfully sent.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - catch (Exception ex) - { - MessageBox.Show("SMTP send exception:\r\n\r\n" + ex.Message, "SMTP send exception", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - #endregion Event Handlers - - #region Private Methods - /// - /// Return the path where the Test Client's settings should be saved and loaded. - /// - private string GetSettingsFileName() - { - return AppDomain.CurrentDomain.BaseDirectory + "\\OpaqueMail.TestClient.xml"; - } - - /// - /// Retrieve a saved XML setting. - /// - private string GetValue(XPathNavigator navigator, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - return valueNavigator.Value; - - return ""; - } - - /// - /// Load settings for this Test Client from an XML file. - /// - private void LoadSettings() - { - string fileName = GetSettingsFileName(); - if (File.Exists(fileName)) - { - try - { - XPathDocument document = new XPathDocument(fileName); - XPathNavigator navigator = document.CreateNavigator(); - - SetTextBoxValue(navigator, ImapHost, "/Settings/IMAP/Host"); - SetTextBoxValue(navigator, ImapPort, "/Settings/IMAP/Port"); - SetTextBoxValue(navigator, ImapUsername, "/Settings/IMAP/Username"); - SetTextBoxValue(navigator, ImapPassword, "/Settings/IMAP/Password"); - SetCheckBoxValue(navigator, ImapSsl, "/Settings/IMAP/SSL"); - - SetTextBoxValue(navigator, Pop3Host, "/Settings/POP3/Host"); - SetTextBoxValue(navigator, Pop3Port, "/Settings/POP3/Port"); - SetTextBoxValue(navigator, Pop3Username, "/Settings/POP3/Username"); - SetTextBoxValue(navigator, Pop3Password, "/Settings/POP3/Password"); - SetCheckBoxValue(navigator, Pop3Ssl, "/Settings/POP3/SSL"); - - SetTextBoxValue(navigator, SmtpHost, "/Settings/SMTP/Host"); - SetTextBoxValue(navigator, SmtpPort, "/Settings/SMTP/Port"); - SetTextBoxValue(navigator, SmtpUsername, "/Settings/SMTP/Username"); - SetTextBoxValue(navigator, SmtpPassword, "/Settings/SMTP/Password"); - SetCheckBoxValue(navigator, SmtpSsl, "/Settings/SMTP/SSL"); - - SetTextBoxValue(navigator, SmtpFrom, "/Settings/SMTP/From"); - SetTextBoxValue(navigator, SmtpTo, "/Settings/SMTP/To"); - SetTextBoxValue(navigator, SmtpCC, "/Settings/SMTP/CC"); - SetTextBoxValue(navigator, SmtpBcc, "/Settings/SMTP/BCC"); - SetTextBoxValue(navigator, SmtpSubject, "/Settings/SMTP/Subject"); - SetTextBoxValue(navigator, SmtpBody, "/Settings/SMTP/Body"); - SetTextBoxValue(navigator, SmtpAttachments, "/Settings/SMTP/Attachments"); - SetTextBoxValue(navigator, SmtpSmimeSerialNumber, "/Settings/SMTP/SMIMESerialNumber"); - SetCheckBoxValue(navigator, SmtpSmimeSign, "/Settings/SMTP/SMIMESign"); - SetCheckBoxValue(navigator, SmtpSmimeEncrypt, "/Settings/SMTP/SMIMEEncrypt"); - SetCheckBoxValue(navigator, SmtpSmimeTripleWrap, "/Settings/SMTP/SMIMETripleWrap"); - SetCheckBoxValue(navigator, SmtpIsHtml, "/Settings/SMTP/IsHTML"); - - switch (GetValue(navigator, "/Settings/Window/State")) - { - case "Maximized": - WindowState = FormWindowState.Maximized; - break; - case "Minimized": - WindowState = FormWindowState.Minimized; - break; - case "Normal": - WindowState = FormWindowState.Normal; - break; - } - int value; - int.TryParse(GetValue(navigator, "/Settings/Window/Width"), out value); - if (value > 0) - Width = value; - int.TryParse(GetValue(navigator, "/Settings/Window/Height"), out value); - if (value > 0) - Height = value; - int.TryParse(GetValue(navigator, "/Settings/Window/Left"), out value); - if (value > 0) - Left = value; - int.TryParse(GetValue(navigator, "/Settings/Window/Top"), out value); - if (value > 0) - Top = value; - int.TryParse(GetValue(navigator, "/Settings/Window/SelectedTab"), out value); - if (value > 0) - TabsControl.SelectedIndex = value; - } - catch (Exception ex) - { - MessageBox.Show("Error loading settings from \"" + fileName + "\".\r\n\r\nException: " + ex.Message, "Error loading settings", MessageBoxButtons.OK, MessageBoxIcon.Error); - } - } - } - - /// - /// Attempt to retrieve up to 25 IMAP messages and populate the list of messages. - /// - private async Task RefreshImapMessages() - { - if (string.IsNullOrEmpty(ImapHost.Text) || string.IsNullOrEmpty(ImapPort.Text) || string.IsNullOrEmpty(ImapUsername.Text) || string.IsNullOrEmpty(ImapPassword.Text)) - { - MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - - // If we're not currently connected to the IMAP server, connect and authenticate. - if (myImapClient == null) - { - int imapPort = 993; - int.TryParse(ImapPort.Text, out imapPort); - - myImapClient = new ImapClient(ImapHost.Text, imapPort, ImapUsername.Text, ImapPassword.Text, ImapSsl.Checked); - } - - // If our connection has timed out or been closed, reconnect. - if (!myImapClient.IsConnected || !myImapClient.IsAuthenticated) - { - myImapClient.Connect(); - myImapClient.Authenticate(); - - // Ensure we connected successfully. - if (!myImapClient.IsConnected || !myImapClient.IsAuthenticated) - { - MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - } - - // Alert if the connection is throttled. - myImapClient.ImapClientThrottleEvent += myImapClient_ImapClientThrottleEvent; - - // Retrieve the headers of up to 25 messages and remember their mailbox/UID pairs for later opening. - await myImapClient.SelectMailboxAsync("INBOX"); - List messages = await myImapClient.GetMessagesAsync("INBOX", 25, 1, true, true, false); - ImapMessageIDs = new Tuple[messages.Count]; - - // Repopulate the message list with the subjects of messages retrieved. - ImapMessageList.Items.Clear(); - for (int i = 0; i < messages.Count; i++) - { - ImapMessageList.Items.Add(messages[i].Subject); - ImapMessageIDs[i] = new Tuple(messages[i].Mailbox, messages[i].ImapUid); - } - - // Start checking for messages on a regular basis. - myImapClient.ImapClientNewMessageEvent += myImapClient_ImapClientNewMessageEvent; - myImapClient.ImapClientMessageExpungeEvent += myImapClient_ImapClientMessageExpungeEvent; - await myImapClient.IdleStartAsync(); - - // Reset the preview viewport. - ImapHeaders.BackColor = Color.White; - ImapHeaders.Text = ""; - ImapWebPreview.DocumentText = "Please select a message from the left-hand panel."; - ImapWebPreviewPanel.Refresh(); - } - - /// - /// Notify the user if the connection is throttled. - /// - void myImapClient_ImapClientThrottleEvent(object sender) - { - MessageBox.Show("Connection throttled by server.", "Connection throttled.", MessageBoxButtons.OK, MessageBoxIcon.Warning); - } - - /// - /// Notify the user of messages expunged while IDLE. - /// - void myImapClient_ImapClientMessageExpungeEvent(object sender, ImapClientEventArgs e) - { - MessageBox.Show("Message expunged.\r\n\r\nMailbox: " + e.MailboxName + "\r\nMessage ID: " + e.MessageId, "Message expunged.", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - - /// - /// Notify the user of new messages received while IDLE. - /// - void myImapClient_ImapClientNewMessageEvent(object sender, ImapClientEventArgs e) - { - MessageBox.Show("New message received.\r\n\r\nMailbox: " + e.MailboxName + "\r\nMessage ID: " + e.MessageId, "New message received", MessageBoxButtons.OK, MessageBoxIcon.Information); - } - - /// - /// Attempt to retrieve up to 25 POP3 messages and populate the list of messages. - /// - private async Task RefreshPop3Messages() - { - if (string.IsNullOrEmpty(Pop3Host.Text) || string.IsNullOrEmpty(Pop3Port.Text) || string.IsNullOrEmpty(Pop3Username.Text) || string.IsNullOrEmpty(Pop3Password.Text)) - { - MessageBox.Show("Unable to connect to the POP3 server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - - // If we're not currently connected to the POP3 server, connect and authenticate. - if (myPop3Client == null) - { - int pop3Port = 993; - int.TryParse(Pop3Port.Text, out pop3Port); - - myPop3Client = new Pop3Client(Pop3Host.Text, pop3Port, Pop3Username.Text, Pop3Password.Text, Pop3Ssl.Checked); - } - - // If our connection has timed out or been closed, reconnect. - if (!myPop3Client.IsConnected || !myPop3Client.IsAuthenticated) - { - myPop3Client.Connect(); - myPop3Client.Authenticate(); - - // Ensure we connected successfully. - if (!myPop3Client.IsConnected || !myPop3Client.IsAuthenticated) - { - MessageBox.Show("Unable to connect to the POP3 server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - } - - // Retrieve the headers of up to 25 messages and remember their mailbox/UID pairs for later opening. - List messages = await myPop3Client.GetMessagesAsync(25, 1, false, true); - Pop3MessageIDs = new List(); - - // Repopulate the message list with the subjects of messages retrieved. - Pop3MessageList.Items.Clear(); - for (int i = 0; i < messages.Count; i++) - { - Pop3MessageList.Items.Add(messages[i].Subject); - Pop3MessageIDs.Add(messages[i].Index); - } - - // Reset the preview viewport. - Pop3Headers.BackColor = Color.White; - Pop3Headers.Text = ""; - Pop3WebPreview.DocumentText = "Please select a message from the left-hand panel."; - Pop3WebPreviewPanel.Refresh(); - } - - /// - /// Render the selected message's body and selected headers to the specified controls. - /// - /// The message to display. - /// The textbox to populate with selected headers. - /// The web browser in which to render the body. - /// The panel containing the web browser. - private void RenderMessage(ReadOnlyMailMessage message, TextBox headerTextBox, WebBrowser bodyWebBrowser, Panel bodyWebBrowserPanel) - { - StringBuilder headersText = new StringBuilder(Constants.SMALLSBSIZE); - - // Output selected headers. - headersText.Append("Date: " + message.Date.ToString() + "\r\n"); - headersText.Append("From: "); - if (message.From != null) - headersText.Append(Functions.ToMailAddressString(new MailAddressCollection() { message.From })); - else if (message.Sender != null) - headersText.Append(Functions.ToMailAddressString(new MailAddressCollection() { message.Sender })); - headersText.Append("\r\n"); - if (message.To.Count > 0) - headersText.Append("To: " + Functions.ToMailAddressString(message.To) + "\r\n"); - if (message.CC.Count > 0) - headersText.Append("CC: " + Functions.ToMailAddressString(message.CC) + "\r\n"); - headersText.Append("Subject: " + message.Subject + "\r\n"); - headersText.Append("S/MIME Signed: " + message.SmimeSigned.ToString() + "\r\n"); - headersText.Append("S/MIME Envelope Encrypted: " + message.SmimeEncryptedEnvelope.ToString() + "\r\n"); - headersText.Append("S/MIME Triple Wrapped: " + message.SmimeTripleWrapped.ToString() + "\r\n"); - headersText.Append("Size: " + string.Format("{0:n0}", message.Size)); - if (message.RawFlags.Count > 0) - { - headersText.Append("\r\nFlags: "); - bool firstFlag = true; - foreach (string flag in message.RawFlags) - { - if (!firstFlag) - headersText.Append("; "); - headersText.Append(flag); - firstFlag = false; - } - } - if (message.Attachments.Count > 0) - { - headersText.Append("\r\nAttachments: "); - for (int i = 0; i < message.Attachments.Count; i++) - { - if (i > 0) - headersText.Append("; "); - headersText.Append(message.Attachments[i].Name + " (" + message.Attachments[i].ContentType.MediaType + ")"); - } - } - - headerTextBox.Text = headersText.ToString(); - - if (message.SmimeTripleWrapped) - headerTextBox.BackColor = Color.LightGreen; - else if (message.SmimeEncryptedEnvelope) - { - if (message.SmimeSigned) - headerTextBox.BackColor = Color.LightGreen; - else - headerTextBox.BackColor = Color.GreenYellow; - } - else if (message.SmimeSigned) - headerTextBox.BackColor = Color.LightBlue; - else - headerTextBox.BackColor = Color.White; - - if (message.IsBodyHtml) - bodyWebBrowser.DocumentText = Functions.EmbedAttachments(Functions.RemoveScriptTags(message.Body), message.Attachments); - else - bodyWebBrowser.DocumentText = Functions.EmbedAttachments(Functions.ConvertPlainTextToHTML(Functions.RemoveScriptTags(message.Body)), null); - bodyWebBrowserPanel.Refresh(); - } - - /// - /// Resize and reposition controls in response to a window resize. - /// - private void ResizeForm() - { - TabsControl.Width = this.Width - 22; - TabsControl.Height = this.Width - 68; - - LoadSettingsButton.Top = this.Height - 63; - SaveSettingsButton.Top = this.Height - 63; - SaveSettingsButton.Left = this.Width - 160; - - ImapSettingsGroup.Width = this.Width - 42; - Pop3SettingsGroup.Width = this.Width - 42; - SmtpSettingsGroup.Width = this.Width - 42; - - ImapTestGroup.Width = this.Width - 42; - ImapMessageGroup.Height = this.Height - 156; - ImapMessageList.Height = this.Height - 209; - ImapDeleteMessageButton.Top = this.Height - 188; - ImapHeaders.Width = this.Width - 246; - ImapPreviewGroup.Width = this.Width - 232; - ImapPreviewGroup.Height = this.Height - 156; - ImapWebPreview.Width = this.Width - 246; - ImapWebPreview.Height = this.Width - 326; - ImapWebPreviewPanel.Width = this.Width - 246; - ImapWebPreviewPanel.Height = this.Height - 331; - ImapWebPreviewPanel.Refresh(); - - Pop3TestGroup.Width = this.Width - 42; - Pop3MessageGroup.Height = this.Height - 156; - Pop3MessageList.Height = this.Height - 209; - Pop3DeleteMessageButton.Top = this.Height - 188; - Pop3Headers.Width = this.Width - 246; - Pop3PreviewGroup.Width = this.Width - 232; - Pop3PreviewGroup.Height = this.Height - 156; - Pop3WebPreview.Width = this.Width - 246; - Pop3WebPreview.Height = this.Width - 326; - Pop3WebPreviewPanel.Width = this.Width - 246; - Pop3WebPreviewPanel.Height = this.Height - 331; - Pop3WebPreviewPanel.Refresh(); - - SmtpTestGroup.Width = this.Width - 42; - SmtpTestSettingsGroup.Width = this.Width - 42; - SmtpTestSettingsGroup.Height = this.Height - 153; - SmtpFrom.Width = this.Width - 94; - SmtpTo.Width = this.Width - 94; - SmtpCC.Width = this.Width - 94; - SmtpBcc.Width = this.Width - 94; - SmtpSubject.Width = this.Width - 94; - SmtpBody.Width = this.Width - 94; - SmtpBody.Height = this.Height - 408; - SmtpAttachmentsLabel.Top = this.Height - 248; - SmtpAttachments.Top = this.Height - 248; - SmtpAttachments.Width = this.Width - 129; - SmtpSmimeLabel.Top = this.Height - 199; - SmtpSmimeSerialNumber.Top = this.Height - 199; - SmtpSmimeSerialNumber.Width = this.Width - 129; - SmtpSmimeSign.Top = this.Height - 176; - SmtpSmimeEncrypt.Top = this.Height - 176; - SmtpSmimeTripleWrap.Top = this.Height - 176; - SmtpIsHtml.Top = this.Height - 176; - } - - /// - /// Save all current Test Client settings to an XML file. - /// - private void SaveSettings() - { - DialogResult result = MessageBox.Show("Are you sure? Settings (including passwords) will be saved as plaintext into \"OpaqueMail.TestClient.xml\".", "Confirm Save", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); - if (result == DialogResult.OK) - { - XmlWriterSettings streamWriterSettings = new XmlWriterSettings(); - streamWriterSettings.Indent = true; - streamWriterSettings.IndentChars = " "; - streamWriterSettings.NewLineChars = "\r\n"; - streamWriterSettings.NewLineHandling = NewLineHandling.Replace; - - using (XmlWriter streamWriter = XmlWriter.Create(GetSettingsFileName(), streamWriterSettings)) - { - streamWriter.WriteStartDocument(); - streamWriter.WriteStartElement("Settings"); - - streamWriter.WriteStartElement("IMAP"); - - streamWriter.WriteElementString("Host", ImapHost.Text.ToString()); - streamWriter.WriteElementString("Port", ImapPort.Text.ToString()); - streamWriter.WriteElementString("Username", ImapUsername.Text.ToString()); - streamWriter.WriteElementString("Password", ImapPassword.Text.ToString()); - streamWriter.WriteElementString("SSL", ImapSsl.Checked.ToString()); - - streamWriter.WriteEndElement(); - - streamWriter.WriteStartElement("POP3"); - - streamWriter.WriteElementString("Host", Pop3Host.Text.ToString()); - streamWriter.WriteElementString("Port", Pop3Port.Text.ToString()); - streamWriter.WriteElementString("Username", Pop3Username.Text.ToString()); - streamWriter.WriteElementString("Password", Pop3Password.Text.ToString()); - streamWriter.WriteElementString("SSL", Pop3Ssl.Checked.ToString()); - - streamWriter.WriteEndElement(); - - streamWriter.WriteStartElement("SMTP"); - - streamWriter.WriteElementString("Host", SmtpHost.Text.ToString()); - streamWriter.WriteElementString("Port", SmtpPort.Text.ToString()); - streamWriter.WriteElementString("Username", SmtpUsername.Text.ToString()); - streamWriter.WriteElementString("Password", SmtpPassword.Text.ToString()); - streamWriter.WriteElementString("SSL", SmtpSsl.Checked.ToString()); - - streamWriter.WriteElementString("From", SmtpFrom.Text.ToString()); - streamWriter.WriteElementString("To", SmtpTo.Text.ToString()); - streamWriter.WriteElementString("CC", SmtpCC.Text.ToString()); - streamWriter.WriteElementString("BCC", SmtpBcc.Text.ToString()); - streamWriter.WriteElementString("Body", SmtpBody.Text.ToString()); - streamWriter.WriteElementString("Subject", SmtpSubject.Text.ToString()); - streamWriter.WriteElementString("Attachments", SmtpAttachments.Text.ToString()); - streamWriter.WriteElementString("SMIMESign", SmtpSmimeSign.Checked.ToString()); - streamWriter.WriteElementString("SMIMESerialNumber", SmtpSmimeSerialNumber.Text.ToString()); - streamWriter.WriteElementString("SMIMEEncrypt", SmtpSmimeEncrypt.Checked.ToString()); - streamWriter.WriteElementString("SMIMETripleWrap", SmtpSmimeTripleWrap.Checked.ToString()); - streamWriter.WriteElementString("IsHTML", SmtpIsHtml.Checked.ToString()); - - streamWriter.WriteEndElement(); - - streamWriter.WriteStartElement("Window"); - streamWriter.WriteElementString("State", WindowState.ToString()); - streamWriter.WriteElementString("Width", Width.ToString()); - streamWriter.WriteElementString("Height", Height.ToString()); - streamWriter.WriteElementString("Left", Left.ToString()); - streamWriter.WriteElementString("Top", Top.ToString()); - streamWriter.WriteElementString("SelectedTab", TabsControl.SelectedIndex.ToString()); - - streamWriter.WriteEndElement(); - - streamWriter.WriteEndElement(); - streamWriter.WriteEndDocument(); - } - } - } - - /// - /// Set a checkbox's value based on its saved XML setting. - /// - /// An XPathNavigator within the current XmlDocument. - /// The checkbox whose value should be set. - /// The XPath expression to evaluate. - private void SetCheckBoxValue(XPathNavigator navigator, CheckBox checkbox, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - { - bool valueChecked = true; - if (bool.TryParse(valueNavigator.Value, out valueChecked)) - checkbox.Checked = valueChecked; - } - } - - /// - /// Set a textbox's value based on its saved XML setting. - /// - /// An XPathNavigator within the current XmlDocument. - /// The textbox whose value should be set. - /// The XPath expression to evaluate. - private void SetTextBoxValue(XPathNavigator navigator, TextBox textbox, string xpathExpression) - { - XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); - if (valueNavigator != null) - textbox.Text = valueNavigator.Value; - } - #endregion Private Methods - } -} +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Mail; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Xml; +using System.Xml.XPath; + +namespace OpaqueMail.TestClient +{ + public partial class Form1 : Form + { + #region Private Members + /// Collection of mailbox/UID pairs for messages loaded over IMAP. + private Tuple[] ImapMessageIDs; + /// List of message IDs for messages loaded over POP3. + private List Pop3MessageIDs; + /// An OpaqueMail.ImapClient to be reused across operations. + ImapClient myImapClient; + /// An OpaqueMail.Pop3Client to be reused across operations. + Pop3Client myPop3Client; + /// A file dialog box to be used when loading or saving *.eml files. + OpenFileDialog FileDialog = new OpenFileDialog(); + #endregion Private Members + + #region Event Handlers + /// + /// Test Client constructor. + /// + public Form1() + { + InitializeComponent(); + } + + /// + /// Test Client load event handler. + /// + private void Form1_Load(object sender, EventArgs e) + { + // Attempt to initialize settings based on values from a previous session. + LoadSettings(); + // Resize the form in case loaded settings changed the window size. + ResizeForm(); + + // Handle the resize event. + this.Resize += Form1_Resize; + this.FormClosing += Form1_FormClosing; + ImapSearchText.GotFocus += ImapSearchText_GotFocus; + ImapSearchText.KeyDown += ImapSearchText_KeyDown; + ImapSearchText.LostFocus += ImapSearchText_LostFocus; + } + + /// + /// Clean up when the Test Client is unloading. + /// + void Form1_FormClosing(object sender, FormClosingEventArgs e) + { + if (myImapClient != null) + myImapClient.Dispose(); + if (myPop3Client != null) + myPop3Client.Dispose(); + } + + /// + /// Handle the window resized event. + /// + void Form1_Resize(object sender, System.EventArgs e) + { + ResizeForm(); + } + + /// + /// Append a sample message to the Inbox. + /// + private void ImapAppendMessageButton_Click(object sender, EventArgs e) + { + // If we're not currently connected to the IMAP server, connect and authenticate. + if (myImapClient == null) + { + int imapPort = 993; + int.TryParse(ImapPort.Text, out imapPort); + + myImapClient = new ImapClient(ImapHost.Text, imapPort, ImapUsername.Text, ImapPassword.Text, ImapSsl.Checked); + } + + // If our connection has timed out or been closed, reconnect. + if (!myImapClient.IsConnected) + { + myImapClient.Connect(); + myImapClient.Authenticate(); + + // Ensure we connected successfully. + if (!myImapClient.IsConnected) + { + MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + myImapClient.AppendMessage("INBOX", +@"Date: " + DateTime.Now.ToString("dd-MM-yyyy hh:mm:ss zzzz") + @" +From: Test User +To: Me <" + ImapUsername.Text + @"> +Message-Id: <" + Guid.NewGuid().ToString().Replace("{", "").Replace("}", "") + "@" + ImapHost.Text + @"> +Subject: Test Append Message +MIME-Version: 1.0 +Content-Type: Text/Plain; Charset=US-ASCII + +This is a test of the APPEND command.", new string[] { @"\Seen" }, DateTime.Now); + } + + /// + /// Copy server settings from POP3 to IMAP. + /// + private void ImapCopyPop3Button_Click(object sender, EventArgs e) + { + ImapHost.Text = Pop3Host.Text; + ImapUsername.Text = Pop3Username.Text; + ImapPassword.Text = Pop3Password.Text; + ImapSsl.Checked = Pop3Ssl.Checked; + } + + /// + /// Copy server settings from SMTP to IMAP. + /// + private void ImapCopySmtpButton_Click(object sender, EventArgs e) + { + ImapHost.Text = SmtpHost.Text; + ImapUsername.Text = SmtpUsername.Text; + ImapPassword.Text = SmtpPassword.Text; + ImapSsl.Checked = SmtpSsl.Checked; + } + + /// + /// Delete the selected IMAP message. + /// + private async void ImapDeleteMessageButton_Click(object sender, EventArgs e) + { + await myImapClient.DeleteMessageUidAsync(ImapMessageIDs[ImapMessageList.SelectedIndex].Item1, ImapMessageIDs[ImapMessageList.SelectedIndex].Item2); + ImapDeleteMessageButton.Enabled = false; + await RefreshImapMessages(); + } + + /// + /// Retrieve the overall quota and the quota for the users Inbox. + /// + private async void ImapGetQuotaButton_Click(object sender, EventArgs e) + { + // If we're not currently connected to the IMAP server, connect and authenticate. + if (myImapClient == null) + { + int imapPort = 993; + int.TryParse(ImapPort.Text, out imapPort); + + myImapClient = new ImapClient(ImapHost.Text, imapPort, ImapUsername.Text, ImapPassword.Text, ImapSsl.Checked); + } + + // If our connection has timed out or been closed, reconnect. + if (!myImapClient.IsConnected) + { + myImapClient.Connect(); + myImapClient.Authenticate(); + + // Ensure we connected successfully. + if (!myImapClient.IsConnected) + { + MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + StringBuilder messageBuilder = new StringBuilder(); + + QuotaUsage totalUsage = await myImapClient.GetQuotaAsync(""); + QuotaUsage inboxUsage = await myImapClient.GetQuotaRootAsync("INBOX"); + + messageBuilder.Append("Overall quota information:\r\n\r\nUsed: " + totalUsage.Usage + "\r\nTotal: " + totalUsage.QuotaMaximum + "\r\n\r\nINBOX quota information:\r\n\r\nUsed: " + inboxUsage.Usage + "\r\nTotal: " + inboxUsage.QuotaMaximum); + + string[] mailboxes = await myImapClient.ListMailboxNamesAsync(true); + messageBuilder.Append("\r\n\r\nFolders (" + mailboxes.Length.ToString() + " Total):\r\n"); + + foreach (string mailbox in mailboxes) + messageBuilder.Append("\r\n" + mailbox); + + MessageBox.Show(messageBuilder.ToString(), "Quota and Mailbox Information", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + /// + /// Load and preview an *.eml file. + /// + private void ImapLoadFileButton_Click(object sender, EventArgs e) + { + FileDialog.CheckFileExists = true; + FileDialog.Filter = "Email messages (*.eml)|*.eml|All files|*.*"; + FileDialog.Multiselect = false; + + if (FileDialog.ShowDialog() == DialogResult.OK) + { + ReadOnlyMailMessage message = ReadOnlyMailMessage.LoadFile(FileDialog.FileName); + RenderMessage(message, ImapHeaders, ImapWebPreview, ImapWebPreviewPanel); + } + } + + /// + /// When a message has been selected, attempt to load its preview. + /// + private async void ImapMessageList_SelectedIndexChanged(object sender, EventArgs e) + { + ImapDeleteMessageButton.Enabled = ImapMessageList.SelectedIndex > -1; + if (ImapMessageList.SelectedIndex > -1) + { + if (myImapClient != null && ImapMessageIDs != null) + { + if (ImapMessageList.SelectedIndex < ImapMessageIDs.Length) + { + if (ImapMessageIDs[ImapMessageList.SelectedIndex].Item2 > 0) + { + // If our connection has timed out or been closed, reconnect. + if (!myImapClient.IsConnected) + { + myImapClient.Connect(); + myImapClient.Authenticate(); + } + + if (myImapClient.IsConnected) + { + if (myImapClient.IsIdle) + await myImapClient.IdleStopAsync(); + + // Retrieve the selected message. + ReadOnlyMailMessage message = myImapClient.GetMessageUid(ImapMessageIDs[ImapMessageList.SelectedIndex].Item1, ImapMessageIDs[ImapMessageList.SelectedIndex].Item2); + + if (message != null) + { + // Populate the IMAP viewport with this message's headers and body. + RenderMessage(message, ImapHeaders, ImapWebPreview, ImapWebPreviewPanel); + } + else + { + // If the message was deleted, null will be returned. + ImapHeaders.BackColor = Color.White; + ImapHeaders.Text = ""; + ImapWebPreview.Document.Write("Message not found."); + ImapWebPreviewPanel.Refresh(); + } + + await myImapClient.IdleStartAsync(); + } + } + } + } + } + else + { + // If no message was retrieved, clear the viewport. + ImapWebPreview.DocumentText = ""; + ImapWebPreviewPanel.Refresh(); + } + } + + /// + /// Attempt to retrieve up to 25 IMAP messages and populate the list of messages. + /// + private async void ImapRetrieveMessagesButton_Click(object sender, EventArgs e) + { + ImapRetrieveMessagesButton.Enabled = false; + ImapMessageList.Enabled = false; + await RefreshImapMessages(); + ImapMessageList.Enabled = true; + ImapRetrieveMessagesButton.Enabled = true; + } + + /// + /// Clear the default search message when clicked. + /// + private void ImapSearchText_GotFocus(object sender, System.EventArgs e) + { + if (ImapSearchText.Text == "Search...") + ImapSearchText.Text = ""; + } + + /// + /// Process a search when clicked. + /// + private async void ImapSearchText_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e) + { + if (e.KeyCode == Keys.Enter) + { + // If we're not currently connected to the IMAP server, connect and authenticate. + if (myImapClient == null) + { + int imapPort = 993; + int.TryParse(ImapPort.Text, out imapPort); + + myImapClient = new ImapClient(ImapHost.Text, imapPort, ImapUsername.Text, ImapPassword.Text, ImapSsl.Checked); + } + + // If our connection has timed out or been closed, reconnect. + if (!myImapClient.IsConnected) + { + myImapClient.Connect(); + myImapClient.Authenticate(); + + // Ensure we connected successfully. + if (!myImapClient.IsConnected) + { + MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + if (myImapClient != null) + { + if (myImapClient.IsConnected) + { + await myImapClient.SelectMailboxAsync("INBOX"); + List messages = await myImapClient.SearchAsync("TEXT \"" + ImapSearchText.Text + "\""); + + ImapMessageIDs = new Tuple[messages.Count]; + + // Repopulate the message list with the subjects of messages retrieved. + ImapMessageList.Items.Clear(); + for (int i = 0; i < messages.Count; i++) + { + ImapMessageList.Items.Add(messages[i].Subject); + ImapMessageIDs[i] = new Tuple(messages[i].Mailbox, messages[i].ImapUid); + } + + // Reset the preview viewport. + ImapHeaders.BackColor = Color.White; + ImapHeaders.Text = ""; + ImapWebPreview.DocumentText = "Please select a message from the left-hand panel."; + ImapWebPreviewPanel.Refresh(); + } + } + } + } + + /// + /// Add the default search message if the query is blank. + /// + private void ImapSearchText_LostFocus(object sender, System.EventArgs e) + { + if (string.IsNullOrEmpty(ImapSearchText.Text)) + ImapSearchText.Text = "Search..."; + } + + /// + /// Load settings for this Test Client from an XML file. + /// + private void LoadSettingsButton_Click(object sender, EventArgs e) + { + MailAddressCollection mac = Functions.FromMailAddressString("'Toni Johnson' "); + + DialogResult result = MessageBox.Show("Are you sure? All settings will be overwritten by the contents of \"OpaqueMail.TestClient.xml\".", "Confirm Load", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); + if (result == DialogResult.OK) + LoadSettings(); + } + + /// + /// Copy server settings from IMAP to POP3. + /// + private void Pop3CopyImapButton_Click(object sender, EventArgs e) + { + Pop3Host.Text = ImapHost.Text; + Pop3Username.Text = ImapUsername.Text; + Pop3Password.Text = ImapPassword.Text; + Pop3Ssl.Checked = ImapSsl.Checked; + } + + /// + /// Copy server settings from SMTP to POP3. + /// + private void Pop3CopySmtpButton_Click(object sender, EventArgs e) + { + Pop3Host.Text = SmtpHost.Text; + Pop3Username.Text = SmtpUsername.Text; + Pop3Password.Text = SmtpPassword.Text; + Pop3Ssl.Checked = SmtpSsl.Checked; + } + + /// + /// Delete the selected IMAP message. + /// + private async void Pop3DeleteMessageButton_Click(object sender, EventArgs e) + { + await myPop3Client.DeleteMessageAsync(Pop3MessageIDs[Pop3MessageList.SelectedIndex]); + Pop3DeleteMessageButton.Enabled = false; + await RefreshPop3Messages(); + } + + /// + /// Attempt to retrieve up to 25 POP3 messages and populate the list of messages. + /// + private async void Pop3RetrieveMessageButton_Click(object sender, EventArgs e) + { + Pop3RetrieveMessageButton.Enabled = false; + Pop3MessageList.Enabled = false; + await RefreshPop3Messages(); + Pop3MessageList.Enabled = true; + Pop3RetrieveMessageButton.Enabled = true; + } + + /// + /// When a message has been selected, attempt to load its preview. + /// + private void Pop3MessageList_SelectedIndexChanged(object sender, EventArgs e) + { + Pop3DeleteMessageButton.Enabled = Pop3MessageList.SelectedIndex > -1; + if (Pop3MessageList.SelectedIndex > -1) + { + if (myPop3Client != null && Pop3MessageIDs != null) + { + if (Pop3MessageList.SelectedIndex < Pop3MessageIDs.Count) + { + if (Pop3MessageIDs[Pop3MessageList.SelectedIndex] > -1) + { + // If our connection has timed out or been closed, reconnect. + if (!myPop3Client.IsConnected) + { + myPop3Client.Connect(); + myPop3Client.Authenticate(); + } + + if (myPop3Client.IsConnected) + { + // Retrieve the selected message. + ReadOnlyMailMessage message = myPop3Client.GetMessage(Pop3MessageIDs[Pop3MessageList.SelectedIndex]); + + if (message != null) + { + // Populate the IMAP viewport with this message's headers and body. + RenderMessage(message, Pop3Headers, Pop3WebPreview, Pop3WebPreviewPanel); + } + else + { + // If the message was deleted, null will be returned. + Pop3Headers.BackColor = Color.White; + Pop3Headers.Text = ""; + Pop3WebPreview.Document.Write("Message not found."); + Pop3WebPreviewPanel.Refresh(); + } + } + } + } + } + } + else + { + // If no message was retrieved, clear the viewport. + Pop3WebPreview.DocumentText = ""; + Pop3WebPreviewPanel.Refresh(); + } + } + + /// + /// Save all current Test Client settings to an XML file. + /// + private void SaveSettingsButton_Click(object sender, EventArgs e) + { + SaveSettings(); + } + + /// + /// Copy server settings from IMAP to SMTP. + /// + private void SmtpCopyImapButton_Click(object sender, EventArgs e) + { + SmtpHost.Text = ImapHost.Text; + SmtpUsername.Text = ImapUsername.Text; + SmtpPassword.Text = ImapPassword.Text; + SmtpSsl.Checked = ImapSsl.Checked; + } + + /// + /// Copy server settings from POP3 to SMTP. + /// + private void SmtpCopyPop3Button_Click(object sender, EventArgs e) + { + SmtpHost.Text = Pop3Host.Text; + SmtpUsername.Text = Pop3Username.Text; + SmtpPassword.Text = Pop3Password.Text; + SmtpSsl.Checked = Pop3Ssl.Checked; + } + + /// + /// Send an e-mail using the SMTP settings specified. + /// + private async void SmtpSendButton_Click(object sender, EventArgs e) + { + X509Certificate2 signingCertificate = null; + if (SmtpSmimeSign.Checked || SmtpSmimeTripleWrap.Checked) + { + // If S/MIME signing the message, attempt to look up a certificate from the Windows certificate store matching the serial number specified. + if (SmtpSmimeSerialNumber.Text.Length < 1) + { + MessageBox.Show("SMTP send exception:\r\n\r\nA signing certificate must be passed prior to signing.", "SMTP send exception", MessageBoxButtons.OK, MessageBoxIcon.Error); + SmtpSmimeSerialNumber.Focus(); + return; + } + + // Try first looking the certificate up by its serial number, falling back to finding it by its subject name. + signingCertificate = CertHelper.GetCertificateBySerialNumber(StoreLocation.CurrentUser, SmtpSmimeSerialNumber.Text); + if (signingCertificate == null) + signingCertificate = CertHelper.GetCertificateBySerialNumber(StoreLocation.LocalMachine, SmtpSmimeSerialNumber.Text); + if (signingCertificate == null) + signingCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.CurrentUser, SmtpSmimeSerialNumber.Text); + if (signingCertificate == null) + signingCertificate = CertHelper.GetCertificateBySubjectName(StoreLocation.LocalMachine, SmtpSmimeSerialNumber.Text); + + if (signingCertificate == null) + { + MessageBox.Show("Certificate with serial # \"" + SmtpSmimeSerialNumber.Text + "\" not found."); + SmtpSmimeSerialNumber.Focus(); + return; + } + } + + try + { + int smtpPort = 25; + int.TryParse(SmtpPort.Text, out smtpPort); + + SmtpClient smtpClient = new SmtpClient(SmtpHost.Text, smtpPort); + smtpClient.Credentials = new NetworkCredential(SmtpUsername.Text, SmtpPassword.Text); + smtpClient.EnableSsl = true; + + MailMessage message = new MailMessage(); + message.From = new MailAddress(SmtpFrom.Text); + + // Parse all addresses provided into MailAddress objects. + if (SmtpTo.Text.Length > 0) + { + MailAddressCollection toAddresses = Functions.FromMailAddressString(SmtpTo.Text); + foreach (MailAddress toAddress in toAddresses) + message.To.Add(toAddress); + } + + if (SmtpCC.Text.Length > 0) + { + MailAddressCollection ccAddresses = Functions.FromMailAddressString(SmtpCC.Text); + foreach (MailAddress ccAddress in ccAddresses) + message.CC.Add(ccAddress); + } + + if (SmtpBcc.Text.Length > 0) + { + MailAddressCollection bccAddresses = Functions.FromMailAddressString(SmtpBcc.Text); + foreach (MailAddress bccAddress in bccAddresses) + message.Bcc.Add(bccAddress); + } + + message.Subject = SmtpSubject.Text; + message.Body = SmtpBody.Text; + + // Process attachments. + string[] attachmentLines = SmtpAttachments.Text.Replace("\r", "").Split('\n'); + foreach (string attachmentLine in attachmentLines) + { + if (attachmentLine.Trim().Length > 0) + message.Attachments.Add(new Attachment(attachmentLine.Trim())); + } + + message.IsBodyHtml = SmtpIsHtml.Checked; + message.SmimeSigningCertificate = signingCertificate; + message.SmimeSigned = SmtpSmimeSign.Checked; + message.SmimeEncryptedEnvelope = SmtpSmimeEncrypt.Checked; + message.SmimeTripleWrapped = SmtpSmimeTripleWrap.Checked; + + await smtpClient.SendAsync(message); + MessageBox.Show("Message successfully sent.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + catch (Exception ex) + { + MessageBox.Show("SMTP send exception:\r\n\r\n" + ex.Message, "SMTP send exception", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + #endregion Event Handlers + + #region Private Methods + /// + /// Return the path where the Test Client's settings should be saved and loaded. + /// + private string GetSettingsFileName() + { + return AppDomain.CurrentDomain.BaseDirectory + "\\OpaqueMail.TestClient.xml"; + } + + /// + /// Retrieve a saved XML setting. + /// + private string GetValue(XPathNavigator navigator, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + return valueNavigator.Value; + + return ""; + } + + /// + /// Load settings for this Test Client from an XML file. + /// + private void LoadSettings() + { + string fileName = GetSettingsFileName(); + if (File.Exists(fileName)) + { + try + { + XPathDocument document = new XPathDocument(fileName); + XPathNavigator navigator = document.CreateNavigator(); + + SetTextBoxValue(navigator, ImapHost, "/Settings/IMAP/Host"); + SetTextBoxValue(navigator, ImapPort, "/Settings/IMAP/Port"); + SetTextBoxValue(navigator, ImapUsername, "/Settings/IMAP/Username"); + SetTextBoxValue(navigator, ImapPassword, "/Settings/IMAP/Password"); + SetCheckBoxValue(navigator, ImapSsl, "/Settings/IMAP/SSL"); + + SetTextBoxValue(navigator, Pop3Host, "/Settings/POP3/Host"); + SetTextBoxValue(navigator, Pop3Port, "/Settings/POP3/Port"); + SetTextBoxValue(navigator, Pop3Username, "/Settings/POP3/Username"); + SetTextBoxValue(navigator, Pop3Password, "/Settings/POP3/Password"); + SetCheckBoxValue(navigator, Pop3Ssl, "/Settings/POP3/SSL"); + + SetTextBoxValue(navigator, SmtpHost, "/Settings/SMTP/Host"); + SetTextBoxValue(navigator, SmtpPort, "/Settings/SMTP/Port"); + SetTextBoxValue(navigator, SmtpUsername, "/Settings/SMTP/Username"); + SetTextBoxValue(navigator, SmtpPassword, "/Settings/SMTP/Password"); + SetCheckBoxValue(navigator, SmtpSsl, "/Settings/SMTP/SSL"); + + SetTextBoxValue(navigator, SmtpFrom, "/Settings/SMTP/From"); + SetTextBoxValue(navigator, SmtpTo, "/Settings/SMTP/To"); + SetTextBoxValue(navigator, SmtpCC, "/Settings/SMTP/CC"); + SetTextBoxValue(navigator, SmtpBcc, "/Settings/SMTP/BCC"); + SetTextBoxValue(navigator, SmtpSubject, "/Settings/SMTP/Subject"); + SetTextBoxValue(navigator, SmtpBody, "/Settings/SMTP/Body"); + SetTextBoxValue(navigator, SmtpAttachments, "/Settings/SMTP/Attachments"); + SetTextBoxValue(navigator, SmtpSmimeSerialNumber, "/Settings/SMTP/SMIMESerialNumber"); + SetCheckBoxValue(navigator, SmtpSmimeSign, "/Settings/SMTP/SMIMESign"); + SetCheckBoxValue(navigator, SmtpSmimeEncrypt, "/Settings/SMTP/SMIMEEncrypt"); + SetCheckBoxValue(navigator, SmtpSmimeTripleWrap, "/Settings/SMTP/SMIMETripleWrap"); + SetCheckBoxValue(navigator, SmtpIsHtml, "/Settings/SMTP/IsHTML"); + + switch (GetValue(navigator, "/Settings/Window/State")) + { + case "Maximized": + WindowState = FormWindowState.Maximized; + break; + case "Minimized": + WindowState = FormWindowState.Minimized; + break; + case "Normal": + WindowState = FormWindowState.Normal; + break; + } + int value; + int.TryParse(GetValue(navigator, "/Settings/Window/Width"), out value); + if (value > 0) + Width = value; + int.TryParse(GetValue(navigator, "/Settings/Window/Height"), out value); + if (value > 0) + Height = value; + int.TryParse(GetValue(navigator, "/Settings/Window/Left"), out value); + if (value > 0) + Left = value; + int.TryParse(GetValue(navigator, "/Settings/Window/Top"), out value); + if (value > 0) + Top = value; + int.TryParse(GetValue(navigator, "/Settings/Window/SelectedTab"), out value); + if (value > 0) + TabsControl.SelectedIndex = value; + } + catch (Exception ex) + { + MessageBox.Show("Error loading settings from \"" + fileName + "\".\r\n\r\nException: " + ex.Message, "Error loading settings", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } + + /// + /// Attempt to retrieve up to 25 IMAP messages and populate the list of messages. + /// + private async Task RefreshImapMessages() + { + if (string.IsNullOrEmpty(ImapHost.Text) || string.IsNullOrEmpty(ImapPort.Text) || string.IsNullOrEmpty(ImapUsername.Text) || string.IsNullOrEmpty(ImapPassword.Text)) + { + MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // If we're not currently connected to the IMAP server, connect and authenticate. + if (myImapClient == null) + { + int imapPort = 993; + int.TryParse(ImapPort.Text, out imapPort); + + myImapClient = new ImapClient(ImapHost.Text, imapPort, ImapUsername.Text, ImapPassword.Text, ImapSsl.Checked); + } + + // If our connection has timed out or been closed, reconnect. + if (!myImapClient.IsConnected || !myImapClient.IsAuthenticated) + { + myImapClient.Connect(); + myImapClient.Authenticate(); + + // Ensure we connected successfully. + if (!myImapClient.IsConnected || !myImapClient.IsAuthenticated) + { + MessageBox.Show("Unable to connect to the IMAP server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + // Alert if the connection is throttled. + myImapClient.ImapClientThrottleEvent += myImapClient_ImapClientThrottleEvent; + + // Retrieve the headers of up to 25 messages and remember their mailbox/UID pairs for later opening. + await myImapClient.SelectMailboxAsync("INBOX"); + List messages = await myImapClient.GetMessagesAsync("INBOX", 25, 1, true, true, false); + ImapMessageIDs = new Tuple[messages.Count]; + + // Repopulate the message list with the subjects of messages retrieved. + ImapMessageList.Items.Clear(); + for (int i = 0; i < messages.Count; i++) + { + ImapMessageList.Items.Add(messages[i].Subject); + ImapMessageIDs[i] = new Tuple(messages[i].Mailbox, messages[i].ImapUid); + } + + // Start checking for messages on a regular basis. + myImapClient.ImapClientNewMessageEvent += myImapClient_ImapClientNewMessageEvent; + myImapClient.ImapClientMessageExpungeEvent += myImapClient_ImapClientMessageExpungeEvent; + await myImapClient.IdleStartAsync(); + + // Reset the preview viewport. + ImapHeaders.BackColor = Color.White; + ImapHeaders.Text = ""; + ImapWebPreview.DocumentText = "Please select a message from the left-hand panel."; + ImapWebPreviewPanel.Refresh(); + } + + /// + /// Notify the user if the connection is throttled. + /// + void myImapClient_ImapClientThrottleEvent(object sender) + { + MessageBox.Show("Connection throttled by server.", "Connection throttled.", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + /// + /// Notify the user of messages expunged while IDLE. + /// + void myImapClient_ImapClientMessageExpungeEvent(object sender, ImapClientEventArgs e) + { + MessageBox.Show("Message expunged.\r\n\r\nMailbox: " + e.MailboxName + "\r\nMessage ID: " + e.MessageId, "Message expunged.", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + /// + /// Notify the user of new messages received while IDLE. + /// + void myImapClient_ImapClientNewMessageEvent(object sender, ImapClientEventArgs e) + { + MessageBox.Show("New message received.\r\n\r\nMailbox: " + e.MailboxName + "\r\nMessage ID: " + e.MessageId, "New message received", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + + /// + /// Attempt to retrieve up to 25 POP3 messages and populate the list of messages. + /// + private async Task RefreshPop3Messages() + { + if (string.IsNullOrEmpty(Pop3Host.Text) || string.IsNullOrEmpty(Pop3Port.Text) || string.IsNullOrEmpty(Pop3Username.Text) || string.IsNullOrEmpty(Pop3Password.Text)) + { + MessageBox.Show("Unable to connect to the POP3 server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // If we're not currently connected to the POP3 server, connect and authenticate. + if (myPop3Client == null) + { + int pop3Port = 993; + int.TryParse(Pop3Port.Text, out pop3Port); + + myPop3Client = new Pop3Client(Pop3Host.Text, pop3Port, Pop3Username.Text, Pop3Password.Text, Pop3Ssl.Checked); + } + + // If our connection has timed out or been closed, reconnect. + if (!myPop3Client.IsConnected || !myPop3Client.IsAuthenticated) + { + myPop3Client.Connect(); + myPop3Client.Authenticate(); + + // Ensure we connected successfully. + if (!myPop3Client.IsConnected || !myPop3Client.IsAuthenticated) + { + MessageBox.Show("Unable to connect to the POP3 server. Please double-check your settings.", "Unable to connect.", MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + } + + // Retrieve the headers of up to 25 messages and remember their mailbox/UID pairs for later opening. + List messages = await myPop3Client.GetMessagesAsync(25, 1, false, true); + Pop3MessageIDs = new List(); + + // Repopulate the message list with the subjects of messages retrieved. + Pop3MessageList.Items.Clear(); + for (int i = 0; i < messages.Count; i++) + { + Pop3MessageList.Items.Add(messages[i].Subject); + Pop3MessageIDs.Add(messages[i].Index); + } + + // Reset the preview viewport. + Pop3Headers.BackColor = Color.White; + Pop3Headers.Text = ""; + Pop3WebPreview.DocumentText = "Please select a message from the left-hand panel."; + Pop3WebPreviewPanel.Refresh(); + } + + /// + /// Render the selected message's body and selected headers to the specified controls. + /// + /// The message to display. + /// The textbox to populate with selected headers. + /// The web browser in which to render the body. + /// The panel containing the web browser. + private void RenderMessage(ReadOnlyMailMessage message, TextBox headerTextBox, WebBrowser bodyWebBrowser, Panel bodyWebBrowserPanel) + { + StringBuilder headersText = new StringBuilder(Constants.SMALLSBSIZE); + + // Output selected headers. + headersText.Append("Date: " + message.Date.ToString() + "\r\n"); + headersText.Append("From: "); + if (message.From != null) + headersText.Append(Functions.ToMailAddressString(new MailAddressCollection() { message.From })); + else if (message.Sender != null) + headersText.Append(Functions.ToMailAddressString(new MailAddressCollection() { message.Sender })); + headersText.Append("\r\n"); + if (message.To.Count > 0) + headersText.Append("To: " + Functions.ToMailAddressString(message.To) + "\r\n"); + if (message.CC.Count > 0) + headersText.Append("CC: " + Functions.ToMailAddressString(message.CC) + "\r\n"); + headersText.Append("Subject: " + message.Subject + "\r\n"); + headersText.Append("S/MIME Signed: " + message.SmimeSigned.ToString() + "\r\n"); + headersText.Append("S/MIME Envelope Encrypted: " + message.SmimeEncryptedEnvelope.ToString() + "\r\n"); + headersText.Append("S/MIME Triple Wrapped: " + message.SmimeTripleWrapped.ToString() + "\r\n"); + headersText.Append("Size: " + string.Format("{0:n0}", message.Size)); + if (message.RawFlags.Count > 0) + { + headersText.Append("\r\nFlags: "); + bool firstFlag = true; + foreach (string flag in message.RawFlags) + { + if (!firstFlag) + headersText.Append("; "); + headersText.Append(flag); + firstFlag = false; + } + } + if (message.Attachments.Count > 0) + { + headersText.Append("\r\nAttachments: "); + for (int i = 0; i < message.Attachments.Count; i++) + { + if (i > 0) + headersText.Append("; "); + headersText.Append(message.Attachments[i].Name + " (" + message.Attachments[i].ContentType.MediaType + ")"); + } + } + + headerTextBox.Text = headersText.ToString(); + + if (message.SmimeTripleWrapped) + headerTextBox.BackColor = Color.LightGreen; + else if (message.SmimeEncryptedEnvelope) + { + if (message.SmimeSigned) + headerTextBox.BackColor = Color.LightGreen; + else + headerTextBox.BackColor = Color.GreenYellow; + } + else if (message.SmimeSigned) + headerTextBox.BackColor = Color.LightBlue; + else + headerTextBox.BackColor = Color.White; + + if (message.IsBodyHtml) + bodyWebBrowser.DocumentText = Functions.EmbedAttachments(Functions.RemoveScriptTags(message.Body), message.Attachments); + else + bodyWebBrowser.DocumentText = Functions.EmbedAttachments(Functions.ConvertPlainTextToHTML(Functions.RemoveScriptTags(message.Body)), null); + bodyWebBrowserPanel.Refresh(); + } + + /// + /// Resize and reposition controls in response to a window resize. + /// + private void ResizeForm() + { + TabsControl.Width = this.Width - 22; + TabsControl.Height = this.Width - 68; + + LoadSettingsButton.Top = this.Height - 63; + SaveSettingsButton.Top = this.Height - 63; + SaveSettingsButton.Left = this.Width - 160; + + ImapSettingsGroup.Width = this.Width - 42; + Pop3SettingsGroup.Width = this.Width - 42; + SmtpSettingsGroup.Width = this.Width - 42; + + ImapTestGroup.Width = this.Width - 42; + ImapMessageGroup.Height = this.Height - 156; + ImapMessageList.Height = this.Height - 209; + ImapDeleteMessageButton.Top = this.Height - 188; + ImapHeaders.Width = this.Width - 246; + ImapPreviewGroup.Width = this.Width - 232; + ImapPreviewGroup.Height = this.Height - 156; + ImapWebPreview.Width = this.Width - 246; + ImapWebPreview.Height = this.Width - 326; + ImapWebPreviewPanel.Width = this.Width - 246; + ImapWebPreviewPanel.Height = this.Height - 331; + ImapWebPreviewPanel.Refresh(); + + Pop3TestGroup.Width = this.Width - 42; + Pop3MessageGroup.Height = this.Height - 156; + Pop3MessageList.Height = this.Height - 209; + Pop3DeleteMessageButton.Top = this.Height - 188; + Pop3Headers.Width = this.Width - 246; + Pop3PreviewGroup.Width = this.Width - 232; + Pop3PreviewGroup.Height = this.Height - 156; + Pop3WebPreview.Width = this.Width - 246; + Pop3WebPreview.Height = this.Width - 326; + Pop3WebPreviewPanel.Width = this.Width - 246; + Pop3WebPreviewPanel.Height = this.Height - 331; + Pop3WebPreviewPanel.Refresh(); + + SmtpTestGroup.Width = this.Width - 42; + SmtpTestSettingsGroup.Width = this.Width - 42; + SmtpTestSettingsGroup.Height = this.Height - 153; + SmtpFrom.Width = this.Width - 94; + SmtpTo.Width = this.Width - 94; + SmtpCC.Width = this.Width - 94; + SmtpBcc.Width = this.Width - 94; + SmtpSubject.Width = this.Width - 94; + SmtpBody.Width = this.Width - 94; + SmtpBody.Height = this.Height - 408; + SmtpAttachmentsLabel.Top = this.Height - 248; + SmtpAttachments.Top = this.Height - 248; + SmtpAttachments.Width = this.Width - 129; + SmtpSmimeLabel.Top = this.Height - 199; + SmtpSmimeSerialNumber.Top = this.Height - 199; + SmtpSmimeSerialNumber.Width = this.Width - 129; + SmtpSmimeSign.Top = this.Height - 176; + SmtpSmimeEncrypt.Top = this.Height - 176; + SmtpSmimeTripleWrap.Top = this.Height - 176; + SmtpIsHtml.Top = this.Height - 176; + } + + /// + /// Save all current Test Client settings to an XML file. + /// + private void SaveSettings() + { + DialogResult result = MessageBox.Show("Are you sure? Settings (including passwords) will be saved as plaintext into \"OpaqueMail.TestClient.xml\".", "Confirm Save", MessageBoxButtons.OKCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1); + if (result == DialogResult.OK) + { + XmlWriterSettings streamWriterSettings = new XmlWriterSettings(); + streamWriterSettings.Indent = true; + streamWriterSettings.IndentChars = " "; + streamWriterSettings.NewLineChars = "\r\n"; + streamWriterSettings.NewLineHandling = NewLineHandling.Replace; + + using (XmlWriter streamWriter = XmlWriter.Create(GetSettingsFileName(), streamWriterSettings)) + { + streamWriter.WriteStartDocument(); + streamWriter.WriteStartElement("Settings"); + + streamWriter.WriteStartElement("IMAP"); + + streamWriter.WriteElementString("Host", ImapHost.Text.ToString()); + streamWriter.WriteElementString("Port", ImapPort.Text.ToString()); + streamWriter.WriteElementString("Username", ImapUsername.Text.ToString()); + streamWriter.WriteElementString("Password", ImapPassword.Text.ToString()); + streamWriter.WriteElementString("SSL", ImapSsl.Checked.ToString()); + + streamWriter.WriteEndElement(); + + streamWriter.WriteStartElement("POP3"); + + streamWriter.WriteElementString("Host", Pop3Host.Text.ToString()); + streamWriter.WriteElementString("Port", Pop3Port.Text.ToString()); + streamWriter.WriteElementString("Username", Pop3Username.Text.ToString()); + streamWriter.WriteElementString("Password", Pop3Password.Text.ToString()); + streamWriter.WriteElementString("SSL", Pop3Ssl.Checked.ToString()); + + streamWriter.WriteEndElement(); + + streamWriter.WriteStartElement("SMTP"); + + streamWriter.WriteElementString("Host", SmtpHost.Text.ToString()); + streamWriter.WriteElementString("Port", SmtpPort.Text.ToString()); + streamWriter.WriteElementString("Username", SmtpUsername.Text.ToString()); + streamWriter.WriteElementString("Password", SmtpPassword.Text.ToString()); + streamWriter.WriteElementString("SSL", SmtpSsl.Checked.ToString()); + + streamWriter.WriteElementString("From", SmtpFrom.Text.ToString()); + streamWriter.WriteElementString("To", SmtpTo.Text.ToString()); + streamWriter.WriteElementString("CC", SmtpCC.Text.ToString()); + streamWriter.WriteElementString("BCC", SmtpBcc.Text.ToString()); + streamWriter.WriteElementString("Body", SmtpBody.Text.ToString()); + streamWriter.WriteElementString("Subject", SmtpSubject.Text.ToString()); + streamWriter.WriteElementString("Attachments", SmtpAttachments.Text.ToString()); + streamWriter.WriteElementString("SMIMESign", SmtpSmimeSign.Checked.ToString()); + streamWriter.WriteElementString("SMIMESerialNumber", SmtpSmimeSerialNumber.Text.ToString()); + streamWriter.WriteElementString("SMIMEEncrypt", SmtpSmimeEncrypt.Checked.ToString()); + streamWriter.WriteElementString("SMIMETripleWrap", SmtpSmimeTripleWrap.Checked.ToString()); + streamWriter.WriteElementString("IsHTML", SmtpIsHtml.Checked.ToString()); + + streamWriter.WriteEndElement(); + + streamWriter.WriteStartElement("Window"); + streamWriter.WriteElementString("State", WindowState.ToString()); + streamWriter.WriteElementString("Width", Width.ToString()); + streamWriter.WriteElementString("Height", Height.ToString()); + streamWriter.WriteElementString("Left", Left.ToString()); + streamWriter.WriteElementString("Top", Top.ToString()); + streamWriter.WriteElementString("SelectedTab", TabsControl.SelectedIndex.ToString()); + + streamWriter.WriteEndElement(); + + streamWriter.WriteEndElement(); + streamWriter.WriteEndDocument(); + } + } + } + + /// + /// Set a checkbox's value based on its saved XML setting. + /// + /// An XPathNavigator within the current XmlDocument. + /// The checkbox whose value should be set. + /// The XPath expression to evaluate. + private void SetCheckBoxValue(XPathNavigator navigator, CheckBox checkbox, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + { + bool valueChecked = true; + if (bool.TryParse(valueNavigator.Value, out valueChecked)) + checkbox.Checked = valueChecked; + } + } + + /// + /// Set a textbox's value based on its saved XML setting. + /// + /// An XPathNavigator within the current XmlDocument. + /// The textbox whose value should be set. + /// The XPath expression to evaluate. + private void SetTextBoxValue(XPathNavigator navigator, TextBox textbox, string xpathExpression) + { + XPathNavigator valueNavigator = navigator.SelectSingleNode(xpathExpression); + if (valueNavigator != null) + textbox.Text = valueNavigator.Value; + } + #endregion Private Methods + } +} diff --git a/OpaqueMail.Net.TestClient/Form1.resx b/OpaqueMail.Net.TestClient/Form1.resx index 75011c4..2b08f9e 100644 --- a/OpaqueMail.Net.TestClient/Form1.resx +++ b/OpaqueMail.Net.TestClient/Form1.resx @@ -1,216 +1,216 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAIAEBAAAAAAIABoBAAAJgAAACAgAAAAACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAEAE - AAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wEHx2aDDchpgwnHZ4MBxmKDAcZigwGuVoMBo1GDAaNRgwF1 - OoMBdTqDAWYygwFKJIMBSiSDAUokgwFKJIMBSiSDOdOF/2bdof9E1Yz/AcZi/wHGYv8Arlb/AaNR/wGj - Uf8BdTr/AXU6/wFmMv8BSiT/AUok/wFKJP8BSiT/AUok/znThf9n3aL/RNWM/wHGYv8BxmL/AK5W/wGj - Uf8Bo1H/AXU6/wF1Ov8BZjL/AUok/wFKJP8BSiT/AUok/wFKJP9c25r/puvI/4vluP9X2Zf/V9mX/x3J - cv8BwF//AcBf/wGcTf8BnE3/AY1F/wFuNv8Bbjb/AVYq/wFKJP8BSiT/Y9yf/7Lu0P+Z6MD/Z92i/2fd - ov8iznf/AcZi/wHGYv8Bo1H/AaNR/wGUSf8BdTr/AXU6/wFYK/8BSiT/AUok/2reo/+/8dj/puvJ/3Tg - qv904Kr/M9GB/wrIaP+L5bf/P8yE/wGqVP8Bm0z/AX0+/wF9Pv8BYC//AVEo/wFRKP+O5rn//////+X5 - 7/+y7tD/q+zM/1jamP/L89////////z+/f+D47L/Bbxf/wGjUf8Bo1H/AIRB/wF1Ov8BdTr/jua5//// - ///l+e//mejA/23fpf/t+/T//////////////////////8Xy2/8hwG//AaRR/wCEQf8BdTr/AXU6/47m - uf//////p+vJ/5Dmuv/9/v7/////////////////////////////////8Pv1/1nVlv8AnE3/AYxF/wGM - Rf+J5bb/i+W3/73w1//////////////////////////////////////////////////+////oOnE/w22 - YP8Bo1H/HM1z/5vowf+17tH/te7R/7Xu0f+17tH/te7R/7Xu0f+17tH/te7R/7Xu0f+17tH/te7R/7Xu - 0f+R5rv/DL9k/wHGYicBxmInAcZiJwHGYicBxmInAcZiJwHGYicBxmInAcZiJwHGYicBxmInAcZiJwHG - YicBxmInAcZiJwHGYif///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AQAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA - //8AAP//AAD//wAA//8AAP//AAD//wAA//8oAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAA - AAAAAAAA////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wEBxmIHAcZiBwHGYgcBxmIHAcZiBwHG - YgcBxmIHAcZiBwHGYgcBxmIHAbpcBwGjUQcBo1EHAaNRBwGjUQcBo1EHAXU6BwF1OgcBdToHAXU6BwF1 - OgcBWCwHAUokBwFKJAcBSiQHAUokBwFKJAcBSiQHAUokBwFKJAcBSiQHAUokBwLHY/8OyWr/Dslq/w7J - av8OyWr/Bcdl/wHGYv8BxmL/AcZi/wHGYv8Bulz/AaNR/wGjUf8Bo1H/AaNR/wGjUf8BdTr/AXU6/wF1 - Ov8BdTr/AXU6/wFYK/8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/DMlp/2bd - of9m3aH/Zt2h/2bdof8iznf/AcZi/wHGYv8BxmL/AcZi/wC6XP8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wF1 - Ov8BdTr/AXU6/wF1Ov8BdTr/AVgr/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFK - JP8MyWn/Z92i/2fdov9n3aL/Z92i/yLOd/8BxmL/AcZi/wHGYv8BxmL/Abpc/wGjUf8Bo1H/AaNR/wGj - Uf8Bo1H/AXU6/wF1Ov8BdTr/AXU6/wF1Ov8BWCv/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFK - JP8BSiT/AUok/wzJaf9m3aL/Z92i/2fdov9n3aH/Is53/wHGYv8BxmL/AcZi/wHGYv8Aulz/AaNR/wGj - Uf8Bo1H/AaNR/wGjUf8BdTr/AXU6/wF1Ov8BdTr/AXU6/wFYK/8BSiT/AUok/wFKJP8BSiT/AUok/wFK - JP8BSiT/AUok/wFKJP8BSiT/DMlp/2fdov9n3aL/Z92i/2fdov8iznf/AcZi/wHGYv8BxmL/AcZi/wG6 - XP8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wF1Ov8BdTr/AXU6/wF1Ov8BdTr/AVgr/wFKJP8BSiT/AUok/wFK - JP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8Rymz/munB/5rpwf+a6cH/munB/2Pcn/9H1o3/R9aN/0fW - jf9H1o3/L819/wG8Xf8Bu13/Abtd/wG7Xf8Bu1z/AZVK/wGVSv8BlUr/AZVK/wGVSv8Bdzr/AWgz/wFo - M/8BaDP/AWgz/wFdLv8BSiT/AUok/wFKJP8BSiT/AUok/xTLbv+y7tD/su7Q/7Lu0P+y7tD/gOKx/2fd - ov9n3aL/Z92i/2fdov9F1Yz/Acdi/wHGYv8BxmL/AcZi/wHGYv8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wGF - Qf8BdTr/AXU6/wF1Ov8BdTr/AWcy/wFKJP8BSiT/AUok/wFKJP8BSiT/FMtu/7Lu0P+y7tD/su7Q/7Lu - 0P+A4rH/Z92i/2fdov9n3aL/Z92i/0XWjP8BxmL/AcZi/wHGYv8BxmL/AcZi/wGjUf8Bo1H/AaNR/wGj - Uf8Bo1H/AYVB/wF1Ov8BdTr/AXU6/wF1Ov8BZzL/AUok/wFKJP8BSiT/AUok/wFKJP8Uy27/su7Q/7Lu - 0P+y7tD/su7Q/4Disf9n3aL/Z92i/2fdov9n3aL/RNWM/wDHYv8BxmL/AcZi/wHGYv8BxmL/AaVS/wGj - Uf8Bo1H/AaNR/wGjUf8BhUH/AXU6/wF1Ov8BdTr/AXU6/wFnMv8BSiT/AUok/wFKJP8BSiT/AUok/xTL - bv+y7tD/su7Q/7Lu0P+y7tD/gOKx/2fdov9n3aL/Z92i/2fdov9F1oz/AcZi/wHGYv8BxmL/B8hm/2Hc - nf8IxGT/AatU/wGjUf8Bo1H/AaNR/wGFQf8BdTr/AXU6/wF1Ov8BdTr/AWcy/wFKJP8BSiT/AUok/wFK - JP8BSiT/F8xw/8304f/N9OH/zfTh/8304f+b6cL/geOy/4Hjsv+B47L/geOy/2Lcn/8lznj/Cchn/x/N - df/I893//////8304P8oz3r/AbVZ/wGwV/8BsFf/AZRJ/wGGQv8BhkL/AYZC/wGGQv8Bdzr/AVks/wFZ - LP8BWSz/AVks/wFZLP8dzXP//////////////////////8v04P+y7tD/su7Q/7Lu0P+y7tD/lei+/yfP - ev9I147/6/vz//////////////////T8+P9k3Z//AcZi/wHGYv8Ar1b/AaNR/wGjUf8Bo1H/AaNR/wCU - Sf8BdTr/AXU6/wF1Ov8BdTr/AXU6/x3Nc///////////////////////y/Tg/7Lu0P+y7tD/su7Q/5no - wP8lz3j/geOx//z+/f////////////////////////////////+q7Mv/Esps/wGzWP8Bo1H/AaNR/wGj - Uf8Bo1H/AZRJ/wF1Ov8BdTr/AXU6/wF1Ov8BdTr/Hc1z///////////////////////L9OD/su7Q/7Lu - 0P964q3/Ic52/7nv1P/////////////////////////////////////////////////g+Oz/PdSH/wG4 - W/8BpFH/AaNR/wGjUf8AlEn/AXU6/wF1Ov8BdTr/AXU6/wF1Ov8dzXP//////////////////////8v0 - 4P+v7c7/UdmU/zrUhf/h+O3///////////////////////////////////////////////////////// - ///7/v3/gOOx/wXCYf8BqVT/AaNR/wCUSf8BdTr/AXU6/wF1Ov8BdTr/AXU6/x3Nc/////////////// - ////////uvDV/yzQff9u36X/+f77//////////////////////////////////////////////////// - ////////////////////////w/La/yDNdf8Bslj/AJVJ/wF2Ov8Bdjr/AXY6/wF2Ov8Bdjr/Hc1z//// - /////////////77x1/8lz3n/qOzJ//////////////////////////////////////////////////// - ////////////////////////////////////////7/v1/1fal/8BwF//AaZS/wGjUf8Bo1H/AaNR/wGj - Uf8dzXP///////3//v+H5bX/L9F//9b25v////////////////////////////////////////////// - /////////////////////////////////////////////////////////v///53pw/8Mxmf/Aa1V/wGj - Uf8Bo1H/AaNR/x3Nc//u+/T/TtiS/1vbmv/z/Pj///////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////9j2 - 5/8z0oH/AbZa/wGkUf8Bo1H/Dspq/yrQe/+W6L7//v////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////n9+/9z4Kn/A8Bf/wGoU/8Ax2L/OtOG/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2ze - pP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2ze - pP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP8t0H3/AcVh/wHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHG - Yk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHG - Yk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJP////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// - /wH///8B////Af///wH///8B////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAIAEBAAAAAAIABoBAAAJgAAACAgAAAAACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAEAE + AAAAAAAAAAAAAAAAAAAAAAAA////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wEHx2aDDchpgwnHZ4MBxmKDAcZigwGuVoMBo1GDAaNRgwF1 + OoMBdTqDAWYygwFKJIMBSiSDAUokgwFKJIMBSiSDOdOF/2bdof9E1Yz/AcZi/wHGYv8Arlb/AaNR/wGj + Uf8BdTr/AXU6/wFmMv8BSiT/AUok/wFKJP8BSiT/AUok/znThf9n3aL/RNWM/wHGYv8BxmL/AK5W/wGj + Uf8Bo1H/AXU6/wF1Ov8BZjL/AUok/wFKJP8BSiT/AUok/wFKJP9c25r/puvI/4vluP9X2Zf/V9mX/x3J + cv8BwF//AcBf/wGcTf8BnE3/AY1F/wFuNv8Bbjb/AVYq/wFKJP8BSiT/Y9yf/7Lu0P+Z6MD/Z92i/2fd + ov8iznf/AcZi/wHGYv8Bo1H/AaNR/wGUSf8BdTr/AXU6/wFYK/8BSiT/AUok/2reo/+/8dj/puvJ/3Tg + qv904Kr/M9GB/wrIaP+L5bf/P8yE/wGqVP8Bm0z/AX0+/wF9Pv8BYC//AVEo/wFRKP+O5rn//////+X5 + 7/+y7tD/q+zM/1jamP/L89////////z+/f+D47L/Bbxf/wGjUf8Bo1H/AIRB/wF1Ov8BdTr/jua5//// + ///l+e//mejA/23fpf/t+/T//////////////////////8Xy2/8hwG//AaRR/wCEQf8BdTr/AXU6/47m + uf//////p+vJ/5Dmuv/9/v7/////////////////////////////////8Pv1/1nVlv8AnE3/AYxF/wGM + Rf+J5bb/i+W3/73w1//////////////////////////////////////////////////+////oOnE/w22 + YP8Bo1H/HM1z/5vowf+17tH/te7R/7Xu0f+17tH/te7R/7Xu0f+17tH/te7R/7Xu0f+17tH/te7R/7Xu + 0f+R5rv/DL9k/wHGYicBxmInAcZiJwHGYicBxmInAcZiJwHGYicBxmInAcZiJwHGYicBxmInAcZiJwHG + YicBxmInAcZiJwHGYif///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AQAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA + //8AAP//AAD//wAA//8AAP//AAD//wAA//8oAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAA + AAAAAAAA////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wEBxmIHAcZiBwHGYgcBxmIHAcZiBwHG + YgcBxmIHAcZiBwHGYgcBxmIHAbpcBwGjUQcBo1EHAaNRBwGjUQcBo1EHAXU6BwF1OgcBdToHAXU6BwF1 + OgcBWCwHAUokBwFKJAcBSiQHAUokBwFKJAcBSiQHAUokBwFKJAcBSiQHAUokBwLHY/8OyWr/Dslq/w7J + av8OyWr/Bcdl/wHGYv8BxmL/AcZi/wHGYv8Bulz/AaNR/wGjUf8Bo1H/AaNR/wGjUf8BdTr/AXU6/wF1 + Ov8BdTr/AXU6/wFYK/8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/DMlp/2bd + of9m3aH/Zt2h/2bdof8iznf/AcZi/wHGYv8BxmL/AcZi/wC6XP8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wF1 + Ov8BdTr/AXU6/wF1Ov8BdTr/AVgr/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFK + JP8MyWn/Z92i/2fdov9n3aL/Z92i/yLOd/8BxmL/AcZi/wHGYv8BxmL/Abpc/wGjUf8Bo1H/AaNR/wGj + Uf8Bo1H/AXU6/wF1Ov8BdTr/AXU6/wF1Ov8BWCv/AUok/wFKJP8BSiT/AUok/wFKJP8BSiT/AUok/wFK + JP8BSiT/AUok/wzJaf9m3aL/Z92i/2fdov9n3aH/Is53/wHGYv8BxmL/AcZi/wHGYv8Aulz/AaNR/wGj + Uf8Bo1H/AaNR/wGjUf8BdTr/AXU6/wF1Ov8BdTr/AXU6/wFYK/8BSiT/AUok/wFKJP8BSiT/AUok/wFK + JP8BSiT/AUok/wFKJP8BSiT/DMlp/2fdov9n3aL/Z92i/2fdov8iznf/AcZi/wHGYv8BxmL/AcZi/wG6 + XP8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wF1Ov8BdTr/AXU6/wF1Ov8BdTr/AVgr/wFKJP8BSiT/AUok/wFK + JP8BSiT/AUok/wFKJP8BSiT/AUok/wFKJP8Rymz/munB/5rpwf+a6cH/munB/2Pcn/9H1o3/R9aN/0fW + jf9H1o3/L819/wG8Xf8Bu13/Abtd/wG7Xf8Bu1z/AZVK/wGVSv8BlUr/AZVK/wGVSv8Bdzr/AWgz/wFo + M/8BaDP/AWgz/wFdLv8BSiT/AUok/wFKJP8BSiT/AUok/xTLbv+y7tD/su7Q/7Lu0P+y7tD/gOKx/2fd + ov9n3aL/Z92i/2fdov9F1Yz/Acdi/wHGYv8BxmL/AcZi/wHGYv8Bo1H/AaNR/wGjUf8Bo1H/AaNR/wGF + Qf8BdTr/AXU6/wF1Ov8BdTr/AWcy/wFKJP8BSiT/AUok/wFKJP8BSiT/FMtu/7Lu0P+y7tD/su7Q/7Lu + 0P+A4rH/Z92i/2fdov9n3aL/Z92i/0XWjP8BxmL/AcZi/wHGYv8BxmL/AcZi/wGjUf8Bo1H/AaNR/wGj + Uf8Bo1H/AYVB/wF1Ov8BdTr/AXU6/wF1Ov8BZzL/AUok/wFKJP8BSiT/AUok/wFKJP8Uy27/su7Q/7Lu + 0P+y7tD/su7Q/4Disf9n3aL/Z92i/2fdov9n3aL/RNWM/wDHYv8BxmL/AcZi/wHGYv8BxmL/AaVS/wGj + Uf8Bo1H/AaNR/wGjUf8BhUH/AXU6/wF1Ov8BdTr/AXU6/wFnMv8BSiT/AUok/wFKJP8BSiT/AUok/xTL + bv+y7tD/su7Q/7Lu0P+y7tD/gOKx/2fdov9n3aL/Z92i/2fdov9F1oz/AcZi/wHGYv8BxmL/B8hm/2Hc + nf8IxGT/AatU/wGjUf8Bo1H/AaNR/wGFQf8BdTr/AXU6/wF1Ov8BdTr/AWcy/wFKJP8BSiT/AUok/wFK + JP8BSiT/F8xw/8304f/N9OH/zfTh/8304f+b6cL/geOy/4Hjsv+B47L/geOy/2Lcn/8lznj/Cchn/x/N + df/I893//////8304P8oz3r/AbVZ/wGwV/8BsFf/AZRJ/wGGQv8BhkL/AYZC/wGGQv8Bdzr/AVks/wFZ + LP8BWSz/AVks/wFZLP8dzXP//////////////////////8v04P+y7tD/su7Q/7Lu0P+y7tD/lei+/yfP + ev9I147/6/vz//////////////////T8+P9k3Z//AcZi/wHGYv8Ar1b/AaNR/wGjUf8Bo1H/AaNR/wCU + Sf8BdTr/AXU6/wF1Ov8BdTr/AXU6/x3Nc///////////////////////y/Tg/7Lu0P+y7tD/su7Q/5no + wP8lz3j/geOx//z+/f////////////////////////////////+q7Mv/Esps/wGzWP8Bo1H/AaNR/wGj + Uf8Bo1H/AZRJ/wF1Ov8BdTr/AXU6/wF1Ov8BdTr/Hc1z///////////////////////L9OD/su7Q/7Lu + 0P964q3/Ic52/7nv1P/////////////////////////////////////////////////g+Oz/PdSH/wG4 + W/8BpFH/AaNR/wGjUf8AlEn/AXU6/wF1Ov8BdTr/AXU6/wF1Ov8dzXP//////////////////////8v0 + 4P+v7c7/UdmU/zrUhf/h+O3///////////////////////////////////////////////////////// + ///7/v3/gOOx/wXCYf8BqVT/AaNR/wCUSf8BdTr/AXU6/wF1Ov8BdTr/AXU6/x3Nc/////////////// + ////////uvDV/yzQff9u36X/+f77//////////////////////////////////////////////////// + ////////////////////////w/La/yDNdf8Bslj/AJVJ/wF2Ov8Bdjr/AXY6/wF2Ov8Bdjr/Hc1z//// + /////////////77x1/8lz3n/qOzJ//////////////////////////////////////////////////// + ////////////////////////////////////////7/v1/1fal/8BwF//AaZS/wGjUf8Bo1H/AaNR/wGj + Uf8dzXP///////3//v+H5bX/L9F//9b25v////////////////////////////////////////////// + /////////////////////////////////////////////////////////v///53pw/8Mxmf/Aa1V/wGj + Uf8Bo1H/AaNR/x3Nc//u+/T/TtiS/1vbmv/z/Pj///////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////9j2 + 5/8z0oH/AbZa/wGkUf8Bo1H/Dspq/yrQe/+W6L7//v////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////n9+/9z4Kn/A8Bf/wGoU/8Ax2L/OtOG/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2ze + pP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP9s3qT/bN6k/2ze + pP9s3qT/bN6k/2zepP9s3qT/bN6k/2zepP8t0H3/AcVh/wHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHG + Yk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHG + Yk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJPAcZiTwHGYk8BxmJP////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af///wH///8B////Af// + /wH///8B////Af///wH///8B////AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + + \ No newline at end of file diff --git a/OpaqueMail.Net.TestClient/Properties/AssemblyInfo.cs b/OpaqueMail.Net.TestClient/Properties/AssemblyInfo.cs index 413c4dc..7ddc276 100644 --- a/OpaqueMail.Net.TestClient/Properties/AssemblyInfo.cs +++ b/OpaqueMail.Net.TestClient/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpaqueMail.TestClient")] -[assembly: AssemblyDescription(".NET e-mail encryption library with S/MIME support for SMTP, IMAP, and POP3.")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Bert Johnson")] -[assembly: AssemblyProduct("OpaqueMail")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("5eba3ce6-92e5-453e-91d5-19b740931c73")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.4.2")] -[assembly: AssemblyFileVersion("1.4.2.2")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpaqueMail.TestClient")] +[assembly: AssemblyDescription(".NET e-mail encryption library with S/MIME support for SMTP, IMAP, and POP3.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Bert Johnson")] +[assembly: AssemblyProduct("OpaqueMail")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5eba3ce6-92e5-453e-91d5-19b740931c73")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.5.0")] +[assembly: AssemblyFileVersion("1.5.0.0")] diff --git a/OpaqueMail.Net/Functions.cs b/OpaqueMail.Net/Functions.cs index 6ba89b5..0a3b99f 100644 --- a/OpaqueMail.Net/Functions.cs +++ b/OpaqueMail.Net/Functions.cs @@ -1,1393 +1,1393 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Mail; -using System.Net.NetworkInformation; -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace OpaqueMail -{ - /// - /// Provides common functions used by other OpaqueMail classes. - /// - public class Functions - { - #region Public Methods - /// - /// Create HTML links for embedded URLs. - /// - /// Useful for text/plain messages. - /// HTML block to process. - /// The HTML block with any URLs encapsulated in HTML links. - public static string ConvertPlainTextToHTML(string html) - { - // Handle the special case of e-mail starting or endding with a link by padding with spaces on either side, which will be removed at the end. - html = " " + html + " "; - - // Treat all whitespace equivalently and ignore case. - string canonicalHtml = html.Replace("\t", " ").Replace("\r\n", " ").Replace("\r", " ").Replace("\n", " ").ToLower(); - - // Convert line breaks to BR tags. - html = html.Replace("\r\n", "
"); - - // Build a new string using the following buffer. - StringBuilder htmlBuilder = new StringBuilder(Constants.SMALLSBSIZE); - - int pos = 0, lastPos = 0; - while (pos > -1) - { - lastPos = pos; - - // Find the next link, whether using the HTTP or HTTPS protocol. - int httpPos = canonicalHtml.IndexOf(" http://", pos, StringComparison.Ordinal); - int httpsPos = canonicalHtml.IndexOf(" https://", pos, StringComparison.Ordinal); - - if (httpPos > -1) - { - if (httpsPos > -1) - pos = httpPos < httpsPos ? httpPos : httpsPos; - else - pos = httpPos; - } - else if (httpsPos > -1) - pos = httpsPos; - else - pos = -1; - - // A URL was found, so insert a link. - if (pos > -1) - { - int endPos = canonicalHtml.IndexOf(" ", pos + 1, StringComparison.Ordinal); - if (endPos > -1) - { - string link = html.Substring(pos + 1, endPos - pos - 1); - - htmlBuilder.Append(html.Substring(lastPos, pos + 1 - lastPos)); - htmlBuilder.Append("" + link + ""); - pos = endPos; - } - else - pos = -1; - } - } - - // Add any text remaining after our last link. - htmlBuilder.Append(html.Substring(lastPos)); - - // Remove spaces padded to the beginning and end. - string returnValue = htmlBuilder.ToString(); - return returnValue.Substring(1, returnValue.Length - 2); - } - - /// - /// Escapes embedded encoding of e-mail headers. - /// - /// E-mail header to be decoded. - /// The decoded e-mail header. - public static string DecodeMailHeader(string header) - { - try - { - // Build a new string using the following buffer. - StringBuilder headerBuilder = new StringBuilder(Constants.TINYSBSIZE); - - int cursor = 0, lastCursor = 0; - while (cursor > -1) - { - lastCursor = cursor; - cursor = header.IndexOf("=?", cursor, StringComparison.Ordinal); - if (cursor > -1) - { - int middleCursor = header.IndexOf("?", cursor + 2, StringComparison.Ordinal); - if (middleCursor > -1 && middleCursor < header.Length - 2) - { - int endCursor = header.IndexOf("?=", middleCursor + 1, StringComparison.Ordinal); - if (endCursor > -1 && endCursor > middleCursor + 2) - { - // Try to create a decoder for the encoding. - string encodingName = header.Substring(cursor + 2, middleCursor - cursor - 2); - Encoding encoding = Encoding.GetEncoding(encodingName); - - byte[] encodedBytes = null; - switch (header.Substring(middleCursor + 1, 2)) - { - case "B?": - encodedBytes = Convert.FromBase64String(header.Substring(middleCursor + 3, endCursor - middleCursor - 3)); - break; - case "Q?": - encodedBytes = Encoding.UTF8.GetBytes(Functions.FromQuotedPrintable(header.Substring(middleCursor + 3, endCursor - middleCursor - 3))); - break; - default: - encodedBytes = Encoding.UTF8.GetBytes(header.Substring(middleCursor, endCursor - middleCursor - 2)); - break; - } - - // Append the decoded string. - headerBuilder.Append(encoding.GetString(encodedBytes)); - - cursor = endCursor + 2; - } - else - cursor = -1; - } - else - cursor = -1; - } - } - - // Append any remaining characters. - headerBuilder.Append(header.Substring(lastCursor)); - - return headerBuilder.ToString(); - } - catch - { - // If the header is malformed, return it as passed in. - return header; - } - } - - /// - /// Convert CID: object references to Base-64 encoded versions. - /// - /// Useful for displaying text/html messages with image references. - /// HTML block to process. - /// Collection of attachments available to be embedded. - /// The HTML block with any CID: object references replaced by their Base-64 encoded bytes. - public static string EmbedAttachments(string html, AttachmentCollection attachments) - { - // Build a new string using the following buffer. - StringBuilder htmlBuilder = new StringBuilder(Constants.MEDIUMSBSIZE); - - int srcStartPos = 0, lastPos = 0; - while (srcStartPos > -1) - { - // Find the next SRC= attribute and handle either single or double quotes. - int srcStartQuotePos = html.IndexOf("src=\"cid:", srcStartPos, StringComparison.Ordinal); - int srcStartApostrophePos = html.IndexOf("src='cid:", srcStartPos, StringComparison.Ordinal); - - if (srcStartQuotePos > -1) - { - if (srcStartApostrophePos > -1) - srcStartPos = srcStartQuotePos < srcStartApostrophePos ? srcStartQuotePos : srcStartApostrophePos; - else - srcStartPos = srcStartQuotePos; - } - else if (srcStartApostrophePos > -1) - srcStartPos = srcStartApostrophePos; - else - srcStartPos = -1; - - string srcEndDelimiter = (srcStartQuotePos == srcStartPos) ? "\"" : "'"; - - if (srcStartPos > -1) - { - int srcEndPos = html.IndexOf(srcEndDelimiter, srcStartPos + 9, StringComparison.Ordinal); - if (srcEndPos > 0) - { - htmlBuilder.Append(html.Substring(lastPos, srcStartPos + 5 - lastPos)); - - string cid = html.Substring(srcStartPos + 9, srcEndPos - srcStartPos - 9); - - // Check for attachments with matching Content-IDs. - bool matchingAttachmentFound = false; - foreach (Attachment attachment in attachments) - { - if (attachment.ContentId == cid) - { - htmlBuilder.Append("data:" + attachment.ContentType.MediaType + ";base64,"); - - matchingAttachmentFound = true; - byte[] contentStreamBytes = ((MemoryStream)attachment.ContentStream).ToArray(); - - htmlBuilder.Append(Functions.ToBase64String(contentStreamBytes, 0, contentStreamBytes.Length)); - } - } - - // If the current object hasn't been matched, look for a matching file name. - if (!matchingAttachmentFound) - { - // Ignore the non-file name portion of this Content-ID. - int cidAtPos = cid.IndexOf("@", StringComparison.Ordinal); - if (cidAtPos > -1) - cid = cid.Substring(0, cidAtPos); - - foreach (Attachment attachment in attachments) - { - if (attachment.Name == cid) - { - htmlBuilder.Append("data:" + attachment.ContentType.MediaType + ";base64,"); - - matchingAttachmentFound = true; - byte[] contentStreamBytes = ((MemoryStream)attachment.ContentStream).ToArray(); - - htmlBuilder.Append(Functions.ToBase64String(contentStreamBytes, 0, contentStreamBytes.Length)); - } - } - - if (!matchingAttachmentFound) - htmlBuilder.Append(cid); - } - - srcStartPos = srcEndPos; - lastPos = srcStartPos; - } - else - srcStartPos = -1; - } - else - srcStartPos = -1; - } - - htmlBuilder.Append(html.Substring(lastPos)); - - return htmlBuilder.ToString(); - } - - /// - /// Encodes e-mail headers to escape extended characters. - /// - /// E-mail header to be encoded. - /// Base-64 encoded version of the e-mail header. - public static string EncodeMailHeader(string header) - { - bool extendedCharacterFound = false; - foreach (char headerCharacter in header.ToCharArray()) - { - if (headerCharacter > 127) - extendedCharacterFound = true; - } - - if (extendedCharacterFound) - return "=?B?" + ToBase64String(header) + "?="; - else - return header; - } - - /// - /// Returns a base-64 string representing the original input. - /// - /// The string to convert. - /// Base-64 representation of the input string. - public static string FromBase64(string input) - { - return Encoding.UTF8.GetString(System.Convert.FromBase64String(input)); - } - - /// - /// Parse a text representation of e-mail addresses into a collection of MailAddress objects. - /// - /// String representation of e-mail addresses to parse. - /// A MailAddressCollection representing the string passed in. - public static MailAddressCollection FromMailAddressString(string addresses) - { - // Escape embedded encoding. - addresses = DecodeMailHeader(addresses); - - // Create a new collection of MailAddresses to be returned. - MailAddressCollection addressCollection = new MailAddressCollection(); - - int cursor = 0, lastCursor = 0; - string displayName = ""; - while (cursor < addresses.Length) - { - int quoteCursor = addresses.IndexOf("\"", cursor, StringComparison.Ordinal); - if (quoteCursor == -1) - quoteCursor = addresses.Length + 1; - int aposCursor = addresses.IndexOf("'", cursor, StringComparison.Ordinal); - if (aposCursor == -1) - aposCursor = addresses.Length + 1; - int angleCursor = addresses.IndexOf("<", cursor, StringComparison.Ordinal); - if (angleCursor == -1) - angleCursor = addresses.Length + 1; - int bracketCursor = addresses.IndexOf("[", cursor, StringComparison.Ordinal); - if (bracketCursor == -1) - bracketCursor = addresses.Length + 1; - int commaCursor = addresses.IndexOf(",", cursor, StringComparison.Ordinal); - if (commaCursor == -1) - commaCursor = addresses.Length + 1; - int semicolonCursor = addresses.IndexOf(";", cursor, StringComparison.Ordinal); - if (semicolonCursor == -1) - semicolonCursor = addresses.Length + 1; - - if (quoteCursor < aposCursor && quoteCursor < angleCursor && quoteCursor < bracketCursor && quoteCursor < commaCursor && quoteCursor < semicolonCursor) - { - // The address display name is enclosed in quotes. - int endQuoteCursor = addresses.IndexOf("\"", quoteCursor + 1, StringComparison.Ordinal); - if (endQuoteCursor > -1) - { - displayName = addresses.Substring(quoteCursor + 1, endQuoteCursor - quoteCursor - 1); - cursor = endQuoteCursor + 1; - } - else - cursor = addresses.Length; - } - else if (aposCursor < quoteCursor && aposCursor < angleCursor && aposCursor < bracketCursor && aposCursor < commaCursor && aposCursor < semicolonCursor) - { - // The address display name may be enclosed in apostrophes. - int endAposCursor = addresses.IndexOf("'", aposCursor + 1, StringComparison.Ordinal); - if (endAposCursor > -1) - { - displayName = addresses.Substring(aposCursor + 1, endAposCursor - aposCursor - 1); - cursor = endAposCursor + 1; - } - else - { - // The address contains an apostophe, but it's not enclosed in apostrophes. - angleCursor = addresses.IndexOf("<", cursor, StringComparison.Ordinal); - if (angleCursor == -1) - angleCursor = addresses.Length + 1; - bracketCursor = addresses.IndexOf("[", cursor, StringComparison.Ordinal); - if (bracketCursor == -1) - bracketCursor = addresses.Length + 1; - - if (angleCursor < bracketCursor) - { - displayName = addresses.Substring(lastCursor, angleCursor - lastCursor).Trim(); - cursor = angleCursor; - } - else if (bracketCursor > -1) - { - displayName = addresses.Substring(lastCursor, bracketCursor - lastCursor).Trim(); - cursor = angleCursor; - } - else - cursor = addresses.Length; - } - } - else if (angleCursor < quoteCursor && angleCursor < aposCursor && angleCursor < bracketCursor && angleCursor < commaCursor && angleCursor < semicolonCursor) - { - // The address is enclosed in angle brackets. - int endAngleCursor = addresses.IndexOf(">", angleCursor + 1, StringComparison.Ordinal); - if (endAngleCursor > -1) - { - // If we didn't find a display name between quotes or apostrophes, look at all characters prior to the angle bracket. - if (displayName.Length < 1) - displayName = addresses.Substring(lastCursor, angleCursor - lastCursor).Trim(); - - string address = addresses.Substring(angleCursor + 1, endAngleCursor - angleCursor - 1); - if (displayName.Length > 0) - addressCollection.Add(new MailAddress(address, displayName)); - else - addressCollection.Add(new MailAddress(address)); - - displayName = ""; - cursor = endAngleCursor + 1; - } - else - cursor = addresses.Length; - } - else if (bracketCursor < quoteCursor && angleCursor < aposCursor && bracketCursor < angleCursor && bracketCursor < commaCursor && bracketCursor < semicolonCursor) - { - // The address is enclosed in brackets. - int endBracketCursor = addresses.IndexOf(">", bracketCursor + 1, StringComparison.Ordinal); - if (endBracketCursor > -1) - { - // If we didn't find a display name between quotes or apostrophes, look at all characters prior to the bracket. - if (displayName.Length < 1) - displayName = addresses.Substring(lastCursor, bracketCursor - lastCursor).Trim(); - - string address = addresses.Substring(bracketCursor + 1, endBracketCursor - bracketCursor - 1); - if (displayName.Length > 0) - addressCollection.Add(new MailAddress(address, displayName)); - else - addressCollection.Add(new MailAddress(address)); - - displayName = ""; - cursor = endBracketCursor + 1; - } - else - cursor = addresses.Length; - } - else if (commaCursor < quoteCursor && commaCursor < aposCursor && commaCursor < angleCursor && commaCursor < bracketCursor && commaCursor < semicolonCursor) - { - // We've found the next address, delimited by a comma. - cursor = commaCursor + 1; - } - else if (semicolonCursor > -1) - { - // We've found the next address, delimited by a semicolon. - cursor = commaCursor + 1; - } - else - cursor = addresses.Length; - - lastCursor = cursor; - } - - // If no encoded email address was parsed, try adding the entire string. - if (addressCollection.Count < 1 && IsValidEmailAddress(addresses)) - addressCollection.Add(addresses); - - return addressCollection; - } - - /// - /// Decode modified UTF-7, as used for IMAP mailbox names. - /// - /// String to decode - /// Decoded version of the input string. - public static string FromModifiedUTF7(string input) - { - StringBuilder outputBuilder = new StringBuilder(Constants.TINYSBSIZE); - - int ampCursor = 0, lastAmpCursor = 0; - while (ampCursor > -1) - { - lastAmpCursor = ampCursor; - ampCursor = input.IndexOf("&", ampCursor, StringComparison.Ordinal); - if (ampCursor > -1) - { - outputBuilder.Append(input.Substring(lastAmpCursor, ampCursor - lastAmpCursor)); - int minusCursor = input.IndexOf("-", ampCursor + 1, StringComparison.Ordinal); - if (minusCursor > -1) - { - // Check if this is an encoded ampersand. - if (minusCursor == ampCursor + 1) - outputBuilder.Append("&"); - else - { - // Unpack the encoded substring. - string base64String = "+" + input.Substring(ampCursor + 1, minusCursor - ampCursor - 1).Replace(',', '/'); - outputBuilder.Append(Encoding.UTF7.GetString(Encoding.UTF8.GetBytes(base64String))); - } - ampCursor = minusCursor + 1; - } - else - ampCursor = -1; - } - } - - // Add the remaining portion after the last escape sequenece. - outputBuilder.Append(input.Substring(lastAmpCursor)); - - return outputBuilder.ToString(); - } - - /// - /// Escapes quoted-printable encoding. - /// - /// The string to convert. - /// The decoded version of the quoted-printable string passed in. - public static string FromQuotedPrintable(string input) - { - try - { - // Remove carriage returns because they'll be added back in for line breaks (=0A). - input = input.Replace("=0D", ""); - - // Build a new string using the following buffer. - StringBuilder outputBuilder = new StringBuilder(Constants.SMALLSBSIZE); - - // Buffer for holding UTF-8 encoded characters. - byte[] utf8Buffer = new byte[Constants.SMALLBUFFERSIZE]; - - // Loop through and process quoted-printable strings, denoted by equals signs. - int equalsPos = 0, lastPos = 0; - while (equalsPos > -1) - { - lastPos = equalsPos; - equalsPos = input.IndexOf("=", equalsPos, StringComparison.Ordinal); - if (equalsPos > -1 && equalsPos < input.Length - 2) - { - outputBuilder.Append(input.Substring(lastPos, equalsPos - lastPos)); - - string afterEquals = input.Substring(equalsPos + 1, 2); - - switch (afterEquals) - { - case "\r\n": - break; - case "09": - outputBuilder.Append("\t"); - break; - case "0A": - outputBuilder.Append("\r\n"); - break; - case "20": - outputBuilder.Append(" "); - break; - default: - int highByte = int.Parse(afterEquals, System.Globalization.NumberStyles.HexNumber); - - // Handle values above 7F as UTF-8 encoded character sequences. - bool processed = false; - if (highByte > 127 && equalsPos < input.Length - 2) - { - utf8Buffer[0] = (byte)highByte; - int utf8ByteCount = 1; - - string encodedString = afterEquals; - equalsPos += 3; - - while (input.Substring(equalsPos, 1) == "=") - { - // Step over a line break if that breaks up our encoded string. - if (input.Substring(equalsPos + 1, 2) != "\r\n") - utf8Buffer[utf8ByteCount++] = (byte)int.Parse(input.Substring(equalsPos + 1, 2), NumberStyles.HexNumber); - - equalsPos += 3; - } - - outputBuilder.Append(Utf8toUnicode(utf8Buffer, utf8ByteCount)); - - processed = true; - equalsPos -= 3; - } - - // Continue if we didn't run into a UTF-8 encoded character sequence. - if (!processed) - outputBuilder.Append((char)highByte); - break; - } - - equalsPos += 3; - } - else - { - outputBuilder.Append(input.Substring(lastPos)); - equalsPos = -1; - } - } - return outputBuilder.ToString(); - } - catch - { - // If the quoted-printable encoding is invalid, return the message as-is. - return input; - } - } - - /// - /// Provide a best guess of a file's content type based on its filename. - /// - /// File extension to interpret. - /// A string representing the best guess for the file extension's content type. - public static string GetDefaultContentTypeForExtension(string fileExtension) - { - // If a full filename pas been supplied, extract the extension. - int lastPeriod = fileExtension.LastIndexOf("."); - if (lastPeriod > -1) - fileExtension = fileExtension.Substring(lastPeriod + 1); - - // Default to "application/octet-string" for unknown content types. - string contentType = "application/octet-string"; - - switch (fileExtension.ToLower()) - { - case "gif": - contentType = "image/gif"; - break; - case "htm": - case "html": - contentType = "text/html"; - break; - case "jpg": - case "jpeg": - contentType = "image/jpeg"; - break; - case "pdf": - contentType = "application/pdf"; - break; - case "png": - contentType = "image/png"; - break; - case "rtf": - contentType = "text/richtext"; - break; - case "tif": - case "tiff": - contentType = "image/tiff"; - break; - case "txt": - contentType = "text/plain"; - break; - case "vcard": - case "vcf": - contentType = "text/vcard"; - break; - case "xml": - contentType = "application/xml"; - break; - case "zip": - contentType = "application/zip"; - break; - } - - return contentType; - } - - /// - /// Return the machine's fully-qualified domain name. - /// - /// The machine's fully-qualified domain name. - public static string GetLocalFQDN() - { - string domainName = IPGlobalProperties.GetIPGlobalProperties().DomainName; - string hostName = Dns.GetHostName(); - - return hostName.IndexOf(domainName, StringComparison.Ordinal) > -1 ? hostName : hostName + "." + domainName; - } - - /// - /// Check if the specified e-mail address validates. - /// - /// Address to validate. - /// True if the e-mail address provided passes validation. - private static bool IsValidEmailAddress(string address) - { - return Regex.IsMatch(address, @"^(?("")(""[^""]+?""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9]{2,17}))$", RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250)); - } - - /// - /// Calculates an MD5 has of the string provided. - /// - /// The string to hash. - /// A hexadecimal representation of the MD5 hash. - public static string MD5(string input) - { - // Compute the hash into a byte array. - MD5 md5 = new MD5CryptoServiceProvider(); - byte[] hb = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); - - // Convert the byte array into a hexadecimal string representation. - StringBuilder hashBuilder = new StringBuilder(Constants.TINYSBSIZE); - for (int i = 0; i < hb.Length; i++) - hashBuilder.Append(Convert.ToString(hb[i], 16).PadLeft(2, '0')); - - return hashBuilder.ToString(); - } - - /// - /// Returns string representation of message sent over stream. - /// - /// Stream to receive message from. - /// A byte array to streamline bit shuffling. - /// Any text read from the stream connection. - public static string ReadStreamString(Stream stream, byte[] buffer) - { - int bytesRead = stream.Read(buffer, 0, Constants.LARGEBUFFERSIZE); - return Encoding.UTF8.GetString(buffer, 0, bytesRead); - } - - /// - /// Returns string representation of message sent over stream. - /// - /// StreamReader to receive message from. - /// A character array to streamline bit shuffling. - /// Any text read from the stream connection. - public static string ReadStreamString(StreamReader streamReader, char[] buffer) - { - int bytesRead = streamReader.Read(buffer, 0, Constants.LARGEBUFFERSIZE); - return new string(buffer, 0, bytesRead); - } - - /// - /// Returns string representation of message sent over stream, up the maximum number of bytes specified. - /// - /// Stream to receive message from. - /// A byte array to streamline bit shuffling. - /// Maximum number of bytes to receive. - /// Any text read from the stream connection. - public static string ReadStreamString(Stream stream, byte[] buffer, int maximumBytes) - { - int bytesRead = stream.Read(buffer, 0, maximumBytes); - return Encoding.UTF8.GetString(buffer, 0, bytesRead); - } - - /// - /// Returns string representation of message sent over stream. - /// - /// StreamReader to receive message from. - /// A character array to streamline bit shuffling. - /// Any text read from the stream connection. - public static string ReadStreamString(StreamReader streamReader, char[] buffer, int maximumBytes) - { - int bytesRead = streamReader.Read(buffer, 0, maximumBytes); - return new string(buffer, 0, bytesRead); - } - - /// - /// Returns string representation of message sent over stream. - /// - /// Stream to receive message from. - /// A byte array to streamline bit shuffling. - /// Any text read from the stream connection. - public async static Task ReadStreamStringAsync(Stream stream, byte[] buffer) - { - int bytesRead = await stream.ReadAsync(buffer, 0, Constants.LARGEBUFFERSIZE); - return Encoding.UTF8.GetString(buffer, 0, bytesRead); - } - - /// - /// Returns string representation of message sent over stream. - /// - /// StreamReader to receive message from. - /// A character array to streamline bit shuffling. - /// Any text read from the stream connection. - public async static Task ReadStreamStringAsync(StreamReader streamReader, char[] buffer) - { - int bytesRead = await streamReader.ReadAsync(buffer, 0, Constants.LARGEBUFFERSIZE); - return new string(buffer, 0, bytesRead); - } - - /// - /// Returns string representation of message sent over stream, up the maximum number of bytes specified. - /// - /// Stream to receive message from. - /// A byte array to streamline bit shuffling. - /// Maximum number of bytes to receive. - /// Any text read from the stream connection. - public async static Task ReadStreamStringAsync(Stream stream, byte[] buffer, int maximumBytes) - { - int bytesRead = await stream.ReadAsync(buffer, 0, maximumBytes); - return Encoding.UTF8.GetString(buffer, 0, bytesRead); - } - - /// - /// Returns string representation of message sent over stream. - /// - /// StreamReader to receive message from. - /// A character array to streamline bit shuffling. - /// Maximum number of bytes to receive. - /// Any text read from the stream connection. - public async static Task ReadStreamStringAsync(StreamReader streamReader, char[] buffer, int maximumBytes) - { - int bytesRead = await streamReader.ReadAsync(buffer, 0, maximumBytes); - return new string(buffer, 0, bytesRead); - } - - /// - /// Remove