Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Support for .NET 3.5

Code can now also be compiled targeting .NET 3.5
  • Loading branch information...
commit 6d448440eae853408a366b8d13c19a37aa1c7816 1 parent 32fe35b
Torben Könke authored
2  Auth/Sasl/Mechanisms/Ntlm/Type2Message.cs
View
@@ -130,7 +130,7 @@ internal class Type2Message {
t2.OSVersion = ReadOSVersion(r);
}
t2.TargetName = GetTargetName(r.ReadBytes(targetLength),
- t2.Flags.HasFlag(Flags.NegotiateUnicode));
+ (t2.Flags & Flags.NegotiateUnicode) == Flags.NegotiateUnicode);
if (t2.Version > Type2Version.Version1) {
t2.TargetInformation = ReadTargetInformation(r);
}
20 Auth/Sasl/Mechanisms/SaslScramSha1.cs
View
@@ -259,17 +259,25 @@ internal SaslScramSha1(string username, string password, string cnonce)
/// <param name="password">The supplied password to use.</param>
/// <param name="salt">The salt received from the server.</param>
/// <param name="count">The iteration count.</param>
- /// <returns>An array of bytes containing the result of the
- /// computation of the "Hi()"-formula.</returns>
- /// <remarks>Hi is, essentially, PBKDF2 with HMAC as the
- /// pseudorandom function (PRF) and with dkLen == output length of
- /// HMAC() == output length of H(). (Refer to RFC 5802, p.6)</remarks>
+ /// <returns>An array of bytes containing the result of the computation of the
+ /// "Hi()"-formula.</returns>
+ /// <remarks>
+ /// Hi is, essentially, PBKDF2 with HMAC as the pseudorandom function (PRF) and with
+ /// dkLen == output length of HMAC() == output length of H(). (Refer to RFC 5802, p.6)
+ /// </remarks>
private byte[] Hi(string password, string salt, int count) {
// The salt is sent by the server as a base64-encoded string.
byte[] saltBytes = Convert.FromBase64String(salt);
- using (var db = new Rfc2898DeriveBytes(password, saltBytes, count)) {
+ // Annoyingly, Rfc2898DeriveBytes only implements IDisposable in .NET 4 and upwards.
+ var db = new Rfc2898DeriveBytes(password, saltBytes, count);
+ try {
// Generate 20 key bytes, which is the size of the hash result of SHA-1.
return db.GetBytes(20);
+ } finally {
+#if !NET35
+ if(db != null)
+ db.Dispose();
+#endif
}
}
4 Auth/Sasl/SaslFactory.cs
View
@@ -81,7 +81,11 @@ internal static class SaslFactory {
{ "Ntlm", typeof(Sasl.Mechanisms.SaslNtlm) },
{ "Ntlmv2", typeof(Sasl.Mechanisms.SaslNtlmv2) },
{ "ScramSha1", typeof(Sasl.Mechanisms.SaslScramSha1) },
+ // SRP is not supported in the .NET 3.5 configuration of the library because it requires
+ // the System.Numerics namespace which has only been part of .NET since version 4.
+#if !NET35
{ "Srp", typeof(Sasl.Mechanisms.SaslSrp) }
+#endif
};
foreach (string key in list.Keys)
Mechanisms.Add(key, list[key]);
1  AuthMethod.cs
View
@@ -57,6 +57,7 @@ public enum AuthMethod {
/// <summary>
/// Login using the Secure Remote Password (SRP) authentication mechanism.
/// </summary>
+ /// <remarks>The SRP mechanism is only available when targetting .NET 4.0 or newer.</remarks>
Srp
}
}
10 IImapClient.cs
View
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using System.Collections.Generic;
using System.Net.Mail;
@@ -498,7 +499,9 @@ public interface IImapClient : IDisposable {
/// operate on.</param>
/// <remarks>When copying many messages, this method is more efficient than calling
/// <see cref="CopyMessage"/> for each individual message.</remarks>
- /// <exception cref="ArgumentNullException">The destination parameter is null.</exception>
+ /// <exception cref="ArgumentNullException">The uids parameter or the destination parameter is
+ /// null.</exception>
+ /// <exception cref="ArgumentException">The specified collection of UIDs is empty.</exception>
/// <exception cref="BadServerResponseException">The mail messages could not be copied to the
/// specified destination. The message property of the exception contains the error message
/// returned by the server.</exception>
@@ -541,7 +544,8 @@ public interface IImapClient : IDisposable {
/// operate on.</param>
/// <remarks>When moving many messages, this method is more efficient than calling
/// <see cref="MoveMessage"/> for each individual message.</remarks>
- /// <exception cref="ArgumentNullException">The destination parameter is null.</exception>
+ /// <exception cref="ArgumentNullException">The uids parameter or the destination parameter is
+ /// null.</exception>
/// <exception cref="BadServerResponseException">The mail messages could not be moved to the
/// specified destination. The message property of the exception contains the error message
/// returned by the server.</exception>
@@ -581,6 +585,8 @@ public interface IImapClient : IDisposable {
/// operate on.</param>
/// <remarks>When deleting many messages, this method is more efficient than calling
/// <see cref="DeleteMessage"/> for each individual message.</remarks>
+ /// <exception cref="ArgumentNullException">The uids parameter is null.</exception>
+ /// <exception cref="ArgumentException">The specified collection of UIDs is empty.</exception>
/// <exception cref="BadServerResponseException">The mail messages could not be deleted. The
/// message property of the exception contains the error message returned by the
/// server.</exception>
8 MessageBuilder.cs
View
@@ -239,8 +239,14 @@ internal static class MessageBuilder {
}
if (header["Reply-to"] != null) {
addr = ParseAddressList(header["Reply-to"]);
+ // The ReplayToList property has only been part of the MailMessage class since .NET 4.0.
+#if !NET35
foreach (MailAddress a in addr)
m.ReplyToList.Add(a);
+#else
+ if (addr.Length > 0)
+ m.ReplyTo = addr[0];
+#endif
}
}
@@ -421,7 +427,7 @@ internal static class MessageBuilder {
p.body = body.ToString();
// Add the MIME part to the list unless body is null or empty which means the body
// contained nested multipart content.
- if (!String.IsNullOrWhiteSpace(p.body))
+ if(p.body != null && p.body.Trim() != String.Empty)
list.Add(p);
// If this boundary is the end boundary, we're done.
if (line == null || line.StartsWith(end))
4 Properties/AssemblyInfo.cs
View
@@ -35,5 +35,5 @@
// 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("3.4.0.2")]
-[assembly: AssemblyFileVersion("3.4.0.2")]
+[assembly: AssemblyVersion("3.4.0.3")]
+[assembly: AssemblyFileVersion("3.4.0.3")]
8 Readme.md
View
@@ -5,8 +5,9 @@ receiving electronic mail from an Internet Message Access Protocol (IMAP) server
### Downloads
-You can always get the latest package on [Nuget](http://nuget.org/packages/S22.Imap/) or download the
-binaries as a .zip archive from [here](http://smiley22.github.com/Downloads/S22.Imap.zip). The
+You can always get the latest package on [Nuget](http://nuget.org/packages/S22.Imap/) (includes
+.NET 4.0 and 3.5 binaries) or download the binaries (targetting .NET 4.0) as a .zip archive from
+[here](http://smiley22.github.com/Downloads/S22.Imap.zip). The
[documentation](http://smiley22.github.com/S22.Imap/Documentation/) is also available for offline
viewing as HTML or CHM and can be downloaded from
[here](http://smiley22.github.com/Downloads/S22.Imap.Html.Documentation.zip) and
@@ -47,6 +48,7 @@ further details on using the classes and methods exposed by the S22.Imap namespa
+ Well documented with lots of example code
+ Robust MIME parser, tested with 100.000+ mails
+ Supports various SASL authentication mechanisms
++ Still supports .NET 3.5
+ Free to use in commercial and personal projects ([MIT license](https://github.com/smiley22/S22.Imap/blob/master/License.md))
### Credits
@@ -63,5 +65,5 @@ This library is released under the [MIT license](https://github.com/smiley22/S22
### Bug reports
-Please send your bug reports and questions to [smileytwentytwo@gmail.com](mailto:smileytwentytwo@gmail.com) or create a new
+Please send your bug reports to [smileytwentytwo@gmail.com](mailto:smileytwentytwo@gmail.com) or create a new
issue on the GitHub project homepage.
40 S22.Imap.csproj
View
@@ -18,27 +18,31 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
+ <OutputPath>bin\$(Configuration) $(TargetFrameworkVersion)\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
- <DocumentationFile>bin\Debug\S22.Imap.XML</DocumentationFile>
+ <DocumentationFile>bin\$(Configuration) $(TargetFrameworkVersion)\S22.Imap.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
+ <OutputPath>bin\$(Configuration) $(TargetFrameworkVersion)\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
+ <PropertyGroup>
+ <!-- http://stackoverflow.com/questions/2923210/c-sharp-conditional-compilation-and-framework-targets -->
+ <DefineConstants Condition=" !$(DefineConstants.Contains(';NET')) ">$(DefineConstants);$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
+ <DefineConstants Condition=" $(DefineConstants.Contains(';NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(";NET"))));$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
+ </PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
- <Reference Include="System.Numerics" />
+ <Reference Include="System.Numerics" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
- <Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
@@ -48,16 +52,16 @@
<Compile Include="Auth\FilterStream.cs" />
<Compile Include="Auth\Handshake.cs" />
<Compile Include="Auth\Sasl\Mechanisms\SaslScramSha1.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\SaslSrp.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\ClientMessage1.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\ClientMessage2.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\Extensions.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\Helper.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\Mpi.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\OctetSequence.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\ServerMessage1.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\ServerMessage2.cs" />
- <Compile Include="Auth\Sasl\Mechanisms\Srp\Utf8String.cs" />
+ <Compile Include="Auth\Sasl\Mechanisms\SaslSrp.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\ClientMessage1.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\ClientMessage2.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\Extensions.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\Helper.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\Mpi.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\OctetSequence.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\ServerMessage1.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\ServerMessage2.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
+ <Compile Include="Auth\Sasl\Mechanisms\Srp\Utf8String.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
<Compile Include="Bodystructure\Bodypart.cs" />
<Compile Include="Bodystructure\Bodystructure.cs" />
<Compile Include="Bodystructure\ContentDisposition.cs" />
@@ -120,4 +124,10 @@
<Target Name="AfterBuild">
</Target>
-->
+ <Target Name="AfterBuild">
+ <MSBuild Condition=" '$(TargetFrameworkVersion)' == 'v4.0'"
+ Projects="$(MSBuildProjectFile)"
+ Properties="TargetFrameworkVersion=v3.5"
+ RunEachTargetSeparately="true" />
+ </Target>
</Project>
29 SearchCondition.cs
View
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Text;
+using System.Linq;
namespace S22.Imap {
/// <summary>
@@ -378,16 +379,34 @@ enum Fields {
}
/// <summary>
+ /// Concatenates the members of a collection, using the specified separator between each
+ /// member.
+ /// </summary>
+ /// <typeparam name="T">The type of the members of values.</typeparam>
+ /// <param name="seperator">The string to use as a separator.</param>
+ /// <param name="values">A collection that contains the objects to concatenate.</param>
+ /// <returns>A string that consists of the members of values delimited by the separator
+ /// string. If values has no members, the method returns System.String.Empty.</returns>
+ /// <exception cref="ArgumentNullException">The values parameter is null.</exception>
+ /// <remarks>This is already part of the String class in .NET 4.0 and newer but is needed
+ /// for backwards compatibility with .NET 3.5.</remarks>
+ static string Join<T>(string seperator, IEnumerable<T> values) {
+ values.ThrowIfNull("values");
+ IList<string> list = new List<string>();
+ foreach (T v in values)
+ list.Add(v.ToString());
+ return string.Join(seperator, list.ToArray());
+ }
+
+ /// <summary>
/// Constructs a string from the SearchCondition object using the proper syntax as is required
/// for the IMAP SEARCH command.
/// </summary>
/// <returns>A string representing this SearchCondition instance that can be used with the IMAP
/// SEARCH command.</returns>
public override string ToString() {
- if (Conditions != null && Conditions.Count > 0 && Operator != null) {
- return (Operator.ToUpper() + " (" +
- string.Join(") (", Conditions) + ")").Trim();
- }
+ if (Conditions != null && Conditions.Count > 0 && Operator != null)
+ return (Operator.ToUpper() + " (" + Join(") (", Conditions) + ")").Trim();
StringBuilder builder = new StringBuilder();
if (Field != null)
builder.Append(Field.ToString().ToUpper());
@@ -409,7 +428,7 @@ enum Fields {
Val = ((DateTime)Val).ToString("dd-MMM-yyyy",
CultureInfo.InvariantCulture).QuoteString();
} else if (Val is uint[]) {
- Val = string.Join<uint>(",", (uint[])Val);
+ Val = Join<uint>(",", (uint[])Val);
}
builder.Append(Val);
}
8 Tests/EncodedWordTest.cs
View
@@ -59,13 +59,13 @@ public class EncodedWordTest {
{ "", "" },
{ "=?gb2312?B?Y21uZHkua2FuZyi/uvb09s4p?= <cindy.kang@xxxcorp.com>",
"\"cmndy.kang(亢鲷鑫)\" <cindy.kang@xxxcorp.com>" },
- { "undisclosed recipients: ;", "" }, // can we get display: "undisclosed recipients: ;"?
- { "undisclosed-recipients:;", "" }, // can we get display: "undisclosed-recipients:;"?
+ { "undisclosed recipients: ;", "" },
+ { "undisclosed-recipients:;", "" },
{ "\"Hiroyuki Tanaka, Japan\" <MLAXXX_XX.Mu-lti+sub@s_u-b.nifty.com>",
"\"Hiroyuki Tanaka, Japan\" <MLAXXX_XX.Mu-lti+sub@s_u-b.nifty.com>" },
{ "test1 <test1@domain>, \"test2\" <test2@domain>, \"test, nr3\" <test3@domain>",
"\"test1\" <test1@domain>, \"test2\" <test2@domain>, \"test, nr3\" <test3@domain>" },
- // This is not supported by the MailAddress Class, but valid RFC.
+ // This is not supported by the MailAddress class, but valid RFC.
{ "only.local", "" },
{ "@;", "" },
// Minimum length of a mail address is at least 3 characters.
@@ -73,6 +73,8 @@ public class EncodedWordTest {
{ "<a@b>", "a@b" },
// This should be decoded to opqkuv@m.linaaken.com@yahoo.com, but not valid in .NET
// From Issue #48.
+ // This test fails when compiling for .NET 3.5, because the .NET 3.5
+ // MailAddressCollection implementation differs from .NET 4.0 and upwards.
{ "opqkuv@m.linaaken.com<=?UTF-8?B?b3Bxa3V2QG0ubGluYWFrZW4uY29t?=@yahoo.com>;",
"\"opqkuv@m.linaaken.com\" <=?UTF-8?B?b3Bxa3V2QG0ubGluYWFrZW4uY29t?=@yahoo.com>" },
};
10 Tests/Tests.csproj
View
@@ -16,6 +16,7 @@
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
+ <TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -34,12 +35,17 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
+ <PropertyGroup>
+ <!-- http://stackoverflow.com/questions/2923210/c-sharp-conditional-compilation-and-framework-targets -->
+ <DefineConstants Condition=" !$(DefineConstants.Contains(';NET')) ">$(DefineConstants);$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
+ <DefineConstants Condition=" $(DefineConstants.Contains(';NET')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(";NET"))));$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants>
+ </PropertyGroup>
<ItemGroup>
<Reference Include="S22.Imap">
<HintPath>..\bin\Debug\S22.Imap.dll</HintPath>
</Reference>
<Reference Include="System" />
- <Reference Include="System.Numerics" />
+ <Reference Include="System.Numerics" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
@@ -75,7 +81,7 @@
<Compile Include="PlainTest.cs" />
<Compile Include="OAuth2Test.cs" />
<Compile Include="SequenceSetTest.cs" />
- <Compile Include="SrpTest.cs" />
+ <Compile Include="SrpTest.cs" Condition=" '$(TargetFrameworkVersion)' != 'v3.5' " />
<Compile Include="UTF7Test.cs" />
</ItemGroup>
<ItemGroup>
Please sign in to comment.
Something went wrong with that request. Please try again.