diff --git a/sparkpost-net-core-compact.png b/sparkpost-net-core-compact.png new file mode 100644 index 0000000..5c0f969 Binary files /dev/null and b/sparkpost-net-core-compact.png differ diff --git a/sparkpost-net-core.png b/sparkpost-net-core.png new file mode 100644 index 0000000..5c0f969 Binary files /dev/null and b/sparkpost-net-core.png differ diff --git a/src/SparkPost.Acceptance/SparkPost.Acceptance.csproj b/src/SparkPost.Acceptance/SparkPost.Acceptance.csproj index b1c6d6b..d16c5e2 100644 --- a/src/SparkPost.Acceptance/SparkPost.Acceptance.csproj +++ b/src/SparkPost.Acceptance/SparkPost.Acceptance.csproj @@ -1,20 +1,28 @@  + - net6.0 - SparkPost.Acceptance - Library + netcoreapp2.1 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + - - - - - + - - Always - + - \ No newline at end of file + + diff --git a/src/SparkPost.Acceptance/TransmissionsSteps.cs b/src/SparkPost.Acceptance/TransmissionsSteps.cs new file mode 100644 index 0000000..7553e3c --- /dev/null +++ b/src/SparkPost.Acceptance/TransmissionsSteps.cs @@ -0,0 +1,63 @@ +using System; +using TechTalk.SpecFlow; + +namespace SparkPost.Acceptance +{ + [Binding] + public class TransmissionsSteps + { + [Given(@"I have a new transmission")] + public void GivenIHaveANewTransmission() + { + ScenarioContext.Current.Pending(); + } + + [Given(@"the transmission is meant to be sent from '(.*)'")] + public void GivenTheTransmissionIsMeantToBeSentFrom(string p0) + { + ScenarioContext.Current.Pending(); + } + + [Given(@"the transmission is meant to be sent to '(.*)'")] + public void GivenTheTransmissionIsMeantToBeSentTo(string p0) + { + ScenarioContext.Current.Pending(); + } + + [Given(@"the transmission content is")] + public void GivenTheTransmissionContentIs(Table table) + { + ScenarioContext.Current.Pending(); + } + + [Given(@"the transmission has a text file attachment")] + public void GivenTheTransmissionHasATextFileAttachment() + { + ScenarioContext.Current.Pending(); + } + + [Given(@"the transmission template id is set to '(.*)'")] + public void GivenTheTransmissionTemplateIdIsSetTo(string p0) + { + ScenarioContext.Current.Pending(); + } + + [Given(@"the transmission is meant to be CCd to '(.*)'")] + public void GivenTheTransmissionIsMeantToBeCCdTo(string p0) + { + ScenarioContext.Current.Pending(); + } + + [Given(@"the transmission is meant to be BCCd to '(.*)'")] + public void GivenTheTransmissionIsMeantToBeBCCdTo(string p0) + { + ScenarioContext.Current.Pending(); + } + + [When(@"I send the transmission")] + public void WhenISendTheTransmission() + { + ScenarioContext.Current.Pending(); + } + } +} diff --git a/src/SparkPost.Tests/SparkPost.Tests.csproj b/src/SparkPost.Tests/SparkPost.Tests.csproj index 1ebd7b3..95dea2a 100644 --- a/src/SparkPost.Tests/SparkPost.Tests.csproj +++ b/src/SparkPost.Tests/SparkPost.Tests.csproj @@ -1,19 +1,32 @@ + - net6.0 - SparkPost.Tests + netcoreapp2.1 + Library + + - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + - \ No newline at end of file + + diff --git a/src/SparkPost.Tests/SuppressionTests.cs b/src/SparkPost.Tests/SuppressionTests.cs index a77ed0b..e7f141c 100644 --- a/src/SparkPost.Tests/SuppressionTests.cs +++ b/src/SparkPost.Tests/SuppressionTests.cs @@ -35,14 +35,14 @@ public DeleteTests() email = Guid.NewGuid().ToString(); } - [Fact] + [Test] public async Task It_should_return_true_if_the_web_request_returns_no_content() { var result = await suppressions.Delete(email); Assert.True(result); } - [Fact] + [Test] public async Task It_should_return_false_if_the_web_request_returns_anything_but_no_content() { response.StatusCode = HttpStatusCode.Accepted; @@ -58,7 +58,7 @@ public async Task It_should_return_false_if_the_web_request_returns_anything_but Assert.False(deleted); } - [Fact] + [Test] public async Task It_should_build_the_web_request_parameters_correctly() { var version = Guid.NewGuid().ToString(); @@ -79,7 +79,7 @@ public async Task It_should_build_the_web_request_parameters_correctly() await suppressions.Delete(email); } - [Fact] + [Test] public async Task It_should_encode_the_email_address() { var version = Guid.NewGuid().ToString(); @@ -127,14 +127,14 @@ public CreateOrUpdateTests() suppressions = new Suppressions(client.Object, requestSender.Object, dataMapper.Object); } - [Fact] + [Test] public async Task It_should_return_a_response_when_the_web_request_is_ok() { var result = await suppressions.CreateOrUpdate(suppressionsList); Assert.NotNull(result); } - [Fact] + [Test] public async Task It_should_return_the_reason_phrase() { response.ReasonPhrase = Guid.NewGuid().ToString(); @@ -142,7 +142,7 @@ public async Task It_should_return_the_reason_phrase() Assert.Equal(response.ReasonPhrase, result.ReasonPhrase); } - [Fact] + [Test] public async Task It_should_return_the_content() { response.Content = Guid.NewGuid().ToString(); @@ -150,7 +150,7 @@ public async Task It_should_return_the_content() Assert.Equal(response.Content, result.Content); } - [Fact] + [Test] public async Task It_should_make_a_properly_formed_request() { client.Setup(x => x.Version).Returns(Guid.NewGuid().ToString()); @@ -168,7 +168,7 @@ public async Task It_should_make_a_properly_formed_request() await suppressions.CreateOrUpdate(suppressionsList); } - [Fact] + [Test] public async Task It_should_throw_if_the_http_status_code_is_not_ok() { response.StatusCode = HttpStatusCode.Accepted; diff --git a/src/SparkPost/File.cs b/src/SparkPost/File.cs index 120cc14..4bba4cd 100644 --- a/src/SparkPost/File.cs +++ b/src/SparkPost/File.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using System.Web; +using SparkPost.Utilities; namespace SparkPost { @@ -25,7 +25,7 @@ public abstract class File public static T Create(byte[] content) where T : File, new() { - return Create(content, String.Empty); + return Create(content, string.Empty); } public static T Create(byte[] content, string name) where T : File, new() @@ -34,7 +34,7 @@ public abstract class File if (content != null) { result.Data = Convert.ToBase64String(content); - result.Type = MimeMapping.MimeUtility.GetMimeMapping(name); + result.Type = MimeMapping.GetContentType(name); result.Name = name; } ; @@ -43,7 +43,7 @@ public abstract class File public static T Create(Stream content) where T : File, new() { - return Create(content, String.Empty); + return Create(content, string.Empty); } public static T Create(Stream content, string name) where T : File, new() diff --git a/src/SparkPost/ITemplates.cs b/src/SparkPost/ITemplates.cs index c3cf830..a5d5a85 100644 --- a/src/SparkPost/ITemplates.cs +++ b/src/SparkPost/ITemplates.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; namespace SparkPost { @@ -25,5 +26,7 @@ public interface ITemplates Task List(); Task Delete(string templateId); + + Task Preview(string templateId, IDictionary substitutionData, bool? draft = null); } } diff --git a/src/SparkPost/RequestMethods/Get.cs b/src/SparkPost/RequestMethods/Get.cs index f6eec35..60a3c3c 100644 --- a/src/SparkPost/RequestMethods/Get.cs +++ b/src/SparkPost/RequestMethods/Get.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; diff --git a/src/SparkPost/SendingDomains.cs b/src/SparkPost/SendingDomains.cs index f08a598..6a56688 100644 --- a/src/SparkPost/SendingDomains.cs +++ b/src/SparkPost/SendingDomains.cs @@ -1,7 +1,5 @@ using System.Net; using System.Threading.Tasks; -using System.Web; -using Microsoft.AspNetCore.WebUtilities; using SparkPost.RequestSenders; using SparkPost.Utilities; @@ -109,7 +107,7 @@ public async Task Delete(string domain) if (response.StatusCode == HttpStatusCode.NoContent) { response.StatusCode = HttpStatusCode.OK; - response.ReasonPhrase = ReasonPhrases.GetReasonPhrase((int)HttpStatusCode.OK); + response.ReasonPhrase = nameof(HttpStatusCode.OK); } if (response.StatusCode != HttpStatusCode.OK) diff --git a/src/SparkPost/SparkPost.csproj b/src/SparkPost/SparkPost.csproj index fa8500a..220f44c 100644 --- a/src/SparkPost/SparkPost.csproj +++ b/src/SparkPost/SparkPost.csproj @@ -1,33 +1,36 @@ - + + - SparkPost - SparkPost - SparkPost - SparkPost API - Copyright 2022 - 2.0.2 - 2.0.2 - 2.0.2 - Darren Cauthon; Adam Hathcock - https://www.sparkpost.com/sites/default/files/attachments/SparkPost_Logo_2-Color_Gray-Orange_RGB.svg - SparkPost - https://github.com/darrencauthon/csharp-sparkpost/blob/master/LICENSE.md - https://github.com/SparkPost/csharp-sparkpost - LICENSE.md - Added support to pass a user-agent. - email netstandard2.0 - 10 + + Library + + 1.14.0.5 + true + SparkPost.NetCore + Lean Sparkpost API client port to .net standard 2.0 + Apache-2.0 + https://github.com/maxlego/csharp-sparkpost + https://github.com/maxlego/csharp-sparkpost + sparkpost-net-core.png + + Added template preview functionality + - - - - + + + - - LICENSE.md + + + + + + True + + diff --git a/src/SparkPost/Templates.cs b/src/SparkPost/Templates.cs index 735d574..2e2d4bf 100644 --- a/src/SparkPost/Templates.cs +++ b/src/SparkPost/Templates.cs @@ -4,6 +4,7 @@ using System.Reflection; using Newtonsoft.Json; using SparkPost.RequestSenders; +using SparkPost.Utilities; namespace SparkPost { @@ -136,5 +137,54 @@ public async Task Delete(string templateId) var response = await requestSender.Send(request); return response.StatusCode == HttpStatusCode.OK; } + + public async Task Preview(string templateId, IDictionary substitutionData, bool? draft = null) + { + var transmission = new Transmission + { + SubstitutionData = substitutionData + }; + + var url = $"api/{client.Version}/templates/{templateId}/preview"; + if (draft.HasValue) + { + url += $"?draft={draft.ToString().ToLower()}"; + } + + var request = new Request + { + Url = url, + Method = "POST", + Data = dataMapper.ToDictionary(transmission) + }; + + var response = await requestSender.Send(request); + if (response.StatusCode != HttpStatusCode.OK) throw new ResponseException(response); + + var results = Jsonification.DeserializeObject(response.Content).results; + + Dictionary Headers = new Dictionary(); + if (results.headers != null) + { + foreach (var property in results.headers.GetType().GetProperties()) + { + Headers[property.Name] = (string)property.GetValue(results.headers); + } + } + + return new TemplateContent + { + From = new Address + { + Email = results.from.email, + Name = results.from.name + }, + Subject = results.subject, + ReplyTo = results.reply_to ?? null, + Text = results.text ?? null, + Html = results.html ?? null, + Headers = Headers + }; + } } } diff --git a/src/SparkPost/Utilities/FileExtensionContentTypeProvider.cs b/src/SparkPost/Utilities/FileExtensionContentTypeProvider.cs new file mode 100644 index 0000000..c78d989 --- /dev/null +++ b/src/SparkPost/Utilities/FileExtensionContentTypeProvider.cs @@ -0,0 +1,422 @@ +using System; +using System.Collections.Generic; + +namespace SparkPost.Utilities +{ + /// + /// Provides a mapping between file extensions and MIME types. + /// + internal static class MimeMapping + { + private const string DEFAULT_CONTENT_TYPE = "application/octet-stream"; + + private static readonly IDictionary _map = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + {".323", "text/h323"}, + {".3g2", "video/3gpp2"}, + {".3gp2", "video/3gpp2"}, + {".3gp", "video/3gpp"}, + {".3gpp", "video/3gpp"}, + {".aac", "audio/aac"}, + {".aaf", "application/octet-stream"}, + {".aca", "application/octet-stream"}, + {".accdb", "application/msaccess"}, + {".accde", "application/msaccess"}, + {".accdt", "application/msaccess"}, + {".acx", "application/internet-property-stream"}, + {".adt", "audio/vnd.dlna.adts"}, + {".adts", "audio/vnd.dlna.adts"}, + {".afm", "application/octet-stream"}, + {".ai", "application/postscript"}, + {".aif", "audio/x-aiff"}, + {".aifc", "audio/aiff"}, + {".aiff", "audio/aiff"}, + {".appcache", "text/cache-manifest"}, + {".application", "application/x-ms-application"}, + {".art", "image/x-jg"}, + {".asd", "application/octet-stream"}, + {".asf", "video/x-ms-asf"}, + {".asi", "application/octet-stream"}, + {".asm", "text/plain"}, + {".asr", "video/x-ms-asf"}, + {".asx", "video/x-ms-asf"}, + {".atom", "application/atom+xml"}, + {".au", "audio/basic"}, + {".avi", "video/x-msvideo"}, + {".axs", "application/olescript"}, + {".bas", "text/plain"}, + {".bcpio", "application/x-bcpio"}, + {".bin", "application/octet-stream"}, + {".bmp", "image/bmp"}, + {".c", "text/plain"}, + {".cab", "application/vnd.ms-cab-compressed"}, + {".calx", "application/vnd.ms-office.calx"}, + {".cat", "application/vnd.ms-pki.seccat"}, + {".cdf", "application/x-cdf"}, + {".chm", "application/octet-stream"}, + {".class", "application/x-java-applet"}, + {".clp", "application/x-msclip"}, + {".cmx", "image/x-cmx"}, + {".cnf", "text/plain"}, + {".cod", "image/cis-cod"}, + {".cpio", "application/x-cpio"}, + {".cpp", "text/plain"}, + {".crd", "application/x-mscardfile"}, + {".crl", "application/pkix-crl"}, + {".crt", "application/x-x509-ca-cert"}, + {".csh", "application/x-csh"}, + {".css", "text/css"}, + {".csv", "application/octet-stream"}, + {".cur", "application/octet-stream"}, + {".dcr", "application/x-director"}, + {".deploy", "application/octet-stream"}, + {".der", "application/x-x509-ca-cert"}, + {".dib", "image/bmp"}, + {".dir", "application/x-director"}, + {".disco", "text/xml"}, + {".dlm", "text/dlm"}, + {".doc", "application/msword"}, + {".docm", "application/vnd.ms-word.document.macroEnabled.12"}, + {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {".dot", "application/msword"}, + {".dotm", "application/vnd.ms-word.template.macroEnabled.12"}, + {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, + {".dsp", "application/octet-stream"}, + {".dtd", "text/xml"}, + {".dvi", "application/x-dvi"}, + {".dvr-ms", "video/x-ms-dvr"}, + {".dwf", "drawing/x-dwf"}, + {".dwp", "application/octet-stream"}, + {".dxr", "application/x-director"}, + {".eml", "message/rfc822"}, + {".emz", "application/octet-stream"}, + {".eot", "application/vnd.ms-fontobject"}, + {".eps", "application/postscript"}, + {".etx", "text/x-setext"}, + {".evy", "application/envoy"}, + {".fdf", "application/vnd.fdf"}, + {".fif", "application/fractals"}, + {".fla", "application/octet-stream"}, + {".flr", "x-world/x-vrml"}, + {".flv", "video/x-flv"}, + {".gif", "image/gif"}, + {".gtar", "application/x-gtar"}, + {".gz", "application/x-gzip"}, + {".h", "text/plain"}, + {".hdf", "application/x-hdf"}, + {".hdml", "text/x-hdml"}, + {".hhc", "application/x-oleobject"}, + {".hhk", "application/octet-stream"}, + {".hhp", "application/octet-stream"}, + {".hlp", "application/winhlp"}, + {".hqx", "application/mac-binhex40"}, + {".hta", "application/hta"}, + {".htc", "text/x-component"}, + {".htm", "text/html"}, + {".html", "text/html"}, + {".htt", "text/webviewhtml"}, + {".hxt", "text/html"}, + {".ical", "text/calendar"}, + {".icalendar", "text/calendar"}, + {".ico", "image/x-icon"}, + {".ics", "text/calendar"}, + {".ief", "image/ief"}, + {".ifb", "text/calendar"}, + {".iii", "application/x-iphone"}, + {".inf", "application/octet-stream"}, + {".ins", "application/x-internet-signup"}, + {".isp", "application/x-internet-signup"}, + {".IVF", "video/x-ivf"}, + {".jar", "application/java-archive"}, + {".java", "application/octet-stream"}, + {".jck", "application/liquidmotion"}, + {".jcz", "application/liquidmotion"}, + {".jfif", "image/pjpeg"}, + {".jpb", "application/octet-stream"}, + {".jpe", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".jpg", "image/jpeg"}, + {".js", "application/javascript"}, + {".json", "application/json"}, + {".jsx", "text/jscript"}, + {".latex", "application/x-latex"}, + {".lit", "application/x-ms-reader"}, + {".lpk", "application/octet-stream"}, + {".lsf", "video/x-la-asf"}, + {".lsx", "video/x-la-asf"}, + {".lzh", "application/octet-stream"}, + {".m13", "application/x-msmediaview"}, + {".m14", "application/x-msmediaview"}, + {".m1v", "video/mpeg"}, + {".m2ts", "video/vnd.dlna.mpeg-tts"}, + {".m3u", "audio/x-mpegurl"}, + {".m4a", "audio/mp4"}, + {".m4v", "video/mp4"}, + {".man", "application/x-troff-man"}, + {".manifest", "application/x-ms-manifest"}, + {".map", "text/plain"}, + {".markdown", "text/markdown"}, + {".md", "text/markdown"}, + {".mdb", "application/x-msaccess"}, + {".mdp", "application/octet-stream"}, + {".me", "application/x-troff-me"}, + {".mht", "message/rfc822"}, + {".mhtml", "message/rfc822"}, + {".mid", "audio/mid"}, + {".midi", "audio/mid"}, + {".mix", "application/octet-stream"}, + {".mmf", "application/x-smaf"}, + {".mno", "text/xml"}, + {".mny", "application/x-msmoney"}, + {".mov", "video/quicktime"}, + {".movie", "video/x-sgi-movie"}, + {".mp2", "video/mpeg"}, + {".mp3", "audio/mpeg"}, + {".mp4", "video/mp4"}, + {".mp4v", "video/mp4"}, + {".mpa", "video/mpeg"}, + {".mpe", "video/mpeg"}, + {".mpeg", "video/mpeg"}, + {".mpg", "video/mpeg"}, + {".mpp", "application/vnd.ms-project"}, + {".mpv2", "video/mpeg"}, + {".ms", "application/x-troff-ms"}, + {".msi", "application/octet-stream"}, + {".mso", "application/octet-stream"}, + {".mvb", "application/x-msmediaview"}, + {".mvc", "application/x-miva-compiled"}, + {".nc", "application/x-netcdf"}, + {".nsc", "video/x-ms-asf"}, + {".nws", "message/rfc822"}, + {".ocx", "application/octet-stream"}, + {".oda", "application/oda"}, + {".odc", "text/x-ms-odc"}, + {".ods", "application/oleobject"}, + {".oga", "audio/ogg"}, + {".ogg", "video/ogg"}, + {".ogv", "video/ogg"}, + {".ogx", "application/ogg"}, + {".one", "application/onenote"}, + {".onea", "application/onenote"}, + {".onetoc", "application/onenote"}, + {".onetoc2", "application/onenote"}, + {".onetmp", "application/onenote"}, + {".onepkg", "application/onenote"}, + {".osdx", "application/opensearchdescription+xml"}, + {".otf", "font/otf"}, + {".p10", "application/pkcs10"}, + {".p12", "application/x-pkcs12"}, + {".p7b", "application/x-pkcs7-certificates"}, + {".p7c", "application/pkcs7-mime"}, + {".p7m", "application/pkcs7-mime"}, + {".p7r", "application/x-pkcs7-certreqresp"}, + {".p7s", "application/pkcs7-signature"}, + {".pbm", "image/x-portable-bitmap"}, + {".pcx", "application/octet-stream"}, + {".pcz", "application/octet-stream"}, + {".pdf", "application/pdf"}, + {".pfb", "application/octet-stream"}, + {".pfm", "application/octet-stream"}, + {".pfx", "application/x-pkcs12"}, + {".pgm", "image/x-portable-graymap"}, + {".pko", "application/vnd.ms-pki.pko"}, + {".pma", "application/x-perfmon"}, + {".pmc", "application/x-perfmon"}, + {".pml", "application/x-perfmon"}, + {".pmr", "application/x-perfmon"}, + {".pmw", "application/x-perfmon"}, + {".png", "image/png"}, + {".pnm", "image/x-portable-anymap"}, + {".pnz", "image/png"}, + {".pot", "application/vnd.ms-powerpoint"}, + {".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"}, + {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"}, + {".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"}, + {".ppm", "image/x-portable-pixmap"}, + {".pps", "application/vnd.ms-powerpoint"}, + {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"}, + {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, + {".ppt", "application/vnd.ms-powerpoint"}, + {".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"}, + {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, + {".prf", "application/pics-rules"}, + {".prm", "application/octet-stream"}, + {".prx", "application/octet-stream"}, + {".ps", "application/postscript"}, + {".psd", "application/octet-stream"}, + {".psm", "application/octet-stream"}, + {".psp", "application/octet-stream"}, + {".pub", "application/x-mspublisher"}, + {".qt", "video/quicktime"}, + {".qtl", "application/x-quicktimeplayer"}, + {".qxd", "application/octet-stream"}, + {".ra", "audio/x-pn-realaudio"}, + {".ram", "audio/x-pn-realaudio"}, + {".rar", "application/octet-stream"}, + {".ras", "image/x-cmu-raster"}, + {".rf", "image/vnd.rn-realflash"}, + {".rgb", "image/x-rgb"}, + {".rm", "application/vnd.rn-realmedia"}, + {".rmi", "audio/mid"}, + {".roff", "application/x-troff"}, + {".rpm", "audio/x-pn-realaudio-plugin"}, + {".rtf", "application/rtf"}, + {".rtx", "text/richtext"}, + {".scd", "application/x-msschedule"}, + {".sct", "text/scriptlet"}, + {".sea", "application/octet-stream"}, + {".setpay", "application/set-payment-initiation"}, + {".setreg", "application/set-registration-initiation"}, + {".sgml", "text/sgml"}, + {".sh", "application/x-sh"}, + {".shar", "application/x-shar"}, + {".sit", "application/x-stuffit"}, + {".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"}, + {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"}, + {".smd", "audio/x-smd"}, + {".smi", "application/octet-stream"}, + {".smx", "audio/x-smd"}, + {".smz", "audio/x-smd"}, + {".snd", "audio/basic"}, + {".snp", "application/octet-stream"}, + {".spc", "application/x-pkcs7-certificates"}, + {".spl", "application/futuresplash"}, + {".spx", "audio/ogg"}, + {".src", "application/x-wais-source"}, + {".ssm", "application/streamingmedia"}, + {".sst", "application/vnd.ms-pki.certstore"}, + {".stl", "application/vnd.ms-pki.stl"}, + {".sv4cpio", "application/x-sv4cpio"}, + {".sv4crc", "application/x-sv4crc"}, + {".svg", "image/svg+xml"}, + {".svgz", "image/svg+xml"}, + {".swf", "application/x-shockwave-flash"}, + {".t", "application/x-troff"}, + {".tar", "application/x-tar"}, + {".tcl", "application/x-tcl"}, + {".tex", "application/x-tex"}, + {".texi", "application/x-texinfo"}, + {".texinfo", "application/x-texinfo"}, + {".tgz", "application/x-compressed"}, + {".thmx", "application/vnd.ms-officetheme"}, + {".thn", "application/octet-stream"}, + {".tif", "image/tiff"}, + {".tiff", "image/tiff"}, + {".toc", "application/octet-stream"}, + {".tr", "application/x-troff"}, + {".trm", "application/x-msterminal"}, + {".ts", "video/vnd.dlna.mpeg-tts"}, + {".tsv", "text/tab-separated-values"}, + {".ttc", "application/x-font-ttf"}, + {".ttf", "application/x-font-ttf"}, + {".tts", "video/vnd.dlna.mpeg-tts"}, + {".txt", "text/plain"}, + {".u32", "application/octet-stream"}, + {".uls", "text/iuls"}, + {".ustar", "application/x-ustar"}, + {".vbs", "text/vbscript"}, + {".vcf", "text/x-vcard"}, + {".vcs", "text/plain"}, + {".vdx", "application/vnd.ms-visio.viewer"}, + {".vml", "text/xml"}, + {".vsd", "application/vnd.visio"}, + {".vss", "application/vnd.visio"}, + {".vst", "application/vnd.visio"}, + {".vsto", "application/x-ms-vsto"}, + {".vsw", "application/vnd.visio"}, + {".vsx", "application/vnd.visio"}, + {".vtx", "application/vnd.visio"}, + {".wasm", "application/wasm"}, + {".wav", "audio/wav"}, + {".wax", "audio/x-ms-wax"}, + {".wbmp", "image/vnd.wap.wbmp"}, + {".wcm", "application/vnd.ms-works"}, + {".wdb", "application/vnd.ms-works"}, + {".webm", "video/webm"}, + {".webp", "image/webp"}, + {".wks", "application/vnd.ms-works"}, + {".wm", "video/x-ms-wm"}, + {".wma", "audio/x-ms-wma"}, + {".wmd", "application/x-ms-wmd"}, + {".wmf", "application/x-msmetafile"}, + {".wml", "text/vnd.wap.wml"}, + {".wmlc", "application/vnd.wap.wmlc"}, + {".wmls", "text/vnd.wap.wmlscript"}, + {".wmlsc", "application/vnd.wap.wmlscriptc"}, + {".wmp", "video/x-ms-wmp"}, + {".wmv", "video/x-ms-wmv"}, + {".wmx", "video/x-ms-wmx"}, + {".wmz", "application/x-ms-wmz"}, + {".woff", "application/font-woff"}, // https://www.w3.org/TR/WOFF/#appendix-b + {".woff2", "font/woff2"}, // https://www.w3.org/TR/WOFF2/#IMT + {".wps", "application/vnd.ms-works"}, + {".wri", "application/x-mswrite"}, + {".wrl", "x-world/x-vrml"}, + {".wrz", "x-world/x-vrml"}, + {".wsdl", "text/xml"}, + {".wtv", "video/x-ms-wtv"}, + {".wvx", "video/x-ms-wvx"}, + {".x", "application/directx"}, + {".xaf", "x-world/x-vrml"}, + {".xaml", "application/xaml+xml"}, + {".xap", "application/x-silverlight-app"}, + {".xbap", "application/x-ms-xbap"}, + {".xbm", "image/x-xbitmap"}, + {".xdr", "text/plain"}, + {".xht", "application/xhtml+xml"}, + {".xhtml", "application/xhtml+xml"}, + {".xla", "application/vnd.ms-excel"}, + {".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"}, + {".xlc", "application/vnd.ms-excel"}, + {".xlm", "application/vnd.ms-excel"}, + {".xls", "application/vnd.ms-excel"}, + {".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"}, + {".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"}, + {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {".xlt", "application/vnd.ms-excel"}, + {".xltm", "application/vnd.ms-excel.template.macroEnabled.12"}, + {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"}, + {".xlw", "application/vnd.ms-excel"}, + {".xml", "text/xml"}, + {".xof", "x-world/x-vrml"}, + {".xpm", "image/x-xpixmap"}, + {".xps", "application/vnd.ms-xpsdocument"}, + {".xsd", "text/xml"}, + {".xsf", "text/xml"}, + {".xsl", "text/xml"}, + {".xslt", "text/xml"}, + {".xsn", "application/octet-stream"}, + {".xtp", "application/octet-stream"}, + {".xwd", "image/x-xwindowdump"}, + {".z", "application/x-compress"}, + {".zip", "application/x-zip-compressed"}, + }; + + /// + /// The cross reference table of file extensions and content-types. + /// + + /// + /// Given a file path, determine the MIME type + /// + /// A file path + /// The resulting MIME type + /// True if MIME type could be determined + public static bool TryGetKnownContentType(string path, out string contentType) + { + string extension = System.IO.Path.GetExtension(path); + if (extension == null) + { + contentType = null; + return false; + } + return _map.TryGetValue(extension, out contentType); + } + + public static string GetContentType(string path) + { + return TryGetKnownContentType(path, out var contentType) ? contentType : DEFAULT_CONTENT_TYPE; + } + } +}