├── src ├── ActionMailerNext.Tests │ ├── Standalone │ │ ├── TestViews │ │ │ ├── UTF8TextView.hbs │ │ │ ├── MultipartNoModel.hbs │ │ │ ├── TextViewWithModel.hbs │ │ │ └── WhitespaceTrimTest.hbs │ │ ├── TestModel.cs │ │ ├── TestMailerBase.cs │ │ ├── RazorEmailResultTests.cs │ │ └── RazorMailerBaseTests.cs │ ├── SampleData │ │ └── logo.png │ ├── MailSendingContextTests.cs │ └── ActionMailerNext.Tests.csproj ├── ActionMailerNext.SendInBlueMailSender.Tests │ ├── content.txt │ ├── ActionMailerNext.SendInBlue.Tests.csproj │ └── SendInBlueMailSenderTests.cs ├── .nuget │ ├── NuGet.exe │ ├── NuGet.Config │ └── NuGet.targets ├── ActionMailerNext │ ├── Interfaces │ │ ├── IPostProcessor.cs │ │ ├── IMailResponse.cs │ │ ├── IEmailResult.cs │ │ ├── IMailInterceptor.cs │ │ └── IMailSender.cs │ ├── DeliveryStatus.cs │ ├── ActionMailerNext.csproj │ ├── GlobalSuppressions.cs │ ├── Utils │ │ ├── AlternativeViewCollection.cs │ │ ├── NoViewsFoundException.cs │ │ ├── AttachmentCollection.cs │ │ └── MimeTypes.cs │ ├── ActionMailerNext.nuspec │ ├── Implementations │ │ └── SMTP │ │ │ ├── SmtpMailResponse.cs │ │ │ └── SmtpMailSender.cs │ ├── MailSendingContext.cs │ ├── PostProccesors │ │ └── InlineCssPostProcessor.cs │ └── MailAttributes.cs ├── ActionMailerNext.Standalone │ ├── Models │ │ ├── ViewSettings.cs │ │ └── MailTemplate.cs │ ├── Interfaces │ │ ├── ITemplateResolver.cs │ │ └── ITemplateService.cs │ ├── TemplateResolvingException.cs │ ├── ActionMailerNext.Standalone.csproj │ ├── Extensions │ │ └── HandlebarsTemplateExtension.cs │ ├── ActionMailerNext.Standalone.nuspec │ ├── GlobalSuppressions.cs │ ├── Implementations │ │ ├── HandlebarsFilesTemplateResolver.cs │ │ └── TemplateService.cs │ ├── HbsEmailResult.cs │ ├── HbsMailerBase.cs │ └── Helpers │ │ └── UtilHelper.cs ├── ActionMailerNext.SendInBlue │ ├── SendInBlueMailResponse.cs │ ├── SendInBlueException.cs │ ├── ActionMailerNext.SendInBlue.csproj │ ├── GlobalSuppressions.cs │ └── SendInBlueMailSender.cs ├── packages │ └── repositories.config ├── ActionMailer.Net.sln └── Settings.StyleCop ├── samples └── StandaloneSample │ ├── packages │ ├── repositories.config │ ├── RazorEngine.2.1 │ │ ├── RazorEngine.2.1.nupkg │ │ └── lib │ │ │ └── .NetFramework 4.0 │ │ │ ├── RazorEngine.dll │ │ │ └── System.Web.Razor.dll │ └── ActionMailer.Standalone.0.6.0 │ │ ├── lib │ │ └── Net40 │ │ │ ├── ActionMailer.Net.dll │ │ │ └── ActionMailer.Net.Standalone.dll │ │ └── ActionMailer.Standalone.0.6.0.nupkg │ ├── StandaloneSample │ ├── packages.config │ ├── Program.cs │ ├── Mailers │ │ └── SampleMailer.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── StandaloneSample.csproj │ └── StandaloneSample.sln ├── .gitattributes ├── README.md ├── LICENSE.txt ├── create-new-release.bat ├── .gitignore └── CHANGES.txt /src/ActionMailerNext.Tests/Standalone/TestViews/UTF8TextView.hbs: -------------------------------------------------------------------------------- 1 | Umlauts are Über! -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/Standalone/TestViews/MultipartNoModel.hbs: -------------------------------------------------------------------------------- 1 |

Testing multipart.

-------------------------------------------------------------------------------- /src/ActionMailerNext.SendInBlueMailSender.Tests/content.txt: -------------------------------------------------------------------------------- 1 | 2 | ****Test SendInBlue******** 3 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/Standalone/TestViews/TextViewWithModel.hbs: -------------------------------------------------------------------------------- 1 | Your name is: {{Model.Name}} -------------------------------------------------------------------------------- /src/.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossvertise/ActionMailerNext/HEAD/src/.nuget/NuGet.exe -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/Standalone/TestViews/WhitespaceTrimTest.hbs: -------------------------------------------------------------------------------- 1 | 2 | This thing has leading and trailing whitespace. 3 | 4 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/SampleData/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossvertise/ActionMailerNext/HEAD/src/ActionMailerNext.Tests/SampleData/logo.png -------------------------------------------------------------------------------- /samples/StandaloneSample/packages/repositories.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/Standalone/TestModel.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Tests.Standalone 2 | { 3 | public class TestModel 4 | { 5 | public string Name { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /samples/StandaloneSample/packages/RazorEngine.2.1/RazorEngine.2.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossvertise/ActionMailerNext/HEAD/samples/StandaloneSample/packages/RazorEngine.2.1/RazorEngine.2.1.nupkg -------------------------------------------------------------------------------- /src/.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/ActionMailerNext/Interfaces/IPostProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Interfaces 2 | { 3 | public interface IPostProcessor 4 | { 5 | MailAttributes Execute(MailAttributes mailAttributes); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/StandaloneSample/StandaloneSample/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /samples/StandaloneSample/packages/RazorEngine.2.1/lib/.NetFramework 4.0/RazorEngine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossvertise/ActionMailerNext/HEAD/samples/StandaloneSample/packages/RazorEngine.2.1/lib/.NetFramework 4.0/RazorEngine.dll -------------------------------------------------------------------------------- /samples/StandaloneSample/packages/ActionMailer.Standalone.0.6.0/lib/Net40/ActionMailer.Net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossvertise/ActionMailerNext/HEAD/samples/StandaloneSample/packages/ActionMailer.Standalone.0.6.0/lib/Net40/ActionMailer.Net.dll -------------------------------------------------------------------------------- /samples/StandaloneSample/packages/RazorEngine.2.1/lib/.NetFramework 4.0/System.Web.Razor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossvertise/ActionMailerNext/HEAD/samples/StandaloneSample/packages/RazorEngine.2.1/lib/.NetFramework 4.0/System.Web.Razor.dll -------------------------------------------------------------------------------- /src/ActionMailerNext/DeliveryStatus.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ActionMailerNext 3 | { 4 | public enum DeliveryStatus 5 | { 6 | DELIVERED = 0, 7 | REJECTED = 1, 8 | QUEUED = 2, 9 | INVALID = 3 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /samples/StandaloneSample/packages/ActionMailer.Standalone.0.6.0/ActionMailer.Standalone.0.6.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossvertise/ActionMailerNext/HEAD/samples/StandaloneSample/packages/ActionMailer.Standalone.0.6.0/ActionMailer.Standalone.0.6.0.nupkg -------------------------------------------------------------------------------- /src/ActionMailerNext/Interfaces/IMailResponse.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace ActionMailerNext.Interfaces 3 | { 4 | public interface IMailResponse 5 | { 6 | string Email { get; } 7 | 8 | DeliveryStatus DeliveryStatus { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /samples/StandaloneSample/packages/ActionMailer.Standalone.0.6.0/lib/Net40/ActionMailer.Net.Standalone.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crossvertise/ActionMailerNext/HEAD/samples/StandaloneSample/packages/ActionMailer.Standalone.0.6.0/lib/Net40/ActionMailer.Net.Standalone.dll -------------------------------------------------------------------------------- /samples/StandaloneSample/StandaloneSample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace StandaloneSample { 7 | class Program { 8 | static void Main(string[] args) { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/Models/ViewSettings.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone.Models 2 | { 3 | public class ViewSettings 4 | { 5 | public string Hostname { get; set; } 6 | 7 | public string Protocol { get; set; } 8 | 9 | public string UrlPattern { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ActionMailerNext.SendInBlue/SendInBlueMailResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.SendInBlue 2 | { 3 | using ActionMailerNext.Interfaces; 4 | 5 | public class SendInBlueMailResponse : IMailResponse 6 | { 7 | public string Email { get; set; } 8 | 9 | public DeliveryStatus DeliveryStatus { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ActionMailerNext/ActionMailerNext.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 3.5.3 6 | crossvertise GmbH 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/Interfaces/ITemplateResolver.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone.Interfaces 2 | { 3 | using System.Collections.Generic; 4 | 5 | using ActionMailerNext.Standalone.Models; 6 | 7 | public interface ITemplateResolver 8 | { 9 | string Resolve(string name, string externalViewPath = null); 10 | 11 | List GetAllPartialTemplates(); 12 | } 13 | } -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/Interfaces/ITemplateService.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone.Interfaces 2 | { 3 | using HandlebarsDotNet; 4 | 5 | public interface ITemplateService 6 | { 7 | HandlebarsTemplate Compile(string viewName, string layout = null, string externalViewPath = null); 8 | 9 | void AddTemplate(string viewName, string key = null, string externalViewPath = null); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/ActionMailerNext.SendInBlue/SendInBlueException.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.SendInBlue 2 | { 3 | using System; 4 | 5 | [Serializable] 6 | public class SendInBlueException : Exception 7 | { 8 | public SendInBlueException() { } 9 | 10 | public SendInBlueException(string message) 11 | : base(message) { } 12 | 13 | public SendInBlueException(string message, Exception inner) 14 | : base(message, inner) { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/ActionMailerNext/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "", Scope = "type", Target = "~T:ActionMailerNext.Utils.AlternativeViewCollection")] 9 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/TemplateResolvingException.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | /// 7 | /// Exception thrown when a template could not be resolved 8 | /// 9 | public class TemplateResolvingException : Exception 10 | { 11 | /// 12 | /// A list of paths that were checked when searching for a template. 13 | /// 14 | public List SearchPaths { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/Models/MailTemplate.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone.Models 2 | { 3 | public class MailTemplate 4 | { 5 | public MailTemplate(string key, string value, string label, bool isPartial) 6 | { 7 | Key = key; 8 | Label = label; 9 | Value = value; 10 | IsPartial = isPartial; 11 | } 12 | 13 | public string Key { get; set; } 14 | 15 | public string Value { get; set; } 16 | 17 | public string Label { get; set; } 18 | 19 | public bool IsPartial { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ActionMailerNext.SendInBlue/ActionMailerNext.SendInBlue.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 1.0.4 6 | crossvertise GmbH 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/ActionMailerNext.Standalone.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 3.5.17 6 | crossvertise GmbH 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/ActionMailerNext/Utils/AlternativeViewCollection.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Utils 2 | { 3 | using System.Collections.Generic; 4 | using System.Net.Mail; 5 | 6 | public class AlternativeViewCollection : Dictionary 7 | { 8 | public AlternativeViewCollection() 9 | { 10 | Inline = new Dictionary(); 11 | } 12 | 13 | /// 14 | /// Any attachments added to this collection will be treated as inline attachments within the mail message. 15 | /// 16 | public Dictionary Inline { get; private set; } 17 | } 18 | } -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/Extensions/HandlebarsTemplateExtension.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone.Helpers 2 | { 3 | using System.Collections.Generic; 4 | 5 | using HandlebarsDotNet; 6 | 7 | public static class HandlebarsTemplateExtension 8 | { 9 | public static string Run(this HandlebarsTemplate self, object model, object viewbag) 10 | { 11 | var context = new 12 | { 13 | Model = model, 14 | ViewBag = viewbag, 15 | __Config = new Dictionary() 16 | }; 17 | 18 | return self(context); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ActionMailerNext/Interfaces/IEmailResult.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Interfaces 2 | { 3 | using System.Text; 4 | 5 | /// 6 | /// Interface for email results. Allows to send the message synchonously or asynchonously. 7 | /// 8 | public interface IEmailResult 9 | { 10 | /// 11 | /// The underlying MailAttributes object that was passed to this object's constructor. 12 | /// 13 | MailAttributes MailAttributes { get; } 14 | 15 | /// 16 | /// The default encoding used to send a message. 17 | /// 18 | Encoding MessageEncoding { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /src/ActionMailerNext/ActionMailerNext.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ActionMailerNext 5 | $version$ 6 | $author$ 7 | $author$ 8 | https://github.com/crossvertise/ActionMailerNext/blob/master/LICENSE.txt 9 | https://github.com/crossvertise/ActionMailerNext 10 | false 11 | $description$ 12 | Copyright 2014 Scott Anderson, crossvertise GmbH 13 | email mvc asp.net razor actionmailer actionmailer.net 14 | 15 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/ActionMailerNext.Standalone.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ActionMailerNext.Standalone 5 | $version$ 6 | $author$ 7 | $author$ 8 | https://github.com/crossvertise/ActionMailerNext/blob/master/LICENSE.txt 9 | https://github.com/crossvertise/ActionMailerNext 10 | false 11 | $description$ 12 | Copyright 2014 Scott Anderson, crossvertise GmbH 13 | email mvc asp.net razor actionmailer actionmailer.net 14 | 15 | -------------------------------------------------------------------------------- /src/ActionMailerNext.SendInBlue/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "", Scope = "type", Target = "~T:ActionMailerNext.SendInBlue.SendInBlueException")] 9 | [assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "", Scope = "type", Target = "~T:ActionMailerNext.SendInBlue.SendInBlueMailSender")] 10 | -------------------------------------------------------------------------------- /samples/StandaloneSample/StandaloneSample/Mailers/SampleMailer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Text; 7 | using ActionMailer.Net.Standalone; 8 | 9 | namespace StandaloneSample.Mailers { 10 | public class SampleMailer : RazorMailerBase { 11 | private readonly string _viewPath; 12 | public override string ViewPath { 13 | get { return _viewPath; } 14 | } 15 | 16 | public SampleMailer() { 17 | var workingDirectory = Assembly.GetExecutingAssembly().Location; 18 | _viewPath = Path.Combine(workingDirectory, "..", "..", "Templates"); 19 | } 20 | 21 | public RazorEmailResult EmailWithNoModel() { 22 | 23 | } 24 | 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ActionMailerNext is a fork of ActionMailer.Net created by Scott Anderson (https://bitbucket.org/swaj/actionmailer.net/wiki/Home) 2 | Since ActionMailer.Net is no longer maintained, this fork will continue maintenance and development. 3 | 4 | Any collaboration, pull-requests and suggestions are highly welcome. 5 | 6 | Check our Wiki for installation, usage and configuration 7 | https://github.com/crossvertise/ActionMailerNext/wiki 8 | 9 | This application is built using AppVeyor: 10 | 11 | Develop Branch 12 | [![Build status](https://ci.appveyor.com/api/projects/status/bb54gt8tseohem8f/branch/develop)](https://ci.appveyor.com/project/Crossvertise/actionmailernext-759/branch/develop) 13 | 14 | Master Branch 15 | [![Build status](https://ci.appveyor.com/api/projects/status/6w54rmi2n2yduhb9/branch/master)](https://ci.appveyor.com/project/Crossvertise/actionmailernext/branch/master) -------------------------------------------------------------------------------- /src/ActionMailerNext/Interfaces/IMailInterceptor.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Interfaces 2 | { 3 | /// 4 | /// A simple interface that allows for reading or manipulating mail messages before and after transfer. 5 | /// 6 | public interface IMailInterceptor 7 | { 8 | /// 9 | /// This method is called before each mail is sent 10 | /// 11 | /// 12 | /// A simple context containing the mail and a boolean value that can be toggled to prevent this mail from being sent. 13 | /// 14 | void OnMailSending(MailSendingContext context); 15 | 16 | /// 17 | /// This method is called after each mail is sent. 18 | /// 19 | /// The mail that was sent. 20 | void OnMailSent(MailAttributes mail); 21 | } 22 | } -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "", Scope = "type", Target = "~T:ActionMailerNext.Standalone.HBSEmailResult")] 9 | [assembly: SuppressMessage("Minor Code Smell", "S101:Types should be named in PascalCase", Justification = "", Scope = "type", Target = "~T:ActionMailerNext.Standalone.HBSMailerBase")] 10 | [assembly: SuppressMessage("Major Code Smell", "S3925:\"ISerializable\" should be implemented correctly", Justification = "", Scope = "type", Target = "~T:ActionMailerNext.Standalone.TemplateResolvingException")] 11 | -------------------------------------------------------------------------------- /samples/StandaloneSample/StandaloneSample.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StandaloneSample", "StandaloneSample\StandaloneSample.csproj", "{94AC929F-B115-4084-A299-AD572F7A8AAF}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|x86 = Debug|x86 9 | Release|x86 = Release|x86 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {94AC929F-B115-4084-A299-AD572F7A8AAF}.Debug|x86.ActiveCfg = Debug|x86 13 | {94AC929F-B115-4084-A299-AD572F7A8AAF}.Debug|x86.Build.0 = Debug|x86 14 | {94AC929F-B115-4084-A299-AD572F7A8AAF}.Release|x86.ActiveCfg = Release|x86 15 | {94AC929F-B115-4084-A299-AD572F7A8AAF}.Release|x86.Build.0 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /src/packages/repositories.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/ActionMailerNext/Implementations/SMTP/SmtpMailResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Implementations.SMTP 2 | { 3 | using System; 4 | using System.Net.Mail; 5 | 6 | using ActionMailerNext.Interfaces; 7 | 8 | public class SmtpMailResponse : IMailResponse 9 | { 10 | public string Email { get; set; } 11 | 12 | public SmtpStatusCode Status { get; set; } 13 | 14 | public DeliveryStatus DeliveryStatus { get; set; } 15 | 16 | public string RejectReason { get; set; } 17 | 18 | public override string ToString() => string.Format("Email : {0}\nStatus : {1}\nRejection Reason : {2}", Email, Status, RejectReason); 19 | 20 | public static SmtpStatusCode GetProspectiveStatus(string statusString) => (SmtpStatusCode)Enum.Parse(typeof(SmtpStatusCode), statusString, true); 21 | 22 | public static DeliveryStatus GetdeliveryStatus(string deliveryStatus) => (DeliveryStatus)Enum.Parse(typeof(DeliveryStatus), deliveryStatus, true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/ActionMailerNext.SendInBlueMailSender.Tests/ActionMailerNext.SendInBlue.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Always 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Scott W. Anderson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /src/ActionMailerNext/MailSendingContext.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext 2 | { 3 | /// 4 | /// A special context object used by the OnMailSending() method 5 | /// to allow you to inspect the underlying MailMessage before it 6 | /// is sent, or prevent it from being sent altogether. 7 | /// 8 | public class MailSendingContext 9 | { 10 | /// 11 | /// The generated mail message that is being sent. 12 | /// 13 | public readonly MailAttributes Mail; 14 | 15 | /// 16 | /// A special flag that you can toggle to prevent this mail 17 | /// from being sent. 18 | /// 19 | public bool Cancel; 20 | 21 | /// 22 | /// Returns a populated context to be used for the OnMailSending() 23 | /// method in MailerBase. 24 | /// 25 | /// The message you wish to wrap within this context. 26 | public MailSendingContext(MailAttributes mail) 27 | { 28 | Mail = mail; 29 | Cancel = false; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/ActionMailerNext/Utils/NoViewsFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Utils 2 | { 3 | using System; 4 | 5 | /// 6 | /// Thrown when ActionMailer cannot locate any views for a given EmailResult 7 | /// 8 | public class NoViewsFoundException : Exception 9 | { 10 | /// 11 | /// Thrown when ActionMailer cannot locate any views for a given EmailResult 12 | /// 13 | public NoViewsFoundException() 14 | { 15 | } 16 | 17 | /// 18 | /// Thrown when ActionMailer cannot locate any views for a given EmailResult 19 | /// 20 | /// The message to include in the exception. 21 | public NoViewsFoundException(string message) : base(message) 22 | { 23 | } 24 | 25 | /// 26 | /// Thrown when ActionMailer cannot locate any views for a given EmailResult 27 | /// 28 | /// The message to include in the exception. 29 | /// An inner exception which contributed to (or caused) this exception. 30 | public NoViewsFoundException(string message, Exception innerException) : base(message, innerException) 31 | { 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/ActionMailerNext/Interfaces/IMailSender.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Interfaces 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Threading.Tasks; 6 | 7 | /// 8 | /// An object used to deliver SMTPMailMessage. 9 | /// 10 | public interface IMailSender : IDisposable 11 | { 12 | /// 13 | /// Sends MailAttributes synchronously. 14 | /// 15 | /// The SMTPMailMessage message you wish to send. 16 | List Send(MailAttributes mailAttributes); 17 | 18 | /// 19 | /// Sends MailAttributes asynchronously using tasks. 20 | /// 21 | /// The SMTPMailMessage message you wish to send. 22 | Task> SendAsync(MailAttributes mailAttributes); 23 | 24 | /// 25 | /// Method to be called directly after defining MailSender to deliver email synchronously. 26 | /// 27 | /// 28 | /// 29 | List Deliver(IEmailResult emailResult); 30 | 31 | /// 32 | /// Method to be called directly after defining MailSender to deliver email asynchronously. 33 | /// 34 | /// 35 | /// 36 | Task DeliverAsync(IEmailResult emailResult); 37 | } 38 | } -------------------------------------------------------------------------------- /samples/StandaloneSample/StandaloneSample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("StandaloneSample")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("American Railcar Industries, Inc.")] 12 | [assembly: AssemblyProduct("StandaloneSample")] 13 | [assembly: AssemblyCopyright("Copyright © American Railcar Industries, Inc. 2011")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e8670975-53c1-490b-af9b-8259093c93a4")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/ActionMailerNext/PostProccesors/InlineCssPostProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.PostProccesors 2 | { 3 | using Interfaces; 4 | 5 | using PreMailer.Net; 6 | 7 | using System.IO; 8 | using System.Net.Mail; 9 | using System.Net.Mime; 10 | using System.Text; 11 | 12 | public class InlineCssPostProcessor : IPostProcessor 13 | { 14 | public MailAttributes Execute(MailAttributes mailAttributes) 15 | { 16 | var newMailAttributes = new MailAttributes(mailAttributes); 17 | 18 | foreach (var view in mailAttributes.AlternateViews) 19 | { 20 | if (!view.ContentStream.CanRead) 21 | { 22 | continue; 23 | } 24 | 25 | using (var reader = new StreamReader(view.ContentStream)) 26 | { 27 | var body = reader.ReadToEnd(); 28 | 29 | if (view.ContentType.MediaType == MediaTypeNames.Text.Html) 30 | { 31 | var inlinedCssString = PreMailer.MoveCssInline(body); 32 | var bytes = Encoding.UTF8.GetBytes(inlinedCssString.Html); 33 | var stream = new MemoryStream(bytes); 34 | 35 | var newAlternateView = new AlternateView(stream, MediaTypeNames.Text.Html); 36 | newMailAttributes.AlternateViews.Add(newAlternateView); 37 | } 38 | else 39 | { 40 | newMailAttributes.AlternateViews.Add(view); 41 | } 42 | } 43 | } 44 | 45 | return newMailAttributes; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/ActionMailerNext/Utils/AttachmentCollection.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Utils 2 | { 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Net.Mail; 6 | 7 | /// 8 | /// A collection of attachments. This is basically a glorified Dictionary. 9 | /// 10 | public class AttachmentCollection : Dictionary 11 | { 12 | public AttachmentCollection() 13 | { 14 | Inline = new Dictionary(); 15 | } 16 | 17 | /// 18 | /// Any attachments added to this collection will be treated as inline attachments within the mail message. 19 | /// 20 | public Dictionary Inline { get; private set; } 21 | 22 | public static Attachment ModifyAttachmentProperties(string fileName, byte[] fileBytes, bool inline) 23 | { 24 | // Ideally we'd like to find the mime type for each attachment automatically based on the file extension. 25 | string mimeType = null; 26 | 27 | if (inline) 28 | { 29 | mimeType = "multipart/related"; 30 | } 31 | else 32 | { 33 | var extension = fileName.Substring(fileName.LastIndexOf(".")); 34 | if (!string.IsNullOrEmpty(extension)) 35 | { 36 | mimeType = MimeTypes.ResolveByExtension(extension); 37 | } 38 | } 39 | 40 | var memoryStream = new MemoryStream(fileBytes); 41 | var modifiedAttachment = new Attachment(memoryStream, fileName, mimeType); 42 | modifiedAttachment.ContentDisposition.Inline = inline; 43 | modifiedAttachment.ContentId = fileName; 44 | return modifiedAttachment; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/MailSendingContextTests.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright (C) 2012 by Scott W. Anderson 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #endregion 25 | 26 | using System.Net.Mail; 27 | 28 | using NUnit.Framework; 29 | 30 | namespace ActionMailerNext.Tests 31 | { 32 | [TestFixture] 33 | public class MailSendingContextTests 34 | { 35 | [Test] 36 | public void MailContextConstructorSetsUpObjectProperly() 37 | { 38 | var mail = new MailAttributes { From = new MailAddress("no-reply@test.com") }; 39 | mail.To.Add(new MailAddress("test@test.com")); 40 | 41 | var context = new MailSendingContext(mail); 42 | 43 | Assert.AreEqual(mail, context.Mail); 44 | Assert.False(context.Cancel); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/ActionMailerNext.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Always 18 | 19 | 20 | Always 21 | 22 | 23 | Always 24 | 25 | 26 | Always 27 | 28 | 29 | Always 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/Standalone/TestMailerBase.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright (C) 2012 by Scott W. Anderson 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #endregion 25 | namespace ActionMailerNext.Tests.Standalone 26 | { 27 | using System.IO; 28 | 29 | using ActionMailerNext.Interfaces; 30 | using ActionMailerNext.Standalone; 31 | using ActionMailerNext.Standalone.Models; 32 | 33 | public class TestMailerBase : HBSMailerBase 34 | { 35 | public TestMailerBase(MailAttributes attributes = null, IMailSender sender = null) 36 | : base(attributes, sender) 37 | { 38 | } 39 | 40 | public override string GlobalViewPath => Path.Combine(Directory.GetCurrentDirectory(), "Standalone", "TestViews"); 41 | 42 | public override ViewSettings ViewSettings => new ViewSettings 43 | { 44 | Hostname = "test.com", 45 | Protocol = "http", 46 | UrlPattern = "{controller}/{action}/{id}" 47 | }; 48 | } 49 | } -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/Standalone/RazorEmailResultTests.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012 by Scott W. Anderson 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to deal 5 | * in the Software without restriction, including without limitation the rights 6 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | * copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | namespace ActionMailerNext.Tests.Standalone 22 | { 23 | using System; 24 | 25 | using ActionMailerNext.Standalone; 26 | using ActionMailerNext.Standalone.Interfaces; 27 | 28 | using FakeItEasy; 29 | 30 | using NUnit.Framework; 31 | 32 | public class RazorEmailResultTests 33 | { 34 | [Test] 35 | public void ConstructorWithNullMailMessageThrows() 36 | { 37 | var mockTemplateService = A.Fake(); 38 | 39 | Assert.Throws( 40 | () => 41 | { 42 | new HBSEmailResult(null, "View", null, "_Layout", "Path", mockTemplateService, 43 | null); 44 | }); 45 | } 46 | 47 | [Test] 48 | public void ConstructorWithNullViewNameThrows() 49 | { 50 | var mockTemplateService = A.Fake(); 51 | 52 | Assert.Throws( 53 | () => 54 | { 55 | new HBSEmailResult(new MailAttributes(), null, null, "_Layout", "Path", 56 | mockTemplateService, null); 57 | }); 58 | } 59 | 60 | [Test] 61 | public void ConstructorWithNullTemplateServiceThrows() 62 | { 63 | Assert.Throws( 64 | () => 65 | { 66 | new HBSEmailResult(new MailAttributes(), "View", null, "_Layout", "Path", 67 | null, null); 68 | }); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /create-new-release.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | CLS 3 | 4 | SET NewVersion= 5 | SET /P NewVersion=What is the new version called? 6 | SET MvcDir=%CD%\src\ActionMailer.Net.Mvc\bin\Release 7 | SET Mvc4Dir=%CD%\src\ActionMailer.Net.Mvc4\bin\Release 8 | SET PostmarkDir=%CD%\src\ActionMailer.Net.Postmark\bin\Release 9 | SET StandaloneDir=%CD%\src\ActionMailer.Net.Standalone\bin\Release 10 | SET NgMvcDir=%CD%\nuget\base\ActionMailer 11 | SET NgMvc4Dir=%CD%\nuget\base\ActionMailer.Mvc4 12 | SET NgPostmarkDir=%CD%\nuget\base\ActionMailer.Postmark 13 | SET NgStandaloneDir=%CD%\nuget\base\ActionMailer.Standalone 14 | 15 | ECHO Performing clean release build... 16 | 17 | CMD /c %WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe %CD%\src\ActionMailer.Net.sln /t:Clean /p:Configuration=Release 18 | CMD /c %WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe %CD%\src\ActionMailer.Net.sln /t:Rebuild /p:Configuration=Release 19 | 20 | ECHO Compressing ActionMailer.Net.Mvc to Zip... 21 | 22 | CMD /c "%PROGRAMFILES%\7-Zip\7z.exe" a -tzip %MvcDir%\ActionMailer.Net.Mvc-v%NewVersion%.zip %MvcDir%\*.dll %MvcDir%\*.pdb %MvcDir%\*.txt %MvcDir%\*.xml 23 | CMD /c "%PROGRAMFILES%\7-Zip\7z.exe" a -tzip %MvcDir%\ActionMailer.Net.Mvc4-v%NewVersion%.zip %Mvc4Dir%\*.dll %Mvc4Dir%\*.pdb %Mvc4Dir%\*.txt %Mvc4Dir%\*.xml 24 | CMD /c "%PROGRAMFILES%\7-Zip\7z.exe" a -tzip %PostmarkDir%\ActionMailer.Net.Postmark-v%NewVersion%.zip %PostmarkDir%\*.dll %PostmarkDir%\*.pdb %PostmarkDir%\*.txt %PostmarkDir%\*.xml 25 | CMD /c "%PROGRAMFILES%\7-Zip\7z.exe" a -tzip %StandaloneDir%\ActionMailer.Net.Standalone-v%NewVersion%.zip %StandaloneDir%\*.dll %StandaloneDir%\*.pdb %StandaloneDir%\*.txt %StandaloneDir%\*.xml 26 | 27 | ECHO Creating NuGet Packages... 28 | 29 | DEL /Q %NgMvcDir%\lib\Net40\*.* 30 | DEL /Q %NgMvc4Dir%\lib\Net45\*.* 31 | DEL /Q %NgPostmarkDir%\lib\Net40\*.* 32 | DEL /Q %NgStandaloneDir%\lib\Net40\*.* 33 | 34 | COPY %MvcDir%\ActionMailer*.dll %NgMvcDir%\lib\Net40\ 35 | COPY %MvcDir%\ActionMailer*.pdb %NgMvcDir%\lib\Net40\ 36 | COPY %MvcDir%\ActionMailer*.xml %NgMvcDir%\lib\Net40\ 37 | COPY %Mvc4Dir%\ActionMailer*.dll %NgMvc4Dir%\lib\Net45\ 38 | COPY %Mvc4Dir%\ActionMailer*.pdb %NgMvc4Dir%\lib\Net45\ 39 | COPY %Mvc4Dir%\ActionMailer*.xml %NgMvc4Dir%\lib\Net45\ 40 | COPY %PostmarkDir%\ActionMailer*.dll %NgPostmarkDir%\lib\Net40\ 41 | COPY %PostmarkDir%\ActionMailer*.pdb %NgPostmarkDir%\lib\Net40\ 42 | COPY %PostmarkDir%\ActionMailer*.xml %NgPostmarkDir%\lib\Net40\ 43 | COPY %StandaloneDir%\ActionMailer*.dll %NgStandaloneDir%\lib\Net40\ 44 | COPY %StandaloneDir%\ActionMailer*.xml %NgStandaloneDir%\lib\Net40\ 45 | COPY %StandaloneDir%\ActionMailer*.pdb %NgStandaloneDir%\lib\Net40\ 46 | 47 | CMD /c nuget pack %NgMvcDir%\ActionMailer.nuspec -Version %NewVersion% -BasePath %NgMvcDir% -OutputDirectory %CD%\nuget\output 48 | CMD /c nuget pack %NgMvc4Dir%\ActionMailer.Mvc4.nuspec -Version %NewVersion% -BasePath %NgMvc4Dir% -OutputDirectory %CD%\nuget\output 49 | CMD /c nuget pack %NgPostmarkDir%\ActionMailer.Postmark.nuspec -Version %NewVersion% -BasePath %NgPostmarkDir% -OutputDirectory %CD%\nuget\output 50 | CMD /c nuget pack %NgStandaloneDir%\ActionMailer.Standalone.nuspec -Version %NewVersion% -BasePath %NgStandaloneDir% -OutputDirectory %CD%\nuget\output 51 | 52 | ECHO Release v%NewVersion% complete! -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/Implementations/HandlebarsFilesTemplateResolver.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone.Helpers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | using ActionMailerNext.Standalone.Interfaces; 10 | using ActionMailerNext.Standalone.Models; 11 | 12 | public class HandlebarsFilesTemplateResolver : ITemplateResolver 13 | { 14 | private readonly string _viewPath; 15 | 16 | /// 17 | /// Creates a template resolver using the specified path. If no path is given, this defaults to "Views". 18 | /// 19 | /// The path containing your views 20 | public HandlebarsFilesTemplateResolver(string viewPath) 21 | { 22 | _viewPath = viewPath; 23 | } 24 | 25 | public string Resolve(string name, string externalViewPath = null) 26 | { 27 | if (string.IsNullOrWhiteSpace(name)) 28 | { 29 | return null; 30 | } 31 | 32 | string csViewName = name; 33 | 34 | if (!csViewName.EndsWith(".hbs")) 35 | { 36 | csViewName += ".hbs"; 37 | } 38 | 39 | var appRoot = AppDomain.CurrentDomain.BaseDirectory; 40 | 41 | var viewPath = string.IsNullOrEmpty(externalViewPath) ? _viewPath : externalViewPath; 42 | 43 | var csViewPath = (csViewName).StartsWith("~") 44 | ? Path.GetFullPath(Path.Combine(appRoot, csViewName.Substring(2))) 45 | : Path.GetFullPath(Path.Combine(appRoot, viewPath, csViewName)); 46 | 47 | // Works with forward and backward slashes in the path 48 | if (File.Exists(csViewPath)) 49 | { 50 | return File.ReadAllText(csViewPath, Encoding.UTF8); 51 | } 52 | throw new TemplateResolvingException { SearchPaths = new List { csViewPath } }; 53 | } 54 | 55 | public List GetAllPartialTemplates() => GetAllTemplates().Where(t => t.IsPartial).ToList(); 56 | 57 | private IEnumerable GetAllTemplates() => SearchTemplates("*"); 58 | 59 | private List SearchTemplates(string searchPattern) 60 | { 61 | searchPattern = $"*{searchPattern}*.hbs"; 62 | var templatesDir = Path.Combine(Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).FullName, _viewPath); 63 | var filePaths = Directory.EnumerateFiles(templatesDir, searchPattern, SearchOption.AllDirectories); 64 | var templates = from path in filePaths 65 | let name = Path.GetFileNameWithoutExtension(path) 66 | let nameParts = name.Split('-') 67 | select new MailTemplate( 68 | key: path.Replace(templatesDir + "\\", "").Replace(".hbs", ""), 69 | value: File.ReadAllText(path, Encoding.UTF8), 70 | label: nameParts.Length > 1 ? nameParts[1] : null, 71 | isPartial: nameParts[0].StartsWith("_") 72 | ); 73 | return templates.ToList(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/Implementations/TemplateService.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone.Implementations 2 | { 3 | using System; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | using HandlebarsDotNet; 8 | 9 | using ActionMailerNext.Standalone.Interfaces; 10 | using ActionMailerNext.Standalone.Helpers; 11 | using ActionMailerNext.Standalone.Models; 12 | 13 | public class TemplateService : ITemplateService 14 | { 15 | protected readonly IHandlebars _hbsService; 16 | private readonly ITemplateResolver _templateResolver; 17 | private readonly string _resourcesDefaultNamespace = "Xv.Infrastructure.Standard.Resources"; 18 | private string _resourcesNamespace; 19 | 20 | public TemplateService(ITemplateResolver templateResolver, ViewSettings viewSettings) 21 | { 22 | _templateResolver = templateResolver; 23 | _hbsService = Handlebars.Create(); 24 | RegisterHelpers(viewSettings); 25 | _templateResolver.GetAllPartialTemplates().ForEach(template => 26 | { 27 | try 28 | { 29 | _hbsService.RegisterTemplate(template.Key, template.Value); 30 | } 31 | catch (Exception ex) 32 | { 33 | throw new Exception($"Error at template key = {template.Key}", ex); 34 | } 35 | }); 36 | } 37 | 38 | public string ResourcesNamespace 39 | { 40 | get 41 | { 42 | return string.IsNullOrEmpty(_resourcesNamespace) ? _resourcesDefaultNamespace : _resourcesNamespace; 43 | } 44 | set 45 | { 46 | _resourcesNamespace = value; 47 | } 48 | } 49 | 50 | public virtual void RegisterHelpers(ViewSettings viewSettings) 51 | { 52 | var helpers = new HandlebarsHelpers(_hbsService, viewSettings, ResourcesNamespace); 53 | 54 | var methods = helpers.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance) 55 | .Where(m => m.Name.StartsWith("Register")); 56 | foreach (var method in methods) 57 | { 58 | method.Invoke(helpers, new object[0]); 59 | } 60 | } 61 | 62 | public HandlebarsTemplate Compile(string viewName, string layout = null, string externalViewPath = null) 63 | { 64 | if (!string.IsNullOrEmpty(layout)) 65 | { 66 | AddTemplate(layout, "_Layout"); 67 | } 68 | 69 | try 70 | { 71 | return _hbsService.Compile(_templateResolver.Resolve(viewName, externalViewPath)); 72 | } 73 | catch (Exception ex) 74 | { 75 | throw new Exception($"Error at template = {viewName}", ex); 76 | } 77 | } 78 | 79 | public void AddTemplate(string viewName, string key = null, string externalViewPath = null) 80 | { 81 | if (string.IsNullOrEmpty(viewName)) 82 | { 83 | return; 84 | } 85 | 86 | _hbsService.RegisterTemplate(string.IsNullOrEmpty(key) ? viewName : key, _templateResolver.Resolve(viewName, externalViewPath)); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/ActionMailerNext.SendInBlueMailSender.Tests/SendInBlueMailSenderTests.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.SendInBlue.Tests 2 | { 3 | using ActionMailerNext.Interfaces; 4 | 5 | using Moq; 6 | 7 | using NUnit.Framework; 8 | 9 | using System.Collections.Generic; 10 | using System.Net.Mail; 11 | using System.Net.Mime; 12 | 13 | [TestFixture] 14 | public class SendInBlueMailSenderTests 15 | { 16 | private readonly SendInBlueMailSender _sendInBlueMailSender; 17 | public SendInBlueMailSenderTests() 18 | { 19 | _sendInBlueMailSender = new SendInBlueMailSender("SendInBlue Api-Key", null); 20 | } 21 | 22 | [Test] 23 | public void Send_GivenCorrectMail_ReturnResponseWithQueuedStatus() 24 | { 25 | //Act 26 | var response = _sendInBlueMailSender.Send( 27 | new MailAttributes() 28 | { 29 | To = new List() { new MailAddress("Email Address") }, 30 | Subject = "SendInBlue Test", 31 | Body = "test body", 32 | TextBody = "Test sending email with SendInBlue from ActionMailer project", 33 | AlternateViews = new List() { new AlternateView("content.txt", MediaTypeNames.Text.Plain) }, 34 | From = new MailAddress("Email Address", "name"), 35 | Tags = new List { "Test" } 36 | }); 37 | 38 | //Assert 39 | Assert.AreEqual(response[0].DeliveryStatus, DeliveryStatus.QUEUED); 40 | } 41 | 42 | [Test] 43 | public void GenerateProspectiveMailMessage_GivenMailAttributesAreNotFilled_CorrespondFieldsShouldBeNull() 44 | { 45 | //Arrange 46 | var mailAttribute = new MailAttributes { From = new MailAddress("sample@crossvertise.com") }; 47 | 48 | //Act 49 | var result = _sendInBlueMailSender.GenerateProspectiveMailMessage(mailAttribute); 50 | 51 | //Assert 52 | Assert.IsNull(result.Attachment); 53 | Assert.IsNull(result.Tags); 54 | Assert.IsNull(result.Bcc); 55 | Assert.IsNull(result.Cc); 56 | Assert.IsNull(result.Headers); 57 | } 58 | 59 | [Test] 60 | public void Deliver_GivenMailAttributesWithNullProps_ShouldThrowProperException() 61 | { 62 | //Arrange 63 | var mailAttribute = new MailAttributes(); 64 | var mockEmailResult = new Mock(); 65 | mockEmailResult.SetupGet(a => a.MailAttributes).Returns(mailAttribute); 66 | 67 | //Assert 68 | Assert.Throws(() => _sendInBlueMailSender.Deliver(mockEmailResult.Object)); 69 | } 70 | 71 | [Test] 72 | public void DeliverAsync_GivenMailAttributesWithNullProps_ShouldThrowProperException() 73 | { 74 | //Arrange 75 | var mailAttribute = new MailAttributes(); 76 | var mockEmailResult = new Mock(); 77 | mockEmailResult.SetupGet(a => a.MailAttributes).Returns(mailAttribute); 78 | 79 | //Assert 80 | Assert.ThrowsAsync(async () => await _sendInBlueMailSender.DeliverAsync(mockEmailResult.Object)); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /samples/StandaloneSample/StandaloneSample/StandaloneSample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {94AC929F-B115-4084-A299-AD572F7A8AAF} 9 | Exe 10 | Properties 11 | StandaloneSample 12 | StandaloneSample 13 | v4.0 14 | Client 15 | 512 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | x86 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\packages\ActionMailer.Standalone.0.6.0\lib\Net40\ActionMailer.Net.dll 39 | 40 | 41 | ..\packages\ActionMailer.Standalone.0.6.0\lib\Net40\ActionMailer.Net.Standalone.dll 42 | 43 | 44 | ..\packages\RazorEngine.2.1\lib\.NetFramework 4.0\RazorEngine.dll 45 | 46 | 47 | 48 | 49 | True 50 | ..\packages\RazorEngine.2.1\lib\.NetFramework 4.0\System.Web.Razor.dll 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | .vs/ 164 | 165 | # SQL Server files 166 | App_Data/*.mdf 167 | App_Data/*.ldf 168 | 169 | ############# 170 | ## Windows detritus 171 | ############# 172 | 173 | # Windows image file caches 174 | Thumbs.db 175 | ehthumbs.db 176 | 177 | # Folder config file 178 | Desktop.ini 179 | 180 | # Recycle Bin used on file shares 181 | $RECYCLE.BIN/ 182 | 183 | # Mac crap 184 | .DS_Store 185 | 186 | 187 | ############# 188 | ## Python 189 | ############# 190 | 191 | *.py[co] 192 | 193 | # Packages 194 | *.egg 195 | *.egg-info 196 | dist/ 197 | build/ 198 | eggs/ 199 | parts/ 200 | var/ 201 | sdist/ 202 | develop-eggs/ 203 | .installed.cfg 204 | 205 | # Installer logs 206 | pip-log.txt 207 | 208 | # Unit test / coverage reports 209 | .coverage 210 | .tox 211 | 212 | #Translations 213 | *.mo 214 | 215 | #Mr Developer 216 | .mr.developer.cfg 217 | 218 | *.nupkg 219 | src/TestProject/Web.config 220 | *.StyleCop 221 | -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | [0.7.4] - 8/5/2012 2 | * Another dispose bug. Fixed for real now. 4 releases in one day. Sigh. 3 | 4 | [0.7.3] - 8/5/2012 5 | * One of these days I'll learn how to do a release. My build script had a bug, 6 | and that caused the nuget packages to have the wrong DLL's. Fixed now. 7 | 8 | [0.7.2] - 8/5/2012 9 | * Accidentally released a bugged 0.7.1, so it's now fixed and had to release 10 | this new version to fix it. Oops! 11 | 12 | [0.7.1] - 8/5/2012 13 | * The MVC MailerBase now inherits from Controller instead of ControllerBase. This 14 | is an attempt to fix issue #46 that outlined problems with the SMTP client 15 | being disposed of too early. Now we'll just let MVC handle it for us. 16 | * There is a new option for Email() methods in both the MVC and Standalone version 17 | that allows you to specify whether we should trim the body of your message. 18 | The default value for this param is "true", but you can override it like so: 19 | return Email("MyView", trimBody: false); 20 | * Inline attachments have been changed to always have the content-type of 21 | "multipart/related". This is a fix for #38 where certain email clients could 22 | not display inline images properly without this MIME type. 23 | * Fixing issue #42 that occurred when developers placed Areas in a namespace that 24 | was outside of the MVC standard "Areas" namespace. We will fall back to the 25 | old behavior if the route data lookup fails. 26 | 27 | [0.7.0] - 2/4/2012 28 | * Adding a new standard mail sender that supports sending messages through 29 | Postmark (www.postmarkapp.com). 30 | 31 | [0.6.2] - 9/26/2011 32 | * The default encoding for mail messages has been changed from UTF8. It was 33 | previously set to Encoding.Default. This resolves issue #30. 34 | * There is a new Url helper called Url.AbsoluteContent. This can be used in the 35 | same way that Url.Content is used in ASP.NET MVC to generate absolute URL's 36 | for virtual content paths. This resolves issue #24. 37 | * Assemblies are now strongly-named as requested in issue #31. The main key is 38 | located at src/ActionMailer.Net/ActionMailer.Net.snk. 39 | 40 | [0.6.1] - 8/16/2011 41 | * Multipart messages will now render the plain text view first and the HTML view 42 | last. This fixes Issue #23 and should cause multipart messages to render 43 | correctly in standards-compliant email clients (i.e. Thunderbird). 44 | 45 | [0.6.0] - 7/5/2011 46 | 47 | * Started support for standalone Razor-based email generation. This means you can 48 | now generate emails with the Razor syntax without any need for ASP.NET MVC 3. 49 | 50 | * Moved EmailResult, MailerBase and all UrlHelper and HtmlHelper extensions into the 51 | ActionMailer.Net.Mvc assembly. This is because of the new standalone Razor 52 | support that has been introduced. All this really means is that you need to 53 | change your current using/imports statements to ActionMailer.Net.Mvc. 54 | 55 | * Re-organized unit tests to account for the separation between MVC and standalone. 56 | 57 | * Breaking Change: MailerBase is now an abstract class with a protected constructor. 58 | This basically ensures that MailerBase can only be inherited (not instantiated). 59 | This should have been the case all along, but I just forgot to make it happen. 60 | 61 | [0.5.0] - 3/15/2011 62 | 63 | * Change log started. 64 | 65 | * MailerBase.Email() method now has only one overload with two optional parameters 66 | for model and masterName. ViewName is now required because of the bug identified 67 | in Issue #7 on BitBucket. This is a breaking change. All you need to do is 68 | modify your existing EmailResult actions to pass a viewName to the Email() method. 69 | 70 | * Areas should now be detected properly. Thanks to Miroslav Popovic for submitting 71 | some sample code to fix this in Issue #11. Also thanks to Jeremy Kuhlman for 72 | bringing this issue to my attention as well. -------------------------------------------------------------------------------- /src/ActionMailerNext.Tests/Standalone/RazorMailerBaseTests.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* Copyright (C) 2012 by Scott W. Anderson 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #endregion 25 | namespace ActionMailerNext.Tests.Standalone 26 | { 27 | using System; 28 | using System.IO; 29 | 30 | using ActionMailerNext.Interfaces; 31 | 32 | using FakeItEasy; 33 | 34 | using NUnit.Framework; 35 | 36 | [TestFixture] 37 | public class RazorMailerBaseTests 38 | { 39 | [Test] 40 | public void EmailWithNoViewNameShouldThrow() 41 | { 42 | var mockSender = A.Fake(); 43 | var attribute = new MailAttributes(); 44 | var mailer = new TestMailerBase(attribute, mockSender); 45 | 46 | Assert.Throws(() => mailer.Email(null)); 47 | } 48 | 49 | [Test] 50 | public void MultipartMessagesShouldRenderBothViews() 51 | { 52 | var mockSender = A.Fake(); 53 | var attribute = new MailAttributes(); 54 | var mailer = new TestMailerBase(attribute, mockSender); 55 | 56 | var email = mailer.Email("MultipartNoModel"); 57 | var htmlBody = new StreamReader(email.MailAttributes.AlternateViews[0].ContentStream).ReadToEnd().Trim(); 58 | 59 | Assert.AreEqual("

Testing multipart.

", htmlBody); 60 | } 61 | 62 | [Test] 63 | public void PassingAModelShouldWork() 64 | { 65 | var mockSender = A.Fake(); 66 | var attribute = new MailAttributes(); 67 | var mailer = new TestMailerBase(attribute, mockSender); 68 | var model = new TestModel 69 | { 70 | Name = "Foo" 71 | }; 72 | 73 | var email = mailer.Email("TextViewWithModel", model); 74 | var body = new StreamReader(email.MailAttributes.AlternateViews[0].ContentStream).ReadToEnd().Trim(); 75 | 76 | Assert.AreEqual("Your name is: Foo", body); 77 | } 78 | 79 | [Test] 80 | public void WhiteSpaceShouldBeIncludedWhenRequired() 81 | { 82 | var mockSender = A.Fake(); 83 | var attribute = new MailAttributes(); 84 | var mailer = new TestMailerBase(attribute, mockSender); 85 | 86 | var email = mailer.Email("WhitespaceTrimTest", trimBody: false); 87 | var body = new StreamReader(email.MailAttributes.AlternateViews[0].ContentStream).ReadToEnd(); 88 | 89 | Assert.True(char.IsWhiteSpace(body, 0)); 90 | Assert.True(char.IsWhiteSpace(body, body.Length - 1)); 91 | } 92 | 93 | [Test] 94 | public void WhiteSpaceShouldBeTrimmedWhenRequired() 95 | { 96 | var mockSender = A.Fake(); 97 | var attribute = new MailAttributes(); 98 | var mailer = new TestMailerBase(attribute, mockSender); 99 | 100 | var email = mailer.Email("WhitespaceTrimTest", trimBody: true); 101 | var body = new StreamReader(email.MailAttributes.AlternateViews[0].ContentStream).ReadToEnd(); 102 | 103 | Assert.AreEqual("This thing has leading and trailing whitespace.", body); 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/HbsEmailResult.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone 2 | { 3 | using System; 4 | using System.Net.Mail; 5 | using System.Net.Mime; 6 | using System.Text; 7 | 8 | using ActionMailerNext.Interfaces; 9 | using ActionMailerNext.Standalone.Helpers; 10 | using ActionMailerNext.Standalone.Interfaces; 11 | using ActionMailerNext.Utils; 12 | 13 | /// 14 | /// An container for MailMessage with the appropriate body rendered by Razor. 15 | /// 16 | public class HBSEmailResult : IEmailResult 17 | { 18 | private readonly Encoding _messageEncoding; 19 | private readonly ITemplateService _templateService; 20 | private readonly object _viewBag; 21 | 22 | private readonly string _viewName; 23 | private readonly string _viewPath; 24 | private readonly string _masterName; 25 | 26 | /// 27 | /// Creates a new EmailResult. You must call Compile() before this result can be successfully delivered. 28 | /// 29 | /// message who's body needs populating. 30 | /// The view to use when rendering the message body. 31 | /// the main layout 32 | /// The path where we should search for the view. 33 | /// The template service defining a ITemplateResolver and a TemplateBase 34 | /// The viewBag is a dynamic object that can transfer data to the view 35 | /// 36 | public HBSEmailResult(MailAttributes mailAttributes, string viewName, 37 | Encoding messageEncoding, string masterName, 38 | string viewPath, ITemplateService templateService, dynamic viewBag) 39 | { 40 | MailAttributes = mailAttributes ?? throw new ArgumentNullException("mailAttributes"); 41 | _viewName = !string.IsNullOrWhiteSpace(viewName) ? viewName : throw new ArgumentNullException("viewName"); 42 | _masterName = masterName; 43 | _viewPath = viewPath; 44 | 45 | _templateService = templateService ?? throw new ArgumentNullException("templateService"); 46 | _messageEncoding = messageEncoding; 47 | 48 | _viewBag = viewBag; 49 | } 50 | 51 | /// 52 | /// The underlying MailMessage object that was passed to this object's constructor. 53 | /// 54 | public MailAttributes MailAttributes { get; set; } 55 | 56 | /// 57 | /// The default encoding used to send a messageBase. 58 | /// 59 | public Encoding MessageEncoding => _messageEncoding; 60 | 61 | /// 62 | /// Compiles the email body using the specified Razor view and model. 63 | /// 64 | public void Compile(T model, bool trimBody, string externalViewPath = null) 65 | { 66 | var body = string.Empty; 67 | AlternateView altView; 68 | 69 | var hasTxtView = false; 70 | try 71 | { 72 | var itemplate = _templateService.Compile(_viewName, _masterName, externalViewPath); 73 | 74 | body = itemplate.Run(model, _viewBag); 75 | } 76 | catch (TemplateResolvingException) 77 | { 78 | if (!hasTxtView) 79 | { 80 | throw new NoViewsFoundException( 81 | string.Format( 82 | "Could not find any HBS views named [{0}] in the path [{1}]. Ensure that you specify the format in the file name (ie: {0}.hbs)", 83 | _viewName, _viewPath)); 84 | } 85 | } 86 | catch (NullReferenceException ex) 87 | { 88 | var error = string.Format("{0}\n{1}\n{2}\n{3}\n\n{4}\nFile:{5}", ex.Message, ex.InnerException, ex.Data, ex.Source, ex.StackTrace, _viewName); 89 | throw new NullReferenceException(error); 90 | } 91 | 92 | if (trimBody) 93 | { 94 | body = body.Trim(); 95 | } 96 | 97 | altView = AlternateView.CreateAlternateViewFromString(body, MessageEncoding ?? Encoding.Default, MediaTypeNames.Text.Html); 98 | MailAttributes.AlternateViews.Add(altView); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/HbsMailerBase.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Dynamic; 6 | 7 | using ActionMailerNext.Implementations.SMTP; 8 | using ActionMailerNext.Interfaces; 9 | using ActionMailerNext.Standalone.Helpers; 10 | using ActionMailerNext.Standalone.Implementations; 11 | using ActionMailerNext.Standalone.Interfaces; 12 | using ActionMailerNext.Standalone.Models; 13 | 14 | /// 15 | /// This is a standalone MailerBase that relies on Handlebars to generate emails. 16 | /// 17 | public abstract class HBSMailerBase : IMailInterceptor 18 | { 19 | private ITemplateService _templateService; 20 | private ITemplateResolver _templateResolver; 21 | 22 | public MailAttributes MailAttributes { get; set; } 23 | 24 | protected HBSMailerBase( 25 | MailAttributes mailAttributes = null, 26 | IMailSender mailSender = null, 27 | ITemplateResolver templateResolver = null, 28 | ITemplateService templateService = null) 29 | { 30 | MailAttributes = mailAttributes ?? new MailAttributes(); 31 | MailSender = mailSender ?? new SmtpMailSender(); 32 | _templateResolver = templateResolver; 33 | ViewBag = new ExpandoObject(); 34 | _templateService = templateService; 35 | } 36 | 37 | /// 38 | /// The path to the folder containing your Razor views. 39 | /// 40 | public abstract string GlobalViewPath { get; } 41 | 42 | /// 43 | /// The view settings needed to implement HTML/URL Helpers 44 | /// 45 | public abstract ViewSettings ViewSettings { get; } 46 | 47 | /// 48 | /// The underlying IMailSender to use for outgoing messages. 49 | /// 50 | public IMailSender MailSender { get; set; } 51 | 52 | /// 53 | /// A template resolver that is used to find the appropriate templates 54 | /// 55 | public ITemplateResolver TemplateResolver 56 | { 57 | get 58 | { 59 | return _templateResolver ?? (_templateResolver = new HandlebarsFilesTemplateResolver(GlobalViewPath)); 60 | } 61 | set 62 | { 63 | _templateResolver = value; 64 | } 65 | } 66 | 67 | /// 68 | /// Used to add needed variable 69 | /// 70 | public dynamic ViewBag { get; set; } 71 | 72 | protected ITemplateService TemplateService 73 | { 74 | get 75 | { 76 | return _templateService ?? (_templateService = new TemplateService(TemplateResolver, ViewSettings)); 77 | } 78 | set 79 | { 80 | _templateService = value; 81 | } 82 | } 83 | 84 | /// 85 | /// This method is called before each mail is sent 86 | /// 87 | /// 88 | /// A simple context containing the mail and a boolean value 89 | /// that can be toggled to prevent this mail from being sent. 90 | /// 91 | void IMailInterceptor.OnMailSending(MailSendingContext context) 92 | { 93 | // No initial handling 94 | } 95 | 96 | /// 97 | /// This method is called after each mail is sent. 98 | /// 99 | /// The mail that was sent. 100 | void IMailInterceptor.OnMailSent(MailAttributes mail) 101 | { 102 | OnMailSent(mail); 103 | } 104 | 105 | /// 106 | /// This method is called when onMailsent is fired. 107 | /// 108 | public void OnMailSent(MailAttributes mail) 109 | { 110 | // No initial handling 111 | } 112 | 113 | /// 114 | /// Constructs your mail message ready for delivery. 115 | /// 116 | /// The view to use when rendering the message body. 117 | /// The model object used while rendering the message body. 118 | /// the main layout 119 | /// Whether or not we should trim whitespace from the beginning and end of the message body. 120 | /// a View path that overrides the one set by the property 121 | /// An EmailResult that you can Deliver(); 122 | public virtual HBSEmailResult Email(string viewName, object model = null, string masterName = null, bool trimBody = true, string externalViewPath = null) 123 | { 124 | if (viewName == null) 125 | { 126 | throw new ArgumentNullException("viewName"); 127 | } 128 | 129 | if (ViewBag != null) 130 | { 131 | ViewBag.ViewSettings = ViewSettings; 132 | } 133 | 134 | var result = new HBSEmailResult(MailAttributes, viewName, MailAttributes.MessageEncoding, masterName, 135 | externalViewPath ?? GlobalViewPath, TemplateService, ViewBag); 136 | 137 | result.Compile(model, trimBody, externalViewPath); 138 | 139 | foreach (var postprocessor in MailAttributes.PostProcessors) 140 | { 141 | result.MailAttributes = postprocessor.Execute(result.MailAttributes); 142 | } 143 | 144 | return result; 145 | } 146 | 147 | /// 148 | /// Pre-Compiles the views in the dictionary using a the template resolver 149 | /// 150 | /// 151 | public virtual void PreCompileViews(List views) 152 | { 153 | foreach (var view in views) 154 | { 155 | TemplateService.AddTemplate(view); 156 | } 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /src/ActionMailerNext/Implementations/SMTP/SmtpMailSender.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Implementations.SMTP 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Linq; 7 | using System.Net.Mail; 8 | using System.Threading.Tasks; 9 | 10 | using Interfaces; 11 | 12 | public class SmtpMailSender : IMailSender 13 | { 14 | private readonly IMailInterceptor _interceptor; 15 | private readonly SmtpClient _client; 16 | 17 | public SmtpMailSender() 18 | : this(new SmtpClient(), null) 19 | { 20 | } 21 | 22 | public SmtpMailSender(SmtpClient client, IMailInterceptor interceptor) 23 | { 24 | _interceptor = interceptor; 25 | _client = client; 26 | } 27 | 28 | protected MailMessage GenerateProspectiveMailMessage(MailAttributes mail) 29 | { 30 | var message = new MailMessage(); 31 | 32 | for (int i = 0; i < mail.To.Count; i++) 33 | { 34 | message.To.Add(mail.To[i]); 35 | } 36 | 37 | for (int i = 0; i < mail.Cc.Count; i++) 38 | { 39 | message.CC.Add(mail.Cc[i]); 40 | } 41 | 42 | for (int i = 0; i < mail.Bcc.Count; i++) 43 | { 44 | message.Bcc.Add(mail.Bcc[i]); 45 | } 46 | 47 | for (int i = 0; i < mail.ReplyTo.Count; i++) 48 | { 49 | message.ReplyToList.Add(mail.ReplyTo[i]); 50 | } 51 | 52 | if (!string.IsNullOrWhiteSpace(mail.From.Address)) 53 | { 54 | message.From = new MailAddress(mail.From.Address, mail.From.DisplayName); 55 | } 56 | 57 | message.Subject = mail.Subject; 58 | // https://connect.microsoft.com/VisualStudio/feedback/details/785710/mailmessage-subject-incorrectly-encoded-in-utf-8-base64 59 | message.SubjectEncoding = Encoding.GetEncoding("ISO-8859-1"); 60 | message.BodyEncoding = Encoding.UTF8; 61 | message.Priority = mail.Priority; 62 | 63 | foreach (var item in mail.Headers) 64 | { 65 | message.Headers[item.Key] = item.Value; 66 | } 67 | 68 | foreach (var item in mail.Attachments) 69 | { 70 | message.Attachments.Add(Utils.AttachmentCollection.ModifyAttachmentProperties(item.Key, item.Value, false)); 71 | } 72 | 73 | foreach (var item in mail.Attachments.Inline) 74 | { 75 | message.Attachments.Add(Utils.AttachmentCollection.ModifyAttachmentProperties(item.Key, item.Value, true)); 76 | } 77 | 78 | foreach (var view in mail.AlternateViews) 79 | { 80 | message.AlternateViews.Add(view); 81 | } 82 | 83 | return message; 84 | } 85 | 86 | public virtual List Deliver(IEmailResult emailResult) => Send(emailResult.MailAttributes); 87 | 88 | /// 89 | /// Sends SMTPMailMessage synchronously. 90 | /// 91 | /// The MailAttributes you wish to send. 92 | public virtual List Send(MailAttributes mailAttributes) 93 | { 94 | var response = new List(); 95 | var mail = GenerateProspectiveMailMessage(mailAttributes); 96 | 97 | try 98 | { 99 | _client.Send(mail); 100 | response.AddRange(mail.To.Select(mailAddr => new SmtpMailResponse() 101 | { 102 | Email = mailAddr.Address, 103 | Status = SmtpMailResponse.GetProspectiveStatus(SmtpStatusCode.Ok.ToString()), 104 | RejectReason = null 105 | })); 106 | } 107 | catch (SmtpFailedRecipientsException ex) 108 | { 109 | response.AddRange(ex.InnerExceptions.Select(e => new SmtpMailResponse 110 | { 111 | Email = e.FailedRecipient, 112 | Status = SmtpMailResponse.GetProspectiveStatus(e.StatusCode.ToString()), 113 | RejectReason = e.Message 114 | })); 115 | } 116 | return response; 117 | } 118 | 119 | /// 120 | /// Sends your message asynchronously. This method does not block. If you need to know 121 | /// when the message has been sent, then override the OnMailSent method in MailerBase which 122 | /// will not fire until the asyonchronous send operation is complete. 123 | /// 124 | public async Task DeliverAsync(IEmailResult emailResult) 125 | { 126 | var deliverTask = SendAsync(emailResult.MailAttributes); 127 | await deliverTask.ContinueWith(t => AsyncSendCompleted(emailResult)); 128 | 129 | return emailResult.MailAttributes; 130 | } 131 | 132 | /// 133 | /// Sends SMTPMailMessage asynchronously using tasks. 134 | /// 135 | /// The MailAttributes message you wish to send. 136 | /// 137 | public virtual async Task> SendAsync(MailAttributes mailAttributes) 138 | { 139 | var response = new List(); 140 | 141 | var mail = GenerateProspectiveMailMessage(mailAttributes); 142 | try 143 | { 144 | await _client.SendMailAsync(mail); 145 | response.AddRange(mail.To.Select(mailAddr => new SmtpMailResponse() 146 | { 147 | Email = mailAddr.Address, 148 | Status = SmtpMailResponse.GetProspectiveStatus(SmtpStatusCode.Ok.ToString()), 149 | RejectReason = null 150 | })); 151 | } 152 | catch (SmtpFailedRecipientsException ex) 153 | { 154 | response.AddRange(ex.InnerExceptions.Select(e => new SmtpMailResponse 155 | { 156 | Email = e.FailedRecipient, 157 | Status = SmtpMailResponse.GetProspectiveStatus(e.StatusCode.ToString()), 158 | RejectReason = e.Message 159 | })); 160 | } 161 | return response; 162 | } 163 | 164 | public void Dispose() 165 | { 166 | Dispose(false); 167 | GC.SuppressFinalize(true); 168 | } 169 | 170 | protected virtual void Dispose(bool disposing) { } 171 | 172 | private void AsyncSendCompleted(IEmailResult email) => _interceptor.OnMailSent(email.MailAttributes); 173 | } 174 | } -------------------------------------------------------------------------------- /src/ActionMailerNext/MailAttributes.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net.Mail; 6 | using System.Text; 7 | 8 | using ActionMailerNext.Interfaces; 9 | 10 | using AttachmentCollection = ActionMailerNext.Utils.AttachmentCollection; 11 | 12 | /// 13 | /// All mailers should implement this interface. 14 | /// 15 | public class MailAttributes 16 | { 17 | public MailAttributes() 18 | { 19 | To = new List(); 20 | Cc = new List(); 21 | Bcc = new List(); 22 | ReplyTo = new List(); 23 | Headers = new Dictionary(); 24 | Attachments = new AttachmentCollection(); 25 | AlternateViews = new List(); 26 | ExtraProperties = new Dictionary(); 27 | PostProcessors = new List(); 28 | Tags = new List(); 29 | } 30 | 31 | public MailAttributes(MailAttributes mailAttributes, 32 | bool copyTo = true, bool copyCc = true, 33 | bool copyBcc = true, bool copyReplyTo = true, bool referenceAttachments = true, 34 | bool copyHeaders = true, bool copyExtraProperties = true, 35 | bool copyPostProcessors = true, bool copyTags = true) 36 | { 37 | From = mailAttributes.From; 38 | Subject = mailAttributes.Subject; 39 | Priority = mailAttributes.Priority; 40 | 41 | IsCcToSupported = mailAttributes.IsCcToSupported; 42 | IsBccSupported = mailAttributes.IsBccSupported; 43 | IsReplyToSupported = mailAttributes.IsReplyToSupported; 44 | 45 | MessageEncoding = mailAttributes.MessageEncoding; 46 | Body = mailAttributes.Body; 47 | TextBody = mailAttributes.TextBody; 48 | HtmlBody = mailAttributes.HtmlBody; 49 | 50 | To = copyTo ? mailAttributes.To.Select(address => new MailAddress(address.Address, address.DisplayName)).ToList() : new List(); 51 | Cc = copyCc ? mailAttributes.Cc.Select(address => new MailAddress(address.Address, address.DisplayName)).ToList() : new List(); 52 | Bcc = copyBcc ? mailAttributes.Bcc.Select(address => new MailAddress(address.Address, address.DisplayName)).ToList() : new List(); 53 | ReplyTo = copyReplyTo ? mailAttributes.ReplyTo.Select(address => new MailAddress(address.Address, address.DisplayName)).ToList() : new List(); 54 | Attachments = referenceAttachments ? mailAttributes.Attachments : new AttachmentCollection(); 55 | Headers = copyHeaders ? mailAttributes.Headers.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) : new Dictionary(); 56 | ExtraProperties = copyExtraProperties ? mailAttributes.ExtraProperties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) : new Dictionary(); 57 | PostProcessors = copyPostProcessors ? mailAttributes.PostProcessors.Select(pp => pp).ToList() : new List(); 58 | Tags = copyTags ? mailAttributes.Tags : null; 59 | 60 | AlternateViews = new List(); 61 | } 62 | 63 | /// 64 | /// A string representation of who this mail should be from. Could be 65 | /// your name and email address or just an email address by itself. 66 | /// 67 | public MailAddress From { get; set; } 68 | 69 | /// 70 | /// The subject line of the email. 71 | /// 72 | public string Subject { get; set; } 73 | 74 | /// 75 | /// The Priority of the email. 76 | /// 77 | public MailPriority Priority { get; set; } 78 | 79 | /// 80 | /// A collection of addresses this email should be sent to. 81 | /// 82 | public List To { get; set; } 83 | 84 | /// 85 | /// Check if the current Sending method supports CC 86 | /// 87 | public bool IsCcToSupported { get; set; } 88 | 89 | /// 90 | /// A collection of addresses that should be CC'ed. 91 | /// 92 | public IList Cc { get; set; } 93 | 94 | /// 95 | /// Check if the current Sending method supports BCC 96 | /// 97 | public bool IsBccSupported { get; set; } 98 | 99 | /// 100 | /// A collection of addresses that should be BCC'ed. 101 | /// 102 | public IList Bcc { get; set; } 103 | 104 | /// 105 | /// Check if the current Sending method supports ReplyTo 106 | /// 107 | public bool IsReplyToSupported { get; set; } 108 | 109 | /// 110 | /// A collection of addresses that should be listed in Reply-To header. 111 | /// 112 | public List ReplyTo { get; set; } 113 | 114 | /// 115 | /// Any custom headers (name and value) that should be placed on the message. 116 | /// 117 | public IDictionary Headers { get; set; } 118 | 119 | /// 120 | /// The generated text body of the message 121 | /// 122 | public string TextBody { get; set; } 123 | 124 | /// 125 | /// The generated html body of the message 126 | /// 127 | public string HtmlBody { get; set; } 128 | 129 | /// 130 | /// The generated body of the message 131 | /// 132 | public string Body { get; set; } 133 | 134 | /// 135 | /// Gets or sets the default message encoding when delivering mail. 136 | /// 137 | public Encoding MessageEncoding { get; set; } 138 | 139 | /// 140 | /// Any attachments you wish to add. The key of this collection is what 141 | /// the file should be named. The value is should represent the actual content 142 | /// of the file. 143 | /// 144 | /// 145 | /// Attachments["picture.jpg"] = File.ReadAllBytes(@"C:\picture.jpg"); 146 | /// 147 | public AttachmentCollection Attachments { get; set; } 148 | 149 | /// 150 | /// Any view you wish to add. The key of this collection is what 151 | /// the view should be named. 152 | /// 153 | public IList AlternateViews { get; set; } 154 | 155 | /// 156 | /// Apply PreMailer.Net to convert all styles to inline styles to 157 | /// avoid problems with different Email Clients 158 | /// 159 | public IList PostProcessors { get; set; } 160 | 161 | /// 162 | /// Any extra properties that needs to be added in case of custom mail sender 163 | /// 164 | public IDictionary ExtraProperties { get; set; } 165 | 166 | /// 167 | /// Tag your emails to find them more easily 168 | /// 169 | public List Tags { get; set; } 170 | } 171 | } -------------------------------------------------------------------------------- /src/ActionMailer.Net.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32421.90 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{4191E949-6E92-4A07-A5C0-5A14C4900CE6}" 7 | ProjectSection(SolutionItems) = preProject 8 | .nuget\NuGet.Config = .nuget\NuGet.Config 9 | .nuget\NuGet.exe = .nuget\NuGet.exe 10 | .nuget\NuGet.targets = .nuget\NuGet.targets 11 | EndProjectSection 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{5AE0534E-6DFD-4511-978F-5D4CAF13AF41}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActionMailerNext", "ActionMailerNext\ActionMailerNext.csproj", "{943C0C0A-0725-4BE1-9035-88DD19C5185F}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActionMailerNext.Standalone", "ActionMailerNext.Standalone\ActionMailerNext.Standalone.csproj", "{EF322256-3E32-410F-9341-0494FC5D3042}" 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActionMailerNext.Tests", "ActionMailerNext.Tests\ActionMailerNext.Tests.csproj", "{7C48B5B5-0A73-4F66-AE94-32E04B369EF9}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActionMailerNext.SendInBlue", "ActionMailerNext.SendInBlue\ActionMailerNext.SendInBlue.csproj", "{4FE327C2-9B01-4A86-B537-649AED462E43}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActionMailerNext.SendInBlue.Tests", "ActionMailerNext.SendInBlueMailSender.Tests\ActionMailerNext.SendInBlue.Tests.csproj", "{79DFA887-92C8-493C-8D77-585A62FF6DE6}" 24 | EndProject 25 | Global 26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 27 | Debug|Any CPU = Debug|Any CPU 28 | Debug|Mixed Platforms = Debug|Mixed Platforms 29 | Debug|x86 = Debug|x86 30 | Release|Any CPU = Release|Any CPU 31 | Release|Mixed Platforms = Release|Mixed Platforms 32 | Release|x86 = Release|x86 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 38 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 39 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Debug|x86.ActiveCfg = Debug|Any CPU 40 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Debug|x86.Build.0 = Debug|Any CPU 41 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 44 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Release|Mixed Platforms.Build.0 = Release|Any CPU 45 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Release|x86.ActiveCfg = Release|Any CPU 46 | {943C0C0A-0725-4BE1-9035-88DD19C5185F}.Release|x86.Build.0 = Release|Any CPU 47 | {EF322256-3E32-410F-9341-0494FC5D3042}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {EF322256-3E32-410F-9341-0494FC5D3042}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {EF322256-3E32-410F-9341-0494FC5D3042}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 50 | {EF322256-3E32-410F-9341-0494FC5D3042}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 51 | {EF322256-3E32-410F-9341-0494FC5D3042}.Debug|x86.ActiveCfg = Debug|Any CPU 52 | {EF322256-3E32-410F-9341-0494FC5D3042}.Debug|x86.Build.0 = Debug|Any CPU 53 | {EF322256-3E32-410F-9341-0494FC5D3042}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {EF322256-3E32-410F-9341-0494FC5D3042}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {EF322256-3E32-410F-9341-0494FC5D3042}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 56 | {EF322256-3E32-410F-9341-0494FC5D3042}.Release|Mixed Platforms.Build.0 = Release|Any CPU 57 | {EF322256-3E32-410F-9341-0494FC5D3042}.Release|x86.ActiveCfg = Release|Any CPU 58 | {EF322256-3E32-410F-9341-0494FC5D3042}.Release|x86.Build.0 = Release|Any CPU 59 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 62 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 63 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Debug|x86.ActiveCfg = Debug|Any CPU 64 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Debug|x86.Build.0 = Debug|Any CPU 65 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Release|Any CPU.ActiveCfg = Release|Any CPU 66 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Release|Any CPU.Build.0 = Release|Any CPU 67 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 68 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Release|Mixed Platforms.Build.0 = Release|Any CPU 69 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Release|x86.ActiveCfg = Release|Any CPU 70 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9}.Release|x86.Build.0 = Release|Any CPU 71 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 72 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Debug|Any CPU.Build.0 = Debug|Any CPU 73 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 74 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 75 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Debug|x86.ActiveCfg = Debug|Any CPU 76 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Debug|x86.Build.0 = Debug|Any CPU 77 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Release|Any CPU.ActiveCfg = Release|Any CPU 78 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Release|Any CPU.Build.0 = Release|Any CPU 79 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 80 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Release|Mixed Platforms.Build.0 = Release|Any CPU 81 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Release|x86.ActiveCfg = Release|Any CPU 82 | {4FE327C2-9B01-4A86-B537-649AED462E43}.Release|x86.Build.0 = Release|Any CPU 83 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 84 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Debug|Any CPU.Build.0 = Debug|Any CPU 85 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 86 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 87 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Debug|x86.ActiveCfg = Debug|Any CPU 88 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Debug|x86.Build.0 = Debug|Any CPU 89 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Release|Any CPU.ActiveCfg = Release|Any CPU 90 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Release|Any CPU.Build.0 = Release|Any CPU 91 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 92 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Release|Mixed Platforms.Build.0 = Release|Any CPU 93 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Release|x86.ActiveCfg = Release|Any CPU 94 | {79DFA887-92C8-493C-8D77-585A62FF6DE6}.Release|x86.Build.0 = Release|Any CPU 95 | EndGlobalSection 96 | GlobalSection(SolutionProperties) = preSolution 97 | HideSolutionNode = FALSE 98 | EndGlobalSection 99 | GlobalSection(NestedProjects) = preSolution 100 | {7C48B5B5-0A73-4F66-AE94-32E04B369EF9} = {5AE0534E-6DFD-4511-978F-5D4CAF13AF41} 101 | {79DFA887-92C8-493C-8D77-585A62FF6DE6} = {5AE0534E-6DFD-4511-978F-5D4CAF13AF41} 102 | EndGlobalSection 103 | GlobalSection(ExtensibilityGlobals) = postSolution 104 | SolutionGuid = {73F0BF58-777C-462A-9745-420470C23967} 105 | EndGlobalSection 106 | EndGlobal 107 | -------------------------------------------------------------------------------- /src/ActionMailerNext.Standalone/Helpers/UtilHelper.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Standalone.Helpers 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.Diagnostics; 7 | using System.Globalization; 8 | using System.Linq; 9 | using System.Linq.Expressions; 10 | using System.Reflection; 11 | using System.Resources; 12 | using System.Text; 13 | using System.Text.RegularExpressions; 14 | using System.Web; 15 | 16 | public static class UtilHelper 17 | { 18 | public static Uri BuildURI(string protocol, string host, string formatString, IDictionary routeDictionary) 19 | { 20 | var uriBuilder = new UriBuilder(protocol, host); 21 | var i = 0; 22 | var newFormatStringBuilder = new StringBuilder(formatString.ToLower()); 23 | var queryDictionary = new Dictionary(); 24 | var keyToInt = new Dictionary(); 25 | 26 | foreach (var kvp in routeDictionary) 27 | { 28 | var key = kvp.Key.ToLower(); 29 | var val = kvp.Value; 30 | if (formatString.IndexOf(key, StringComparison.Ordinal) < 0) 31 | { 32 | queryDictionary.Add(key, val); 33 | } 34 | else 35 | { 36 | newFormatStringBuilder = newFormatStringBuilder.Replace("{" + key + "}", "{" + i + "}"); 37 | } 38 | keyToInt.Add(kvp.Key, i); 39 | i++; 40 | } 41 | uriBuilder.Query = FormatQuery(queryDictionary, false); 42 | 43 | 44 | var routeString = Regex.Replace(newFormatStringBuilder.ToString(), "{([A-Z])+}", "", RegexOptions.IgnoreCase); 45 | if (routeString.EndsWith("/")) 46 | { 47 | routeString = routeString.Substring(0, routeString.Length - 1); 48 | } 49 | 50 | uriBuilder.Path = string.Format(routeString, routeDictionary.OrderBy(x => keyToInt[x.Key]).Select(x => x.Value).ToArray()).Replace("//", "/"); 51 | 52 | return uriBuilder.Uri; 53 | } 54 | 55 | public static string FormatQuery(IDictionary queryDictionary, bool splitCommas = true) 56 | { 57 | var stringBuilder = new StringBuilder(); 58 | foreach (var key in queryDictionary.Keys) 59 | { 60 | var entries = splitCommas ? queryDictionary[key].Split(',') : new[] { queryDictionary[key] }; 61 | foreach (var value in entries) 62 | { 63 | stringBuilder.AppendFormat("&{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(value)); 64 | } 65 | } 66 | 67 | var queryString = stringBuilder.ToString(); 68 | if (queryString.Length == 0) 69 | { 70 | return string.Empty; 71 | } 72 | 73 | return queryString.Substring(1); 74 | } 75 | 76 | public static string ConvertDictionaryToString(IDictionary htmlAttributes) 77 | { 78 | return (htmlAttributes == null) ? string.Empty : htmlAttributes.Aggregate(string.Empty, (current, kvp) => current + (kvp.Key + "=\"" + kvp.Value + "\" ")); 79 | } 80 | 81 | public static IDictionary ObjectToDictionary(object value) 82 | { 83 | var dictionary = new Dictionary(); 84 | const BindingFlags bindingAttrs = BindingFlags.Public | BindingFlags.Instance; 85 | 86 | if (value != null) 87 | { 88 | foreach (var property in value 89 | .GetType() 90 | .GetProperties(bindingAttrs) 91 | .Where(property => property.CanRead)) 92 | { 93 | try 94 | { 95 | dictionary.Add(property.Name, property.GetValue(value).ToString()); 96 | 97 | } 98 | catch (NullReferenceException) 99 | { 100 | } 101 | } 102 | 103 | return dictionary; 104 | } 105 | 106 | return new Dictionary(); 107 | } 108 | 109 | public static T GetAttribute(this MemberInfo member, bool isRequired) where T : Attribute 110 | { 111 | var attribute = member.GetCustomAttributes(typeof(T), false).SingleOrDefault(); 112 | 113 | if (attribute == null && isRequired) 114 | { 115 | throw new ArgumentException( 116 | string.Format( 117 | CultureInfo.InvariantCulture, 118 | "The {0} attribute must be defined on member {1}", 119 | typeof(T).Name, 120 | member.Name)); 121 | } 122 | 123 | return (T)attribute; 124 | } 125 | 126 | public static string GetPropertyDisplayName(Expression> propertyExpression) 127 | { 128 | var memberInfo = GetPropertyInformation(propertyExpression.Body); 129 | if (memberInfo == null) 130 | { 131 | throw new ArgumentException( 132 | "No property reference expression was found.", 133 | "propertyExpression"); 134 | } 135 | 136 | var attr = memberInfo.GetAttribute(false); 137 | 138 | if (attr == null) 139 | { 140 | return memberInfo.Name; 141 | } 142 | 143 | if (attr.ResourceType == null) 144 | { 145 | return attr.Name; 146 | } 147 | 148 | var resourceManager = new ResourceManager(attr.ResourceType); 149 | return resourceManager.GetString(attr.Name); 150 | } 151 | 152 | public static string GetPropertyDisplayFormat(Expression> propertyExpression) 153 | { 154 | var memberInfo = GetPropertyInformation(propertyExpression.Body); 155 | if (memberInfo == null) 156 | { 157 | return "{0}"; 158 | } 159 | 160 | var attr = memberInfo.GetAttribute(false); 161 | 162 | return (attr == null || attr.DataFormatString == null) ? "{0}" : attr.DataFormatString; 163 | } 164 | public static string GetPropertyName(Expression> propertyExpression) 165 | { 166 | var memberInfo = GetPropertyInformation(propertyExpression.Body); 167 | if (memberInfo == null) 168 | { 169 | throw new ArgumentException( 170 | "No property reference expression was found.", 171 | "propertyExpression"); 172 | } 173 | 174 | return memberInfo.Name; 175 | } 176 | 177 | public static MemberInfo GetPropertyInformation(Expression propertyExpression) 178 | { 179 | Debug.Assert(propertyExpression != null, "propertyExpression != null"); 180 | var memberExpr = propertyExpression as MemberExpression; 181 | 182 | if (memberExpr == null && 183 | propertyExpression is UnaryExpression unaryExpr && 184 | unaryExpr.NodeType == ExpressionType.Convert) 185 | { 186 | memberExpr = unaryExpr.Operand as MemberExpression; 187 | } 188 | 189 | return memberExpr != null && memberExpr.Member.MemberType == MemberTypes.Property ? memberExpr.Member : null; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/ActionMailerNext.SendInBlue/SendInBlueMailSender.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.SendInBlue 2 | { 3 | using System; 4 | using System.Text; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.Globalization; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Net.Mime; 11 | using System.Threading.Tasks; 12 | 13 | using sib_api_v3_sdk.Api; 14 | using sib_api_v3_sdk.Model; 15 | using SibClient = sib_api_v3_sdk.Client; 16 | using ActionMailerNext.Interfaces; 17 | 18 | public class SendInBlueMailSender : IMailSender 19 | { 20 | private readonly IMailInterceptor _interceptor; 21 | private readonly TransactionalEmailsApi _client; 22 | 23 | public SendInBlueMailSender() : this(ConfigurationManager.AppSettings["SendInBlueApiKey"], null) { } 24 | 25 | public SendInBlueMailSender(string apiKey, IMailInterceptor interceptor) 26 | { 27 | if (string.IsNullOrWhiteSpace(apiKey)) 28 | { 29 | throw new ArgumentNullException(nameof(apiKey), 30 | "The AppSetting 'SendInBlueApiKey' is not defined. Either define this configuration section or use the constructor with apiKey parameter."); 31 | } 32 | 33 | _interceptor = interceptor; 34 | if (!SibClient.Configuration.Default.ApiKey.ContainsKey("api-key")) 35 | { 36 | SibClient.Configuration.Default.ApiKey.Add("api-key", apiKey); 37 | } 38 | 39 | _client = new TransactionalEmailsApi(); 40 | } 41 | 42 | public SendSmtpEmail GenerateProspectiveMailMessage(MailAttributes mail) 43 | { 44 | var idnmapping = new IdnMapping(); 45 | 46 | string parseEmail(string address) 47 | { 48 | var parts = address.Split('@'); 49 | return $"{parts[0]}@{idnmapping.GetAscii(parts[1])}"; 50 | } 51 | 52 | var message = new SendSmtpEmail 53 | { 54 | Sender = new SendSmtpEmailSender(mail.From.DisplayName, mail.From.Address), 55 | To = mail.To.Select(t => new SendSmtpEmailTo(parseEmail(t.Address))).ToList(), 56 | Bcc = mail.Bcc.Select(t => new SendSmtpEmailBcc(parseEmail(t.Address))).ToList(), 57 | Cc = mail.Cc.Select(t => new SendSmtpEmailCc(parseEmail(t.Address))).ToList(), 58 | Subject = mail.Subject 59 | }; 60 | 61 | var emailHeaders = new Dictionary(); 62 | if (mail.ReplyTo.Any()) 63 | { 64 | emailHeaders.Add("Reply-To", string.Join(" , ", mail.ReplyTo)); 65 | } 66 | 67 | foreach (var view in mail.AlternateViews) 68 | { 69 | var reader = new StreamReader(view.ContentStream, Encoding.UTF8, true, 1024, true); 70 | 71 | var body = reader.ReadToEnd(); 72 | 73 | if (view.ContentType.MediaType == MediaTypeNames.Text.Plain) 74 | { 75 | message.TextContent = body; 76 | } 77 | if (view.ContentType.MediaType == MediaTypeNames.Text.Html) 78 | { 79 | message.HtmlContent = body; 80 | } 81 | } 82 | 83 | mail.Headers.ToList().ForEach(h => emailHeaders.Add(h.Key, h.Value)); 84 | 85 | var attachments = new List(mail.Attachments.Count); 86 | 87 | foreach (var mailAttachment in mail.Attachments.Select(attachment => 88 | Utils.AttachmentCollection.ModifyAttachmentProperties(attachment.Key, attachment.Value, false))) 89 | { 90 | using (var stream = new MemoryStream()) 91 | { 92 | mailAttachment.ContentStream.CopyTo(stream); 93 | var byteData = stream.ToArray(); 94 | 95 | attachments.Add(new SendSmtpEmailAttachment() 96 | { 97 | Content = byteData, 98 | Name = ReplaceGermanCharacters(mailAttachment.Name) 99 | }); 100 | } 101 | } 102 | 103 | message.Tags = mail.Tags; 104 | message.Attachment = attachments.Count == 0 ? null : attachments; 105 | message.Headers = emailHeaders.Count == 0 ? null : emailHeaders; 106 | message.Bcc = message.Bcc.Count == 0 ? null : message.Bcc; 107 | message.Cc = message.Cc.Count == 0 ? null : message.Cc; 108 | message.Tags = message.Tags.Count == 0 ? null : message.Tags; 109 | 110 | return message; 111 | } 112 | 113 | public virtual List Deliver(IEmailResult emailResult) 114 | { 115 | try 116 | { 117 | return Send(emailResult.MailAttributes); 118 | } 119 | catch (Exception ex) 120 | { 121 | throw new SendInBlueException(ex.Message, ex); 122 | } 123 | } 124 | 125 | public virtual List Send(MailAttributes mailAttributes) 126 | { 127 | var mail = GenerateProspectiveMailMessage(mailAttributes); 128 | var responses = new List(); 129 | 130 | _client.SendTransacEmail(mail); 131 | 132 | responses.Add(new SendInBlueMailResponse 133 | { 134 | Email = mailAttributes.From.Address, 135 | DeliveryStatus = DeliveryStatus.QUEUED 136 | }); 137 | 138 | return responses; 139 | } 140 | 141 | /// 142 | /// Sends your message asynchronously. This method does not block. If you need to know 143 | /// when the message has been sent, then override the OnMailSent method in MailerBase which 144 | /// will not fire until the asyonchronous send operation is complete. 145 | /// 146 | public async Task DeliverAsync(IEmailResult emailResult) 147 | { 148 | try 149 | { 150 | await SendAsync(emailResult.MailAttributes); 151 | AsyncSendCompleted(emailResult.MailAttributes); 152 | 153 | return emailResult.MailAttributes; 154 | } 155 | catch (Exception ex) 156 | { 157 | throw new SendInBlueException(ex.Message, ex); 158 | } 159 | } 160 | 161 | public virtual async Task> SendAsync(MailAttributes mailAttributes) 162 | { 163 | var mail = GenerateProspectiveMailMessage(mailAttributes); 164 | var responses = new List(); 165 | 166 | await _client.SendTransacEmailAsync(mail); 167 | 168 | responses.Add(new SendInBlueMailResponse 169 | { 170 | Email = mailAttributes.From.Address, 171 | DeliveryStatus = DeliveryStatus.QUEUED 172 | }); 173 | 174 | return responses; 175 | } 176 | 177 | private void AsyncSendCompleted(MailAttributes mail) 178 | { 179 | _interceptor.OnMailSent(mail); 180 | } 181 | 182 | private string ReplaceGermanCharacters(string s) 183 | { 184 | return s.Replace("ö", "oe") 185 | .Replace("ü", "ue") 186 | .Replace("ä", "ae") 187 | .Replace("Ö", "Oe") 188 | .Replace("Ü", "Ue") 189 | .Replace("Ä", "Ae") 190 | .Replace("ß", "ss"); 191 | } 192 | 193 | public void Dispose() 194 | { 195 | Dispose(false); 196 | GC.SuppressFinalize(true); 197 | } 198 | 199 | protected virtual void Dispose(bool disposing) 200 | { 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/.nuget/NuGet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildProjectDirectory)\..\ 5 | 6 | 7 | false 8 | 9 | 10 | false 11 | 12 | 13 | true 14 | 15 | 16 | false 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) 31 | 32 | 33 | 34 | 35 | $(SolutionDir).nuget 36 | 37 | 38 | 39 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config 40 | $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config 41 | 42 | 43 | 44 | $(MSBuildProjectDirectory)\packages.config 45 | $(PackagesProjectConfig) 46 | 47 | 48 | 49 | 50 | $(NuGetToolsPath)\NuGet.exe 51 | @(PackageSource) 52 | 53 | "$(NuGetExePath)" 54 | mono --runtime=v4.0.30319 "$(NuGetExePath)" 55 | 56 | $(TargetDir.Trim('\\')) 57 | 58 | -RequireConsent 59 | -NonInteractive 60 | 61 | "$(SolutionDir) " 62 | "$(SolutionDir)" 63 | 64 | 65 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) 66 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols 67 | 68 | 69 | 70 | RestorePackages; 71 | $(BuildDependsOn); 72 | 73 | 74 | 75 | 76 | $(BuildDependsOn); 77 | BuildPackage; 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 99 | 100 | 103 | 104 | 105 | 106 | 108 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 141 | 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /src/Settings.StyleCop: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | False 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | False 15 | 16 | 17 | 18 | 19 | False 20 | 21 | 22 | 23 | 24 | False 25 | 26 | 27 | 28 | 29 | False 30 | 31 | 32 | 33 | 34 | False 35 | 36 | 37 | 38 | 39 | False 40 | 41 | 42 | 43 | 44 | False 45 | 46 | 47 | 48 | 49 | False 50 | 51 | 52 | 53 | 54 | False 55 | 56 | 57 | 58 | 59 | False 60 | 61 | 62 | 63 | 64 | False 65 | 66 | 67 | 68 | 69 | False 70 | 71 | 72 | 73 | 74 | False 75 | 76 | 77 | 78 | 79 | False 80 | 81 | 82 | 83 | 84 | False 85 | 86 | 87 | 88 | 89 | False 90 | 91 | 92 | 93 | 94 | False 95 | 96 | 97 | 98 | 99 | False 100 | 101 | 102 | 103 | 104 | False 105 | 106 | 107 | 108 | 109 | False 110 | 111 | 112 | 113 | 114 | False 115 | 116 | 117 | 118 | 119 | False 120 | 121 | 122 | 123 | 124 | False 125 | 126 | 127 | 128 | 129 | False 130 | 131 | 132 | 133 | 134 | False 135 | 136 | 137 | 138 | 139 | False 140 | 141 | 142 | 143 | 144 | False 145 | 146 | 147 | 148 | 149 | False 150 | 151 | 152 | 153 | 154 | False 155 | 156 | 157 | 158 | 159 | False 160 | 161 | 162 | 163 | 164 | False 165 | 166 | 167 | 168 | 169 | False 170 | 171 | 172 | 173 | 174 | False 175 | 176 | 177 | 178 | 179 | False 180 | 181 | 182 | 183 | 184 | False 185 | 186 | 187 | 188 | 189 | False 190 | 191 | 192 | 193 | 194 | False 195 | 196 | 197 | 198 | 199 | False 200 | 201 | 202 | 203 | 204 | False 205 | 206 | 207 | 208 | 209 | False 210 | 211 | 212 | 213 | 214 | False 215 | 216 | 217 | 218 | 219 | False 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | False 230 | 231 | 232 | 233 | 234 | False 235 | 236 | 237 | 238 | 239 | False 240 | 241 | 242 | 243 | 244 | False 245 | 246 | 247 | 248 | 249 | False 250 | 251 | 252 | 253 | 254 | False 255 | 256 | 257 | 258 | 259 | False 260 | 261 | 262 | 263 | 264 | False 265 | 266 | 267 | 268 | 269 | False 270 | 271 | 272 | 273 | 274 | False 275 | 276 | 277 | 278 | 279 | False 280 | 281 | 282 | 283 | 284 | False 285 | 286 | 287 | 288 | 289 | False 290 | 291 | 292 | 293 | 294 | False 295 | 296 | 297 | 298 | 299 | False 300 | 301 | 302 | 303 | 304 | False 305 | 306 | 307 | 308 | 309 | False 310 | 311 | 312 | 313 | 314 | False 315 | 316 | 317 | 318 | 319 | False 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | False 330 | 331 | 332 | 333 | 334 | False 335 | 336 | 337 | 338 | 339 | False 340 | 341 | 342 | 343 | 344 | False 345 | 346 | 347 | 348 | 349 | False 350 | 351 | 352 | 353 | 354 | False 355 | 356 | 357 | 358 | 359 | False 360 | 361 | 362 | 363 | 364 | False 365 | 366 | 367 | 368 | 369 | False 370 | 371 | 372 | 373 | 374 | False 375 | 376 | 377 | 378 | 379 | False 380 | 381 | 382 | 383 | 384 | False 385 | 386 | 387 | 388 | 389 | False 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | False 400 | 401 | 402 | 403 | 404 | False 405 | 406 | 407 | 408 | 409 | False 410 | 411 | 412 | 413 | 414 | False 415 | 416 | 417 | 418 | 419 | False 420 | 421 | 422 | 423 | 424 | False 425 | 426 | 427 | 428 | 429 | False 430 | 431 | 432 | 433 | 434 | False 435 | 436 | 437 | 438 | 439 | False 440 | 441 | 442 | 443 | 444 | False 445 | 446 | 447 | 448 | 449 | False 450 | 451 | 452 | 453 | 454 | False 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | False 465 | 466 | 467 | 468 | 469 | False 470 | 471 | 472 | 473 | 474 | False 475 | 476 | 477 | 478 | 479 | False 480 | 481 | 482 | 483 | 484 | False 485 | 486 | 487 | 488 | 489 | False 490 | 491 | 492 | 493 | 494 | False 495 | 496 | 497 | 498 | 499 | False 500 | 501 | 502 | 503 | 504 | False 505 | 506 | 507 | 508 | 509 | False 510 | 511 | 512 | 513 | 514 | False 515 | 516 | 517 | 518 | 519 | False 520 | 521 | 522 | 523 | 524 | False 525 | 526 | 527 | 528 | 529 | False 530 | 531 | 532 | 533 | 534 | False 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | False 545 | 546 | 547 | 548 | 549 | False 550 | 551 | 552 | 553 | 554 | False 555 | 556 | 557 | 558 | 559 | False 560 | 561 | 562 | 563 | 564 | False 565 | 566 | 567 | 568 | 569 | False 570 | 571 | 572 | 573 | 574 | False 575 | 576 | 577 | 578 | 579 | False 580 | 581 | 582 | 583 | 584 | False 585 | 586 | 587 | 588 | 589 | False 590 | 591 | 592 | 593 | 594 | False 595 | 596 | 597 | 598 | 599 | False 600 | 601 | 602 | 603 | 604 | False 605 | 606 | 607 | 608 | 609 | False 610 | 611 | 612 | 613 | 614 | False 615 | 616 | 617 | 618 | 619 | False 620 | 621 | 622 | 623 | 624 | False 625 | 626 | 627 | 628 | 629 | False 630 | 631 | 632 | 633 | 634 | False 635 | 636 | 637 | 638 | 639 | False 640 | 641 | 642 | 643 | 644 | False 645 | 646 | 647 | 648 | 649 | False 650 | 651 | 652 | 653 | 654 | False 655 | 656 | 657 | 658 | 659 | False 660 | 661 | 662 | 663 | 664 | False 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | False 675 | 676 | 677 | 678 | 679 | False 680 | 681 | 682 | 683 | 684 | False 685 | 686 | 687 | 688 | 689 | False 690 | 691 | 692 | 693 | 694 | False 695 | 696 | 697 | 698 | 699 | False 700 | 701 | 702 | 703 | 704 | False 705 | 706 | 707 | 708 | 709 | False 710 | 711 | 712 | 713 | 714 | False 715 | 716 | 717 | 718 | 719 | False 720 | 721 | 722 | 723 | 724 | False 725 | 726 | 727 | 728 | 729 | False 730 | 731 | 732 | 733 | 734 | False 735 | 736 | 737 | 738 | 739 | False 740 | 741 | 742 | 743 | 744 | False 745 | 746 | 747 | 748 | 749 | False 750 | 751 | 752 | 753 | 754 | False 755 | 756 | 757 | 758 | 759 | False 760 | 761 | 762 | 763 | 764 | False 765 | 766 | 767 | 768 | 769 | False 770 | 771 | 772 | 773 | 774 | False 775 | 776 | 777 | 778 | 779 | False 780 | 781 | 782 | 783 | 784 | False 785 | 786 | 787 | 788 | 789 | False 790 | 791 | 792 | 793 | 794 | False 795 | 796 | 797 | 798 | 799 | False 800 | 801 | 802 | 803 | 804 | False 805 | 806 | 807 | 808 | 809 | False 810 | 811 | 812 | 813 | 814 | 815 | 816 | -------------------------------------------------------------------------------- /src/ActionMailerNext/Utils/MimeTypes.cs: -------------------------------------------------------------------------------- 1 | namespace ActionMailerNext.Utils 2 | { 3 | using System; 4 | 5 | public static class MimeTypes 6 | { 7 | /// 8 | /// Resolves a mime type based on the given file extension. The extension is case-insensitive. 9 | /// Shamelessly stolen from: http://stackoverflow.com/questions/1029740/get-a-mime-from-an-extention/3393525#3393525 10 | /// 11 | /// The file extension to search for. 12 | /// The proper mime type for the supplied extension. 13 | public static string ResolveByExtension(string extension) 14 | { 15 | if (extension == null) 16 | { 17 | throw new ArgumentNullException("extension"); 18 | } 19 | 20 | if (!extension.StartsWith(".")) 21 | { 22 | extension = "." + extension; 23 | } 24 | 25 | extension = extension.ToLowerInvariant(); 26 | 27 | switch (extension) 28 | { 29 | // combination of values from Windows 7 Registry and 30 | // from C:\Windows\System32\inetsrv\config\applicationHost.config 31 | // some added, including .7z and .dat 32 | 33 | case ".323": 34 | return "text/h323"; 35 | case ".3g2": 36 | return "video/3gpp2"; 37 | case ".3gp": 38 | return "video/3gpp"; 39 | case ".3gp2": 40 | return "video/3gpp2"; 41 | case ".3gpp": 42 | return "video/3gpp"; 43 | case ".7z": 44 | return "application/x-7z-compressed"; 45 | case ".aa": 46 | return "audio/audible"; 47 | case ".aac": 48 | return "audio/aac"; 49 | case ".aaf": 50 | return "application/octet-stream"; 51 | case ".aax": 52 | return "audio/vnd.audible.aax"; 53 | case ".ac3": 54 | return "audio/ac3"; 55 | case ".aca": 56 | return "application/octet-stream"; 57 | case ".accda": 58 | return "application/msaccess.addin"; 59 | case ".accdb": 60 | return "application/msaccess"; 61 | case ".accdc": 62 | return "application/msaccess.cab"; 63 | case ".accde": 64 | return "application/msaccess"; 65 | case ".accdr": 66 | return "application/msaccess.runtime"; 67 | case ".accdt": 68 | return "application/msaccess"; 69 | case ".accdw": 70 | return "application/msaccess.webapplication"; 71 | case ".accft": 72 | return "application/msaccess.ftemplate"; 73 | case ".acx": 74 | return "application/internet-property-stream"; 75 | case ".addin": 76 | return "text/xml"; 77 | case ".ade": 78 | return "application/msaccess"; 79 | case ".adobebridge": 80 | return "application/x-bridge-url"; 81 | case ".adp": 82 | return "application/msaccess"; 83 | case ".adt": 84 | return "audio/vnd.dlna.adts"; 85 | case ".adts": 86 | return "audio/aac"; 87 | case ".afm": 88 | return "application/octet-stream"; 89 | case ".ai": 90 | return "application/postscript"; 91 | case ".aif": 92 | return "audio/x-aiff"; 93 | case ".aifc": 94 | return "audio/aiff"; 95 | case ".aiff": 96 | return "audio/aiff"; 97 | case ".air": 98 | return "application/vnd.adobe.air-application-installer-package+zip"; 99 | case ".amc": 100 | return "application/x-mpeg"; 101 | case ".application": 102 | return "application/x-ms-application"; 103 | case ".art": 104 | return "image/x-jg"; 105 | case ".asa": 106 | return "application/xml"; 107 | case ".asax": 108 | return "application/xml"; 109 | case ".ascx": 110 | return "application/xml"; 111 | case ".asd": 112 | return "application/octet-stream"; 113 | case ".asf": 114 | return "video/x-ms-asf"; 115 | case ".ashx": 116 | return "application/xml"; 117 | case ".asi": 118 | return "application/octet-stream"; 119 | case ".asm": 120 | return "text/plain"; 121 | case ".asmx": 122 | return "application/xml"; 123 | case ".aspx": 124 | return "application/xml"; 125 | case ".asr": 126 | return "video/x-ms-asf"; 127 | case ".asx": 128 | return "video/x-ms-asf"; 129 | case ".atom": 130 | return "application/atom+xml"; 131 | case ".au": 132 | return "audio/basic"; 133 | case ".avi": 134 | return "video/x-msvideo"; 135 | case ".axs": 136 | return "application/olescript"; 137 | case ".bas": 138 | return "text/plain"; 139 | case ".bcpio": 140 | return "application/x-bcpio"; 141 | case ".bin": 142 | return "application/octet-stream"; 143 | case ".bmp": 144 | return "image/bmp"; 145 | case ".c": 146 | return "text/plain"; 147 | case ".cab": 148 | return "application/octet-stream"; 149 | case ".caf": 150 | return "audio/x-caf"; 151 | case ".calx": 152 | return "application/vnd.ms-office.calx"; 153 | case ".cat": 154 | return "application/vnd.ms-pki.seccat"; 155 | case ".cc": 156 | return "text/plain"; 157 | case ".cd": 158 | return "text/plain"; 159 | case ".cdda": 160 | return "audio/aiff"; 161 | case ".cdf": 162 | return "application/x-cdf"; 163 | case ".cer": 164 | return "application/x-x509-ca-cert"; 165 | case ".chm": 166 | return "application/octet-stream"; 167 | case ".class": 168 | return "application/x-java-applet"; 169 | case ".clp": 170 | return "application/x-msclip"; 171 | case ".cmx": 172 | return "image/x-cmx"; 173 | case ".cnf": 174 | return "text/plain"; 175 | case ".cod": 176 | return "image/cis-cod"; 177 | case ".config": 178 | return "application/xml"; 179 | case ".contact": 180 | return "text/x-ms-contact"; 181 | case ".coverage": 182 | return "application/xml"; 183 | case ".cpio": 184 | return "application/x-cpio"; 185 | case ".cpp": 186 | return "text/plain"; 187 | case ".crd": 188 | return "application/x-mscardfile"; 189 | case ".crl": 190 | return "application/pkix-crl"; 191 | case ".crt": 192 | return "application/x-x509-ca-cert"; 193 | case ".cs": 194 | return "text/plain"; 195 | case ".csdproj": 196 | return "text/plain"; 197 | case ".csh": 198 | return "application/x-csh"; 199 | case ".csproj": 200 | return "text/plain"; 201 | case ".css": 202 | return "text/css"; 203 | case ".csv": 204 | return "application/octet-stream"; 205 | case ".cur": 206 | return "application/octet-stream"; 207 | case ".cxx": 208 | return "text/plain"; 209 | case ".dat": 210 | return "application/octet-stream"; 211 | case ".datasource": 212 | return "application/xml"; 213 | case ".dbproj": 214 | return "text/plain"; 215 | case ".dcr": 216 | return "application/x-director"; 217 | case ".def": 218 | return "text/plain"; 219 | case ".deploy": 220 | return "application/octet-stream"; 221 | case ".der": 222 | return "application/x-x509-ca-cert"; 223 | case ".dgml": 224 | return "application/xml"; 225 | case ".dib": 226 | return "image/bmp"; 227 | case ".dif": 228 | return "video/x-dv"; 229 | case ".dir": 230 | return "application/x-director"; 231 | case ".disco": 232 | return "text/xml"; 233 | case ".dll": 234 | return "application/x-msdownload"; 235 | case ".dll.config": 236 | return "text/xml"; 237 | case ".dlm": 238 | return "text/dlm"; 239 | case ".doc": 240 | return "application/msword"; 241 | case ".docm": 242 | return "application/vnd.ms-word.document.macroEnabled.12"; 243 | case ".docx": 244 | return "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; 245 | case ".dot": 246 | return "application/msword"; 247 | case ".dotm": 248 | return "application/vnd.ms-word.template.macroEnabled.12"; 249 | case ".dotx": 250 | return "application/vnd.openxmlformats-officedocument.wordprocessingml.template"; 251 | case ".dsp": 252 | return "application/octet-stream"; 253 | case ".dsw": 254 | return "text/plain"; 255 | case ".dtd": 256 | return "text/xml"; 257 | case ".dtsConfig": 258 | return "text/xml"; 259 | case ".dv": 260 | return "video/x-dv"; 261 | case ".dvi": 262 | return "application/x-dvi"; 263 | case ".dwf": 264 | return "drawing/x-dwf"; 265 | case ".dwp": 266 | return "application/octet-stream"; 267 | case ".dxr": 268 | return "application/x-director"; 269 | case ".eml": 270 | return "message/rfc822"; 271 | case ".emz": 272 | return "application/octet-stream"; 273 | case ".eot": 274 | return "application/octet-stream"; 275 | case ".eps": 276 | return "application/postscript"; 277 | case ".etl": 278 | return "application/etl"; 279 | case ".etx": 280 | return "text/x-setext"; 281 | case ".evy": 282 | return "application/envoy"; 283 | case ".exe": 284 | return "application/octet-stream"; 285 | case ".exe.config": 286 | return "text/xml"; 287 | case ".fdf": 288 | return "application/vnd.fdf"; 289 | case ".fif": 290 | return "application/fractals"; 291 | case ".filters": 292 | return "Application/xml"; 293 | case ".fla": 294 | return "application/octet-stream"; 295 | case ".flr": 296 | return "x-world/x-vrml"; 297 | case ".flv": 298 | return "video/x-flv"; 299 | case ".fsscript": 300 | return "application/fsharp-script"; 301 | case ".fsx": 302 | return "application/fsharp-script"; 303 | case ".generictest": 304 | return "application/xml"; 305 | case ".gif": 306 | return "image/gif"; 307 | case ".group": 308 | return "text/x-ms-group"; 309 | case ".gsm": 310 | return "audio/x-gsm"; 311 | case ".gtar": 312 | return "application/x-gtar"; 313 | case ".gz": 314 | return "application/x-gzip"; 315 | case ".h": 316 | return "text/plain"; 317 | case ".hdf": 318 | return "application/x-hdf"; 319 | case ".hdml": 320 | return "text/x-hdml"; 321 | case ".hhc": 322 | return "application/x-oleobject"; 323 | case ".hhk": 324 | return "application/octet-stream"; 325 | case ".hhp": 326 | return "application/octet-stream"; 327 | case ".hlp": 328 | return "application/winhlp"; 329 | case ".hpp": 330 | return "text/plain"; 331 | case ".hqx": 332 | return "application/mac-binhex40"; 333 | case ".hta": 334 | return "application/hta"; 335 | case ".htc": 336 | return "text/x-component"; 337 | case ".htm": 338 | return "text/html"; 339 | case ".html": 340 | return "text/html"; 341 | case ".htt": 342 | return "text/webviewhtml"; 343 | case ".hxa": 344 | return "application/xml"; 345 | case ".hxc": 346 | return "application/xml"; 347 | case ".hxd": 348 | return "application/octet-stream"; 349 | case ".hxe": 350 | return "application/xml"; 351 | case ".hxf": 352 | return "application/xml"; 353 | case ".hxh": 354 | return "application/octet-stream"; 355 | case ".hxi": 356 | return "application/octet-stream"; 357 | case ".hxk": 358 | return "application/xml"; 359 | case ".hxq": 360 | return "application/octet-stream"; 361 | case ".hxr": 362 | return "application/octet-stream"; 363 | case ".hxs": 364 | return "application/octet-stream"; 365 | case ".hxt": 366 | return "text/html"; 367 | case ".hxv": 368 | return "application/xml"; 369 | case ".hxw": 370 | return "application/octet-stream"; 371 | case ".hxx": 372 | return "text/plain"; 373 | case ".i": 374 | return "text/plain"; 375 | case ".ico": 376 | return "image/x-icon"; 377 | case ".ics": 378 | return "application/octet-stream"; 379 | case ".idl": 380 | return "text/plain"; 381 | case ".ief": 382 | return "image/ief"; 383 | case ".iii": 384 | return "application/x-iphone"; 385 | case ".inc": 386 | return "text/plain"; 387 | case ".inf": 388 | return "application/octet-stream"; 389 | case ".inl": 390 | return "text/plain"; 391 | case ".ins": 392 | return "application/x-internet-signup"; 393 | case ".ipa": 394 | return "application/x-itunes-ipa"; 395 | case ".ipg": 396 | return "application/x-itunes-ipg"; 397 | case ".ipproj": 398 | return "text/plain"; 399 | case ".ipsw": 400 | return "application/x-itunes-ipsw"; 401 | case ".iqy": 402 | return "text/x-ms-iqy"; 403 | case ".isp": 404 | return "application/x-internet-signup"; 405 | case ".ite": 406 | return "application/x-itunes-ite"; 407 | case ".itlp": 408 | return "application/x-itunes-itlp"; 409 | case ".itms": 410 | return "application/x-itunes-itms"; 411 | case ".itpc": 412 | return "application/x-itunes-itpc"; 413 | case ".IVF": 414 | return "video/x-ivf"; 415 | case ".jar": 416 | return "application/java-archive"; 417 | case ".java": 418 | return "application/octet-stream"; 419 | case ".jck": 420 | return "application/liquidmotion"; 421 | case ".jcz": 422 | return "application/liquidmotion"; 423 | case ".jfif": 424 | return "image/pjpeg"; 425 | case ".jnlp": 426 | return "application/x-java-jnlp-file"; 427 | case ".jpb": 428 | return "application/octet-stream"; 429 | case ".jpe": 430 | return "image/jpeg"; 431 | case ".jpeg": 432 | return "image/jpeg"; 433 | case ".jpg": 434 | return "image/jpeg"; 435 | case ".js": 436 | return "application/x-javascript"; 437 | case ".jsx": 438 | return "text/jscript"; 439 | case ".jsxbin": 440 | return "text/plain"; 441 | case ".latex": 442 | return "application/x-latex"; 443 | case ".library-ms": 444 | return "application/windows-library+xml"; 445 | case ".lit": 446 | return "application/x-ms-reader"; 447 | case ".loadtest": 448 | return "application/xml"; 449 | case ".lpk": 450 | return "application/octet-stream"; 451 | case ".lsf": 452 | return "video/x-la-asf"; 453 | case ".lst": 454 | return "text/plain"; 455 | case ".lsx": 456 | return "video/x-la-asf"; 457 | case ".lzh": 458 | return "application/octet-stream"; 459 | case ".m13": 460 | return "application/x-msmediaview"; 461 | case ".m14": 462 | return "application/x-msmediaview"; 463 | case ".m1v": 464 | return "video/mpeg"; 465 | case ".m2t": 466 | return "video/vnd.dlna.mpeg-tts"; 467 | case ".m2ts": 468 | return "video/vnd.dlna.mpeg-tts"; 469 | case ".m2v": 470 | return "video/mpeg"; 471 | case ".m3u": 472 | return "audio/x-mpegurl"; 473 | case ".m3u8": 474 | return "audio/x-mpegurl"; 475 | case ".m4a": 476 | return "audio/m4a"; 477 | case ".m4b": 478 | return "audio/m4b"; 479 | case ".m4p": 480 | return "audio/m4p"; 481 | case ".m4r": 482 | return "audio/x-m4r"; 483 | case ".m4v": 484 | return "video/x-m4v"; 485 | case ".mac": 486 | return "image/x-macpaint"; 487 | case ".mak": 488 | return "text/plain"; 489 | case ".man": 490 | return "application/x-troff-man"; 491 | case ".manifest": 492 | return "application/x-ms-manifest"; 493 | case ".map": 494 | return "text/plain"; 495 | case ".master": 496 | return "application/xml"; 497 | case ".mda": 498 | return "application/msaccess"; 499 | case ".mdb": 500 | return "application/x-msaccess"; 501 | case ".mde": 502 | return "application/msaccess"; 503 | case ".mdp": 504 | return "application/octet-stream"; 505 | case ".me": 506 | return "application/x-troff-me"; 507 | case ".mfp": 508 | return "application/x-shockwave-flash"; 509 | case ".mht": 510 | return "message/rfc822"; 511 | case ".mhtml": 512 | return "message/rfc822"; 513 | case ".mid": 514 | return "audio/mid"; 515 | case ".midi": 516 | return "audio/mid"; 517 | case ".mix": 518 | return "application/octet-stream"; 519 | case ".mk": 520 | return "text/plain"; 521 | case ".mmf": 522 | return "application/x-smaf"; 523 | case ".mno": 524 | return "text/xml"; 525 | case ".mny": 526 | return "application/x-msmoney"; 527 | case ".mod": 528 | return "video/mpeg"; 529 | case ".mov": 530 | return "video/quicktime"; 531 | case ".movie": 532 | return "video/x-sgi-movie"; 533 | case ".mp2": 534 | return "video/mpeg"; 535 | case ".mp2v": 536 | return "video/mpeg"; 537 | case ".mp3": 538 | return "audio/mpeg"; 539 | case ".mp4": 540 | return "video/mp4"; 541 | case ".mp4v": 542 | return "video/mp4"; 543 | case ".mpa": 544 | return "video/mpeg"; 545 | case ".mpe": 546 | return "video/mpeg"; 547 | case ".mpeg": 548 | return "video/mpeg"; 549 | case ".mpf": 550 | return "application/vnd.ms-mediapackage"; 551 | case ".mpg": 552 | return "video/mpeg"; 553 | case ".mpp": 554 | return "application/vnd.ms-project"; 555 | case ".mpv2": 556 | return "video/mpeg"; 557 | case ".mqv": 558 | return "video/quicktime"; 559 | case ".ms": 560 | return "application/x-troff-ms"; 561 | case ".msi": 562 | return "application/octet-stream"; 563 | case ".mso": 564 | return "application/octet-stream"; 565 | case ".mts": 566 | return "video/vnd.dlna.mpeg-tts"; 567 | case ".mtx": 568 | return "application/xml"; 569 | case ".mvb": 570 | return "application/x-msmediaview"; 571 | case ".mvc": 572 | return "application/x-miva-compiled"; 573 | case ".mxp": 574 | return "application/x-mmxp"; 575 | case ".nc": 576 | return "application/x-netcdf"; 577 | case ".nsc": 578 | return "video/x-ms-asf"; 579 | case ".nws": 580 | return "message/rfc822"; 581 | case ".ocx": 582 | return "application/octet-stream"; 583 | case ".oda": 584 | return "application/oda"; 585 | case ".odc": 586 | return "text/x-ms-odc"; 587 | case ".odh": 588 | return "text/plain"; 589 | case ".odl": 590 | return "text/plain"; 591 | case ".odp": 592 | return "application/vnd.oasis.opendocument.presentation"; 593 | case ".ods": 594 | return "application/oleobject"; 595 | case ".odt": 596 | return "application/vnd.oasis.opendocument.text"; 597 | case ".one": 598 | return "application/onenote"; 599 | case ".onea": 600 | return "application/onenote"; 601 | case ".onepkg": 602 | return "application/onenote"; 603 | case ".onetmp": 604 | return "application/onenote"; 605 | case ".onetoc": 606 | return "application/onenote"; 607 | case ".onetoc2": 608 | return "application/onenote"; 609 | case ".orderedtest": 610 | return "application/xml"; 611 | case ".osdx": 612 | return "application/opensearchdescription+xml"; 613 | case ".p10": 614 | return "application/pkcs10"; 615 | case ".p12": 616 | return "application/x-pkcs12"; 617 | case ".p7b": 618 | return "application/x-pkcs7-certificates"; 619 | case ".p7c": 620 | return "application/pkcs7-mime"; 621 | case ".p7m": 622 | return "application/pkcs7-mime"; 623 | case ".p7r": 624 | return "application/x-pkcs7-certreqresp"; 625 | case ".p7s": 626 | return "application/pkcs7-signature"; 627 | case ".pbm": 628 | return "image/x-portable-bitmap"; 629 | case ".pcast": 630 | return "application/x-podcast"; 631 | case ".pct": 632 | return "image/pict"; 633 | case ".pcx": 634 | return "application/octet-stream"; 635 | case ".pcz": 636 | return "application/octet-stream"; 637 | case ".pdf": 638 | return "application/pdf"; 639 | case ".pfb": 640 | return "application/octet-stream"; 641 | case ".pfm": 642 | return "application/octet-stream"; 643 | case ".pfx": 644 | return "application/x-pkcs12"; 645 | case ".pgm": 646 | return "image/x-portable-graymap"; 647 | case ".pic": 648 | return "image/pict"; 649 | case ".pict": 650 | return "image/pict"; 651 | case ".pkgdef": 652 | return "text/plain"; 653 | case ".pkgundef": 654 | return "text/plain"; 655 | case ".pko": 656 | return "application/vnd.ms-pki.pko"; 657 | case ".pls": 658 | return "audio/scpls"; 659 | case ".pma": 660 | return "application/x-perfmon"; 661 | case ".pmc": 662 | return "application/x-perfmon"; 663 | case ".pml": 664 | return "application/x-perfmon"; 665 | case ".pmr": 666 | return "application/x-perfmon"; 667 | case ".pmw": 668 | return "application/x-perfmon"; 669 | case ".png": 670 | return "image/png"; 671 | case ".pnm": 672 | return "image/x-portable-anymap"; 673 | case ".pnt": 674 | return "image/x-macpaint"; 675 | case ".pntg": 676 | return "image/x-macpaint"; 677 | case ".pnz": 678 | return "image/png"; 679 | case ".pot": 680 | return "application/vnd.ms-powerpoint"; 681 | case ".potm": 682 | return "application/vnd.ms-powerpoint.template.macroEnabled.12"; 683 | case ".potx": 684 | return "application/vnd.openxmlformats-officedocument.presentationml.template"; 685 | case ".ppa": 686 | return "application/vnd.ms-powerpoint"; 687 | case ".ppam": 688 | return "application/vnd.ms-powerpoint.addin.macroEnabled.12"; 689 | case ".ppm": 690 | return "image/x-portable-pixmap"; 691 | case ".pps": 692 | return "application/vnd.ms-powerpoint"; 693 | case ".ppsm": 694 | return "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"; 695 | case ".ppsx": 696 | return "application/vnd.openxmlformats-officedocument.presentationml.slideshow"; 697 | case ".ppt": 698 | return "application/vnd.ms-powerpoint"; 699 | case ".pptm": 700 | return "application/vnd.ms-powerpoint.presentation.macroEnabled.12"; 701 | case ".pptx": 702 | return "application/vnd.openxmlformats-officedocument.presentationml.presentation"; 703 | case ".prf": 704 | return "application/pics-rules"; 705 | case ".prm": 706 | return "application/octet-stream"; 707 | case ".prx": 708 | return "application/octet-stream"; 709 | case ".ps": 710 | return "application/postscript"; 711 | case ".psc1": 712 | return "application/PowerShell"; 713 | case ".psd": 714 | return "application/octet-stream"; 715 | case ".psess": 716 | return "application/xml"; 717 | case ".psm": 718 | return "application/octet-stream"; 719 | case ".psp": 720 | return "application/octet-stream"; 721 | case ".pub": 722 | return "application/x-mspublisher"; 723 | case ".pwz": 724 | return "application/vnd.ms-powerpoint"; 725 | case ".qht": 726 | return "text/x-html-insertion"; 727 | case ".qhtm": 728 | return "text/x-html-insertion"; 729 | case ".qt": 730 | return "video/quicktime"; 731 | case ".qti": 732 | return "image/x-quicktime"; 733 | case ".qtif": 734 | return "image/x-quicktime"; 735 | case ".qtl": 736 | return "application/x-quicktimeplayer"; 737 | case ".qxd": 738 | return "application/octet-stream"; 739 | case ".ra": 740 | return "audio/x-pn-realaudio"; 741 | case ".ram": 742 | return "audio/x-pn-realaudio"; 743 | case ".rar": 744 | return "application/octet-stream"; 745 | case ".ras": 746 | return "image/x-cmu-raster"; 747 | case ".rat": 748 | return "application/rat-file"; 749 | case ".rc": 750 | return "text/plain"; 751 | case ".rc2": 752 | return "text/plain"; 753 | case ".rct": 754 | return "text/plain"; 755 | case ".rdlc": 756 | return "application/xml"; 757 | case ".resx": 758 | return "application/xml"; 759 | case ".rf": 760 | return "image/vnd.rn-realflash"; 761 | case ".rgb": 762 | return "image/x-rgb"; 763 | case ".rgs": 764 | return "text/plain"; 765 | case ".rm": 766 | return "application/vnd.rn-realmedia"; 767 | case ".rmi": 768 | return "audio/mid"; 769 | case ".rmp": 770 | return "application/vnd.rn-rn_music_package"; 771 | case ".roff": 772 | return "application/x-troff"; 773 | case ".rpm": 774 | return "audio/x-pn-realaudio-plugin"; 775 | case ".rqy": 776 | return "text/x-ms-rqy"; 777 | case ".rtf": 778 | return "application/rtf"; 779 | case ".rtx": 780 | return "text/richtext"; 781 | case ".ruleset": 782 | return "application/xml"; 783 | case ".s": 784 | return "text/plain"; 785 | case ".safariextz": 786 | return "application/x-safari-safariextz"; 787 | case ".scd": 788 | return "application/x-msschedule"; 789 | case ".sct": 790 | return "text/scriptlet"; 791 | case ".sd2": 792 | return "audio/x-sd2"; 793 | case ".sdp": 794 | return "application/sdp"; 795 | case ".sea": 796 | return "application/octet-stream"; 797 | case ".searchConnector-ms": 798 | return "application/windows-search-connector+xml"; 799 | case ".setpay": 800 | return "application/set-payment-initiation"; 801 | case ".setreg": 802 | return "application/set-registration-initiation"; 803 | case ".settings": 804 | return "application/xml"; 805 | case ".sgimb": 806 | return "application/x-sgimb"; 807 | case ".sgml": 808 | return "text/sgml"; 809 | case ".sh": 810 | return "application/x-sh"; 811 | case ".shar": 812 | return "application/x-shar"; 813 | case ".shtml": 814 | return "text/html"; 815 | case ".sit": 816 | return "application/x-stuffit"; 817 | case ".sitemap": 818 | return "application/xml"; 819 | case ".skin": 820 | return "application/xml"; 821 | case ".sldm": 822 | return "application/vnd.ms-powerpoint.slide.macroEnabled.12"; 823 | case ".sldx": 824 | return "application/vnd.openxmlformats-officedocument.presentationml.slide"; 825 | case ".slk": 826 | return "application/vnd.ms-excel"; 827 | case ".sln": 828 | return "text/plain"; 829 | case ".slupkg-ms": 830 | return "application/x-ms-license"; 831 | case ".smd": 832 | return "audio/x-smd"; 833 | case ".smi": 834 | return "application/octet-stream"; 835 | case ".smx": 836 | return "audio/x-smd"; 837 | case ".smz": 838 | return "audio/x-smd"; 839 | case ".snd": 840 | return "audio/basic"; 841 | case ".snippet": 842 | return "application/xml"; 843 | case ".snp": 844 | return "application/octet-stream"; 845 | case ".sol": 846 | return "text/plain"; 847 | case ".sor": 848 | return "text/plain"; 849 | case ".spc": 850 | return "application/x-pkcs7-certificates"; 851 | case ".spl": 852 | return "application/futuresplash"; 853 | case ".src": 854 | return "application/x-wais-source"; 855 | case ".srf": 856 | return "text/plain"; 857 | case ".ssisdeploymentmanifest": 858 | return "text/xml"; 859 | case ".ssm": 860 | return "application/streamingmedia"; 861 | case ".sst": 862 | return "application/vnd.ms-pki.certstore"; 863 | case ".stl": 864 | return "application/vnd.ms-pki.stl"; 865 | case ".sv4cpio": 866 | return "application/x-sv4cpio"; 867 | case ".sv4crc": 868 | return "application/x-sv4crc"; 869 | case ".svc": 870 | return "application/xml"; 871 | case ".swf": 872 | return "application/x-shockwave-flash"; 873 | case ".t": 874 | return "application/x-troff"; 875 | case ".tar": 876 | return "application/x-tar"; 877 | case ".tcl": 878 | return "application/x-tcl"; 879 | case ".testrunconfig": 880 | return "application/xml"; 881 | case ".testsettings": 882 | return "application/xml"; 883 | case ".tex": 884 | return "application/x-tex"; 885 | case ".texi": 886 | return "application/x-texinfo"; 887 | case ".texinfo": 888 | return "application/x-texinfo"; 889 | case ".tgz": 890 | return "application/x-compressed"; 891 | case ".thmx": 892 | return "application/vnd.ms-officetheme"; 893 | case ".thn": 894 | return "application/octet-stream"; 895 | case ".tif": 896 | return "image/tiff"; 897 | case ".tiff": 898 | return "image/tiff"; 899 | case ".tlh": 900 | return "text/plain"; 901 | case ".tli": 902 | return "text/plain"; 903 | case ".toc": 904 | return "application/octet-stream"; 905 | case ".tr": 906 | return "application/x-troff"; 907 | case ".trm": 908 | return "application/x-msterminal"; 909 | case ".trx": 910 | return "application/xml"; 911 | case ".ts": 912 | return "video/vnd.dlna.mpeg-tts"; 913 | case ".tsv": 914 | return "text/tab-separated-values"; 915 | case ".ttf": 916 | return "application/octet-stream"; 917 | case ".tts": 918 | return "video/vnd.dlna.mpeg-tts"; 919 | case ".txt": 920 | return "text/plain"; 921 | case ".u32": 922 | return "application/octet-stream"; 923 | case ".uls": 924 | return "text/iuls"; 925 | case ".user": 926 | return "text/plain"; 927 | case ".ustar": 928 | return "application/x-ustar"; 929 | case ".vb": 930 | return "text/plain"; 931 | case ".vbdproj": 932 | return "text/plain"; 933 | case ".vbk": 934 | return "video/mpeg"; 935 | case ".vbproj": 936 | return "text/plain"; 937 | case ".vbs": 938 | return "text/vbscript"; 939 | case ".vcf": 940 | return "text/x-vcard"; 941 | case ".vcproj": 942 | return "Application/xml"; 943 | case ".vcs": 944 | return "text/plain"; 945 | case ".vcxproj": 946 | return "Application/xml"; 947 | case ".vddproj": 948 | return "text/plain"; 949 | case ".vdp": 950 | return "text/plain"; 951 | case ".vdproj": 952 | return "text/plain"; 953 | case ".vdx": 954 | return "application/vnd.ms-visio.viewer"; 955 | case ".vml": 956 | return "text/xml"; 957 | case ".vscontent": 958 | return "application/xml"; 959 | case ".vsct": 960 | return "text/xml"; 961 | case ".vsd": 962 | return "application/vnd.visio"; 963 | case ".vsi": 964 | return "application/ms-vsi"; 965 | case ".vsix": 966 | return "application/vsix"; 967 | case ".vsixlangpack": 968 | return "text/xml"; 969 | case ".vsixmanifest": 970 | return "text/xml"; 971 | case ".vsmdi": 972 | return "application/xml"; 973 | case ".vspscc": 974 | return "text/plain"; 975 | case ".vss": 976 | return "application/vnd.visio"; 977 | case ".vsscc": 978 | return "text/plain"; 979 | case ".vssettings": 980 | return "text/xml"; 981 | case ".vssscc": 982 | return "text/plain"; 983 | case ".vst": 984 | return "application/vnd.visio"; 985 | case ".vstemplate": 986 | return "text/xml"; 987 | case ".vsto": 988 | return "application/x-ms-vsto"; 989 | case ".vsw": 990 | return "application/vnd.visio"; 991 | case ".vsx": 992 | return "application/vnd.visio"; 993 | case ".vtx": 994 | return "application/vnd.visio"; 995 | case ".wav": 996 | return "audio/wav"; 997 | case ".wave": 998 | return "audio/wav"; 999 | case ".wax": 1000 | return "audio/x-ms-wax"; 1001 | case ".wbk": 1002 | return "application/msword"; 1003 | case ".wbmp": 1004 | return "image/vnd.wap.wbmp"; 1005 | case ".wcm": 1006 | return "application/vnd.ms-works"; 1007 | case ".wdb": 1008 | return "application/vnd.ms-works"; 1009 | case ".wdp": 1010 | return "image/vnd.ms-photo"; 1011 | case ".webarchive": 1012 | return "application/x-safari-webarchive"; 1013 | case ".webtest": 1014 | return "application/xml"; 1015 | case ".wiq": 1016 | return "application/xml"; 1017 | case ".wiz": 1018 | return "application/msword"; 1019 | case ".wks": 1020 | return "application/vnd.ms-works"; 1021 | case ".wlmp": 1022 | return "application/wlmoviemaker"; 1023 | case ".wlpginstall": 1024 | return "application/x-wlpg-detect"; 1025 | case ".wlpginstall3": 1026 | return "application/x-wlpg3-detect"; 1027 | case ".wm": 1028 | return "video/x-ms-wm"; 1029 | case ".wma": 1030 | return "audio/x-ms-wma"; 1031 | case ".wmd": 1032 | return "application/x-ms-wmd"; 1033 | case ".WMD": 1034 | return "application/x-ms-wmd"; 1035 | case ".wmf": 1036 | return "application/x-msmetafile"; 1037 | case ".wml": 1038 | return "text/vnd.wap.wml"; 1039 | case ".wmlc": 1040 | return "application/vnd.wap.wmlc"; 1041 | case ".wmls": 1042 | return "text/vnd.wap.wmlscript"; 1043 | case ".wmlsc": 1044 | return "application/vnd.wap.wmlscriptc"; 1045 | case ".wmp": 1046 | return "video/x-ms-wmp"; 1047 | case ".wmv": 1048 | return "video/x-ms-wmv"; 1049 | case ".wmx": 1050 | return "video/x-ms-wmx"; 1051 | case ".wmz": 1052 | return "application/x-ms-wmz"; 1053 | case ".wpl": 1054 | return "application/vnd.ms-wpl"; 1055 | case ".wps": 1056 | return "application/vnd.ms-works"; 1057 | case ".wri": 1058 | return "application/x-mswrite"; 1059 | case ".wrl": 1060 | return "x-world/x-vrml"; 1061 | case ".wrz": 1062 | return "x-world/x-vrml"; 1063 | case ".wsc": 1064 | return "text/scriptlet"; 1065 | case ".wsdl": 1066 | return "text/xml"; 1067 | case ".wvx": 1068 | return "video/x-ms-wvx"; 1069 | case ".x": 1070 | return "application/directx"; 1071 | case ".xaf": 1072 | return "x-world/x-vrml"; 1073 | case ".xaml": 1074 | return "application/xaml+xml"; 1075 | case ".xap": 1076 | return "application/x-silverlight-app"; 1077 | case ".xbap": 1078 | return "application/x-ms-xbap"; 1079 | case ".xbm": 1080 | return "image/x-xbitmap"; 1081 | case ".xdr": 1082 | return "text/plain"; 1083 | case ".xht": 1084 | return "application/xhtml+xml"; 1085 | case ".xhtml": 1086 | return "application/xhtml+xml"; 1087 | case ".xla": 1088 | return "application/vnd.ms-excel"; 1089 | case ".xlam": 1090 | return "application/vnd.ms-excel.addin.macroEnabled.12"; 1091 | case ".xlc": 1092 | return "application/vnd.ms-excel"; 1093 | case ".xld": 1094 | return "application/vnd.ms-excel"; 1095 | case ".xlk": 1096 | return "application/vnd.ms-excel"; 1097 | case ".xll": 1098 | return "application/vnd.ms-excel"; 1099 | case ".xlm": 1100 | return "application/vnd.ms-excel"; 1101 | case ".xls": 1102 | return "application/vnd.ms-excel"; 1103 | case ".xlsb": 1104 | return "application/vnd.ms-excel.sheet.binary.macroEnabled.12"; 1105 | case ".xlsm": 1106 | return "application/vnd.ms-excel.sheet.macroEnabled.12"; 1107 | case ".xlsx": 1108 | return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 1109 | case ".xlt": 1110 | return "application/vnd.ms-excel"; 1111 | case ".xltm": 1112 | return "application/vnd.ms-excel.template.macroEnabled.12"; 1113 | case ".xltx": 1114 | return "application/vnd.openxmlformats-officedocument.spreadsheetml.template"; 1115 | case ".xlw": 1116 | return "application/vnd.ms-excel"; 1117 | case ".xml": 1118 | return "text/xml"; 1119 | case ".xmta": 1120 | return "application/xml"; 1121 | case ".xof": 1122 | return "x-world/x-vrml"; 1123 | case ".xoml": 1124 | return "text/plain"; 1125 | case ".xpm": 1126 | return "image/x-xpixmap"; 1127 | case ".xps": 1128 | return "application/vnd.ms-xpsdocument"; 1129 | case ".xrm-ms": 1130 | return "text/xml"; 1131 | case ".xsc": 1132 | return "application/xml"; 1133 | case ".xsd": 1134 | return "text/xml"; 1135 | case ".xsf": 1136 | return "text/xml"; 1137 | case ".xsl": 1138 | return "text/xml"; 1139 | case ".xslt": 1140 | return "text/xml"; 1141 | case ".xsn": 1142 | return "application/octet-stream"; 1143 | case ".xss": 1144 | return "application/xml"; 1145 | case ".xtp": 1146 | return "application/octet-stream"; 1147 | case ".xwd": 1148 | return "image/x-xwindowdump"; 1149 | case ".z": 1150 | return "application/x-compress"; 1151 | case ".zip": 1152 | return "application/x-zip-compressed"; 1153 | 1154 | default: 1155 | return "application/octet-stream"; 1156 | } 1157 | } 1158 | } 1159 | } --------------------------------------------------------------------------------