├── .env_sample ├── .github └── workflows │ ├── pr-lint.yml │ └── test-and-deploy.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FIRST_TIMERS.md ├── LICENSE ├── Makefile ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── Smtpapi ├── Example │ ├── Program.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ └── SendGrid.SmtpApi.Example.csproj ├── HeaderTests │ ├── SendGrid.SmtpApi.HeaderTests.csproj │ ├── TestHeader.cs │ ├── TestRepositoryFiles.cs │ ├── TestTreeNode.cs │ └── TestUtils.cs ├── SendGrid.SmtpApi.sln ├── Smtpapi │ ├── Header.cs │ ├── HeaderSettingsNode.cs │ ├── IHeader.cs │ ├── SendGrid.SmtpApi.csproj │ └── Utils.cs └── Tests │ └── RequiredFilesExistTest.cs ├── TROUBLESHOOTING.md ├── UseCases └── README.md ├── smptpapicsharp.snk ├── static └── img │ ├── github-fork.png │ └── github-sign-up.png └── twilio_sendgrid_logo.png /.env_sample: -------------------------------------------------------------------------------- 1 | Environment.SetEnvironmentVariable("NAME_OF_THE_ENVIRONMENT_VARIABLE_FOR_YOUR_SENDGRID_KEY", value); -------------------------------------------------------------------------------- /.github/workflows/pr-lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint PR 2 | on: 3 | pull_request_target: 4 | types: [ opened, edited, synchronize, reopened ] 5 | 6 | jobs: 7 | validate: 8 | name: Validate title 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: amannn/action-semantic-pull-request@v4 12 | with: 13 | types: chore docs fix feat test misc 14 | env: 15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /.github/workflows/test-and-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Test and Deploy 2 | on: 3 | push: 4 | branches: [ '*' ] 5 | tags: [ '*' ] 6 | pull_request: 7 | branches: [ main ] 8 | schedule: 9 | # Run automatically at 8AM PST Monday-Friday 10 | - cron: '0 15 * * 1-5' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | test: 15 | name: Test 16 | runs-on: ubuntu-latest 17 | timeout-minutes: 20 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - name: Setup .NET Core SDK 22 | uses: actions/setup-dotnet@v1.8.2 23 | with: 24 | dotnet-version: '3.1.x' 25 | 26 | - name: Build & Test 27 | run: make install test 28 | env: 29 | FrameworkPathOverride: /usr/lib/mono/4.5/ 30 | - run: bash <(curl -s https://codecov.io/bash) 31 | 32 | deploy: 33 | name: Deploy 34 | if: success() && github.ref_type == 'tag' 35 | needs: [ test ] 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v2 39 | 40 | - name: Setup .NET Core SDK 41 | uses: actions/setup-dotnet@v1.8.2 42 | with: 43 | dotnet-version: '3.1.x' 44 | 45 | - name: Create GitHub Release 46 | uses: sendgrid/dx-automator/actions/release@main 47 | with: 48 | footer: '**[NuGet](https://www.nuget.org/packages/SendGrid.SmtpApi/${version})**' 49 | env: 50 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 51 | 52 | - name: Publish package to NuGet 53 | run: | 54 | make install release 55 | dotnet nuget push **/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json 56 | env: 57 | FrameworkPathOverride: /usr/lib/mono/4.5/ 58 | 59 | - name: Submit metric to Datadog 60 | uses: sendgrid/dx-automator/actions/datadog-release-metric@main 61 | env: 62 | DD_API_KEY: ${{ secrets.DATADOG_API_KEY }} 63 | 64 | notify-on-failure: 65 | name: Slack notify on failure 66 | if: failure() && github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') 67 | needs: [ test, deploy ] 68 | runs-on: ubuntu-latest 69 | steps: 70 | - uses: rtCamp/action-slack-notify@v2 71 | env: 72 | SLACK_COLOR: failure 73 | SLACK_ICON_EMOJI: ':github:' 74 | SLACK_MESSAGE: ${{ format('Test *{0}*, Deploy *{1}*, {2}/{3}/actions/runs/{4}', needs.test.result, needs.deploy.result, github.server_url, github.repository, github.run_id) }} 75 | SLACK_TITLE: Action Failure - ${{ github.repository }} 76 | SLACK_USERNAME: GitHub Actions 77 | SLACK_MSG_AUTHOR: twilio-dx 78 | SLACK_FOOTER: Posted automatically using GitHub Actions 79 | SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} 80 | MSG_MINIMAL: true 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 20 | !packages/*/build/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | *_i.c 31 | *_p.c 32 | *_i.h 33 | *.ilk 34 | *.meta 35 | *.obj 36 | *.pch 37 | *.pdb 38 | *.pgc 39 | *.pgd 40 | *.rsp 41 | *.sbr 42 | *.tlb 43 | *.tli 44 | *.tlh 45 | *.tmp 46 | *.tmp_proj 47 | *.log 48 | *.vspscc 49 | *.vssscc 50 | .builds 51 | *.pidb 52 | *.svclog 53 | *.scc 54 | *.nupkg 55 | 56 | # Chutzpah Test files 57 | _Chutzpah* 58 | 59 | # Visual C++ cache files 60 | ipch/ 61 | *.aps 62 | *.ncb 63 | *.opensdf 64 | *.sdf 65 | *.cachefile 66 | 67 | # Visual Studio profiler 68 | *.psess 69 | *.vsp 70 | *.vspx 71 | 72 | # TFS 2012 Local Workspace 73 | $tf/ 74 | 75 | # Guidance Automation Toolkit 76 | *.gpState 77 | 78 | # ReSharper is a .NET coding add-in 79 | _ReSharper*/ 80 | *.[Rr]e[Ss]harper 81 | *.DotSettings 82 | *.DotSettings.user 83 | 84 | # JustCode is a .NET coding addin-in 85 | .JustCode 86 | 87 | # TeamCity is a build add-in 88 | _TeamCity* 89 | 90 | # DotCover is a Code Coverage Tool 91 | *.dotCover 92 | 93 | # NCrunch 94 | *.ncrunch* 95 | _NCrunch_* 96 | .*crunch*.local.xml 97 | 98 | # MightyMoose 99 | *.mm.* 100 | AutoTest.Net/ 101 | 102 | # Installshield output folder 103 | [Ee]xpress/ 104 | 105 | # DocProject is a documentation generator add-in 106 | DocProject/buildhelp/ 107 | DocProject/Help/*.HxT 108 | DocProject/Help/*.HxC 109 | DocProject/Help/*.hhc 110 | DocProject/Help/*.hhk 111 | DocProject/Help/*.hhp 112 | DocProject/Help/Html2 113 | DocProject/Help/html 114 | 115 | # Click-Once directory 116 | publish/ 117 | 118 | # Publish Web Output 119 | *.[Pp]ublish.xml 120 | *.azurePubxml 121 | 122 | # NuGet Packages Directory 123 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 124 | packages/ 125 | ## TODO: If the tool you use requires repositories.config, also uncomment the next line 126 | !packages/repositories.config 127 | 128 | # Windows Azure Build Output 129 | csx/ 130 | *.build.csdef 131 | 132 | # Windows Store app package directory 133 | AppPackages/ 134 | 135 | # Others 136 | sql/ 137 | *.Cache 138 | ClientBin/ 139 | [Ss]tyle[Cc]op.* 140 | ~$* 141 | *~ 142 | *.dbmdl 143 | *.dbproj.schemaview 144 | *.pfx 145 | *.publishsettings 146 | 147 | # RIA/Silverlight projects 148 | Generated_Code/ 149 | 150 | # Backup & report files from converting an old project file to a newer 151 | # Visual Studio version. Backup files are not needed, because we have git ;-) 152 | _UpgradeReport_Files/ 153 | Backup*/ 154 | UpgradeLog*.XML 155 | UpgradeLog*.htm 156 | 157 | # SQL Server files 158 | App_Data/*.mdf 159 | App_Data/*.ldf 160 | 161 | # Business Intelligence projects 162 | *.rdl.data 163 | *.bim.layout 164 | *.bim_*.settings 165 | 166 | # Microsoft Fakes 167 | FakesAssemblies/ 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 | *.tss 184 | .env -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | [2022-05-18] Version 1.4.6 5 | -------------------------- 6 | **Library - Docs** 7 | - [PR #114](https://github.com/sendgrid/smtpapi-csharp/pull/114): Add Docs Changes to align with SendGrid Support. Thanks to [@garethpaul](https://github.com/garethpaul)! 8 | 9 | **Library - Chore** 10 | - [PR #113](https://github.com/sendgrid/smtpapi-csharp/pull/113): Security upgrade Newtonsoft.Json from 9.0.1 to 13.0.1. Thanks to [@svcprodsec-sendgrid](https://github.com/svcprodsec-sendgrid)! 11 | 12 | 13 | [2022-03-09] Version 1.4.5 14 | -------------------------- 15 | **Library - Chore** 16 | - [PR #112](https://github.com/sendgrid/smtpapi-csharp/pull/112): push Datadog Release Metric upon deploy success. Thanks to [@eshanholtz](https://github.com/eshanholtz)! 17 | 18 | 19 | [2022-02-09] Version 1.4.4 20 | -------------------------- 21 | **Library - Chore** 22 | - [PR #111](https://github.com/sendgrid/smtpapi-csharp/pull/111): add gh release to workflow. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! 23 | - [PR #110](https://github.com/sendgrid/smtpapi-csharp/pull/110): merge test and deploy workflows. Thanks to [@shwetha-manvinkurke](https://github.com/shwetha-manvinkurke)! 24 | 25 | 26 | [2022-01-12] Version 1.4.3 27 | -------------------------- 28 | **Library - Chore** 29 | - [PR #109](https://github.com/sendgrid/smtpapi-csharp/pull/109): update license year. Thanks to [@JenniferMah](https://github.com/JenniferMah)! 30 | 31 | 32 | [2021-12-01] Version 1.4.2 33 | -------------------------- 34 | **Library - Chore** 35 | - [PR #108](https://github.com/sendgrid/smtpapi-csharp/pull/108): migrate to GitHub Actions. Thanks to [@eshanholtz](https://github.com/eshanholtz)! 36 | 37 | 38 | [2021-05-05] Version 1.4.1 39 | -------------------------- 40 | **Library - Chore** 41 | - [PR #107](https://github.com/sendgrid/smtpapi-csharp/pull/107): manually bump version number. Thanks to [@eshanholtz](https://github.com/eshanholtz)! 42 | 43 | 44 | [2021-05-05] Version 1.4.0 45 | -------------------------- 46 | **Library - Feature** 47 | - [PR #106](https://github.com/sendgrid/smtpapi-csharp/pull/106): added datetimeoffset support to send_at. Thanks to [@LouisWhite15](https://github.com/LouisWhite15)! 48 | 49 | **Library - Chore** 50 | - [PR #103](https://github.com/sendgrid/smtpapi-csharp/pull/103): add targets for .NET Standard to support .NET Core. Thanks to [@thinkingserious](https://github.com/thinkingserious)! 51 | 52 | 53 | [2020-08-19] Version 1.3.9 54 | -------------------------- 55 | **Library - Chore** 56 | - [PR #102](https://github.com/sendgrid/smtpapi-csharp/pull/102): update GitHub branch references to use HEAD. Thanks to [@thinkingserious](https://github.com/thinkingserious)! 57 | 58 | 59 | [2020-07-22] Version 1.3.8 60 | -------------------------- 61 | **Library - Docs** 62 | - [PR #90](https://github.com/sendgrid/smtpapi-csharp/pull/90): Fix link to smtp-csharp git repo. Thanks to [@sanjaysingh](https://github.com/sanjaysingh)! 63 | 64 | 65 | [2020-04-15] Version 1.3.7 66 | -------------------------- 67 | **Library - Docs** 68 | - [PR #100](https://github.com/sendgrid/smtpapi-csharp/pull/100): baseline all the templated markdown docs. Thanks to [@childish-sambino](https://github.com/childish-sambino)! 69 | 70 | 71 | [2020-02-19] Version 1.3.6 72 | -------------------------- 73 | **Library - Docs** 74 | - [PR #84](https://github.com/sendgrid/smtpapi-csharp/pull/84): Add first-timers.md file for newcomers. Thanks to [@ely-alamillo](https://github.com/ely-alamillo)! 75 | 76 | 77 | [2020-02-05] Version 1.3.5 78 | -------------------------- 79 | **Library - Docs** 80 | - [PR #64](https://github.com/sendgrid/smtpapi-csharp/pull/64): makes Environmental Variables a sub topic in CONTRIBUTING.md. Thanks to [@thepriefy](https://github.com/thepriefy)! 81 | 82 | 83 | [2020-01-29] Version 1.3.4 84 | -------------------------- 85 | **Library - Chore** 86 | - [PR #99](https://github.com/sendgrid/smtpapi-csharp/pull/99): updates for auto deploy via Travis CI. Thanks to [@thinkingserious](https://github.com/thinkingserious)! 87 | - [PR #98](https://github.com/sendgrid/smtpapi-csharp/pull/98): updates for auto-deploy via Travis CI. Thanks to [@thinkingserious](https://github.com/thinkingserious)! 88 | - [PR #96](https://github.com/sendgrid/smtpapi-csharp/pull/96): prep the repo for automated releasing. Thanks to [@thinkingserious](https://github.com/thinkingserious)! 89 | - [PR #80](https://github.com/sendgrid/smtpapi-csharp/pull/80): Adds a Deploy Travis task that publishes to NuGet. Thanks to [@Gimly](https://github.com/Gimly)! 90 | - [PR #76](https://github.com/sendgrid/smtpapi-csharp/pull/76): Adds UseCase folder and README.md file. Thanks to [@Gimly](https://github.com/Gimly)! 91 | - [PR #75](https://github.com/sendgrid/smtpapi-csharp/pull/75): Update LICENSE.txt. Thanks to [@nocategory](https://github.com/nocategory)! 92 | - [PR #68](https://github.com/sendgrid/smtpapi-csharp/pull/68): Add Code Review to Contributing.md #66. Thanks to [@derekneuland](https://github.com/derekneuland)! 93 | - [PR #61](https://github.com/sendgrid/smtpapi-csharp/pull/61): Update License Date. Thanks to [@shucon](https://github.com/shucon)! 94 | - [PR #51](https://github.com/sendgrid/smtpapi-csharp/pull/51): Add a .env_sample file, update gitignore, update README.md. Thanks to [@thepriefy](https://github.com/thepriefy)! 95 | - [PR #57](https://github.com/sendgrid/smtpapi-csharp/pull/57): update LICENSE - fix year. Thanks to [@pushkyn](https://github.com/pushkyn)! 96 | - [PR #59](https://github.com/sendgrid/smtpapi-csharp/pull/59): License.md file date range ut. Thanks to [@blitzerpl](https://github.com/blitzerpl)! 97 | - [PR #58](https://github.com/sendgrid/smtpapi-csharp/pull/58): Added unittest to check for specific repo files. Thanks to [@mptap](https://github.com/mptap)! 98 | - [PR #52](https://github.com/sendgrid/smtpapi-csharp/pull/52): Add a env sample. Thanks to [@gabrielgene](https://github.com/gabrielgene)! 99 | - [PR #47](https://github.com/sendgrid/smtpapi-csharp/pull/47): add GitHub PR template. Thanks to [@pushkyn](https://github.com/pushkyn)! 100 | - [PR #44](https://github.com/sendgrid/smtpapi-csharp/pull/44): add .github/ISSUE_TEMPLATE. Thanks to [@thepriefy](https://github.com/thepriefy)! 101 | - [PR #38](https://github.com/sendgrid/smtpapi-csharp/pull/38): Add/Update Badges on README. Thanks to [@mptap](https://github.com/mptap)! 102 | - [PR #37](https://github.com/sendgrid/smtpapi-csharp/pull/37): SEO friendly links for README.md and CONTRIBUTING.md. Thanks to [@whitneyrosenberg](https://github.com/whitneyrosenberg)! 103 | 104 | **Library - Fix** 105 | - [PR #97](https://github.com/sendgrid/smtpapi-csharp/pull/97): nuget push flag. Thanks to [@thinkingserious](https://github.com/thinkingserious)! 106 | - [PR #31](https://github.com/sendgrid/smtpapi-csharp/pull/31): README.md typo. Thanks to [@ciceropablo](https://github.com/ciceropablo)! 107 | 108 | **Library - Docs** 109 | - [PR #92](https://github.com/sendgrid/smtpapi-csharp/pull/92): Added Announcement. Thanks to [@luciajimenez](https://github.com/luciajimenez)! 110 | - [PR #45](https://github.com/sendgrid/smtpapi-csharp/pull/45): README.md typo. Thanks to [@abdullaharshad](https://github.com/abdullaharshad)! 111 | - [PR #42](https://github.com/sendgrid/smtpapi-csharp/pull/42): update contributing.md - fix typo. Thanks to [@pushkyn](https://github.com/pushkyn)! 112 | - [PR #41](https://github.com/sendgrid/smtpapi-csharp/pull/41): update readme - logo on separate line, fix ToC. Thanks to [@pushkyn](https://github.com/pushkyn)! 113 | - [PR #40](https://github.com/sendgrid/smtpapi-csharp/pull/40): add table of contents in README.md. Thanks to [@thepriefy](https://github.com/thepriefy)! 114 | - [PR #39](https://github.com/sendgrid/smtpapi-csharp/pull/39): Demonstrate how to review the request body for troubleshooting. Thanks to [@mptap](https://github.com/mptap)! 115 | - [PR #33](https://github.com/sendgrid/smtpapi-csharp/pull/33): Fix #32: Add a Code Of Conduct. Thanks to [@hbbq](https://github.com/hbbq)! 116 | - [PR #81](https://github.com/sendgrid/smtpapi-csharp/pull/81): Ran *.md files through grammarly service. Thanks to [@nathan78906](https://github.com/nathan78906)! 117 | 118 | 119 | [2017-05-18] Version 1.3.3 120 | --------------------------- 121 | ### Updated 122 | - Pull request #28: Added Newtonsoft.Json as a package dependency in the nuspec file. 123 | - Thanks to [webster354](https://github.com/webster354) for the PR! 124 | 125 | ## [1.3.2] - 2017-3-8 126 | ### Updated 127 | - Pull request #14: Refactor to JSON.net and simplify the code 128 | - Thanks to [Leon de Pruyssenaere de la Woestijne](https://github.com/leonpw) for the PR! 129 | 130 | ## [1.3.0] - 2015-4-22 131 | ### Added 132 | - travis-ci build 133 | - SetAsmGroupId() method for suppression groups (ASM) 134 | - SetSendAt() and SetSendEachAt() methods for scheduled sends 135 | - SetIpPool() method for using IP Pools 136 | 137 | ### Changed 138 | - HeaderSettingsNode.AddArray now takes a collection of type object rather 139 | than string 140 | 141 | ## [1.2.0] - 2015-1-26 142 | ### Added 143 | - This changelog. Nice. 144 | - Example of using header `To` array and substitutions in README. 145 | 146 | ### Changed 147 | - Unicode characters in X-SMTPAPI header values or keys are now encoded as ASCII escape sequences. 148 | - Null header values or keys result in an `ArgumentNullException`. 149 | 150 | ### Fixed 151 | - Removed `System.Core` reference 152 | - Removed invalid symbol path from compiled DLL (I think) 153 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at open-source@twilio.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Hello! Thank you for choosing to help contribute to one of the SendGrid open source projects. There are many ways you can contribute and help is always welcome. We simply ask that you follow the following contribution policies. 2 | 3 | - [Improvements to the Codebase](#improvements-to-the-codebase) 4 | - [Understanding the Code Base](#understanding-the-codebase) 5 | - [Testing](#testing) 6 | - [Style Guidelines & Naming Conventions](#style-guidelines-and-naming-conventions) 7 | - [Creating a Pull Request](#creating-a-pull-request) 8 | - [Code Reviews](#code-reviews) 9 | 10 | 11 | ## Improvements to the Codebase 12 | 13 | We welcome direct contributions to the smtpapi-csharp code base. Thank you! 14 | 15 | ### Development Environment ### 16 | 17 | #### Install and Run Locally #### 18 | 19 | ##### Prerequisites ##### 20 | 21 | - .NET version 4.5.2 22 | - Microsoft Visual Studio Community 2015 or greater 23 | 24 | ##### Initial setup: ##### 25 | 26 | ```bash 27 | git clone https://github.com/sendgrid/smtpapi-csharp.git 28 | ``` 29 | 30 | Open `smtpapi-csharp/Smtpapi/SendGrid.SmtpApi.sln` 31 | 32 | ### Environment Variables 33 | 34 | First, get your free SendGrid account [here](https://sendgrid.com/free?source=smtpapi-csharp). 35 | 36 | Next, update your Environment (user space) with your SENDGRID_USERNAME and SENDGRID_PASSWORD. 37 | 38 | ##### Execute: ##### 39 | 40 | See the [examples folder](Smtpapi/Example) to get started quickly. 41 | 42 | 43 | ## Understanding the Code Base 44 | 45 | 46 | 47 | **/Smtpapi Example** 48 | 49 | Working examples that demonstrate usage. 50 | 51 | **/Smtpapi/HeaderTests** 52 | 53 | Unit tests 54 | 55 | **/Smtpapi/Smtpapi** 56 | 57 | Source code 58 | 59 | 60 | ## Testing 61 | 62 | All PRs require passing tests before the PR will be reviewed. 63 | 64 | All test files are in the [`Smtpapi/HeaderTests`](Smtpapi/HeaderTests) directory. 65 | 66 | For the purposes of contributing to this repo, please update the [`TestHeader.cs`](Smtpapi/HeaderTests/TestHeader.cs) file with unit tests as you modify the code. 67 | 68 | From the Visual Studio menu: `Tests->Run->All Tests` 69 | 70 | 71 | ## Style Guidelines & Naming Conventions 72 | 73 | Generally, we follow the style guidelines as suggested by the official language. However, we ask that you conform to the styles that already exist in the library. If you wish to deviate, please explain your reasoning. In this case, we generally follow the [C# Naming Conventions](https://msdn.microsoft.com/library/ms229045(v=vs.100).aspx) and the suggestions provided by the Visual Studio IDE. 74 | 75 | ## Creating a Pull Request 76 | 77 | 1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, 78 | and configure the remotes: 79 | 80 | ```bash 81 | # Clone your fork of the repo into the current directory 82 | git clone https://github.com/sendgrid/smtpapi-csharp 83 | # Navigate to the newly cloned directory 84 | cd smtpapi-csharp 85 | # Assign the original repo to a remote called "upstream" 86 | git remote add upstream https://github.com/sendgrid/smtpapi-csharp 87 | ``` 88 | 89 | 2. If you cloned a while ago, get the latest changes from upstream: 90 | 91 | ```bash 92 | git checkout 93 | git pull upstream 94 | ``` 95 | 96 | 3. Create a new topic branch (off the main project development branch) to 97 | contain your feature, change, or fix: 98 | 99 | ```bash 100 | git checkout -b 101 | ``` 102 | 103 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 104 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 105 | or your code is unlikely be merged into the main project. Use Git's 106 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 107 | feature to tidy up your commits before making them public. 108 | 109 | 4a. Create tests. 110 | 111 | 4b. Create or update the example code that demonstrates the functionality of this change to the code. 112 | 113 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 114 | 115 | ```bash 116 | git pull [--rebase] upstream main 117 | ``` 118 | 119 | 6. Push your topic branch up to your fork: 120 | 121 | ```bash 122 | git push origin 123 | ``` 124 | 125 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 126 | with a clear title and description against the `main` branch. All tests must be passing before we will review the PR. 127 | 128 | ## Code Reviews 129 | If you can, please look at open PRs and review them. Give feedback and help us merge these PRs much faster! If you don't know how, Github has some great information on how to review a Pull Request. 130 | -------------------------------------------------------------------------------- /FIRST_TIMERS.md: -------------------------------------------------------------------------------- 1 | # How To Contribute to Twilio SendGrid Repositories via GitHub 2 | Contributing to the Twilio SendGrid repositories is easy! All you need to do is find an open issue (see the bottom of this page for a list of repositories containing open issues), fix it and submit a pull request. Once you have submitted your pull request, the team can easily review it before it is merged into the repository. 3 | 4 | To make a pull request, follow these steps: 5 | 6 | 1. Log into GitHub. If you do not already have a GitHub account, you will have to create one in order to submit a change. Click the Sign up link in the upper right-hand corner to create an account. Enter your username, password, and email address. If you are an employee of Twilio SendGrid, please use your full name with your GitHub account and enter Twilio SendGrid as your company so we can easily identify you. 7 | 8 | 9 | 10 | 2. __[Fork](https://help.github.com/fork-a-repo/)__ the [smtpapi-csharp](https://github.com/sendgrid/smtpapi-csharp) repository: 11 | 12 | 13 | 14 | 3. __Clone__ your fork via the following commands: 15 | 16 | ```bash 17 | # Clone your fork of the repo into the current directory 18 | git clone https://github.com/your_username/smtpapi-csharp 19 | # Navigate to the newly cloned directory 20 | cd smtpapi-csharp 21 | # Assign the original repo to a remote called "upstream" 22 | git remote add upstream https://github.com/sendgrid/smtpapi-csharp 23 | ``` 24 | 25 | > Don't forget to replace *your_username* in the URL by your real GitHub username. 26 | 27 | 4. __Create a new topic branch__ (off the main project development branch) to contain your feature, change, or fix: 28 | 29 | ```bash 30 | git checkout -b 31 | ``` 32 | 33 | 5. __Commit your changes__ in logical chunks. 34 | 35 | Please adhere to these [git commit message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) or your code is unlikely be merged into the main project. Use Git's [interactive rebase](https://help.github.com/articles/interactive-rebase) feature to tidy up your commits before making them public. Probably you will also have to create tests (if needed) or create or update the example code that demonstrates the functionality of this change to the code. 36 | 37 | 6. __Locally merge (or rebase)__ the upstream development branch into your topic branch: 38 | 39 | ```bash 40 | git pull [--rebase] upstream main 41 | ``` 42 | 43 | 7. __Push__ your topic branch up to your fork: 44 | 45 | ```bash 46 | git push origin 47 | ``` 48 | 49 | 8. __[Open a Pull Request](https://help.github.com/articles/creating-a-pull-request/#changing-the-branch-range-and-destination-repository/)__ with a clear title and description against the `main` branch. All tests must be passing before we will review the PR. 50 | 51 | ## Important notice 52 | 53 | Before creating a pull request, make sure that you respect the repository's constraints regarding contributions. You can find them in the [CONTRIBUTING.md](CONTRIBUTING.md) file. 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2023, Twilio SendGrid, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean install test release 2 | 3 | clean: 4 | dotnet clean Smtpapi/SendGrid.SmtpApi.sln 5 | 6 | install: 7 | @dotnet --version || (echo "Dotnet is not installed, please install Dotnet CLI"; exit 1); 8 | dotnet restore Smtpapi/SendGrid.SmtpApi.sln 9 | 10 | test: 11 | dotnet build -c Release Smtpapi/SendGrid.SmtpApi.sln 12 | dotnet test -c Release Smtpapi/SendGrid.SmtpApi.sln 13 | curl -s https://codecov.io/bash > .codecov 14 | chmod +x .codecov 15 | ./.codecov 16 | 17 | release: 18 | dotnet pack -c Release Smtpapi/SendGrid.SmtpApi.sln 19 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | # Fixes # 18 | 19 | A short description of what this PR does. 20 | 21 | ### Checklist 22 | - [x] I acknowledge that all my contributions will be made under the project's license 23 | - [ ] I have made a material change to the repo (functionality, testing, spelling, grammar) 24 | - [ ] I have read the [Contribution Guidelines](https://github.com/sendgrid/smtpapi-csharp/blob/main/CONTRIBUTING.md) and my PR follows them 25 | - [ ] I have titled the PR appropriately 26 | - [ ] I have updated my branch with the main branch 27 | - [ ] I have added tests that prove my fix is effective or that my feature works 28 | - [ ] I have added the necessary documentation about the functionality in the appropriate .md file 29 | - [ ] I have added inline documentation to the code I modified 30 | 31 | If you have questions, please file a [support ticket](https://support.sendgrid.com), or create a GitHub Issue in this repository. 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![SendGrid Logo](twilio_sendgrid_logo.png) 2 | 3 | [![Test and Deploy](https://github.com/sendgrid/smtpapi-csharp/actions/workflows/test-and-deploy.yml/badge.svg)](https://github.com/sendgrid/smtpapi-csharp/actions/workflows/test-and-deploy.yml) 4 | [![Twitter Follow](https://img.shields.io/twitter/follow/sendgrid.svg?style=social&label=Follow)](https://twitter.com/sendgrid) 5 | [![GitHub contributors](https://img.shields.io/github/contributors/sendgrid/smtpapi-csharp.svg)](https://github.com/sendgrid/smtpapi-csharp/graphs/contributors) 6 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 7 | 8 | **This module helps build SendGrid's SMTP API headers.** 9 | 10 | # Announcements 11 | All updates to this module are documented in our [CHANGELOG](CHANGELOG.md). 12 | 13 | # Table of Contents 14 | - [Installation](#installation) 15 | - [Quick Start](#quick-start) 16 | - [Usage](#usage) 17 | - [How to Contribute](#contribute) 18 | - [About](#about) 19 | - [Support](#support) 20 | - [License](#license) 21 | 22 | 23 | # Installation 24 | 25 | ## Prerequisites 26 | 27 | - .NET Framework 4.0+ 28 | - [The SendGrid service](https://sendgrid.com/free?source=smtpapi-csharp) 29 | 30 | ## Setup Environment Variables 31 | 32 | ### Environment Variable 33 | 34 | Update the development environment with your [SENDGRID_API_KEY](https://app.sendgrid.com/settings/api_keys), for example: 35 | 36 | 1. Make a copy of the .env_sample file and name it .env 37 | 2. Edit the file changing only the value of SENDGRID_PASSWORD variable 38 | 3. Save the file 39 | 4. Add the .env file to your environment path 40 | 41 | ## Install Package 42 | 43 | To use SendGrid.SmtpApi in your C# project, you can either download the SendGrid C# .NET libraries directly from our Github repository or, if you have the NuGet package manager installed, you can grab them automatically. 44 | 45 | ```bash 46 | PM> Install-Package SendGrid.SmtpApi 47 | ``` 48 | 49 | Once you have the library properly referenced in your project, you can include calls to them in your code. 50 | 51 | For a sample implementation, check the [Example](Smtpapi/Example/Program.cs) 52 | 53 | 54 | # Quick Start 55 | 56 | ```csharp 57 | var header = new Header(); 58 | 59 | var uniqueArgs = new Dictionary { 60 | { "foo", "bar" }, 61 | { "chunky", "bacon"} 62 | }; 63 | 64 | header.AddUniqueArgs(uniqueArgs); 65 | 66 | var xmstpapiJson = header.JsonString(); 67 | ``` 68 | You can then use the generated JSON in conjunction with your favorite SMTP library. 69 | 70 | ```csharp 71 | SmtpClient client = new SmtpClient(); 72 | client.Port = 587; 73 | client.Host = "smtp.sendgrid.net"; 74 | client.Timeout = 10000; 75 | client.DeliveryMethod = SmtpDeliveryMethod.Network; 76 | client.UseDefaultCredentials = false; 77 | client.Credentials = new System.Net.NetworkCredential("your_sendgrid_username","your_sendgrid_password"); 78 | 79 | MailMessage mail = new MailMessage(); 80 | mail.To.Add(new MailAddress("user@example.com")); 81 | mail.From = new MailAddress("you@yourcompany.com"); 82 | mail.Subject = "this is a test email."; 83 | mail.Body = "this is my test email body"; 84 | 85 | // add the custom header that we built above 86 | mail.Headers.Add( "X-SMTPAPI", xmstpapiJson ); 87 | 88 | client.SendAsync(mail, null); 89 | ``` 90 | 91 | If you want to add multiple recipients to the X-SMTPAPI header for a mail merge type send, you can do something like the following: 92 | 93 | ```csharp 94 | var header = new Header(); 95 | 96 | var recipients = new List {"test1@example.com", "test2@example.com", "test3@example.com"}; 97 | header.SetTo(recipients); 98 | 99 | var subs = new List {"A","B","C"}; 100 | header.AddSubstitution("%name%", subs); 101 | 102 | var mail = new MailMessage 103 | { 104 | From = new MailAddress("test@example.com"), 105 | Subject = "Welcome", 106 | Body = "Hi there %name%" 107 | }; 108 | 109 | // add the custom header that we built above 110 | mail.Headers.Add("X-SMTPAPI", header.JsonString()); 111 | ``` 112 | 113 | 114 | # Usage 115 | 116 | - [SendGrid Docs](https://sendgrid.com/docs/API_Reference/SMTP_API/index.html) 117 | - [Example Code](Smtpapi/Example/Program.cs) 118 | 119 | 120 | # How to Contribute 121 | 122 | We encourage contribution to our projects, please see our [CONTRIBUTING](CONTRIBUTING.md) guide for details. 123 | 124 | Quick links: 125 | 126 | - [Feature Request](CONTRIBUTING.md) 127 | - [Bug Reports](CONTRIBUTING.md#submit-a-bug-report) 128 | - [Improvements to the Codebase](CONTRIBUTING.md#improvements-to-the-codebase) 129 | 130 | 131 | # About 132 | 133 | smtpapi-csharp is maintained and funded by Twilio SendGrid, Inc. The names and logos for smtpapi-csharp are trademarks of Twilio SendGrid, Inc. 134 | 135 | 136 | # Support 137 | 138 | If you need help using SendGrid, please check the [Twilio SendGrid Support Help Center](https://support.sendgrid.com). 139 | 140 | 141 | # License 142 | [The MIT License (MIT)](LICENSE) 143 | -------------------------------------------------------------------------------- /Smtpapi/Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Net; 5 | using System.Net.Mail; 6 | using System.Text; 7 | 8 | namespace SendGrid.SmtpApi.Example 9 | { 10 | internal class MainClass 11 | { 12 | private static bool _mailSent; 13 | 14 | private static void SendCompletedCallback(object sender, AsyncCompletedEventArgs e) 15 | { 16 | // Get the unique identifier for this asynchronous operation. 17 | var token = (string) e.UserState; 18 | 19 | if (e.Cancelled) 20 | { 21 | Console.WriteLine("[{0}] Send canceled.", token); 22 | } 23 | if (e.Error != null) 24 | { 25 | Console.WriteLine("[{0}] {1}", token, e.Error); 26 | } 27 | else 28 | { 29 | Console.WriteLine("Message sent."); 30 | } 31 | _mailSent = true; 32 | } 33 | 34 | private static string XsmtpapiHeaderAsJson() 35 | { 36 | var header = new Header(); 37 | 38 | var uniqueArgs = new Dictionary 39 | { 40 | { 41 | "foo", 42 | "bar" 43 | }, 44 | { 45 | "chunky", 46 | "bacon" 47 | }, 48 | { 49 | // UTF8 encoding test 50 | Encoding.UTF8.GetString(Encoding.Default.GetBytes("dead")), 51 | Encoding.UTF8.GetString(Encoding.Default.GetBytes("beef")) 52 | } 53 | }; 54 | header.AddUniqueArgs(uniqueArgs); 55 | 56 | var subs = new List {"私はラーメンが大好き"}; 57 | header.AddSubstitution("%tag%", subs); 58 | 59 | return header.JsonString(); 60 | } 61 | 62 | public static void Main(string[] args) 63 | { 64 | string xmstpapiJson = XsmtpapiHeaderAsJson(); 65 | 66 | var client = new SmtpClient 67 | { 68 | Port = 587, 69 | Host = "smtp.sendgrid.net", 70 | Timeout = 10000, 71 | DeliveryMethod = SmtpDeliveryMethod.Network, 72 | UseDefaultCredentials = false 73 | }; 74 | 75 | Console.WriteLine("Enter your SendGrid username:"); 76 | string username = Console.ReadLine(); 77 | 78 | Console.WriteLine("Enter your SendGrid password (warning: password will be visible):"); 79 | string password = Console.ReadLine(); 80 | 81 | client.Credentials = new NetworkCredential(username, password); 82 | 83 | var mail = new MailMessage 84 | { 85 | From = new MailAddress("please-reply@example.com"), 86 | Subject = "Good Choice Signing Up for Our Service!.", 87 | Body = "Hi there. Thanks for signing up for Appsterify.ly. It's disruptive! %tag%" 88 | }; 89 | 90 | // add the custom header that we built above 91 | mail.Headers.Add("X-SMTPAPI", xmstpapiJson); 92 | mail.BodyEncoding = Encoding.UTF8; 93 | 94 | //async event handler 95 | client.SendCompleted += SendCompletedCallback; 96 | const string state = "test1"; 97 | 98 | Console.WriteLine("Enter an email address to which a test email will be sent:"); 99 | string email = Console.ReadLine(); 100 | 101 | if (email != null) 102 | { 103 | // Remember that MIME To's are different than SMTPAPI Header To's! 104 | mail.To.Add(new MailAddress(email)); 105 | client.SendAsync(mail, state); 106 | 107 | Console.WriteLine( 108 | "Sending message... press c to cancel, or wait for completion. Press any other key to exit."); 109 | string answer = Console.ReadLine(); 110 | 111 | if (answer != null && answer.StartsWith("c") && _mailSent == false) 112 | { 113 | client.SendAsyncCancel(); 114 | } 115 | } 116 | 117 | mail.Dispose(); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /Smtpapi/Example/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | // Information about this assembly is defined by the following attributes. 4 | // Change them to the values specific to your project. 5 | 6 | [assembly: AssemblyTitle("Example")] 7 | [assembly: AssemblyDescription("")] 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("")] 10 | [assembly: AssemblyProduct("")] 11 | [assembly: AssemblyCopyright("brandonwest")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 15 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 16 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 17 | 18 | [assembly: AssemblyVersion("1.0.*")] 19 | // The following attributes are used to specify the signing key for the assembly, 20 | // if desired. See the Mono documentation for more information about signing. 21 | //[assembly: AssemblyDelaySign(false)] 22 | //[assembly: AssemblyKeyFile("")] -------------------------------------------------------------------------------- /Smtpapi/Example/SendGrid.SmtpApi.Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | false 5 | Debug 6 | AnyCPU 7 | 12.0.0 8 | 2.0 9 | {3672A2E2-487F-475D-B40B-3B78634A43FF} 10 | Exe 11 | SendGrid.SmtpApi.Example 12 | SendGrid.SmtpApi.Example 13 | 14 | 15 | true 16 | full 17 | false 18 | bin\Debug 19 | DEBUG; 20 | prompt 21 | 4 22 | true 23 | 24 | 25 | full 26 | true 27 | bin\Release 28 | prompt 29 | 4 30 | true 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {314665ce-fb24-4984-9b0d-2c39218930f1} 43 | SendGrid.SmtpApi 44 | 45 | 46 | -------------------------------------------------------------------------------- /Smtpapi/HeaderTests/SendGrid.SmtpApi.HeaderTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net40 5 | true 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Smtpapi/HeaderTests/TestHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | 5 | namespace SendGrid.SmtpApi.HeaderTests 6 | { 7 | [TestFixture] 8 | public class TestHeader 9 | { 10 | [Test] 11 | public void TestAddFilterSetting() 12 | { 13 | var test = new Header(); 14 | test.AddFilterSetting("foo", new List { "a", "b" }, "bar"); 15 | string result = test.JsonString(); 16 | Assert.AreEqual("{\"filters\" : {\"foo\" : {\"settings\" : {\"a\" : {\"b\" : \"bar\"}}}}}", result); 17 | } 18 | 19 | [Test] 20 | public void TestAddSection() 21 | { 22 | var test = new Header(); 23 | test.AddSection("foo", "bar"); 24 | string result = test.JsonString(); 25 | Assert.AreEqual("{\"section\" : {\"foo\" : \"bar\"}}", result); 26 | } 27 | 28 | [Test] 29 | public void TestAddSubstitution() 30 | { 31 | var test = new Header(); 32 | test.AddSubstitution("foo", new List { "bar", "raz" }); 33 | string result = test.JsonString(); 34 | Assert.AreEqual("{\"sub\" : {\"foo\" : [\"bar\",\"raz\"]}}", result); 35 | } 36 | 37 | [Test] 38 | public void TestAddUniqueArgs() 39 | { 40 | var test = new Header(); 41 | test.AddUniqueArgs(new Dictionary { { "foo", "bar" } }); 42 | string result = test.JsonString(); 43 | Assert.AreEqual("{\"unique_args\" : {\"foo\" : \"bar\"}}", result); 44 | } 45 | 46 | [Test] 47 | public void TestDisable() 48 | { 49 | var test = new Header(); 50 | test.DisableFilter("foo"); 51 | string result = test.JsonString(); 52 | Assert.AreEqual("{\"filters\" : {\"foo\" : {\"settings\" : {\"enable\" : \"0\"}}}}", result); 53 | } 54 | 55 | [Test] 56 | public void TestEnable() 57 | { 58 | var test = new Header(); 59 | test.EnableFilter("foo"); 60 | string result = test.JsonString(); 61 | Assert.AreEqual("{\"filters\" : {\"foo\" : {\"settings\" : {\"enable\" : \"1\"}}}}", result); 62 | } 63 | 64 | [Test] 65 | public void TestJsonString() 66 | { 67 | var test = new Header(); 68 | string result = test.JsonString(); 69 | Assert.AreEqual("", result); 70 | 71 | test = new Header(); 72 | test.AddSubstitution("foo", new List { "a", "b" }); 73 | result = test.JsonString(); 74 | Assert.AreEqual("{\"sub\" : {\"foo\" : [\"a\",\"b\"]}}", result); 75 | } 76 | 77 | [Test] 78 | public void TestSetCategories() 79 | { 80 | var test = new Header(); 81 | test.SetCategories(new List { "dogs", "animals", "pets", "mammals" }); 82 | string result = test.JsonString(); 83 | Assert.AreEqual("{\"category\" : [\"dogs\",\"animals\",\"pets\",\"mammals\"]}", result); 84 | } 85 | 86 | [Test] 87 | public void TestSetCategory() 88 | { 89 | var test = new Header(); 90 | test.SetCategory("foo"); 91 | string result = test.JsonString(); 92 | Assert.AreEqual("{\"category\" : \"foo\"}", result); 93 | } 94 | 95 | [Test] 96 | public void TestSetTo() 97 | { 98 | var test = new Header(); 99 | test.SetTo(new List { "joe@example.com", "jane@example.com" }); 100 | string result = test.JsonString(); 101 | Assert.AreEqual("{\"to\" : [\"joe@example.com\",\"jane@example.com\"]}", result); 102 | } 103 | 104 | [Test] 105 | public void TestSetAsmGroupId() 106 | { 107 | var test = new Header(); 108 | test.SetAsmGroupId(123); 109 | string result = test.JsonString(); 110 | Assert.AreEqual("{\"asm_group_id\" : 123}", result); 111 | } 112 | 113 | [Test] 114 | public void TestSetIpPool() 115 | { 116 | var test = new Header(); 117 | test.SetIpPool("test_pool"); 118 | string result = test.JsonString(); 119 | Assert.AreEqual("{\"ip_pool\" : \"test_pool\"}", result); 120 | } 121 | 122 | [Test] 123 | public void TestSetSendAt_DateTime() 124 | { 125 | var test = new Header(); 126 | var now = DateTime.UtcNow; 127 | test.SetSendAt(now); 128 | string result = test.JsonString(); 129 | Assert.AreEqual("{\"send_at\" : " + Utils.DateTimeToUnixTimestamp(now) + "}", result); 130 | } 131 | 132 | [Test] 133 | public void TestSetSendAt_DateTimeOffset() 134 | { 135 | var test = new Header(); 136 | var now = DateTimeOffset.UtcNow; 137 | test.SetSendAt(now); 138 | string result = test.JsonString(); 139 | Assert.AreEqual("{\"send_at\" : " + Utils.DateTimeOffsetToUnixTimestamp(now) + "}", result); 140 | } 141 | 142 | [Test] 143 | public void TestSetSendEachAt_DateTime() 144 | { 145 | var test = new Header(); 146 | var now = DateTime.UtcNow; 147 | test.SetSendEachAt(new List { now, now.AddSeconds(10) }); 148 | string result = test.JsonString(); 149 | Assert.AreEqual("{\"send_each_at\" : [" + Utils.DateTimeToUnixTimestamp(now) + "," + Utils.DateTimeToUnixTimestamp(now.AddSeconds(10)) + "]}", result); 150 | } 151 | 152 | [Test] 153 | public void TestSetSendEachAt_DateTimeOffset() 154 | { 155 | var test = new Header(); 156 | var now = DateTimeOffset.UtcNow; 157 | test.SetSendEachAt(new List { now, now.AddSeconds(10) }); 158 | string result = test.JsonString(); 159 | Assert.AreEqual("{\"send_each_at\" : [" + Utils.DateTimeOffsetToUnixTimestamp(now) + "," + Utils.DateTimeOffsetToUnixTimestamp(now.AddSeconds(10)) + "]}", result); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Smtpapi/HeaderTests/TestRepositoryFiles.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using System; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace SendGrid.SmtpApi.HeaderTests 7 | { 8 | [TestFixture] 9 | public class TestRepositoryFiles 10 | { 11 | [Test] 12 | public void TestLicenseEndYear() 13 | { 14 | string[] pathsArray = new string[] { "..", "..", "..", "..", "LICENSE" }; 15 | string licensePath = Path.Combine(pathsArray); 16 | 17 | string line = File.ReadLines(licensePath).Skip(2).Take(1).First(); 18 | 19 | Assert.AreEqual(DateTime.Now.Year.ToString(), line.Substring(14, 4)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Smtpapi/HeaderTests/TestTreeNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NUnit.Framework; 5 | 6 | namespace SendGrid.SmtpApi.HeaderTests 7 | { 8 | [TestFixture] 9 | public class TestTreeNode 10 | { 11 | [Test] 12 | public void TestAddArray() 13 | { 14 | var test = new HeaderSettingsNode(); 15 | test.AddArray(new List { "foo", "bar" }, new[] { "raz", "blam" }); 16 | IEnumerable result = test.GetArray("foo", "bar"); 17 | IList enumerable = result as IList ?? result.ToList(); 18 | // Fix for possible multiple enumerations 19 | Assert.AreEqual(enumerable.ToList()[0], "raz"); 20 | Assert.AreEqual(enumerable.ToList()[1], "blam"); 21 | } 22 | 23 | [Test] 24 | public void TestAddSetting() 25 | { 26 | var test = new HeaderSettingsNode(); 27 | test.AddSetting(new List(), "foo"); 28 | Assert.AreEqual("foo", test.GetLeaf(), "Get the leaf of the first node"); 29 | 30 | test = new HeaderSettingsNode(); 31 | test.AddSetting(new List { "foo" }, "bar"); 32 | Assert.AreEqual("bar", test.GetSetting(new List { "foo" }), 33 | "Get the item in the first branch 'foo', make sure its set to 'bar'"); 34 | 35 | test = new HeaderSettingsNode(); 36 | test.AddSetting(new List { "foo" }, "bar"); 37 | Assert.AreEqual("bar", test.GetSetting("foo"), 38 | "tests the convienence get setting function that omits the lists stuff..."); 39 | 40 | test = new HeaderSettingsNode(); 41 | test.AddSetting(new List { "foo", "bar", "raz" }, "foobar"); 42 | Assert.AreEqual("foobar", test.GetSetting("foo", "bar", "raz"), 43 | "tests a tree that is multiple branches deep"); 44 | 45 | test = new HeaderSettingsNode(); 46 | test.AddSetting(new List { "foo", "bar", "raz" }, "foobar"); 47 | test.AddSetting(new List { "barfoo", "barbar", "barraz" }, "barfoobar"); 48 | Assert.AreEqual("foobar", test.GetSetting("foo", "bar", "raz"), "tests a tree that has multiple branches"); 49 | Assert.AreEqual("barfoobar", test.GetSetting("barfoo", "barbar", "barraz"), "tests the other branch"); 50 | 51 | test = new HeaderSettingsNode(); 52 | test.AddSetting(new List { "foo" }, "bar"); 53 | try 54 | { 55 | test.AddSetting(new List { "foo", "raz" }, "blam"); 56 | Assert.Fail("exception not thrown"); 57 | } 58 | catch (ArgumentException ex) 59 | { 60 | Assert.AreEqual("Attempt to overwrite setting", ex.Message); 61 | } 62 | } 63 | 64 | [Test] 65 | public void TestIsEmpty() 66 | { 67 | var test = new HeaderSettingsNode(); 68 | Assert.IsTrue(test.IsEmpty()); 69 | 70 | test = new HeaderSettingsNode(); 71 | test.AddSetting(new List { "foo" }, "bar"); 72 | Assert.IsFalse(test.IsEmpty()); 73 | 74 | test = new HeaderSettingsNode(); 75 | test.AddArray(new List { "raz" }, new List { "blam" }); 76 | Assert.IsFalse(test.IsEmpty()); 77 | } 78 | 79 | [Test] 80 | public void TestToJson() 81 | { 82 | var test = new HeaderSettingsNode(); 83 | test.AddSetting(new List { "foo", "bar", "raz" }, "foobar"); 84 | 85 | string result = test.ToJson(); 86 | Assert.AreEqual("{\"foo\" : {\"bar\" : {\"raz\" : \"foobar\"}}}", result); 87 | 88 | test = new HeaderSettingsNode(); 89 | test.AddSetting(new List { "foo", "bar", "raz" }, "foobar"); 90 | test.AddSetting(new List { "barfoo", "barbar", "barraz" }, "barfoobar"); 91 | 92 | result = test.ToJson(); 93 | Assert.AreEqual( 94 | "{\"foo\" : {\"bar\" : {\"raz\" : \"foobar\"}},\"barfoo\" : {\"barbar\" : {\"barraz\" : \"barfoobar\"}}}", 95 | result); 96 | 97 | test = new HeaderSettingsNode(); 98 | test.AddArray(new List { "foo" }, new List { "bar", "raz" }); 99 | result = test.ToJson(); 100 | Assert.AreEqual("{\"foo\" : [\"bar\",\"raz\"]}", result); 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /Smtpapi/HeaderTests/TestUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using NUnit.Framework; 4 | 5 | namespace SendGrid.SmtpApi.HeaderTests 6 | { 7 | [TestFixture] 8 | public class TestUtils 9 | { 10 | [Test] 11 | public void TestSerialize() 12 | { 13 | var testcase = "foo"; 14 | String result = Utils.Serialize(testcase); 15 | Assert.AreEqual("\"foo\"", result); 16 | 17 | var testcase2 = 1; 18 | result = Utils.Serialize(testcase2); 19 | Assert.AreEqual("1", result); 20 | } 21 | 22 | [Test] 23 | public void TestSerializeDictionary() 24 | { 25 | var test = new Dictionary 26 | { 27 | {"a", "b"}, 28 | {"c", "d/e"} 29 | }; 30 | var result = Utils.SerializeDictionary(test); 31 | var expected = "{\"a\":\"b\",\"c\":\"d/e\"}"; 32 | Assert.AreEqual(expected, result); 33 | } 34 | 35 | [Test] 36 | [ExpectedException("System.ArgumentNullException")] 37 | public void TestSerializeNullThrowsException() 38 | { 39 | string test = null; 40 | Utils.Serialize(test); 41 | } 42 | 43 | [Test] 44 | [ExpectedException("System.ArgumentNullException")] 45 | public void TestSerializeNullKeyThrowsException() 46 | { 47 | var test = new Dictionary { { null, "test" } }; 48 | Utils.Serialize(test); 49 | } 50 | 51 | [Test] 52 | public void TestEncodeNonAsciiCharacters() 53 | { 54 | var test = "私はラーメンが大好き"; 55 | var result = Utils.EncodeNonAsciiCharacters(test); 56 | var expected = "\\u79c1\\u306f\\u30e9\\u30fc\\u30e1\\u30f3\\u304c\\u5927\\u597d\\u304d"; 57 | 58 | Assert.AreEqual(expected, result); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Smtpapi/SendGrid.SmtpApi.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30723.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SendGrid.SmtpApi.HeaderTests", "HeaderTests\SendGrid.SmtpApi.HeaderTests.csproj", "{F9F61DE0-D1AF-4A1D-9D62-5C09B22AC31D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SendGrid.SmtpApi", "Smtpapi\SendGrid.SmtpApi.csproj", "{314665CE-FB24-4984-9B0D-2C39218930F1}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {F9F61DE0-D1AF-4A1D-9D62-5C09B22AC31D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {F9F61DE0-D1AF-4A1D-9D62-5C09B22AC31D}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {F9F61DE0-D1AF-4A1D-9D62-5C09B22AC31D}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {F9F61DE0-D1AF-4A1D-9D62-5C09B22AC31D}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {3672A2E2-487F-475D-B40B-3B78634A43FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {3672A2E2-487F-475D-B40B-3B78634A43FF}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {3672A2E2-487F-475D-B40B-3B78634A43FF}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {3672A2E2-487F-475D-B40B-3B78634A43FF}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {314665CE-FB24-4984-9B0D-2C39218930F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {314665CE-FB24-4984-9B0D-2C39218930F1}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {314665CE-FB24-4984-9B0D-2C39218930F1}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {314665CE-FB24-4984-9B0D-2C39218930F1}.Release|Any CPU.Build.0 = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(SolutionProperties) = preSolution 30 | HideSolutionNode = FALSE 31 | EndGlobalSection 32 | GlobalSection(MonoDevelopProperties) = preSolution 33 | StartupItem = Example\Example.csproj 34 | EndGlobalSection 35 | EndGlobal 36 | -------------------------------------------------------------------------------- /Smtpapi/Smtpapi/Header.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SendGrid.SmtpApi 6 | { 7 | /// 8 | /// 9 | public class Header : IHeader 10 | { 11 | #region Private Members 12 | 13 | private readonly HeaderSettingsNode _settings; 14 | 15 | #endregion 16 | 17 | #region Properties 18 | 19 | /// 20 | /// Gets the array of recipient addresses from the X-SMTPAPI header 21 | /// 22 | public IEnumerable To 23 | { 24 | get { return _settings.GetArray("to").Select(t => t.ToString()); } 25 | } 26 | 27 | #endregion 28 | 29 | #region Public Methods 30 | 31 | /// 32 | /// 33 | public Header() 34 | { 35 | _settings = new HeaderSettingsNode(); 36 | } 37 | 38 | /// 39 | /// Allows you to specify a filter setting. You can find a list of filters and settings here: 40 | /// http://docs.sendgrid.com/documentation/api/web-api/filtersettings/ 41 | /// 42 | /// The name of the filter to set 43 | /// The multipart name of the parameter being set 44 | /// The value that the settings name will be assigning 45 | public void AddFilterSetting(string filter, IEnumerable settings, string value) 46 | { 47 | List keys = new List {"filters", filter, "settings"}.Concat(settings).ToList(); 48 | _settings.AddSetting(keys, value); 49 | } 50 | 51 | /// 52 | /// Adds a substitution section to be used during the mail merge. 53 | /// 54 | /// string to be replaced with the section in the message 55 | /// The text of the section. May include substituion tags. 56 | public void AddSection(string tag, string text) 57 | { 58 | var keys = new List {"section", tag}; 59 | _settings.AddSetting(keys, text); 60 | } 61 | 62 | /// 63 | /// This adds a substitution value to be used during the mail merge. Substitutions 64 | /// will happen in order added, so calls to this should match calls to AddTo. 65 | /// If a tag already exists, it will be overwritten. 66 | /// 67 | /// string to be replaced in the message 68 | /// substitutions to be made, one per recipient 69 | public void AddSubstitution(string tag, IEnumerable substitutions) 70 | { 71 | var keys = new List {"sub", tag}; 72 | _settings.AddArray(keys, substitutions); 73 | } 74 | 75 | /// 76 | /// This adds parameters and values that will be bassed back through SendGrid's 77 | /// Event API if an event notification is triggered by this email. 78 | /// 79 | /// parameter value pairs to be passed back on event notification 80 | public void AddUniqueArgs(IDictionary identifiers) 81 | { 82 | foreach (string key in identifiers.Keys) 83 | { 84 | var keys = new List {"unique_args", key}; 85 | string value = identifiers[key]; 86 | _settings.AddSetting(keys, value); 87 | } 88 | } 89 | 90 | /// 91 | /// Shortcut method for disabling a filter. 92 | /// 93 | /// The name of the filter to disable 94 | public void DisableFilter(string filter) 95 | { 96 | AddFilterSetting(filter, new List {"enable"}, "0"); 97 | } 98 | 99 | /// 100 | /// Shortcut method for enabling a filter. 101 | /// 102 | /// The name of the filter to enable 103 | public void EnableFilter(string filter) 104 | { 105 | AddFilterSetting(filter, new List {"enable"}, "1"); 106 | } 107 | 108 | /// 109 | /// Converts the filter settings into a JSON string. 110 | /// 111 | /// String representation of the SendGrid headers 112 | public string JsonString() 113 | { 114 | return _settings.IsEmpty() ? "" : _settings.ToJson(); 115 | } 116 | 117 | /// 118 | /// This sets the categories for this email. Statistics are stored on a per category 119 | /// basis, so this can be useful for tracking on a per group basis. 120 | /// 121 | /// categories applied to the message 122 | public void SetCategories(IEnumerable categories) 123 | { 124 | if (categories == null) return; 125 | var keys = new List {"category"}; 126 | _settings.AddArray(keys, categories); 127 | } 128 | 129 | /// 130 | /// This sets the category for this email. Statistics are stored on a per category 131 | /// basis, so this can be useful for tracking on a per group basis. 132 | /// 133 | /// categories applied to the message 134 | public void SetCategory(string category) 135 | { 136 | var keys = new List {"category"}; 137 | _settings.AddSetting(keys, category); 138 | } 139 | 140 | /// 141 | /// This adds the "to" array to the X-SMTPAPI header so that multiple recipients 142 | /// may be addressed in a single email. (but they each get their own email, instead of a single email with multiple TO: 143 | /// addressees) 144 | /// 145 | /// List of email addresses 146 | public void SetTo(IEnumerable addresses) 147 | { 148 | _settings.AddArray(new List {"to"}, addresses); 149 | } 150 | 151 | /// 152 | /// This sets the ASM Group ID for this email. You can find further documentation about ASM here: 153 | /// https://sendgrid.com/docs/API_Reference/Web_API_v3/Advanced_Suppression_Manager/index.html 154 | /// 155 | /// ASM group applied to the message 156 | public void SetAsmGroupId(int id) 157 | { 158 | var keys = new List { "asm_group_id" }; 159 | _settings.AddSetting(keys, id); 160 | } 161 | 162 | /// 163 | /// This sets the IP Pool for this email. You can find further documentation about IP Pools here: 164 | /// https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_pools.html 165 | /// 166 | /// Name of the IP Pool with which to send the message. 167 | public void SetIpPool(string pool) 168 | { 169 | var keys = new List { "ip_pool" }; 170 | _settings.AddSetting(keys, pool); 171 | } 172 | 173 | /// 174 | /// Schedule the email to be sent in the future. You can find further documentation about scheduled sends here: 175 | /// https://sendgrid.com/docs/for-developers/sending-email/scheduling-parameters/ 176 | /// 177 | /// DateTime representing the time to send the email. See docs for limitations. 178 | public void SetSendAt(DateTime sendTime) 179 | { 180 | var keys = new List { "send_at" }; 181 | _settings.AddSetting(keys, Utils.DateTimeToUnixTimestamp(sendTime)); 182 | } 183 | 184 | /// 185 | /// Schedule the email to be sent in the future. You can find further documentation about scheduled sends here: 186 | /// https://sendgrid.com/docs/for-developers/sending-email/scheduling-parameters/ 187 | /// 188 | /// DateTimeOffset representing the time to send the email. See docs for limitations. 189 | public void SetSendAt(DateTimeOffset sendTime) 190 | { 191 | var keys = new List { "send_at" }; 192 | _settings.AddSetting(keys, Utils.DateTimeOffsetToUnixTimestamp(sendTime)); 193 | } 194 | 195 | /// 196 | /// Schedule each email in your batch to be sent at a specific time in the future. You can find further documentation about scheduled sends here: 197 | /// https://sendgrid.com/docs/for-developers/sending-email/scheduling-parameters/ 198 | /// 199 | /// A collection of DateTimes, with each time corresponding to one recipient in the SMTP API header 200 | public void SetSendEachAt(IEnumerable sendDateTimes) 201 | { 202 | _settings.AddArray(new List { "send_each_at" }, sendDateTimes.Select(Utils.DateTimeToUnixTimestamp).Cast().ToArray()); 203 | } 204 | 205 | /// 206 | /// Schedule each email in your batch to be sent at a specific time in the future. You can find further documentation about scheduled sends here: 207 | /// https://sendgrid.com/docs/for-developers/sending-email/scheduling-parameters/ 208 | /// 209 | /// A collection of DateTimeOffsets, with each time corresponding to one recipient in the SMTP API header 210 | public void SetSendEachAt(IEnumerable sendDateTimes) 211 | { 212 | _settings.AddArray(new List { "send_each_at" }, sendDateTimes.Select(Utils.DateTimeOffsetToUnixTimestamp).Cast().ToArray()); 213 | } 214 | 215 | #endregion 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /Smtpapi/Smtpapi/HeaderSettingsNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SendGrid.SmtpApi 6 | { 7 | /// 8 | /// 9 | public class HeaderSettingsNode 10 | { 11 | #region Private Members 12 | 13 | private readonly Dictionary _branches; 14 | private IEnumerable _array; 15 | private object _leaf; 16 | 17 | #endregion 18 | 19 | public HeaderSettingsNode() 20 | { 21 | _branches = new Dictionary(); 22 | } 23 | 24 | public void AddArray(List keys, IEnumerable value) 25 | { 26 | if (keys.Count == 0) 27 | { 28 | _array = value; 29 | } 30 | else 31 | { 32 | if (_leaf != null || _array != null) 33 | throw new ArgumentException("Attempt to overwrite setting"); 34 | 35 | string key = keys.First(); 36 | if (!_branches.ContainsKey(key)) 37 | _branches[key] = new HeaderSettingsNode(); 38 | 39 | List remainingKeys = keys.Skip(1).ToList(); 40 | _branches[key].AddArray(remainingKeys, value); 41 | } 42 | } 43 | 44 | public void AddSetting(List keys, object value) 45 | { 46 | if (keys.Count == 0) 47 | { 48 | _leaf = value; 49 | } 50 | else 51 | { 52 | if (_leaf != null || _array != null) 53 | throw new ArgumentException("Attempt to overwrite setting"); 54 | 55 | string key = keys.First(); 56 | if (!_branches.ContainsKey(key)) 57 | _branches[key] = new HeaderSettingsNode(); 58 | 59 | List remainingKeys = keys.Skip(1).ToList(); 60 | _branches[key].AddSetting(remainingKeys, value); 61 | } 62 | } 63 | 64 | public object GetSetting(params string[] keys) 65 | { 66 | return GetSetting(keys.ToList()); 67 | } 68 | 69 | public object GetSetting(List keys) 70 | { 71 | if (keys.Count == 0) 72 | return _leaf; 73 | string key = keys.First(); 74 | if (!_branches.ContainsKey(key)) 75 | throw new ArgumentException("Bad key path!"); 76 | List remainingKeys = keys.Skip(1).ToList(); 77 | return _branches[key].GetSetting(remainingKeys); 78 | } 79 | 80 | public IEnumerable GetArray(params string[] keys) 81 | { 82 | return GetArray(keys.ToList()); 83 | } 84 | 85 | public IEnumerable GetArray(List keys) 86 | { 87 | if (keys.Count == 0) 88 | return _array; 89 | string key = keys.First(); 90 | if (!_branches.ContainsKey(key)) 91 | throw new ArgumentException("Bad key path!"); 92 | List remainingKeys = keys.Skip(1).ToList(); 93 | return _branches[key].GetArray(remainingKeys); 94 | } 95 | 96 | public object GetLeaf() 97 | { 98 | return _leaf; 99 | } 100 | 101 | public string ToJson() 102 | { 103 | string json = ""; 104 | if (_branches.Count > 0) 105 | { 106 | json = "{" + 107 | string.Join(",", _branches.Keys.Select(k => Utils.Serialize(k) + " : " + _branches[k].ToJson())) + 108 | "}"; 109 | } 110 | if (_leaf != null) 111 | { 112 | json = Utils.Serialize(_leaf); 113 | } 114 | if (_array != null) 115 | { 116 | json = Utils.Serialize(_array); 117 | } 118 | if (json.Length > 0) 119 | { 120 | return Utils.EncodeNonAsciiCharacters(json); 121 | } 122 | return "{}"; 123 | } 124 | 125 | public bool IsEmpty() 126 | { 127 | if (_leaf != null) return false; 128 | return _branches == null || _branches.Keys.Count == 0; 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Smtpapi/Smtpapi/IHeader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SendGrid.SmtpApi 5 | { 6 | /// 7 | /// Represents the additional functionality to add SendGrid specific mail headers 8 | /// 9 | public interface IHeader 10 | { 11 | /// 12 | /// Gets the array of recipient addresses from the X-SMTPAPI header 13 | /// 14 | IEnumerable To { get; } 15 | 16 | /// 17 | /// Allows you to specify a filter setting. You can find a list of filters and settings here: 18 | /// http://docs.sendgrid.com/documentation/api/web-api/filtersettings/ 19 | /// 20 | /// The name of the filter to set 21 | /// The multipart name of the parameter being set 22 | /// The value that the settings name will be assigning 23 | void AddFilterSetting(string filter, IEnumerable settings, string value); 24 | 25 | /// 26 | /// Adds a substitution section to be used during the mail merge. 27 | /// 28 | /// string to be replaced with the section in the message 29 | /// The text of the section. May include substituion tags. 30 | void AddSection(string tag, string text); 31 | 32 | /// 33 | /// This adds a substitution value to be used during the mail merge. Substitutions 34 | /// will happen in order added, so calls to this should match calls to AddTo. 35 | /// If a tag already exists, it will be overwritten. 36 | /// 37 | /// string to be replaced in the message 38 | /// substitutions to be made, one per recipient 39 | void AddSubstitution(string tag, IEnumerable substitutions); 40 | 41 | /// 42 | /// This adds parameters and values that will be bassed back through SendGrid's 43 | /// Event API if an event notification is triggered by this email. 44 | /// 45 | /// parameter value pairs to be passed back on event notification 46 | void AddUniqueArgs(IDictionary identifiers); 47 | 48 | /// 49 | /// Shortcut method for disabling a filter. 50 | /// 51 | /// The name of the filter to disable 52 | void DisableFilter(string filter); 53 | 54 | /// 55 | /// Shortcut method for enabling a filter. 56 | /// 57 | /// The name of the filter to enable 58 | void EnableFilter(string filter); 59 | 60 | /// 61 | /// Converts the filter settings into a JSON string. 62 | /// 63 | /// String representation of the SendGrid headers 64 | string JsonString(); 65 | 66 | /// 67 | /// This sets the categories for this email. Statistics are stored on a per category 68 | /// basis, so this can be useful for tracking on a per group basis. 69 | /// 70 | /// categories applied to the message 71 | void SetCategories(IEnumerable categories); 72 | 73 | /// 74 | /// This sets the category for this email. Statistics are stored on a per category 75 | /// basis, so this can be useful for tracking on a per group basis. 76 | /// 77 | /// categories applied to the message 78 | void SetCategory(string category); 79 | 80 | /// 81 | /// This adds the "to" array to the X-SMTPAPI header so that multiple recipients 82 | /// may be addressed in a single email. (but they each get their own email, instead of a single email with multiple TO: 83 | /// addressees) 84 | /// 85 | /// List of email addresses 86 | void SetTo(IEnumerable addresses); 87 | 88 | /// 89 | /// This sets the ASM Group ID for this email. You can find further documentation about ASM here: 90 | /// https://sendgrid.com/docs/API_Reference/Web_API_v3/Advanced_Suppression_Manager/index.html 91 | /// 92 | /// ASM group applied to the message 93 | void SetAsmGroupId(int id); 94 | 95 | /// 96 | /// This sets the IP Pool for this email. You can find further documentation about IP Pools here: 97 | /// https://sendgrid.com/docs/API_Reference/Web_API_v3/IP_Management/ip_pools.html 98 | /// 99 | /// Name of the IP Pool with which to send the message. 100 | void SetIpPool(string pool); 101 | 102 | 103 | } 104 | } -------------------------------------------------------------------------------- /Smtpapi/Smtpapi/SendGrid.SmtpApi.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 1.4.6 5 | true 6 | netstandard1.3;netstandard2.0;net452;net40 7 | anycpu 8 | true 9 | Library 10 | ../../smptpapicsharp.snk 11 | true 12 | true 13 | full 14 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb;.xml 15 | 16 | 17 | 18 | SendGrid.SmtpApi 19 | Twilio DX Team 20 | Twilio SendGrid 21 | Twilio SendGrid 22 | MIT 23 | https://sendgrid.com/ 24 | https://github.com/sendgrid/smtpapi-csharp.git 25 | git 26 | https://sendgrid.com/wp-content/themes/sgdotcom/pages/resource/brand//2016/SendGrid-Logomark.png 27 | Easily build SendGrid SMTPAPI headers. 28 | Please see: https://github.com/sendgrid/smtpapi-csharp/releases 29 | Twilio;SendGrid;Email;Mail;Microsoft;Azure;Transactional;.NET Core 30 | Twilio SendGrid, Inc. 2020 31 | 32 | 33 | 34 | Twilio 35 | Easily build SendGrid SMTPAPI headers. 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Smtpapi/Smtpapi/Utils.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace SendGrid.SmtpApi 7 | { 8 | /// 9 | /// 10 | public class Utils 11 | { 12 | /// 13 | /// 14 | /// 15 | /// 16 | /// 17 | public static string Serialize(T objectToSerialize) 18 | { 19 | if (objectToSerialize == null) 20 | throw new ArgumentNullException("A key or value in your X-SMTPAPI header is null."); 21 | 22 | return JsonConvert.SerializeObject(objectToSerialize); 23 | } 24 | 25 | /// 26 | /// 27 | /// 28 | /// 29 | public static string SerializeDictionary(IDictionary dictionaryToSerialize) 30 | { 31 | return JsonConvert.SerializeObject(dictionaryToSerialize); 32 | } 33 | 34 | /// 35 | /// ASCII escapes non-ASCII characters 36 | /// 37 | /// The string to escape 38 | /// Escaped string 39 | public static string EncodeNonAsciiCharacters(string value) 40 | { 41 | var sb = new StringBuilder(); 42 | foreach (char c in value) 43 | { 44 | if (c > 127) 45 | { 46 | // This character is too big for ASCII 47 | string encodedValue = "\\u" + ((int)c).ToString("x4"); 48 | sb.Append(encodedValue); 49 | } 50 | else 51 | { 52 | sb.Append(c); 53 | } 54 | } 55 | return sb.ToString(); 56 | } 57 | 58 | /// 59 | /// Convert a DateTime to a UNIX Epoch Timestamp 60 | /// 61 | /// Date to convert to timestamp 62 | /// Timestamp 63 | public static int DateTimeToUnixTimestamp(DateTime dateTime) 64 | { 65 | var span = (dateTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)); 66 | return (int)span.TotalSeconds; 67 | } 68 | 69 | /// 70 | /// Convert a DateTimeOffset to a UNIX Epoch Timestamp 71 | /// 72 | /// Date to convert to timestamp 73 | /// Timestamp 74 | public static int DateTimeOffsetToUnixTimestamp(DateTimeOffset dateTimeOffset) 75 | { 76 | var span = (dateTimeOffset - new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero)); 77 | return (int)span.TotalSeconds; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Smtpapi/Tests/RequiredFilesExistTest.cs: -------------------------------------------------------------------------------- 1 | namespace SendGrid.Tests 2 | { 3 | using System; 4 | using Xunit; 5 | 6 | public class TestRequiredFilesExist 7 | { 8 | 9 | // ./Docker or docker/Docker 10 | public void checkDockerExists() 11 | { 12 | bool dockerExists = File.Exists("./Dockerfile") || 13 | File.Exists("./docker/Dockerfile"); 14 | Assert.True(dockerExists); 15 | } 16 | 17 | // ./docker-compose.yml or ./docker/docker-compose.yml 18 | public void checkDockerComposeExists() 19 | { 20 | bool dockerComposeExists = File.Exists("./docker-compose.yml") || 21 | File.Exists("./docker/docker-compose.yml"); 22 | Assert.True(dockerComposeExists); 23 | } 24 | 25 | // ./.env_sample 26 | public void checkEnvSampleExists() 27 | { 28 | Assert.True(File.Exists("./.env_sample")); 29 | } 30 | 31 | // ./.gitignore 32 | public void checkGitIgnoreExists() 33 | { 34 | Assert.True(File.Exists("./.gitignore")); 35 | } 36 | 37 | // ./.travis.yml 38 | public void checkTravisExists() 39 | { 40 | Assert.True(File.Exists("./.travis.yml")); 41 | } 42 | 43 | // ./.codeclimate.yml 44 | public void checkCodeClimateExists() 45 | { 46 | Assert.True(File.Exists("./.codeclimate.yml")); 47 | } 48 | 49 | // ./CHANGELOG.md 50 | public void checkChangelogExists() 51 | { 52 | Assert.True(File.Exists("./CHANGELOG.md")); 53 | } 54 | 55 | // ./CODE_OF_CONDUCT.md 56 | public void checkCodeOfConductExists() 57 | { 58 | Assert.True(File.Exists("./CODE_OF_CONDUCT.md")); 59 | } 60 | 61 | // ./CONTRIBUTING.md 62 | public void checkContributingGuideExists() 63 | { 64 | Assert.True(File.Exists("./CONTRIBUTING.md")); 65 | } 66 | 67 | // ./LICENSE 68 | public void checkLicenseExists() 69 | { 70 | Assert.True(File.Exists("./LICENSE")); 71 | } 72 | 73 | // ./PULL_REQUEST_TEMPLATE.md 74 | public void checkPullRequestExists() 75 | { 76 | Assert.True(File.Exists("./PULL_REQUEST_TEMPLATE.md")); 77 | } 78 | 79 | // ./README.md 80 | public void checkReadMeExists() 81 | { 82 | Assert.True(File.Exists("./README.md")); 83 | } 84 | 85 | // ./TROUBLESHOOTING.md 86 | public void checkTroubleShootingGuideExists() 87 | { 88 | Assert.True(File.Exists("./TROUBLESHOOTING.md")); 89 | } 90 | 91 | // ./USAGE.md 92 | public void checkUsageGuideExists() 93 | { 94 | Assert.True(File.Exists("./USAGE.md")); 95 | } 96 | 97 | // ./USE_CASES.md 98 | public void checkUseCases() 99 | { 100 | Assert.True(File.Exists("./UseCases/README.md")); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | If you have a SendGrid issue, please contact our [support team](https://support.sendgrid.com). 2 | 3 | 4 | ## Table of Contents 5 | 6 | * [Viewing the Request Body](#request-body) 7 | 8 | 9 | ## Viewing the Request Body 10 | 11 | When debugging or testing, it may be useful to examine the raw request body to compare against the [documented format](https://sendgrid.com/docs/API_Reference/api_v3.html). 12 | 13 | You can do this right before you call `var response = await client.SendEmailAsync(msg);` like so: 14 | 15 | ```csharp 16 | Console.WriteLine(msg.Serialize()); 17 | ``` 18 | -------------------------------------------------------------------------------- /UseCases/README.md: -------------------------------------------------------------------------------- 1 | This directory provides examples for specific use cases. Please [open an issue](https://github.com/sendgrid/smtpapi-csharp/issues) or make a pull request for any use cases you would like us to document here. Thank you! 2 | 3 | # Table of Contents 4 | No use cases yet -------------------------------------------------------------------------------- /smptpapicsharp.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sendgrid/smtpapi-csharp/39540eb89d54a075d4e59bd8061e24fbdcd6d4b5/smptpapicsharp.snk -------------------------------------------------------------------------------- /static/img/github-fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sendgrid/smtpapi-csharp/39540eb89d54a075d4e59bd8061e24fbdcd6d4b5/static/img/github-fork.png -------------------------------------------------------------------------------- /static/img/github-sign-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sendgrid/smtpapi-csharp/39540eb89d54a075d4e59bd8061e24fbdcd6d4b5/static/img/github-sign-up.png -------------------------------------------------------------------------------- /twilio_sendgrid_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sendgrid/smtpapi-csharp/39540eb89d54a075d4e59bd8061e24fbdcd6d4b5/twilio_sendgrid_logo.png --------------------------------------------------------------------------------