├── .github └── workflows │ ├── cmake.yml │ └── sync-to-azure-devops.yml ├── .gitignore ├── .vscode ├── c_cpp_properties.json └── tasks.json ├── CHANGELOG.md ├── CMakeLists.txt ├── CMakeLists.txt.in ├── CMakeSettings.json ├── LICENSE ├── README.md ├── cmake ├── CMakeUninstall.cmake.in ├── modules │ ├── VersionInfo.in │ ├── VersionResource.rc │ └── generate_product_version.cmake └── smtpclientConfig.cmake.in ├── product.ico ├── src ├── attachment.cpp ├── attachment.h ├── base64.cpp ├── base64.h ├── cpp │ ├── attachment.cpp │ ├── attachment.hpp │ ├── credential.cpp │ ├── credential.hpp │ ├── example │ │ └── send-mail.cpp │ ├── forcedsecuresmtpclient.cpp │ ├── forcedsecuresmtpclient.hpp │ ├── htmlmessage.cpp │ ├── htmlmessage.hpp │ ├── message.cpp │ ├── message.hpp │ ├── messageaddress.cpp │ ├── messageaddress.hpp │ ├── opportunisticsecuresmtpclient.cpp │ ├── opportunisticsecuresmtpclient.hpp │ ├── plaintextmessage.cpp │ ├── plaintextmessage.hpp │ ├── smtpclient.cpp │ └── smtpclient.hpp ├── credential.cpp ├── credential.h ├── errorresolver.cpp ├── errorresolver.h ├── forcedsecuresmtpclient.cpp ├── forcedsecuresmtpclient.h ├── htmlmessage.cpp ├── htmlmessage.h ├── message.cpp ├── message.h ├── messageaddress.cpp ├── messageaddress.h ├── opportunisticsecuresmtpclient.cpp ├── opportunisticsecuresmtpclient.h ├── plaintextmessage.cpp ├── plaintextmessage.h ├── securesmtpclientbase.cpp ├── securesmtpclientbase.h ├── serverauthoptions.h ├── serveroptionsanalyzer.cpp ├── serveroptionsanalyzer.h ├── smtpclient.cpp ├── smtpclient.h ├── smtpclientbase.cpp ├── smtpclientbase.h ├── smtpclienterrors.h ├── smtpserverstatuscodes.h ├── socketerrors.h ├── sslerrors.h ├── sslsmtpclient.h ├── stringutils.cpp └── stringutils.h └── test └── smtpclient_unittest ├── attachment_unittest.cpp ├── credential_unittest.cpp ├── errorresolver_unittest.cpp ├── htmlmessage_cpp_unittest.cpp ├── main.cpp ├── message_cpp_unittest.cpp ├── message_unittest.cpp ├── messageaddress_unittest.cpp ├── opportunisticsecuresmtpclient_unittest.cpp ├── plaintextmessage_cpp_unittest.cpp ├── plaintextmessage_unittest.cpp ├── serveroptionsanalyzer_unittest.cpp ├── smtpclient_unittest.cpp ├── smtpclientbase_unittest.cpp └── stringutils_unittest.cpp /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | jobs: 10 | build: 11 | # The CMake configure and build commands are platform agnostic and should work equally 12 | # well on Windows or Mac. You can convert this to a matrix build if you need 13 | # cross-platform coverage. 14 | # See: https://docs.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow#configuring-a-build-matrix 15 | runs-on: ${{ matrix.os }} 16 | strategy: 17 | matrix: 18 | os: [ubuntu-latest, windows-latest] 19 | 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | # Install OS specific dependencies 25 | - name: Install Windows dependencies 26 | if: matrix.os == 'windows-latest' 27 | run: | 28 | Import-Module $env:ChocolateyInstall\helpers\chocolateyProfile.psm1 29 | choco install openssl 30 | refreshenv 31 | 32 | - name: Create Build Environment 33 | # Some projects don't allow in-source building, so create a separate build directory 34 | # We'll use this as our working directory for all subsequent commands 35 | run: cmake -E make_directory ${{runner.workspace}}/build 36 | 37 | - name: Configure CMake (Ubuntu) 38 | # Use a bash shell so we can use the same syntax for environment variable 39 | # access regardless of the host operating system 40 | shell: bash 41 | if: matrix.os == 'ubuntu-latest' 42 | working-directory: ${{runner.workspace}}/build 43 | # Note the current convention is to use the -S and -B options here to specify source 44 | # and build directories, but this is only available with CMake 3.13 and higher. 45 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 46 | run: | 47 | source ~/.profile 48 | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=ON 49 | 50 | - name: Configure CMake (Windows) 51 | # Use a bash shell so we can use the same syntax for environment variable 52 | # access regardless of the host operating system 53 | shell: bash 54 | if: matrix.os == 'windows-latest' 55 | working-directory: ${{runner.workspace}}/build 56 | # Note the current convention is to use the -S and -B options here to specify source 57 | # and build directories, but this is only available with CMake 3.13 and higher. 58 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 59 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBUILD_TESTING=ON -DOPENSSL_INCLUDE_DIRECTORY="C:/Program Files/OpenSSL/include" -DOPENSSL_LIBRARY_DIRECTORY="C:/Program Files/OpenSSL/lib/VC" -DOPENSSL_LIBRARIES_BY_MACHINE_TYPE="libssl64MD;libcrypto64MD" 60 | 61 | - name: Build 62 | working-directory: ${{runner.workspace}}/build 63 | shell: bash 64 | # Exeute the build. You can specify a specific target with "--target " 65 | run: cmake --build . --config $BUILD_TYPE 66 | 67 | - name: Test 68 | working-directory: ${{runner.workspace}}/build 69 | shell: bash 70 | # Execute tests defined by the CMake configuration. 71 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 72 | run: ctest -C $BUILD_TYPE 73 | -------------------------------------------------------------------------------- /.github/workflows/sync-to-azure-devops.yml: -------------------------------------------------------------------------------- 1 | name: Sync to Azure DevOps 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | sync: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout full repository 13 | uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 # Important: get full history for --mirror to work 16 | 17 | - name: Mirror to Azure DevOps 18 | env: 19 | AZURE_REPO_URL: https://jeremydumais.visualstudio.com/CPP-SMTPClient-library/_git/CPP-SMTPClient-library 20 | AZURE_PAT: ${{ secrets.AZURE_DEVOPS_PAT }} 21 | run: | 22 | git config --global user.email "jeremydumais@hotmail.com" 23 | git config --global user.name "Jeremy Dumais" 24 | git remote add azure https://jeremydumais:${AZURE_PAT}@jeremydumais.visualstudio.com/CPP-SMTPClient-library/_git/CPP-SMTPClient-library 25 | git push --mirror azure 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /.vs 6 | /out 7 | /build 8 | /Testing 9 | /.vscode/tasks.json 10 | /.vscode/launch.json 11 | /.vscode/settings.json 12 | simpleclient 13 | .cache 14 | tags 15 | Session.vim 16 | .vimspector.json 17 | .ccls-cache 18 | .idea 19 | cmake-build-debug -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "/usr/include/c++/**" 8 | ], 9 | "defines": [ 10 | "_DEBUG", 11 | "UNICODE", 12 | "_UNICODE" 13 | ], 14 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.13.26128/bin/Hostx86/x86/cl.exe", 15 | "cStandard": "c11", 16 | "cppStandard": "c++11", 17 | "intelliSenseMode": "${default}", 18 | "configurationProvider": "vector-of-bool.cmake-tools", 19 | "compileCommands": "${workspaceFolder}/build/compile_commands.json" 20 | } 21 | ], 22 | "version": 4 23 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "type": "shell", 7 | "group": { 8 | "kind": "build", 9 | "isDefault": true 10 | }, 11 | "options": { 12 | "cwd": "${workspaceFolder}/build" 13 | }, 14 | "command": "cmake", 15 | "args": [ 16 | "--build", 17 | "${workspaceFolder}/build", 18 | "--config", 19 | "Debug", 20 | "--target", 21 | "all", 22 | "--", 23 | "-j", 24 | "10" 25 | ], 26 | "problemMatcher": [] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file 4 | 5 | ## [1.1.10] 6 | 7 | ## Enhancement / Bug fixes 8 | - Add support for macOS. 9 | - Fix the install/uninstall process of the library. 10 | - Solve the issue #38 where STARTTLS is not recognized if it is returned as the 11 | last response from the mail server. 12 | 13 | ## [1.1.9] 14 | 15 | ## Enhancement / Bug fixes 16 | - Rework the build system to support static build and to generate correct 17 | release version. 18 | - The build configuration now works with multi-config generators like Visual 19 | Studio 20 | - The default build configurations in Visual Studio has been changed to : 21 | - x64-Debug 22 | - x64-Debug-Static 23 | - x64-Debug-WithUnitTests 24 | - x64-Release 25 | - x64-Release-Static 26 | - x64-Release-WithUnitTests 27 | 28 | ## [1.1.8] 29 | 30 | ## Enhancement / Bug fixes 31 | - Some SMTP server send their list of supported extensions in multiple 32 | buffers like Zoho Mail. The EHLO command when in uncrypted mode, now supports 33 | receiving multiple buffers. In return, a delay of one second must be added for 34 | each segment sent by the SMTP server. For SMTP servers that send the list of 35 | supported extensions in a single segment like Gmail and Live, no additional 36 | delay is added for the EHLO command. This doesn't affect the other commands. 37 | - Now when we send an email to multiple recipients (to or cc), the recipients 38 | appears as a single mail header instead of multiple headers. The old method was 39 | not RFC 5322 compliant. 40 | 41 | Before: 42 | 43 | To: test@example.com 44 | 45 | To: test2@example.com 46 | 47 | After: 48 | 49 | To: test@example.com, test2@example.com 50 | 51 | 52 | ## [1.1.7] 53 | 54 | ### Added 55 | - Added an option in the Credential class to indicate the preferred 56 | authentication option. Support for the XOAUTH2 authentication has also been 57 | added.This change has been made by rcosnita (https://github.com/rcosnita). 58 | Many thanks! 59 | - Added a new flag in the different SMTP client classes to indicate whether we 60 | want to send emails in batch (getBatchMode/setBatchMode). In this mode the connection to an 61 | SMTP server will only be made once when the first email is sent and will 62 | remain open until the client instance is destroy. 63 | - Added the authentication feature on the SMTPClient class. 64 | - Added a new flag on the ForcedSecureSMTPClient and OpportunisticSecureSMTPClient 65 | to indicate whether we accept self signed certificate 66 | (getAcceptSelfSignedCert/setAcceptSelfSignedCert). 67 | 68 | 69 | ### Enhancement / Bug fixes 70 | The following changes have been made by ino-josh (https://github.com/ino-josh). 71 | Many thanks! 72 | 73 | - Changed the default MAIL FROM format to fit the RFC. 74 | - mCredential being a member of SMTPClientBase is now freed by it. 75 | - Base64 encoding: added CRLF every so often to prevent rejection. 76 | - Attachments: added mime types for audio files. 77 | - Fix massive memory leak in createAttachmentsText of the SMTPClientBase class. 78 | 79 | 80 | ## [1.1.6] 81 | 82 | ### Added 83 | 84 | - Added support in the attachment class for Content-ID. It will be really 85 | useful to uniquely identify and reference resources to embed in the message. 86 | This change has been made by hesa2020 (https://github.com/hesa2020). 87 | Many thanks! 88 | 89 | ### Enhancement 90 | 91 | - Correction to the CMakeLists.txt, so when the repository is added as a git 92 | submodule and linked in the parent cmake project, it will now compile. 93 | This change has been made by hesa2020 (https://github.com/hesa2020). 94 | Many thanks! 95 | 96 | ## [1.1.5] 97 | 98 | ### Added 99 | 100 | - Added OpenSSL variables in CMakeLists to be able to specify include, 101 | library path and library files. 102 | - A set of new classes has been added to the jed_utils::cpp namespace 103 | to provide a pure C++ way to consume the library. This is the new 104 | standard from version 1.1.5. See the new class documentation in the wiki. 105 | 106 | ### Updated 107 | 108 | - The ErrorResolver class now compiles on Windows. 109 | - Changed the ErrorResolver field mErrorMessage from an std::string to an 110 | char * to keep a Plain C Interface. 111 | - Code formatting applied throughout the project using cpplint and following 112 | Google's C++ style guide. 113 | - Configured Linux Socket connect function to non-blocking mode to make 114 | the SmtpClientBase timeout working as expected. 115 | - Added WSACleanup error return code to the communication log. 116 | 117 | ### Bug fixes 118 | 119 | - Changed the size of the communication log buffer from static (4096 bytes) to an 120 | auto-growing dynamic buffer. 121 | 122 | ### Security fixes 123 | 124 | - Replaced all insecure strcpy by functions that support length arguments like 125 | strncpy. 126 | 127 | ## [1.1.4] 128 | 129 | ### Added 130 | 131 | - Add the BUILD_TESTING flag in the CMake project so the unit tests are not 132 | build by default and Google Test is no longer required. 133 | - Add a new uninstall target in the CMake project. 134 | - Add a new ErrorResolver class to get a string representation (error message) 135 | of a return code obtained by the 136 | sendMail method from the different classes of SMTP clients. 137 | - Two new methods has been added to the SMTPClientBase class : getErrorMessage 138 | and getErrorMessage_r. Those two methods can be used to get a string 139 | representation (error message) of a return code obtained by the sendMail 140 | method from the different classes of SMTP clients. 141 | 142 | ### Updated 143 | 144 | - The Google Test dependency branch has been switched from master to main. 145 | - Code: The using namespace std; has been removed as it is considered bad practice. 146 | - The Windows documentation has been updated to explain how to use 147 | OPENSSL_ROOT_DIR variable. 148 | - Added documentation in all headers files for public methods. 149 | 150 | ### Removed 151 | 152 | - The exception classes AttachmentError and CommunicationError has been removed. 153 | 154 | ## [1.1.3] 155 | 156 | ### Updated 157 | 158 | - Rename the class SSLSmtpClient to OpportunisticSecureSMTPClient but added a 159 | typedef and kept the sslsmtpclient.h for backward compatibility. 160 | 161 | ### Added 162 | 163 | - Add support for forced ssl connection (SMTP port 465) via the 164 | ForcedSecureSMTPClient class 165 | - Add the new base class SecureSMTPClientBase to centralize the common code of 166 | the classes ForcedSecureSMTPClient and OpportunisticSecureSMTPClient. 167 | 168 | ## [1.1.2] 169 | 170 | ### Updated 171 | 172 | - Refactor the code of the SmtpClient class to inherit the SmtpClientBase class. 173 | - You must now call the method getCommunicationLog() instead of getServerReply() 174 | 175 | ## [1.1.1] 176 | 177 | ### Added 178 | 179 | - Add support for the cc and bcc field in the sendMail method 180 | 181 | ## [1.1.0] 182 | 183 | ### Added 184 | 185 | - The SSL/TLS version of the SMTP Client (sslsmtpclient.cpp) 186 | - Authentication capabilities have been added to the new SSL/TLS client 187 | - Error code headers have been created so the return codes are now more 188 | descriptive (socketerrors.h and sslerrors.h) 189 | 190 | ### Bug fixes 191 | 192 | - The MessageAddress class now support uppercase characters in the email address 193 | 194 | ## [1.0.0] 195 | 196 | ### Added 197 | 198 | - The initial version development 199 | -------------------------------------------------------------------------------- /CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.2) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG main 9 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | -------------------------------------------------------------------------------- /CMakeSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "x64-Debug", 5 | "generator": "Ninja", 6 | "configurationType": "Debug", 7 | "inheritEnvironments": [ "msvc_x64_x64" ], 8 | "buildRoot": "${projectDir}\\out\\build\\${name}", 9 | "installRoot": "${projectDir}\\out\\install\\${name}", 10 | "cmakeCommandArgs": "", 11 | "buildCommandArgs": "-v", 12 | "ctestCommandArgs": "" 13 | }, 14 | { 15 | "name": "x64-Release", 16 | "generator": "Ninja", 17 | "configurationType": "Release", 18 | "buildRoot": "${projectDir}\\out\\build\\${name}", 19 | "installRoot": "${projectDir}\\out\\install\\${name}", 20 | "cmakeCommandArgs": "", 21 | "buildCommandArgs": "-v", 22 | "ctestCommandArgs": "", 23 | "inheritEnvironments": [ "msvc_x64_x64" ] 24 | }, 25 | { 26 | "name": "x64-Debug-WithUnitTests", 27 | "generator": "Ninja", 28 | "configurationType": "Debug", 29 | "buildRoot": "${projectDir}\\out\\build\\${name}", 30 | "installRoot": "${projectDir}\\out\\install\\${name}", 31 | "cmakeCommandArgs": "-DBUILD_TESTING=ON", 32 | "buildCommandArgs": "-v", 33 | "ctestCommandArgs": "", 34 | "inheritEnvironments": [ "msvc_x64_x64" ] 35 | }, 36 | { 37 | "name": "x64-Release-WithUnitTests", 38 | "generator": "Ninja", 39 | "configurationType": "Release", 40 | "buildRoot": "${projectDir}\\out\\build\\${name}", 41 | "installRoot": "${projectDir}\\out\\install\\${name}", 42 | "cmakeCommandArgs": "-DBUILD_TESTING=ON", 43 | "buildCommandArgs": "-v", 44 | "ctestCommandArgs": "", 45 | "inheritEnvironments": [ "msvc_x64_x64" ] 46 | }, 47 | { 48 | "name": "x64-Debug-Static", 49 | "generator": "Ninja", 50 | "configurationType": "Debug", 51 | "buildRoot": "${projectDir}\\out\\build\\${name}", 52 | "installRoot": "${projectDir}\\out\\install\\${name}", 53 | "cmakeCommandArgs": "-DBUILD_SHARED_LIBS=OFF", 54 | "buildCommandArgs": "-v", 55 | "ctestCommandArgs": "", 56 | "inheritEnvironments": [ "msvc_x64_x64" ] 57 | }, 58 | { 59 | "name": "x64-Release-Static", 60 | "generator": "Ninja", 61 | "configurationType": "Release", 62 | "buildRoot": "${projectDir}\\out\\build\\${name}", 63 | "installRoot": "${projectDir}\\out\\install\\${name}", 64 | "cmakeCommandArgs": "-DBUILD_SHARED_LIBS=OFF", 65 | "buildCommandArgs": "-v", 66 | "ctestCommandArgs": "", 67 | "inheritEnvironments": [ "msvc_x64_x64" ], 68 | "variables": [] 69 | } 70 | ] 71 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Jeremy Dumais 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 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 | -------------------------------------------------------------------------------- /cmake/CMakeUninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 3 | endif() 4 | 5 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach(file ${files}) 8 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 9 | if(EXISTS "$ENV{DESTDIR}${file}") 10 | execute_process( 11 | COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${file}" 12 | RESULT_VARIABLE rm_retval 13 | ) 14 | if(NOT "${rm_retval}" STREQUAL "0") 15 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 16 | endif() 17 | else() 18 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 19 | endif() 20 | endforeach() 21 | -------------------------------------------------------------------------------- /cmake/modules/VersionInfo.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef PRODUCT_VERSION_MAJOR 4 | #define PRODUCT_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ 5 | #endif 6 | 7 | #ifndef PRODUCT_VERSION_MINOR 8 | #define PRODUCT_VERSION_MINOR @PRODUCT_VERSION_MINOR@ 9 | #endif 10 | 11 | #ifndef PRODUCT_VERSION_PATCH 12 | #define PRODUCT_VERSION_PATCH @PRODUCT_VERSION_PATCH@ 13 | #endif 14 | 15 | #ifndef PRODUCT_VERSION_BUILD 16 | #define PRODUCT_VERSION_BUILD @PRODUCT_VERSION_REVISION@ 17 | #endif 18 | 19 | #ifndef FILE_VERSION_MAJOR 20 | #define FILE_VERSION_MAJOR @PRODUCT_VERSION_MAJOR@ 21 | #endif 22 | 23 | #ifndef FILE_VERSION_MINOR 24 | #define FILE_VERSION_MINOR @PRODUCT_VERSION_MINOR@ 25 | #endif 26 | 27 | #ifndef FILE_VERSION_PATCH 28 | #define FILE_VERSION_PATCH @PRODUCT_VERSION_PATCH@ 29 | #endif 30 | 31 | #ifndef FILE_VERSION_BUILD 32 | #define FILE_VERSION_BUILD @PRODUCT_VERSION_REVISION@ 33 | #endif 34 | 35 | #ifndef __TO_STRING 36 | #define __TO_STRING_IMPL(x) #x 37 | #define __TO_STRING(x) __TO_STRING_IMPL(x) 38 | #endif 39 | 40 | #define PRODUCT_VERSION_MAJOR_MINOR_STR __TO_STRING(PRODUCT_VERSION_MAJOR) "." __TO_STRING(PRODUCT_VERSION_MINOR) 41 | #define PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR PRODUCT_VERSION_MAJOR_MINOR_STR "." __TO_STRING(PRODUCT_VERSION_PATCH) 42 | #define PRODUCT_VERSION_FULL_STR PRODUCT_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(PRODUCT_VERSION_BUILD) 43 | #define PRODUCT_VERSION_RESOURCE PRODUCT_VERSION_MAJOR,PRODUCT_VERSION_MINOR,PRODUCT_VERSION_PATCH,PRODUCT_VERSION_BUILD 44 | #define PRODUCT_VERSION_RESOURCE_STR PRODUCT_VERSION_FULL_STR "\0" 45 | 46 | #define FILE_VERSION_MAJOR_MINOR_STR __TO_STRING(FILE_VERSION_MAJOR) "." __TO_STRING(FILE_VERSION_MINOR) 47 | #define FILE_VERSION_MAJOR_MINOR_PATCH_STR FILE_VERSION_MAJOR_MINOR_STR "." __TO_STRING(FILE_VERSION_PATCH) 48 | #define FILE_VERSION_FULL_STR FILE_VERSION_MAJOR_MINOR_PATCH_STR "." __TO_STRING(FILE_VERSION_BUILD) 49 | #define FILE_VERSION_RESOURCE FILE_VERSION_MAJOR,FILE_VERSION_MINOR,FILE_VERSION_PATCH,FILE_VERSION_BUILD 50 | #define FILE_VERSION_RESOURCE_STR FILE_VERSION_FULL_STR "\0" 51 | 52 | #ifndef PRODUCT_ICON 53 | #define PRODUCT_ICON "@PRODUCT_ICON@" 54 | #endif 55 | 56 | #ifndef PRODUCT_COMMENTS 57 | #define PRODUCT_COMMENTS "@PRODUCT_COMMENTS@\0" 58 | #endif 59 | 60 | #ifndef PRODUCT_COMPANY_NAME 61 | #define PRODUCT_COMPANY_NAME "@PRODUCT_COMPANY_NAME@\0" 62 | #endif 63 | 64 | #ifndef PRODUCT_COMPANY_COPYRIGHT 65 | #define PRODUCT_COMPANY_COPYRIGHT "@PRODUCT_COMPANY_COPYRIGHT@\0" 66 | #endif 67 | 68 | #ifndef PRODUCT_FILE_DESCRIPTION 69 | #define PRODUCT_FILE_DESCRIPTION "@PRODUCT_FILE_DESCRIPTION@\0" 70 | #endif 71 | 72 | #ifndef PRODUCT_INTERNAL_NAME 73 | #define PRODUCT_INTERNAL_NAME "@PRODUCT_NAME@\0" 74 | #endif 75 | 76 | #ifndef PRODUCT_ORIGINAL_FILENAME 77 | #define PRODUCT_ORIGINAL_FILENAME "@PRODUCT_ORIGINAL_FILENAME@\0" 78 | #endif 79 | 80 | #ifndef PRODUCT_BUNDLE 81 | #define PRODUCT_BUNDLE "@PRODUCT_BUNDLE@\0" 82 | #endif -------------------------------------------------------------------------------- /cmake/modules/VersionResource.rc: -------------------------------------------------------------------------------- 1 | #include "VersionInfo.h" 2 | 3 | #if defined(__MINGW64__) || defined(__MINGW32__) 4 | // MinGW-w64, MinGW 5 | #if defined(__has_include) && __has_include() 6 | #include 7 | #else 8 | #include 9 | #include 10 | #endif 11 | #else 12 | // MSVC, Windows SDK 13 | #include 14 | #endif 15 | 16 | IDI_ICON1 ICON PRODUCT_ICON 17 | 18 | LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT 19 | 20 | VS_VERSION_INFO VERSIONINFO 21 | FILEVERSION FILE_VERSION_RESOURCE 22 | PRODUCTVERSION PRODUCT_VERSION_RESOURCE 23 | FILEFLAGSMASK 0x3fL 24 | #ifdef _DEBUG 25 | FILEFLAGS 0x1L 26 | #else 27 | FILEFLAGS 0x0L 28 | #endif 29 | FILEOS 0x4L 30 | FILETYPE 0x1L 31 | FILESUBTYPE 0x0L 32 | BEGIN 33 | BLOCK "StringFileInfo" 34 | BEGIN 35 | BLOCK "040904E4" 36 | BEGIN 37 | VALUE "Comments", PRODUCT_COMMENTS 38 | VALUE "CompanyName", PRODUCT_COMPANY_NAME 39 | VALUE "FileDescription", PRODUCT_FILE_DESCRIPTION 40 | VALUE "FileVersion", FILE_VERSION_RESOURCE_STR 41 | VALUE "InternalName", PRODUCT_INTERNAL_NAME 42 | VALUE "LegalCopyright", PRODUCT_COMPANY_COPYRIGHT 43 | VALUE "OriginalFilename", PRODUCT_ORIGINAL_FILENAME 44 | VALUE "ProductName", PRODUCT_BUNDLE 45 | VALUE "ProductVersion", PRODUCT_VERSION_RESOURCE_STR 46 | END 47 | END 48 | BLOCK "VarFileInfo" 49 | BEGIN 50 | VALUE "Translation", 0x409, 1200 51 | END 52 | END -------------------------------------------------------------------------------- /cmake/modules/generate_product_version.cmake: -------------------------------------------------------------------------------- 1 | include (CMakeParseArguments) 2 | 3 | set (GenerateProductVersionCurrentDir ${CMAKE_CURRENT_LIST_DIR}) 4 | 5 | # generate_product_version() function 6 | # 7 | # This function uses VersionInfo.in template file and VersionResource.rc file 8 | # to generate WIN32 resource with version information and general resource strings. 9 | # 10 | # Usage: 11 | # generate_product_version( 12 | # SomeOutputResourceVariable 13 | # NAME MyGreatProject 14 | # ICON ${PATH_TO_APP_ICON} 15 | # VERSION_MAJOR 2 16 | # VERSION_MINOR 3 17 | # VERSION_PATCH ${BUILD_COUNTER} 18 | # VERSION_REVISION ${BUILD_REVISION} 19 | # ) 20 | # where BUILD_COUNTER and BUILD_REVISION could be values from your CI server. 21 | # 22 | # You can use generated resource for your executable targets: 23 | # add_executable(target-name ${target-files} ${SomeOutputResourceVariable}) 24 | # 25 | # You can specify resource strings in arguments: 26 | # NAME - name of executable (no defaults, ex: Microsoft Word) 27 | # BUNDLE - bundle (${NAME} is default, ex: Microsoft Office) 28 | # ICON - path to application icon (${CMAKE_SOURCE_DIR}/product.ico by default) 29 | # VERSION_MAJOR - 1 is default 30 | # VERSION_MINOR - 0 is default 31 | # VERSION_PATCH - 0 is default 32 | # VERSION_REVISION - 0 is default 33 | # COMPANY_NAME - your company name (no defaults) 34 | # COMPANY_COPYRIGHT - ${COMPANY_NAME} (C) Copyright ${CURRENT_YEAR} is default 35 | # COMMENTS - ${NAME} v${VERSION_MAJOR}.${VERSION_MINOR} is default 36 | # ORIGINAL_FILENAME - ${NAME} is default 37 | # INTERNAL_NAME - ${NAME} is default 38 | # FILE_DESCRIPTION - ${NAME} is default 39 | function(generate_product_version outfiles) 40 | set (options) 41 | set (oneValueArgs 42 | NAME 43 | BUNDLE 44 | ICON 45 | VERSION_MAJOR 46 | VERSION_MINOR 47 | VERSION_PATCH 48 | VERSION_REVISION 49 | COMPANY_NAME 50 | COMPANY_COPYRIGHT 51 | COMMENTS 52 | ORIGINAL_FILENAME 53 | INTERNAL_NAME 54 | FILE_DESCRIPTION) 55 | set (multiValueArgs) 56 | cmake_parse_arguments(PRODUCT "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 57 | 58 | if (NOT PRODUCT_BUNDLE OR "${PRODUCT_BUNDLE}" STREQUAL "") 59 | set(PRODUCT_BUNDLE "${PRODUCT_NAME}") 60 | endif() 61 | if (NOT PRODUCT_ICON OR "${PRODUCT_ICON}" STREQUAL "") 62 | set(PRODUCT_ICON "${CMAKE_SOURCE_DIR}/product.ico") 63 | endif() 64 | 65 | if (NOT PRODUCT_VERSION_MAJOR EQUAL 0 AND (NOT PRODUCT_VERSION_MAJOR OR "${PRODUCT_VERSION_MAJOR}" STREQUAL "")) 66 | set(PRODUCT_VERSION_MAJOR 1) 67 | endif() 68 | if (NOT PRODUCT_VERSION_MINOR EQUAL 0 AND (NOT PRODUCT_VERSION_MINOR OR "${PRODUCT_VERSION_MINOR}" STREQUAL "")) 69 | set(PRODUCT_VERSION_MINOR 0) 70 | endif() 71 | if (NOT PRODUCT_VERSION_PATCH EQUAL 0 AND (NOT PRODUCT_VERSION_PATCH OR "${PRODUCT_VERSION_PATCH}" STREQUAL "")) 72 | set(PRODUCT_VERSION_PATCH 0) 73 | endif() 74 | if (NOT PRODUCT_VERSION_REVISION EQUAL 0 AND (NOT PRODUCT_VERSION_REVISION OR "${PRODUCT_VERSION_REVISION}" STREQUAL "")) 75 | set(PRODUCT_VERSION_REVISION 0) 76 | endif() 77 | 78 | if (NOT PRODUCT_COMPANY_COPYRIGHT OR "${PRODUCT_COMPANY_COPYRIGHT}" STREQUAL "") 79 | string(TIMESTAMP PRODUCT_CURRENT_YEAR "%Y") 80 | set(PRODUCT_COMPANY_COPYRIGHT "${PRODUCT_COMPANY_NAME} (C) Copyright ${PRODUCT_CURRENT_YEAR}") 81 | endif() 82 | if (NOT PRODUCT_COMMENTS OR "${PRODUCT_COMMENTS}" STREQUAL "") 83 | set(PRODUCT_COMMENTS "${PRODUCT_NAME} v${PRODUCT_VERSION_MAJOR}.${PRODUCT_VERSION_MINOR}") 84 | endif() 85 | if (NOT PRODUCT_ORIGINAL_FILENAME OR "${PRODUCT_ORIGINAL_FILENAME}" STREQUAL "") 86 | set(PRODUCT_ORIGINAL_FILENAME "${PRODUCT_NAME}") 87 | endif() 88 | if (NOT PRODUCT_INTERNAL_NAME OR "${PRODUCT_INTERNAL_NAME}" STREQUAL "") 89 | set(PRODUCT_INTERNAL_NAME "${PRODUCT_NAME}") 90 | endif() 91 | if (NOT PRODUCT_FILE_DESCRIPTION OR "${PRODUCT_FILE_DESCRIPTION}" STREQUAL "") 92 | set(PRODUCT_FILE_DESCRIPTION "${PRODUCT_NAME}") 93 | endif() 94 | 95 | set (_VersionInfoFile ${CMAKE_CURRENT_BINARY_DIR}/VersionInfo.h) 96 | set (_VersionResourceFile ${CMAKE_CURRENT_BINARY_DIR}/VersionResource.rc) 97 | configure_file( 98 | ${GenerateProductVersionCurrentDir}/VersionInfo.in 99 | ${_VersionInfoFile} 100 | @ONLY) 101 | configure_file( 102 | ${GenerateProductVersionCurrentDir}/VersionResource.rc 103 | ${_VersionResourceFile} 104 | COPYONLY) 105 | list(APPEND ${outfiles} ${_VersionInfoFile} ${_VersionResourceFile}) 106 | set (${outfiles} ${${outfiles}} PARENT_SCOPE) 107 | endfunction() -------------------------------------------------------------------------------- /cmake/smtpclientConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/smtpclientTargets.cmake") 4 | -------------------------------------------------------------------------------- /product.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremydumais/CPP-SMTPClient-library/4ce35c32f6aa4737e1ad5d4b683261fd2c9919d9/product.ico -------------------------------------------------------------------------------- /src/attachment.h: -------------------------------------------------------------------------------- 1 | #ifndef ATTACHMENT_H 2 | #define ATTACHMENT_H 3 | 4 | #include 5 | #include 6 | #include "base64.h" 7 | 8 | #ifdef _WIN32 9 | #ifdef SMTPCLIENT_STATIC 10 | #define ATTACHMENT_API 11 | #elif defined(SMTPCLIENT_EXPORTS) 12 | #define ATTACHMENT_API __declspec(dllexport) 13 | #else 14 | #define ATTACHMENT_API __declspec(dllimport) 15 | #endif 16 | #else 17 | #define ATTACHMENT_API 18 | #endif 19 | 20 | namespace jed_utils { 21 | /** @brief The Attachment class represent a file attachment in a 22 | * message. It can be a picture, a document, a text file etc. 23 | */ 24 | class ATTACHMENT_API Attachment { 25 | public: 26 | /** 27 | * @brief Construct a new Attachment. 28 | * @param pFilename The full path of the file. 29 | * @param pName The display name of the file that will appear in 30 | * the mail content 31 | */ 32 | explicit Attachment(const char *pFilename, const char *pName = "", const char *pContentId = ""); 33 | 34 | /** Destructor of the Attachment */ 35 | virtual ~Attachment(); 36 | 37 | /** Attachment copy constructor. */ 38 | Attachment(const Attachment& other); 39 | 40 | /** Attachment copy assignment operator. */ 41 | Attachment& operator=(const Attachment& other); 42 | 43 | /** Attachment move constructor. */ 44 | Attachment(Attachment&& other) noexcept; 45 | 46 | /** Attachment move assignment operator. */ 47 | Attachment& operator=(Attachment&& other) noexcept; 48 | 49 | /** Set the attachment content id. */ 50 | void setContentId(const char * pContentId); 51 | 52 | /** Return the display name. */ 53 | const char *getName() const; 54 | 55 | /** Return the file name including the path. */ 56 | const char *getFilename() const; 57 | 58 | /** Return the attachment content id. */ 59 | const char *getContentId() const; 60 | 61 | /** Return the base64 representation of the file content. */ 62 | const char *getBase64EncodedFile() const; 63 | 64 | /** Return the MIME type corresponding to the file extension. */ 65 | const char *getMimeType() const; 66 | 67 | friend class Message; 68 | 69 | private: 70 | Attachment() = default; 71 | char *mName; 72 | char *mFilename; 73 | char *mContentId; 74 | }; 75 | } // namespace jed_utils 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /src/base64.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | base64.cpp and base64.h 3 | 4 | Copyright (C) 2004-2008 Ren� Nyffenegger 5 | 6 | This source code is provided 'as-is', without any express or implied 7 | warranty. In no event will the author be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this source code must not be misrepresented; you must not 15 | claim that you wrote the original source code. If you use this source code 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original source code. 21 | 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | Ren� Nyffenegger rene.nyffenegger@adp-gmbh.ch 25 | 26 | */ 27 | 28 | #include "base64.h" 29 | #include 30 | 31 | using namespace jed_utils; 32 | 33 | static const std::string base64_chars = 34 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 35 | "abcdefghijklmnopqrstuvwxyz" 36 | "0123456789+/"; 37 | 38 | 39 | static inline bool is_base64(unsigned char c) { 40 | return (isalnum(c) || (c == '+') || (c == '/')); 41 | } 42 | 43 | std::string Base64::Encode(unsigned char const *bytes_to_encode, size_t in_len) { 44 | std::string ret; 45 | int i = 0; 46 | size_t out_len = 0; 47 | unsigned char char_array_3[3]; 48 | unsigned char char_array_4[4]; 49 | 50 | while (in_len--) { 51 | char_array_3[i++] = *(bytes_to_encode++); 52 | if (i == 3) { 53 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 54 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 55 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 56 | char_array_4[3] = char_array_3[2] & 0x3f; 57 | 58 | for (i = 0; (i < 4); i++) 59 | ret += base64_chars[char_array_4[i]]; 60 | i = 0; 61 | 62 | // Some servers reject any email with a line that is "too long" (hMailServer does this, for example). 63 | // Since attachments can be quite large, we break up the output with \r\n to avoid this. 64 | out_len += 4; 65 | if (out_len % (64 * 1024) == 0) { 66 | ret += "\r\n"; 67 | } 68 | } 69 | } 70 | 71 | if (i) { 72 | for (int j = i; j < 3; j++) 73 | char_array_3[j] = '\0'; 74 | 75 | char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; 76 | char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); 77 | char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); 78 | char_array_4[3] = char_array_3[2] & 0x3f; 79 | 80 | for (int j = 0; (j < i + 1); j++) 81 | ret += base64_chars[char_array_4[j]]; 82 | 83 | while ((i++ < 3)) 84 | ret += '='; 85 | 86 | } 87 | 88 | return ret; 89 | 90 | } 91 | 92 | std::string Base64::Decode(std::string const &encoded_string) { 93 | size_t in_len = encoded_string.size(); 94 | int i = 0; 95 | int in_ = 0; 96 | unsigned char char_array_4[4], char_array_3[3]; 97 | std::string ret; 98 | 99 | while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { 100 | char_array_4[i++] = encoded_string[in_]; in_++; 101 | if (i == 4) { 102 | for (i = 0; i < 4; i++) 103 | char_array_4[i] = static_cast(base64_chars.find(char_array_4[i])); 104 | 105 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 106 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 107 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 108 | 109 | for (i = 0; (i < 3); i++) 110 | ret += char_array_3[i]; 111 | i = 0; 112 | } 113 | } 114 | 115 | if (i) { 116 | for (int j = i; j < 4; j++) 117 | char_array_4[j] = 0; 118 | 119 | for (int j = 0; j < 4; j++) 120 | char_array_4[j] = static_cast(base64_chars.find(char_array_4[j])); 121 | 122 | char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); 123 | char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); 124 | char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; 125 | 126 | for (int j = 0; (j < i - 1); j++) ret += char_array_3[j]; 127 | } 128 | 129 | return ret; 130 | } 131 | -------------------------------------------------------------------------------- /src/base64.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE64UTILS_H 2 | #define BASE64UTILS_H 3 | 4 | #include 5 | 6 | namespace jed_utils { 7 | class Base64 { 8 | public: 9 | static std::string Encode(unsigned char const *bytes_to_encode, size_t in_len); 10 | static std::string Decode(std::string const &encoded_string); 11 | }; 12 | } // namespace jed_utils 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/cpp/attachment.cpp: -------------------------------------------------------------------------------- 1 | #include "attachment.hpp" 2 | 3 | using namespace jed_utils::cpp; 4 | 5 | Attachment::Attachment(const std::string &pFilename, const std::string &pName, const std::string &pContentId) 6 | : jed_utils::Attachment(pFilename.c_str(), pName.c_str(), pContentId.c_str()) { 7 | } 8 | 9 | void Attachment::setContentId(std::string pContentId) { 10 | return jed_utils::Attachment::setContentId(pContentId.c_str()); 11 | } 12 | 13 | std::string Attachment::getName() const { 14 | return jed_utils::Attachment::getName(); 15 | } 16 | 17 | std::string Attachment::getFilename() const { 18 | return jed_utils::Attachment::getFilename(); 19 | } 20 | 21 | std::string Attachment::getContentId() const { 22 | return jed_utils::Attachment::getContentId(); 23 | } 24 | 25 | std::string Attachment::getBase64EncodedFile() const { 26 | const char *retval = jed_utils::Attachment::getBase64EncodedFile(); 27 | return retval == nullptr ? "" : retval; 28 | } 29 | 30 | std::string Attachment::getMimeType() const { 31 | return jed_utils::Attachment::getMimeType(); 32 | } 33 | 34 | jed_utils::Attachment Attachment::toStdAttachment() const { 35 | return jed_utils::Attachment(jed_utils::Attachment::getFilename(), 36 | jed_utils::Attachment::getName(), 37 | jed_utils::Attachment::getContentId()); 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/cpp/attachment.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPATTACHMENT_H 2 | #define CPPATTACHMENT_H 3 | 4 | #include 5 | #include 6 | #include "../attachment.h" 7 | #include "../base64.h" 8 | 9 | #ifdef _WIN32 10 | #ifdef SMTPCLIENT_STATIC 11 | #define CPP_ATTACHMENT_API 12 | #elif defined(SMTPCLIENT_EXPORTS) 13 | #define CPP_ATTACHMENT_API __declspec(dllexport) 14 | #else 15 | #define CPP_ATTACHMENT_API __declspec(dllimport) 16 | #endif 17 | #else 18 | #define CPP_ATTACHMENT_API 19 | #endif 20 | 21 | namespace jed_utils { 22 | namespace cpp { 23 | /** @brief The Attachment class represent a file attachment in a 24 | * message. It can be a picture, a document, a text file etc. 25 | */ 26 | class CPP_ATTACHMENT_API Attachment : private jed_utils::Attachment { 27 | public: 28 | /** 29 | * @brief Construct a new Attachment. 30 | * @param pFilename The full path of the file. 31 | * @param pName The display name of the file that will appear in 32 | * the mail content 33 | */ 34 | explicit Attachment(const std::string &pFilename, const std::string &pName = "", const std::string &pContentId = ""); 35 | 36 | /** Destructor of the Attachment */ 37 | ~Attachment() override = default; 38 | 39 | /** Attachment copy constructor. */ 40 | Attachment(const Attachment& other) = default; 41 | 42 | /** Attachment copy assignment operator. */ 43 | Attachment& operator=(const Attachment& other) = default; 44 | 45 | /** Attachment move constructor. */ 46 | Attachment(Attachment&& other) noexcept = default; 47 | 48 | /** Attachment move assignment operator. */ 49 | Attachment& operator=(Attachment&& other) noexcept = default; 50 | 51 | /** Set the attachment content id. */ 52 | void setContentId(std::string pContentId); 53 | 54 | /** Return the display name. */ 55 | std::string getName() const; 56 | 57 | /** Return the file name including the path. */ 58 | std::string getFilename() const; 59 | 60 | /** Return the attachment content id. */ 61 | std::string getContentId() const; 62 | 63 | /** Return the base64 representation of the file content. */ 64 | std::string getBase64EncodedFile() const; 65 | 66 | /** Return the MIME type corresponding to the file extension. */ 67 | std::string getMimeType() const; 68 | 69 | jed_utils::Attachment toStdAttachment() const; 70 | }; 71 | } // namespace cpp 72 | } // namespace jed_utils 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/cpp/credential.cpp: -------------------------------------------------------------------------------- 1 | #include "credential.hpp" 2 | 3 | using namespace jed_utils::cpp; 4 | 5 | Credential::Credential(const std::string &pUsername, const std::string &pPassword) 6 | : jed_utils::Credential(pUsername.c_str(), pPassword.c_str()) { 7 | } 8 | 9 | Credential::Credential(const std::string &pUsername, const std::string &pPassword, 10 | RecommendedAuthenticationMethod authOption) 11 | : Credential(pUsername, pPassword) { 12 | jed_utils::Credential::setRecommendedAuthOption(authOption); 13 | } 14 | 15 | std::string Credential::getUsername() const { 16 | return jed_utils::Credential::getUsername(); 17 | } 18 | 19 | std::string Credential::getPassword() const { 20 | return jed_utils::Credential::getPassword(); 21 | } 22 | 23 | auto Credential::getRecommendedAuthOption() const 24 | -> RecommendedAuthenticationMethod { 25 | return jed_utils::Credential::getRecommendedAuthOption(); 26 | } 27 | 28 | void Credential::setUsername(const std::string &pUsername) { 29 | jed_utils::Credential::setUsername(pUsername.c_str()); 30 | } 31 | 32 | void Credential::setPassword(const std::string &pPassword) { 33 | jed_utils::Credential::setPassword(pPassword.c_str()); 34 | } 35 | 36 | void Credential::setRecommendedAuthOption( 37 | RecommendedAuthenticationMethod authOption) { 38 | jed_utils::Credential::setRecommendedAuthOption(authOption); 39 | } 40 | -------------------------------------------------------------------------------- /src/cpp/credential.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPCREDENTIAL_H 2 | #define CPPCREDENTIAL_H 3 | 4 | #ifdef _WIN32 5 | #ifdef SMTPCLIENT_STATIC 6 | #define CPP_CREDENTIAL_API 7 | #elif defined(SMTPCLIENT_EXPORTS) 8 | #define CPP_CREDENTIAL_API __declspec(dllexport) 9 | #else 10 | #define CPP_CREDENTIAL_API __declspec(dllimport) 11 | #endif 12 | #else 13 | #define CPP_CREDENTIAL_API 14 | #endif 15 | 16 | #include 17 | #include "../credential.h" 18 | 19 | namespace jed_utils { 20 | namespace cpp { 21 | /** @brief The Credential class is used to authenticate with the 22 | * SMTP server. 23 | */ 24 | class CPP_CREDENTIAL_API Credential : private jed_utils::Credential { 25 | public: 26 | /** 27 | * @brief Construct a new Credential. 28 | * @param pUsername The user name. 29 | * @param pPassword The password. 30 | */ 31 | Credential(const std::string &pUsername, const std::string &pPassword); 32 | 33 | /** 34 | * @brief Construct a new Credential. 35 | * @param pUsername The user name. 36 | * @param pPassword The password. 37 | */ 38 | Credential(const std::string &pUsername, const std::string &pPassword, 39 | RecommendedAuthenticationMethod authOption); 40 | 41 | /** The destructor og Credential */ 42 | ~Credential() override = default; 43 | 44 | /** Credential copy constructor */ 45 | Credential(const Credential& other) = default; 46 | 47 | /** Credential copy assignment operator */ 48 | Credential& operator=(const Credential& other) = default; 49 | 50 | /** Credential move constructor */ 51 | Credential(Credential&& other) noexcept = default; 52 | 53 | /** Credential move assignment operator */ 54 | Credential& operator=(Credential&& other) noexcept = default; 55 | 56 | /** Return the username. */ 57 | std::string getUsername() const; 58 | 59 | /** Return the password. */ 60 | std::string getPassword() const; 61 | 62 | /** Returns the recommended authentication option 63 | * currently set for the credentials. */ 64 | RecommendedAuthenticationMethod getRecommendedAuthOption() const; 65 | 66 | /** 67 | * @brief Set the user name. 68 | * @param pUsername A char array pointer of the user name. 69 | */ 70 | void setUsername(const std::string &pUsername); 71 | 72 | /** 73 | * @brief Set the password. 74 | * @param pPassword A char array pointer of the password. 75 | */ 76 | void setPassword(const std::string &pPassword); 77 | 78 | /** 79 | * @brief Changes the recommended authentication method for this set 80 | * of credentials. 81 | * @param authOption The recommended authentication option that we should use 82 | * while authenticating the client using the current set of credentials. 83 | */ 84 | void setRecommendedAuthOption(RecommendedAuthenticationMethod authOption); 85 | }; 86 | } // namespace cpp 87 | } // namespace jed_utils 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/cpp/example/send-mail.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace email = jed_utils::cpp; 8 | 9 | static const std::string kSenderAddress = "changeme@gmail.com"; 10 | static const std::string kPassword = "changeme"; // This will be your access token 11 | 12 | template 13 | class ClientWrapper { 14 | public: 15 | ClientWrapper(T client, 16 | email::Credential&& credentials) : mClient{std::move(client)} { 17 | mClient.setCredentials(credentials); 18 | } 19 | 20 | auto client() -> T& { return mClient; } 21 | 22 | private: 23 | email::OpportunisticSecureSMTPClient mClient; 24 | }; 25 | 26 | auto buildClient() -> email::OpportunisticSecureSMTPClient& { 27 | static ClientWrapper clientWrapper { 28 | email::OpportunisticSecureSMTPClient{"smtp.gmail.com", 587}, 29 | email::Credential(kSenderAddress, kPassword, 30 | jed_utils::RecommendedAuthenticationMethod::kXOauth2) 31 | }; 32 | 33 | return clientWrapper.client(); 34 | } 35 | 36 | void sendTextEmail() { 37 | using email::PlaintextMessage; 38 | using email::MessageAddress; 39 | 40 | auto& client = buildClient(); 41 | 42 | PlaintextMessage msg(MessageAddress(kSenderAddress, "Test Address Display"), 43 | { MessageAddress("radu.cosnita@gmail.com", "Another Address display") }, 44 | "This is a test (Subject)", 45 | "Hello\nHow are you?"); 46 | 47 | int err_no = client.sendMail(msg); 48 | if (err_no != 0) { 49 | std::cerr << client.getCommunicationLog() << '\n'; 50 | std::string errorMessage = client.getErrorMessage(err_no); 51 | std::stringstream err{}; 52 | err << "An error occurred: " << errorMessage << " (error no: " << err_no << ")"; 53 | throw std::invalid_argument{err.str()}; 54 | } 55 | 56 | std::cout << client.getCommunicationLog() << '\n'; 57 | std::cout << "Operation completed!" << std::endl; 58 | } 59 | 60 | void generateTestFile() { 61 | std::ofstream out{"/tmp/test.txt"}; 62 | out << "Generated content" << std::endl; 63 | out.flush(); 64 | out.close(); 65 | } 66 | 67 | void sendAttachmentEmail() { 68 | using email::MessageAddress; 69 | using email::Attachment; 70 | using email::PlaintextMessage; 71 | 72 | generateTestFile(); 73 | 74 | const auto to = MessageAddress(kSenderAddress); 75 | const auto& from = to; 76 | const auto subject = "This is an email with an attachment"; 77 | const auto body = "See attachment"; 78 | const auto attachments = { 79 | Attachment{"/tmp/test.txt", "test.txt", "test.txt"}, 80 | }; 81 | 82 | PlaintextMessage msg{ 83 | from, { to }, subject, body, {}, {}, attachments, 84 | }; 85 | 86 | auto& client = buildClient(); 87 | 88 | int err_no = client.sendMail(msg); 89 | if (err_no != 0) { 90 | std::cerr << client.getCommunicationLog() << '\n'; 91 | std::string errorMessage = client.getErrorMessage(err_no); 92 | std::stringstream err{}; 93 | err << "An error occurred: " << errorMessage << " (error no: " << err_no << ")"; 94 | throw std::invalid_argument{err.str()}; 95 | } 96 | 97 | std::cout << client.getCommunicationLog() << '\n'; 98 | std::cout << "Operation completed!" << std::endl; 99 | } 100 | 101 | void sendHTMLEmail() { 102 | using email::MessageAddress; 103 | using email::Attachment; 104 | using email::HTMLMessage; 105 | 106 | const auto to = MessageAddress(kSenderAddress); 107 | const auto& from = to; 108 | const auto subject = "This is an email with an attachment"; 109 | const auto body = "

This is a html formatted email

"; 110 | const auto attachments = { 111 | Attachment{"/tmp/test.txt", "test.txt", "test.txt"}, 112 | }; 113 | 114 | HTMLMessage msg(from, { to }, subject, body, {}, {}, attachments); 115 | 116 | auto& client = buildClient(); 117 | 118 | int err_no = client.sendMail(msg); 119 | if (err_no != 0) { 120 | std::cerr << client.getCommunicationLog() << '\n'; 121 | std::string errorMessage = client.getErrorMessage(err_no); 122 | std::stringstream err{}; 123 | err << "An error occurred: " << errorMessage << " (error no: " << err_no << ")"; 124 | throw std::invalid_argument{err.str()}; 125 | } 126 | 127 | std::cout << client.getCommunicationLog() << '\n'; 128 | std::cout << "Operation completed!" << std::endl; 129 | } 130 | 131 | auto main() -> int { 132 | sendTextEmail(); 133 | sendAttachmentEmail(); 134 | sendHTMLEmail(); 135 | return 0; 136 | } 137 | -------------------------------------------------------------------------------- /src/cpp/forcedsecuresmtpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "forcedsecuresmtpclient.hpp" 2 | #include 3 | 4 | using namespace jed_utils::cpp; 5 | 6 | ForcedSecureSMTPClient::ForcedSecureSMTPClient(const std::string &pServerName, unsigned int pPort) 7 | : jed_utils::ForcedSecureSMTPClient(pServerName.c_str(), pPort) { 8 | } 9 | 10 | std::string ForcedSecureSMTPClient::getServerName() const { 11 | return jed_utils::ForcedSecureSMTPClient::getServerName(); 12 | } 13 | 14 | unsigned int ForcedSecureSMTPClient::getServerPort() const { 15 | return jed_utils::ForcedSecureSMTPClient::getServerPort(); 16 | } 17 | 18 | bool ForcedSecureSMTPClient::getBatchMode() const { 19 | return jed_utils::ForcedSecureSMTPClient::getBatchMode(); 20 | } 21 | 22 | unsigned int ForcedSecureSMTPClient::getCommandTimeout() const { 23 | return jed_utils::SMTPClientBase::getCommandTimeout(); 24 | } 25 | 26 | std::string ForcedSecureSMTPClient::getCommunicationLog() const { 27 | return jed_utils::ForcedSecureSMTPClient::getCommunicationLog(); 28 | } 29 | 30 | const Credential *ForcedSecureSMTPClient::getCredentials() const { 31 | return mCredential; 32 | } 33 | 34 | bool ForcedSecureSMTPClient::getAcceptSelfSignedCert() const { 35 | return jed_utils::SecureSMTPClientBase::getAcceptSelfSignedCert(); 36 | } 37 | 38 | void ForcedSecureSMTPClient::setServerName(const std::string &pServerName) { 39 | jed_utils::ForcedSecureSMTPClient::setServerName(pServerName.c_str()); 40 | } 41 | 42 | void ForcedSecureSMTPClient::setServerPort(unsigned int pPort) { 43 | jed_utils::ForcedSecureSMTPClient::setServerPort(pPort); 44 | } 45 | 46 | void ForcedSecureSMTPClient::setBatchMode(bool pEnabled) { 47 | jed_utils::ForcedSecureSMTPClient::setBatchMode(pEnabled); 48 | } 49 | 50 | void ForcedSecureSMTPClient::setCommandTimeout(unsigned int pTimeOutInSeconds) { 51 | jed_utils::SMTPClientBase::setCommandTimeout(pTimeOutInSeconds); 52 | } 53 | 54 | void ForcedSecureSMTPClient::setCredentials(const Credential &pCredential) { 55 | jed_utils::SMTPClientBase::setCredentials(jed_utils::Credential(pCredential.getUsername().c_str(), 56 | pCredential.getPassword().c_str(), 57 | pCredential.getRecommendedAuthOption())); 58 | delete mCredential; 59 | mCredential = new Credential(pCredential); 60 | } 61 | 62 | void ForcedSecureSMTPClient::setAcceptSelfSignedCert(bool pValue) { 63 | jed_utils::SecureSMTPClientBase::setAcceptSelfSignedCert(pValue); 64 | } 65 | 66 | void ForcedSecureSMTPClient::setKeepUsingBaseSendCommands(bool pValue) { 67 | jed_utils::SMTPClientBase::setKeepUsingBaseSendCommands(pValue); 68 | } 69 | 70 | std::string ForcedSecureSMTPClient::getErrorMessage(int errorCode) { 71 | return jed_utils::SMTPClientBase::getErrorMessage(errorCode); 72 | } 73 | 74 | int ForcedSecureSMTPClient::getErrorMessage_r(int errorCode, 75 | std::string &errorMessage) { 76 | const size_t MAXSIZE = 1024; 77 | char *errorMsg = new char[MAXSIZE]; 78 | int result = jed_utils::SMTPClientBase::getErrorMessage_r(errorCode, errorMsg, MAXSIZE); 79 | errorMessage = errorMsg; 80 | return result; 81 | } 82 | 83 | int ForcedSecureSMTPClient::extractReturnCode(const std::string &pOutput) { 84 | return jed_utils::SMTPClientBase::extractReturnCode(pOutput.c_str()); 85 | } 86 | 87 | jed_utils::ServerAuthOptions *ForcedSecureSMTPClient::extractAuthenticationOptions(const std::string &pEhloOutput) { 88 | return jed_utils::SMTPClientBase::extractAuthenticationOptions(pEhloOutput.c_str()); 89 | } 90 | 91 | int ForcedSecureSMTPClient::sendMail(const jed_utils::Message &pMsg) { 92 | return jed_utils::ForcedSecureSMTPClient::sendMail(pMsg); 93 | } 94 | -------------------------------------------------------------------------------- /src/cpp/forcedsecuresmtpclient.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPFORCEDSECURESMTPCLIENT_H 2 | #define CPPFORCEDSECURESMTPCLIENT_H 3 | 4 | #include "credential.hpp" 5 | #include "../forcedsecuresmtpclient.h" 6 | 7 | #ifdef _WIN32 8 | #ifdef SMTPCLIENT_STATIC 9 | #define CPP_FORCEDSECURESMTPCLIENT_API 10 | #elif defined(SMTPCLIENT_EXPORTS) 11 | #define CPP_FORCEDSECURESMTPCLIENT_API __declspec(dllexport) 12 | #else 13 | #define CPP_FORCEDSECURESMTPCLIENT_API __declspec(dllimport) 14 | #endif 15 | #else 16 | #define CPP_FORCEDSECURESMTPCLIENT_API 17 | #endif 18 | 19 | namespace jed_utils { 20 | namespace cpp { 21 | /** @brief The ForcedSecureSMTPClient should be your default choice for 22 | * communicating with modern SMTP servers. The communication is usually done 23 | * via port 587. 24 | */ 25 | class CPP_FORCEDSECURESMTPCLIENT_API ForcedSecureSMTPClient : private jed_utils::ForcedSecureSMTPClient { 26 | public: 27 | /** 28 | * @brief Construct a new ForcedSecureSMTPClient. 29 | * @param pServerName The name of the server. 30 | * Example: smtp.domainexample.com 31 | * @param pPort The server port number. 32 | * Example: 25, 465, 587 33 | */ 34 | ForcedSecureSMTPClient(const std::string &pServerName, unsigned int pPort); 35 | 36 | /** Destructor of the ForcedSecureSMTPClient. */ 37 | ~ForcedSecureSMTPClient() override = default; 38 | 39 | /** ForcedSecureSMTPClient copy constructor. */ 40 | ForcedSecureSMTPClient(const ForcedSecureSMTPClient& other) = default; 41 | 42 | /** ForcedSecureSMTPClient copy assignment operator. */ 43 | ForcedSecureSMTPClient& operator=(const ForcedSecureSMTPClient& other) = default; 44 | 45 | /** ForcedSecureSMTPClient move constructor. */ 46 | ForcedSecureSMTPClient(ForcedSecureSMTPClient&& other) noexcept = default; 47 | 48 | /** ForcedSecureSMTPClient move assignment operator. */ 49 | ForcedSecureSMTPClient& operator=(ForcedSecureSMTPClient&& other) noexcept = default; 50 | 51 | /** Return the server name. */ 52 | std::string getServerName() const; 53 | 54 | /** Return the server port number. */ 55 | unsigned int getServerPort() const; 56 | 57 | /** Return the batch mode enable flag. */ 58 | bool getBatchMode() const; 59 | 60 | /** Return the command timeout in seconds. */ 61 | unsigned int getCommandTimeout() const; 62 | 63 | /** Return the communication log produced by the sendMail method. */ 64 | std::string getCommunicationLog() const; 65 | 66 | /** Return the credentials configured. */ 67 | const Credential *getCredentials() const; 68 | 69 | /** Return the accept self certificate flag. */ 70 | bool getAcceptSelfSignedCert() const; 71 | 72 | /** 73 | * @brief Set the server name. 74 | * @param pServerName A std::string of the server name. 75 | * Example: smtp.domainexample.com 76 | */ 77 | void setServerName(const std::string &pServerName); 78 | 79 | /** 80 | * @brief Set the server port number. 81 | * @param pPort The port number. 82 | * Example: 25, 465, 587 83 | */ 84 | void setServerPort(unsigned int pPort); 85 | 86 | /** 87 | * @brief Set the batch mode flag. 88 | * @param pEnabled Indicate if the batch mode is enabled. 89 | * Default: false 90 | */ 91 | void setBatchMode(bool pEnabled); 92 | 93 | /** 94 | * @brief Set the command timeout in seconds. 95 | * @param pTimeOutInSeconds The timeout in seconds. 96 | * Default: 3 seconds 97 | */ 98 | void setCommandTimeout(unsigned int pTimeOutInSeconds); 99 | 100 | /** 101 | * @brief Set the credentials. 102 | * @param pCredential The credential containing the username and the password. 103 | */ 104 | void setCredentials(const Credential &pCredential); 105 | 106 | /** 107 | * @brief Set the accept self signed certificate 108 | * @param pValue Indicate if self signed certificate is accepted. 109 | * Default: false 110 | */ 111 | void setAcceptSelfSignedCert(bool pValue); 112 | 113 | /** 114 | * @brief Indicate if the class will keep using base send command even 115 | * if a child class as overriden the sendCommand and sendCommandWithFeedback. 116 | * 117 | * This is used for example if you are using a secure client class but 118 | * the STARTTLS feature is not available. The communication will then 119 | * remain unsecured. 120 | * @param pValue True to enable this behavior, false for the default 121 | */ 122 | void setKeepUsingBaseSendCommands(bool pValue); 123 | 124 | /** 125 | * @brief Retreive the error message string that correspond to 126 | * the error code provided. 127 | * @return A std::string containing the error message. 128 | */ 129 | static std::string getErrorMessage(int errorCode); 130 | 131 | /** 132 | * @brief This is the reentrant version of the getErrorMessage method 133 | * @param errorCode The error code return by the SMTP client. 134 | * @param errorMessagePtr A pointer to an allocated char array 135 | * @param maxLength The size of the allocated char array. 136 | * @return Return 0 for success, -1 if an error occurred and a positive 137 | * number representing the number of characters copied to errorMessagePtr 138 | * if the message was longer than that allocated char array. 139 | * 140 | * Retreive the error message string that correspond to the error code 141 | * provided. 142 | */ 143 | static int getErrorMessage_r(int errorCode, 144 | std::string &errorMessage); 145 | 146 | int sendMail(const jed_utils::Message &pMsg); 147 | 148 | protected: 149 | static int extractReturnCode(const std::string &pOutput); 150 | static jed_utils::ServerAuthOptions *extractAuthenticationOptions(const std::string &pEhloOutput); 151 | 152 | private: 153 | Credential* mCredential = nullptr; 154 | }; 155 | } // namespace cpp 156 | } // namespace jed_utils 157 | 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /src/cpp/htmlmessage.cpp: -------------------------------------------------------------------------------- 1 | #include "htmlmessage.hpp" 2 | #include 3 | #include 4 | #include 5 | #include "message.hpp" 6 | #include "../messageaddress.h" 7 | 8 | using namespace jed_utils::cpp; 9 | 10 | HTMLMessage::HTMLMessage(const MessageAddress &pFrom, 11 | const std::vector &pTo, 12 | const std::string &pSubject, 13 | const std::string &pBody, 14 | const std::vector &pCc, 15 | const std::vector &pBcc, 16 | const std::vector &pAttachments) 17 | : Message(pFrom, 18 | pTo, 19 | pSubject, 20 | pBody, 21 | pCc, 22 | pBcc, 23 | pAttachments) { 24 | } 25 | 26 | std::string HTMLMessage::getMimeType() const { 27 | return "text/html"; 28 | } 29 | 30 | HTMLMessage::operator jed_utils::HTMLMessage() const { 31 | const auto to = getStdMessageAddressVec(getTo()); 32 | const auto cc = getStdMessageAddressVec(getCc()); 33 | const auto bcc = getStdMessageAddressVec(getBcc()); 34 | const auto att = getStdAttachmentVec(getAttachments()); 35 | const jed_utils::HTMLMessage msg (getFrom().toStdMessageAddress(), 36 | to.data(), 37 | to.size(), 38 | getSubject().c_str(), 39 | getBody().c_str(), 40 | cc.data(), 41 | cc.size(), 42 | bcc.data(), 43 | bcc.size(), 44 | att.data(), 45 | att.size()); 46 | return msg; 47 | } 48 | -------------------------------------------------------------------------------- /src/cpp/htmlmessage.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPHTMLMESSAGE_H 2 | #define CPPHTMLMESSAGE_H 3 | 4 | #include 5 | #include 6 | #include "attachment.hpp" 7 | #include "message.hpp" 8 | #include "../htmlmessage.h" 9 | #include "messageaddress.hpp" 10 | 11 | #ifdef _WIN32 12 | #ifdef SMTPCLIENT_STATIC 13 | #define CPP_HTMLMESSAGE_API 14 | #elif defined(SMTPCLIENT_EXPORTS) 15 | #define CPP_HTMLMESSAGE_API __declspec(dllexport) 16 | #else 17 | #define CPP_HTMLMESSAGE_API __declspec(dllimport) 18 | #endif 19 | #else 20 | #define CPP_HTMLMESSAGE_API 21 | #endif 22 | 23 | namespace jed_utils { 24 | namespace cpp { 25 | /** @brief The Message class represents the base class of an email message. */ 26 | class CPP_HTMLMESSAGE_API HTMLMessage : public Message { 27 | public: 28 | /** 29 | * @brief Construct a new single recipient HTMLMessage class. 30 | * @param pFrom The sender email address of the message. 31 | * @param pTo The recipient email addresses of the message. 32 | * @param pSubject The subject of the message. 33 | * @param pBody The content of the message. 34 | * @param pCc The carbon-copy recipient email addresses. 35 | * @param pBcc The blind carbon-copy recipient email addresses. 36 | * @param pAttachments The attachments of the message 37 | */ 38 | HTMLMessage(const MessageAddress &pFrom, 39 | const std::vector &pTo, 40 | const std::string &pSubject, 41 | const std::string &pBody, 42 | const std::vector &pCc = {}, 43 | const std::vector &pBcc = {}, 44 | const std::vector &pAttachments = {}); 45 | 46 | /** The destructor of the Message */ 47 | virtual ~HTMLMessage() = default; 48 | 49 | /** Return the string MIME type of the message (Pure virtual function). */ 50 | std::string getMimeType() const override; 51 | 52 | operator jed_utils::HTMLMessage() const; 53 | }; 54 | } // namespace cpp 55 | } // namespace jed_utils 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/cpp/message.cpp: -------------------------------------------------------------------------------- 1 | #include "message.hpp" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace jed_utils::cpp; 7 | 8 | Message::Message(const MessageAddress &pFrom, 9 | const std::vector &pTo, 10 | const std::string &pSubject, 11 | const std::string &pBody, 12 | const std::vector &pCc, 13 | const std::vector &pBcc, 14 | const std::vector &pAttachments) 15 | : mFrom(pFrom), 16 | mTo(pTo), 17 | mSubject(pSubject), 18 | mBody(pBody), 19 | mCc(pCc), 20 | mBcc(pBcc), 21 | mAttachments(pAttachments) { 22 | if (pTo.empty()) { 23 | throw std::invalid_argument("To cannot be empty"); 24 | } 25 | } 26 | 27 | const MessageAddress &Message::getFrom() const { 28 | return mFrom; 29 | } 30 | 31 | const std::vector &Message::getTo() const { 32 | return mTo; 33 | } 34 | 35 | size_t Message::getToCount() const { 36 | return mTo.size(); 37 | } 38 | 39 | std::string Message::getSubject() const { 40 | return mSubject; 41 | } 42 | 43 | std::string Message::getBody() const { 44 | return mBody; 45 | } 46 | 47 | const std::vector &Message::getCc() const { 48 | return mCc; 49 | } 50 | 51 | size_t Message::getCcCount() const { 52 | return mCc.size(); 53 | } 54 | 55 | const std::vector &Message::getBcc() const { 56 | return mBcc; 57 | } 58 | 59 | size_t Message::getBccCount() const { 60 | return mBcc.size(); 61 | } 62 | 63 | const std::vector &Message::getAttachments() const { 64 | return mAttachments; 65 | } 66 | 67 | size_t Message::getAttachmentsCount() const { 68 | return mAttachments.size(); 69 | } 70 | 71 | std::vector Message::getStdMessageAddressVec(const std::vector &src) const { 72 | std::vector retval = {}; 73 | std::transform(src.cbegin(), 74 | src.cend(), 75 | std::back_inserter(retval), 76 | [](const auto &recipient) { return recipient.toStdMessageAddress(); }); 77 | return retval; 78 | } 79 | 80 | std::vector Message::getStdAttachmentVec(const std::vector &src) const { 81 | std::vector retval = {}; 82 | std::transform(src.cbegin(), 83 | src.cend(), 84 | std::back_inserter(retval), 85 | [](const auto &recipient) { return recipient.toStdAttachment(); }); 86 | return retval; 87 | } 88 | -------------------------------------------------------------------------------- /src/cpp/message.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPMESSAGE_H 2 | #define CPPMESSAGE_H 3 | 4 | #include 5 | #include 6 | #include "attachment.hpp" 7 | #include "../message.h" 8 | #include "messageaddress.hpp" 9 | 10 | #ifdef _WIN32 11 | #pragma warning(disable: 4251) 12 | #ifdef SMTPCLIENT_STATIC 13 | #define CPP_MESSAGE_API 14 | #elif defined(SMTPCLIENT_EXPORTS) 15 | #define CPP_MESSAGE_API __declspec(dllexport) 16 | #else 17 | #define CPP_MESSAGE_API __declspec(dllimport) 18 | #endif 19 | #else 20 | #define CPP_MESSAGE_API 21 | #endif 22 | 23 | namespace jed_utils { 24 | namespace cpp { 25 | /** @brief The Message class represents the base class of an email message. */ 26 | class CPP_MESSAGE_API Message { 27 | public: 28 | /** 29 | * @brief Construct a new single recipient Message base class. 30 | * @param pFrom The sender email address of the message. 31 | * @param pTo The recipient email addresses of the message. 32 | * @param pSubject The subject of the message. 33 | * @param pBody The content of the message. 34 | * @param pCc The carbon-copy recipient email addresses. 35 | * @param pBcc The blind carbon-copy recipient email addresses. 36 | * @param pAttachments The attachments of the message 37 | */ 38 | Message(const MessageAddress &pFrom, 39 | const std::vector &pTo, 40 | const std::string &pSubject, 41 | const std::string &pBody, 42 | const std::vector &pCc = {}, 43 | const std::vector &pBcc = {}, 44 | const std::vector &pAttachments = {}); 45 | 46 | /** The destructor of the Message */ 47 | virtual ~Message() = default; 48 | 49 | /** Return the string MIME type of the message (Pure virtual function). */ 50 | virtual std::string getMimeType() const = 0; 51 | 52 | /** Return the Sender MessageAddress of the message. */ 53 | const MessageAddress &getFrom() const; 54 | 55 | /** Return the recipient MessageAddress vector of the message */ 56 | const std::vector &getTo() const; 57 | 58 | /** Return the number message recipients in the vector. */ 59 | size_t getToCount() const; 60 | 61 | /** Return the subject of the message. */ 62 | std::string getSubject() const; 63 | 64 | /** Return the body of the message. */ 65 | std::string getBody() const; 66 | 67 | /** Return the carbon-copy recipient MessageAddress vector of the message */ 68 | const std::vector &getCc() const; 69 | 70 | /** Return the number of message carbon-copy recipients in the vector. */ 71 | size_t getCcCount() const; 72 | 73 | /** Return the blind carbon-copy recipient MessageAddress vector of the message */ 74 | const std::vector &getBcc() const; 75 | 76 | /** Return the number of message blind carbon-copy recipients in the vector. */ 77 | size_t getBccCount() const; 78 | 79 | /** Return the attachment vector of the message */ 80 | const std::vector &getAttachments() const; 81 | 82 | /** Return the number of message attachments in the vector. */ 83 | size_t getAttachmentsCount() const; 84 | 85 | protected: 86 | std::vector getStdMessageAddressVec(const std::vector &src) const; 87 | std::vector getStdAttachmentVec(const std::vector &src) const; 88 | 89 | private: 90 | MessageAddress mFrom; 91 | std::vector mTo; 92 | std::string mSubject; 93 | std::string mBody; 94 | std::vector mCc; 95 | std::vector mBcc; 96 | std::vector mAttachments; 97 | }; 98 | } // namespace cpp 99 | } // namespace jed_utils 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /src/cpp/messageaddress.cpp: -------------------------------------------------------------------------------- 1 | #include "messageaddress.hpp" 2 | #include 3 | 4 | using namespace jed_utils::cpp; 5 | 6 | MessageAddress::MessageAddress(const std::string &pEmailAddress, 7 | const std::string &pDisplayName) 8 | : jed_utils::MessageAddress(pEmailAddress.c_str(), pDisplayName.c_str()) { 9 | } 10 | 11 | MessageAddress::operator std::string() const { 12 | return jed_utils::MessageAddress::operator std::string(); 13 | } 14 | 15 | std::string MessageAddress::getEmailAddress() const { 16 | return jed_utils::MessageAddress::getEmailAddress(); 17 | } 18 | 19 | std::string MessageAddress::getDisplayName() const { 20 | return jed_utils::MessageAddress::getDisplayName(); 21 | } 22 | 23 | jed_utils::MessageAddress MessageAddress::toStdMessageAddress() const { 24 | return jed_utils::MessageAddress(jed_utils::MessageAddress::getEmailAddress(), 25 | jed_utils::MessageAddress::getDisplayName()); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/cpp/messageaddress.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPMESSAGEADDRESS_H 2 | #define CPPMESSAGEADDRESS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "../messageaddress.h" 9 | 10 | #ifdef _WIN32 11 | #ifdef SMTPCLIENT_STATIC 12 | #define CPP_MESSAGEADDRESS_API 13 | #elif defined(SMTPCLIENT_EXPORTS) 14 | #define CPP_MESSAGEADDRESS_API __declspec(dllexport) 15 | #else 16 | #define CPP_MESSAGEADDRESS_API __declspec(dllimport) 17 | #endif 18 | #else 19 | #define CPP_MESSAGEADDRESS_API 20 | #endif 21 | 22 | namespace jed_utils { 23 | namespace cpp { 24 | /** @brief The MessageAddress class represents a sender or a recipient address containing 25 | * the email address and the display name. 26 | * Example : joeblow@domainexample.com Joe Blow 27 | */ 28 | class CPP_MESSAGEADDRESS_API MessageAddress : private jed_utils::MessageAddress { 29 | public: 30 | /** 31 | * @brief Construct a new MessageAddress. 32 | * @param pEmailAddress The email address. The prefix appears to the left of the @ symbol. 33 | * The domain appears to the right of the @ symbol. 34 | * @param pDisplayName The display name of the address that will appear in 35 | * the message. Example : Joe Blow 36 | */ 37 | explicit MessageAddress(const std::string &pEmailAddress, 38 | const std::string &pDisplayName = ""); 39 | 40 | /** Destructor of the MessageAddress */ 41 | ~MessageAddress() override = default; 42 | 43 | /** MessageAddress copy constructor. */ 44 | MessageAddress(const MessageAddress& other) = default; 45 | 46 | /** MessageAddress copy assignment operator. */ 47 | MessageAddress& operator=(const MessageAddress& other) = default; 48 | 49 | /** MessageAddress move constructor. */ 50 | MessageAddress(MessageAddress&& other) noexcept = default; 51 | 52 | /** MessageAddress move assignment operator. */ 53 | MessageAddress& operator=(MessageAddress&& other) noexcept = default; 54 | 55 | /** MessageAddress equality operator. */ 56 | bool operator==(const MessageAddress &pMsgComp) const; 57 | 58 | /** MessageAddress implicit string conversion operator. */ 59 | explicit operator std::string() const; 60 | 61 | /** Return the email address. */ 62 | std::string getEmailAddress() const; 63 | 64 | /** Return the display name. */ 65 | std::string getDisplayName() const; 66 | 67 | jed_utils::MessageAddress toStdMessageAddress() const; 68 | 69 | friend class Message; 70 | 71 | private: 72 | MessageAddress(); 73 | }; 74 | } // namespace cpp 75 | } // namespace jed_utils 76 | 77 | #endif 78 | 79 | -------------------------------------------------------------------------------- /src/cpp/opportunisticsecuresmtpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "opportunisticsecuresmtpclient.hpp" 2 | #include 3 | 4 | using namespace jed_utils::cpp; 5 | 6 | OpportunisticSecureSMTPClient::OpportunisticSecureSMTPClient(const std::string &pServerName, unsigned int pPort) 7 | : jed_utils::OpportunisticSecureSMTPClient(pServerName.c_str(), pPort) { 8 | } 9 | 10 | std::string OpportunisticSecureSMTPClient::getServerName() const { 11 | return jed_utils::OpportunisticSecureSMTPClient::getServerName(); 12 | } 13 | 14 | unsigned int OpportunisticSecureSMTPClient::getServerPort() const { 15 | return jed_utils::OpportunisticSecureSMTPClient::getServerPort(); 16 | } 17 | 18 | bool OpportunisticSecureSMTPClient::getBatchMode() const { 19 | return jed_utils::OpportunisticSecureSMTPClient::getBatchMode(); 20 | } 21 | 22 | unsigned int OpportunisticSecureSMTPClient::getCommandTimeout() const { 23 | return jed_utils::SMTPClientBase::getCommandTimeout(); 24 | } 25 | 26 | std::string OpportunisticSecureSMTPClient::getCommunicationLog() const { 27 | return jed_utils::OpportunisticSecureSMTPClient::getCommunicationLog(); 28 | } 29 | 30 | const Credential *OpportunisticSecureSMTPClient::getCredentials() const { 31 | return mCredential; 32 | } 33 | 34 | bool OpportunisticSecureSMTPClient::getAcceptSelfSignedCert() const { 35 | return jed_utils::SecureSMTPClientBase::getAcceptSelfSignedCert(); 36 | } 37 | 38 | void OpportunisticSecureSMTPClient::setServerName(const std::string &pServerName) { 39 | jed_utils::OpportunisticSecureSMTPClient::setServerName(pServerName.c_str()); 40 | } 41 | 42 | void OpportunisticSecureSMTPClient::setServerPort(unsigned int pPort) { 43 | jed_utils::OpportunisticSecureSMTPClient::setServerPort(pPort); 44 | } 45 | 46 | void OpportunisticSecureSMTPClient::setBatchMode(bool pEnabled) { 47 | jed_utils::OpportunisticSecureSMTPClient::setBatchMode(pEnabled); 48 | } 49 | 50 | void OpportunisticSecureSMTPClient::setCommandTimeout(unsigned int pTimeOutInSeconds) { 51 | jed_utils::SMTPClientBase::setCommandTimeout(pTimeOutInSeconds); 52 | } 53 | 54 | void OpportunisticSecureSMTPClient::setCredentials(const Credential &pCredential) { 55 | jed_utils::SMTPClientBase::setCredentials(jed_utils::Credential(pCredential.getUsername().c_str(), 56 | pCredential.getPassword().c_str(), 57 | pCredential.getRecommendedAuthOption())); 58 | delete mCredential; 59 | mCredential = new Credential(pCredential); 60 | } 61 | 62 | void OpportunisticSecureSMTPClient::setAcceptSelfSignedCert(bool pValue) { 63 | jed_utils::SecureSMTPClientBase::setAcceptSelfSignedCert(pValue); 64 | } 65 | 66 | void OpportunisticSecureSMTPClient::setKeepUsingBaseSendCommands(bool pValue) { 67 | jed_utils::SMTPClientBase::setKeepUsingBaseSendCommands(pValue); 68 | } 69 | 70 | std::string OpportunisticSecureSMTPClient::getErrorMessage(int errorCode) { 71 | return jed_utils::SMTPClientBase::getErrorMessage(errorCode); 72 | } 73 | 74 | int OpportunisticSecureSMTPClient::getErrorMessage_r(int errorCode, 75 | std::string &errorMessage) { 76 | const size_t MAXSIZE = 1024; 77 | char *errorMsg = new char[MAXSIZE]; 78 | int result = jed_utils::SMTPClientBase::getErrorMessage_r(errorCode, errorMsg, MAXSIZE); 79 | errorMessage = errorMsg; 80 | return result; 81 | } 82 | 83 | int OpportunisticSecureSMTPClient::extractReturnCode(const std::string &pOutput) { 84 | return jed_utils::SMTPClientBase::extractReturnCode(pOutput.c_str()); 85 | } 86 | 87 | jed_utils::ServerAuthOptions *OpportunisticSecureSMTPClient::extractAuthenticationOptions(const std::string &pEhloOutput) { 88 | return jed_utils::SMTPClientBase::extractAuthenticationOptions(pEhloOutput.c_str()); 89 | } 90 | 91 | int OpportunisticSecureSMTPClient::sendMail(const jed_utils::Message &pMsg) { 92 | return jed_utils::OpportunisticSecureSMTPClient::sendMail(pMsg); 93 | } 94 | -------------------------------------------------------------------------------- /src/cpp/opportunisticsecuresmtpclient.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPOPPORTUNISTICSECURESMTPCLIENT_H 2 | #define CPPOPPORTUNISTICSECURESMTPCLIENT_H 3 | 4 | #include "credential.hpp" 5 | #include "../opportunisticsecuresmtpclient.h" 6 | 7 | #ifdef _WIN32 8 | #ifdef SMTPCLIENT_STATIC 9 | #define CPP_OPPORTUNISTICSECURESMTPCLIENT_API 10 | #elif defined(SMTPCLIENT_EXPORTS) 11 | #define CPP_OPPORTUNISTICSECURESMTPCLIENT_API __declspec(dllexport) 12 | #else 13 | #define CPP_OPPORTUNISTICSECURESMTPCLIENT_API __declspec(dllimport) 14 | #endif 15 | #else 16 | #define CPP_OPPORTUNISTICSECURESMTPCLIENT_API 17 | #endif 18 | 19 | namespace jed_utils { 20 | namespace cpp { 21 | /** @brief The OpportunisticSecureSMTPClient should be your default choice for 22 | * communicating with modern SMTP servers. The communication is usually done 23 | * via port 587. 24 | */ 25 | class CPP_OPPORTUNISTICSECURESMTPCLIENT_API OpportunisticSecureSMTPClient : private jed_utils::OpportunisticSecureSMTPClient { 26 | public: 27 | /** 28 | * @brief Construct a new OpportunisticSecureSMTPClient. 29 | * @param pServerName The name of the server. 30 | * Example: smtp.domainexample.com 31 | * @param pPort The server port number. 32 | * Example: 25, 465, 587 33 | */ 34 | OpportunisticSecureSMTPClient(const std::string &pServerName, unsigned int pPort); 35 | 36 | /** Destructor of the OpportunisticSecureSMTPClient. */ 37 | ~OpportunisticSecureSMTPClient() override = default; 38 | 39 | /** OpportunisticSecureSMTPClient copy constructor. */ 40 | OpportunisticSecureSMTPClient(const OpportunisticSecureSMTPClient& other) = default; 41 | 42 | /** OpportunisticSecureSMTPClient copy assignment operator. */ 43 | OpportunisticSecureSMTPClient& operator=(const OpportunisticSecureSMTPClient& other) = default; 44 | 45 | /** OpportunisticSecureSMTPClient move constructor. */ 46 | OpportunisticSecureSMTPClient(OpportunisticSecureSMTPClient&& other) noexcept = default; 47 | 48 | /** OpportunisticSecureSMTPClient move assignment operator. */ 49 | OpportunisticSecureSMTPClient& operator=(OpportunisticSecureSMTPClient&& other) noexcept = default; 50 | 51 | /** Return the server name. */ 52 | std::string getServerName() const; 53 | 54 | /** Return the server port number. */ 55 | unsigned int getServerPort() const; 56 | 57 | /** Return the batch mode enable flag. */ 58 | bool getBatchMode() const; 59 | 60 | /** Return the command timeout in seconds. */ 61 | unsigned int getCommandTimeout() const; 62 | 63 | /** Return the communication log produced by the sendMail method. */ 64 | std::string getCommunicationLog() const; 65 | 66 | /** Return the credentials configured. */ 67 | const Credential *getCredentials() const; 68 | 69 | /** Return the accept self certificate flag. */ 70 | bool getAcceptSelfSignedCert() const; 71 | 72 | /** 73 | * @brief Set the server name. 74 | * @param pServerName A std::string of the server name. 75 | * Example: smtp.domainexample.com 76 | */ 77 | void setServerName(const std::string &pServerName); 78 | 79 | /** 80 | * @brief Set the server port number. 81 | * @param pPort The port number. 82 | * Example: 25, 465, 587 83 | */ 84 | void setServerPort(unsigned int pPort); 85 | 86 | /** 87 | * @brief Set the batch mode flag. 88 | * @param pEnabled Indicate if the batch mode is enabled. 89 | * Default: false 90 | */ 91 | void setBatchMode(bool pEnabled); 92 | 93 | /** 94 | * @brief Set the command timeout in seconds. 95 | * @param pTimeOutInSeconds The timeout in seconds. 96 | * Default: 3 seconds 97 | */ 98 | void setCommandTimeout(unsigned int pTimeOutInSeconds); 99 | 100 | /** 101 | * @brief Set the credentials. 102 | * @param pCredential The credential containing the username and the password. 103 | */ 104 | void setCredentials(const Credential &pCredential); 105 | 106 | /** 107 | * @brief Set the accept self signed certificate 108 | * @param pValue Indicate if self signed certificate is accepted. 109 | * Default: false 110 | */ 111 | void setAcceptSelfSignedCert(bool pValue); 112 | 113 | /** 114 | * @brief Indicate if the class will keep using base send command even 115 | * if a child class as overriden the sendCommand and sendCommandWithFeedback. 116 | * 117 | * This is used for example if you are using a secure client class but 118 | * the STARTTLS feature is not available. The communication will then 119 | * remain unsecured. 120 | * @param pValue True to enable this behavior, false for the default 121 | */ 122 | void setKeepUsingBaseSendCommands(bool pValue); 123 | 124 | /** 125 | * @brief Retreive the error message string that correspond to 126 | * the error code provided. 127 | * @return A std::string containing the error message. 128 | */ 129 | static std::string getErrorMessage(int errorCode); 130 | 131 | /** 132 | * @brief This is the reentrant version of the getErrorMessage method 133 | * @param errorCode The error code return by the SMTP client. 134 | * @param errorMessagePtr A pointer to an allocated char array 135 | * @param maxLength The size of the allocated char array. 136 | * @return Return 0 for success, -1 if an error occurred and a positive 137 | * number representing the number of characters copied to errorMessagePtr 138 | * if the message was longer than that allocated char array. 139 | * 140 | * Retreive the error message string that correspond to the error code 141 | * provided. 142 | */ 143 | static int getErrorMessage_r(int errorCode, 144 | std::string &errorMessage); 145 | 146 | int sendMail(const jed_utils::Message &pMsg); 147 | 148 | protected: 149 | static int extractReturnCode(const std::string &pOutput); 150 | static jed_utils::ServerAuthOptions *extractAuthenticationOptions(const std::string &pEhloOutput); 151 | 152 | private: 153 | Credential* mCredential = nullptr; 154 | }; 155 | } // namespace cpp 156 | } // namespace jed_utils 157 | 158 | 159 | #endif 160 | -------------------------------------------------------------------------------- /src/cpp/plaintextmessage.cpp: -------------------------------------------------------------------------------- 1 | #include "plaintextmessage.hpp" 2 | #include "message.hpp" 3 | 4 | using namespace jed_utils::cpp; 5 | 6 | PlaintextMessage::PlaintextMessage(const MessageAddress &pFrom, 7 | const std::vector &pTo, 8 | const std::string &pSubject, 9 | const std::string &pBody, 10 | const std::vector &pCc, 11 | const std::vector &pBcc, 12 | const std::vector &pAttachments) 13 | : Message(pFrom, 14 | pTo, 15 | pSubject, 16 | pBody, 17 | pCc, 18 | pBcc, 19 | pAttachments) { 20 | } 21 | 22 | std::string PlaintextMessage::getMimeType() const { 23 | return "text/plain"; 24 | } 25 | 26 | PlaintextMessage::operator jed_utils::PlaintextMessage() const { 27 | const auto to = getStdMessageAddressVec(getTo()); 28 | const auto cc = getStdMessageAddressVec(getCc()); 29 | const auto bcc = getStdMessageAddressVec(getBcc()); 30 | const auto att = getStdAttachmentVec(getAttachments()); 31 | const jed_utils::PlaintextMessage msg (getFrom().toStdMessageAddress(), 32 | to.data(), 33 | to.size(), 34 | getSubject().c_str(), 35 | getBody().c_str(), 36 | cc.data(), 37 | cc.size(), 38 | bcc.data(), 39 | bcc.size(), 40 | att.data(), 41 | att.size()); 42 | return msg; 43 | } 44 | -------------------------------------------------------------------------------- /src/cpp/plaintextmessage.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPPLAINTEXTMESSAGE_H 2 | #define CPPPLAINTEXTMESSAGE_H 3 | 4 | #include 5 | #include 6 | #include "attachment.hpp" 7 | #include "message.hpp" 8 | #include "messageaddress.hpp" 9 | #include "../plaintextmessage.h" 10 | 11 | #ifdef _WIN32 12 | #ifdef SMTPCLIENT_STATIC 13 | #define CPP_PLAINTEXTMESSAGE_API 14 | #elif defined(SMTPCLIENT_EXPORTS) 15 | #define CPP_PLAINTEXTMESSAGE_API __declspec(dllexport) 16 | #else 17 | #define CPP_PLAINTEXTMESSAGE_API __declspec(dllimport) 18 | #endif 19 | #else 20 | #define CPP_PLAINTEXTMESSAGE_API 21 | #endif 22 | 23 | namespace jed_utils { 24 | namespace cpp { 25 | /** @brief The Message class represents the base class of an email message. */ 26 | class CPP_PLAINTEXTMESSAGE_API PlaintextMessage : public Message { 27 | public: 28 | /** 29 | * @brief Construct a new single recipient PlaintextMessage class. 30 | * @param pFrom The sender email address of the message. 31 | * @param pTo The recipient email addresses of the message. 32 | * @param pSubject The subject of the message. 33 | * @param pBody The content of the message. 34 | * @param pCc The carbon-copy recipient email addresses. 35 | * @param pBcc The blind carbon-copy recipient email addresses. 36 | * @param pAttachments The attachments of the message 37 | */ 38 | PlaintextMessage(const MessageAddress &pFrom, 39 | const std::vector &pTo, 40 | const std::string &pSubject, 41 | const std::string &pBody, 42 | const std::vector &pCc = {}, 43 | const std::vector &pBcc = {}, 44 | const std::vector &pAttachments = {}); 45 | 46 | /** The destructor of the Message */ 47 | virtual ~PlaintextMessage() = default; 48 | 49 | /** Return the string MIME type of the message (Pure virtual function). */ 50 | std::string getMimeType() const override; 51 | 52 | operator jed_utils::PlaintextMessage() const; 53 | 54 | }; 55 | } // namespace cpp 56 | } // namespace jed_utils 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /src/cpp/smtpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "smtpclient.hpp" 2 | #include 3 | 4 | using namespace jed_utils::cpp; 5 | 6 | SmtpClient::SmtpClient(const std::string &pServerName, unsigned int pPort) 7 | : jed_utils::SmtpClient(pServerName.c_str(), pPort) { 8 | } 9 | 10 | std::string SmtpClient::getServerName() const { 11 | return jed_utils::SmtpClient::getServerName(); 12 | } 13 | 14 | unsigned int SmtpClient::getServerPort() const { 15 | return jed_utils::SmtpClient::getServerPort(); 16 | } 17 | 18 | bool SmtpClient::getBatchMode() const { 19 | return jed_utils::SmtpClient::getBatchMode(); 20 | } 21 | 22 | unsigned int SmtpClient::getCommandTimeout() const { 23 | return jed_utils::SMTPClientBase::getCommandTimeout(); 24 | } 25 | 26 | std::string SmtpClient::getCommunicationLog() const { 27 | return jed_utils::SmtpClient::getCommunicationLog(); 28 | } 29 | 30 | const Credential *SmtpClient::getCredentials() const { 31 | return mCredential; 32 | } 33 | 34 | void SmtpClient::setServerName(const std::string &pServerName) { 35 | jed_utils::SmtpClient::setServerName(pServerName.c_str()); 36 | } 37 | 38 | void SmtpClient::setServerPort(unsigned int pPort) { 39 | jed_utils::SmtpClient::setServerPort(pPort); 40 | } 41 | 42 | void SmtpClient::setBatchMode(bool pEnabled) { 43 | jed_utils::SmtpClient::setBatchMode(pEnabled); 44 | } 45 | 46 | void SmtpClient::setCommandTimeout(unsigned int pTimeOutInSeconds) { 47 | jed_utils::SMTPClientBase::setCommandTimeout(pTimeOutInSeconds); 48 | } 49 | 50 | void SmtpClient::setCredentials(const Credential &pCredential) { 51 | jed_utils::SMTPClientBase::setCredentials(jed_utils::Credential(pCredential.getUsername().c_str(), 52 | pCredential.getPassword().c_str())); 53 | delete mCredential; 54 | mCredential = new Credential(pCredential); 55 | } 56 | 57 | void SmtpClient::setKeepUsingBaseSendCommands(bool pValue) { 58 | jed_utils::SMTPClientBase::setKeepUsingBaseSendCommands(pValue); 59 | } 60 | 61 | std::string SmtpClient::getErrorMessage(int errorCode) { 62 | return jed_utils::SMTPClientBase::getErrorMessage(errorCode); 63 | } 64 | 65 | int SmtpClient::getErrorMessage_r(int errorCode, 66 | std::string &errorMessage) { 67 | const size_t MAXSIZE = 1024; 68 | char *errorMsg = new char[MAXSIZE]; 69 | int result = jed_utils::SMTPClientBase::getErrorMessage_r(errorCode, errorMsg, MAXSIZE); 70 | errorMessage = errorMsg; 71 | return result; 72 | } 73 | 74 | int SmtpClient::extractReturnCode(const std::string &pOutput) { 75 | return jed_utils::SMTPClientBase::extractReturnCode(pOutput.c_str()); 76 | } 77 | 78 | jed_utils::ServerAuthOptions *SmtpClient::extractAuthenticationOptions(const std::string &pEhloOutput) { 79 | return jed_utils::SMTPClientBase::extractAuthenticationOptions(pEhloOutput.c_str()); 80 | } 81 | 82 | int SmtpClient::sendMail(const jed_utils::Message &pMsg) { 83 | return jed_utils::SmtpClient::sendMail(pMsg); 84 | } 85 | -------------------------------------------------------------------------------- /src/cpp/smtpclient.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPPSMTPCLIENT 2 | #define CPPSMTPCLIENT 3 | 4 | #include 5 | #include "credential.hpp" 6 | #include "message.hpp" 7 | #include "../serverauthoptions.h" 8 | #include "../smtpclient.h" 9 | 10 | #ifdef _WIN32 11 | #ifdef SMTPCLIENT_STATIC 12 | #define CPP_SMTPCLIENT_API 13 | #elif defined(SMTPCLIENT_EXPORTS) 14 | #define CPP_SMTPCLIENT_API __declspec(dllexport) 15 | #else 16 | #define CPP_SMTPCLIENT_API __declspec(dllimport) 17 | #endif 18 | #else 19 | #define CPP_SMTPCLIENT_API 20 | #endif 21 | 22 | namespace jed_utils { 23 | namespace cpp { 24 | /** @brief The SmtpClient should be used to communicate with internal relay servers. 25 | * This client doesn't provided encryption for communication. 26 | * The communication is usually done via port 25. 27 | */ 28 | class CPP_SMTPCLIENT_API SmtpClient : private jed_utils::SmtpClient { 29 | public: 30 | /** 31 | * @brief Construct a new SmtpClient. 32 | * @param pServerName The name of the server. 33 | * Example: smtp.domainexample.com 34 | * @param pPort The server port number. 35 | * Example: 25, 465, 587 36 | */ 37 | SmtpClient(const std::string &pServerName, unsigned int pPort); 38 | 39 | /** Destructor of the SmtpClient. */ 40 | ~SmtpClient() override = default; 41 | 42 | /** SmtpClient copy constructor. */ 43 | SmtpClient(const SmtpClient &other) = default; 44 | 45 | /** SmtpClient copy assignment operator. */ 46 | SmtpClient& operator=(const SmtpClient &other) = default; 47 | 48 | /** SmtpClient move constructor. */ 49 | SmtpClient(SmtpClient &&other) noexcept = default; 50 | 51 | /** SmtpClient move assignment operator. */ 52 | SmtpClient& operator=(SmtpClient &&other) noexcept = default; 53 | 54 | /** Return the server name. */ 55 | std::string getServerName() const; 56 | 57 | /** Return the server port number. */ 58 | unsigned int getServerPort() const; 59 | 60 | /** Return the batch mode enable flag. */ 61 | bool getBatchMode() const; 62 | 63 | /** Return the command timeout in seconds. */ 64 | unsigned int getCommandTimeout() const; 65 | 66 | /** Return the communication log produced by the sendMail method. */ 67 | std::string getCommunicationLog() const; 68 | 69 | /** Return the credentials configured. */ 70 | const Credential *getCredentials() const; 71 | 72 | /** 73 | * @brief Set the server name. 74 | * @param pServerName A std::string of the server name. 75 | * Example: smtp.domainexample.com 76 | */ 77 | void setServerName(const std::string &pServerName); 78 | 79 | /** 80 | * @brief Set the server port number. 81 | * @param pPort The port number. 82 | * Example: 25, 465, 587 83 | */ 84 | void setServerPort(unsigned int pPort); 85 | 86 | /** 87 | * @brief Set the batch mode flag. 88 | * @param pEnabled Indicate if the batch mode is enabled. 89 | * Default: false 90 | */ 91 | void setBatchMode(bool pEnabled); 92 | 93 | /** 94 | * @brief Set the command timeout in seconds. 95 | * @param pTimeOutInSeconds The timeout in seconds. 96 | * Default: 3 seconds 97 | */ 98 | void setCommandTimeout(unsigned int pTimeOutInSeconds); 99 | 100 | /** 101 | * @brief Set the credentials. 102 | * @param pCredential The credential containing the username and the password. 103 | */ 104 | void setCredentials(const Credential &pCredential); 105 | 106 | /** 107 | * @brief Indicate if the class will keep using base send command even 108 | * if a child class as overriden the sendCommand and sendCommandWithFeedback. 109 | * 110 | * This is used for example if you are using a secure client class but 111 | * the STARTTLS feature is not available. The communication will then 112 | * remain unsecured. 113 | * @param pValue True to enable this behavior, false for the default 114 | */ 115 | void setKeepUsingBaseSendCommands(bool pValue); 116 | 117 | /** 118 | * @brief Retreive the error message string that correspond to 119 | * the error code provided. 120 | * @return A std::string containing the error message. 121 | */ 122 | static std::string getErrorMessage(int errorCode); 123 | 124 | /** 125 | * @brief This is the reentrant version of the getErrorMessage method 126 | * @param errorCode The error code return by the SMTP client. 127 | * @param errorMessagePtr A pointer to an allocated char array 128 | * @param maxLength The size of the allocated char array. 129 | * @return Return 0 for success, -1 if an error occurred and a positive 130 | * number representing the number of characters copied to errorMessagePtr 131 | * if the message was longer than that allocated char array. 132 | * 133 | * Retreive the error message string that correspond to the error code 134 | * provided. 135 | */ 136 | static int getErrorMessage_r(int errorCode, 137 | std::string &errorMessage); 138 | 139 | int sendMail(const jed_utils::Message &pMsg); 140 | 141 | protected: 142 | static int extractReturnCode(const std::string &pOutput); 143 | static jed_utils::ServerAuthOptions *extractAuthenticationOptions(const std::string &pEhloOutput); 144 | 145 | private: 146 | Credential* mCredential = nullptr; 147 | }; 148 | } // namespace cpp 149 | } // namespace jed_utils 150 | 151 | #endif 152 | -------------------------------------------------------------------------------- /src/credential.cpp: -------------------------------------------------------------------------------- 1 | #include "credential.h" 2 | #include 3 | #include 4 | #include 5 | #include "stringutils.h" 6 | 7 | using namespace jed_utils; 8 | 9 | Credential::Credential(const char *pUsername, const char *pPassword) 10 | : mUsername(nullptr), 11 | mPassword(nullptr), 12 | mRecommendedAuthMethod(RecommendedAuthenticationMethod::kImplicit) { 13 | std::string username_str { pUsername == nullptr ? "" : pUsername }; 14 | if (pUsername == nullptr || strcmp(pUsername, "") == 0 || StringUtils::trim(username_str).empty()) { 15 | throw std::invalid_argument("Username cannot be null or empty"); 16 | } 17 | 18 | if (pPassword == nullptr || strcmp(pPassword, "") == 0) { 19 | throw std::invalid_argument("Password cannot be null or empty"); 20 | } 21 | 22 | size_t username_len = strlen(pUsername); 23 | mUsername = new char[username_len + 1]; 24 | std::strncpy(mUsername, pUsername, username_len); 25 | mUsername[username_len] = '\0'; 26 | 27 | size_t password_len = strlen(pPassword); 28 | mPassword = new char[password_len + 1]; 29 | std::strncpy(mPassword, pPassword, password_len); 30 | mPassword[password_len] = '\0'; 31 | } 32 | 33 | Credential::~Credential() { 34 | delete[] mUsername; 35 | delete[] mPassword; 36 | mUsername = nullptr; 37 | mPassword = nullptr; 38 | } 39 | 40 | // Copy constructor 41 | Credential::Credential(const Credential& other) 42 | : mUsername(new char[strlen(other.mUsername) + 1]), 43 | mPassword(new char[strlen(other.mPassword) + 1]), 44 | mRecommendedAuthMethod{other.mRecommendedAuthMethod} { 45 | size_t username_len = strlen(other.mUsername); 46 | strncpy(mUsername, other.mUsername, username_len); 47 | mUsername[username_len] = '\0'; 48 | 49 | size_t password_len = strlen(other.mPassword); 50 | strncpy(mPassword, other.mPassword, password_len); 51 | mPassword[password_len] = '\0'; 52 | } 53 | 54 | Credential::Credential(const char *pUsername, const char *pPassword, 55 | RecommendedAuthenticationMethod authOption) 56 | : Credential(pUsername, pPassword) { 57 | mRecommendedAuthMethod = authOption; 58 | } 59 | 60 | // Assignment operator 61 | Credential& Credential::operator=(const Credential& other) { 62 | if (this != &other) { 63 | delete[] mUsername; 64 | delete[] mPassword; 65 | // mUsername 66 | size_t username_len = strlen(other.mUsername); 67 | mUsername = new char[username_len + 1]; 68 | strncpy(mUsername, other.mUsername, username_len); 69 | mUsername[username_len] = '\0'; 70 | // mPassword 71 | size_t password_len = strlen(other.mPassword); 72 | mPassword = new char[password_len + 1]; 73 | strncpy(mPassword, other.mPassword, password_len); 74 | mPassword[password_len] = '\0'; 75 | 76 | mRecommendedAuthMethod = other.mRecommendedAuthMethod; 77 | } 78 | return *this; 79 | } 80 | 81 | // Move constructor 82 | Credential::Credential(Credential&& other) noexcept 83 | : mUsername(other.mUsername), 84 | mPassword(other.mPassword), 85 | mRecommendedAuthMethod{other.mRecommendedAuthMethod} { 86 | // Release the data pointer from the source object so that the destructor 87 | // does not free the memory multiple times. 88 | other.mUsername = nullptr; 89 | other.mPassword = nullptr; 90 | } 91 | 92 | // Move assignement operator 93 | Credential& Credential::operator=(Credential&& other) noexcept { 94 | if (this != &other) { 95 | delete[] mUsername; 96 | delete[] mPassword; 97 | // Copy the data pointer and its length from the source object. 98 | mUsername = other.mUsername; 99 | mPassword = other.mPassword; 100 | mRecommendedAuthMethod = other.mRecommendedAuthMethod; 101 | 102 | // Release the data pointer from the source object so that 103 | // the destructor does not free the memory multiple times. 104 | other.mUsername = nullptr; 105 | other.mPassword = nullptr; 106 | } 107 | return *this; 108 | } 109 | 110 | 111 | const char *Credential::getUsername() const { 112 | return mUsername; 113 | } 114 | 115 | const char *Credential::getPassword() const { 116 | return mPassword; 117 | } 118 | 119 | auto Credential::getRecommendedAuthOption() const 120 | -> RecommendedAuthenticationMethod { 121 | return mRecommendedAuthMethod; 122 | } 123 | 124 | void Credential::setUsername(const char *pUsername) { 125 | std::string username_str { pUsername == nullptr ? "" : pUsername }; 126 | if (pUsername == nullptr || strcmp(pUsername, "") == 0 || StringUtils::trim(username_str).empty()) { 127 | throw std::invalid_argument("Username cannot be null or empty"); 128 | } 129 | 130 | delete []mUsername; 131 | size_t username_len = strlen(pUsername); 132 | mUsername = new char[username_len + 1]; 133 | strncpy(mUsername, pUsername, username_len); 134 | mUsername[username_len] = '\0'; 135 | } 136 | 137 | void Credential::setPassword(const char *pPassword) { 138 | if (pPassword == nullptr || strcmp(pPassword, "") == 0) { 139 | throw std::invalid_argument("Password cannot be null or empty"); 140 | } 141 | 142 | delete []mPassword; 143 | size_t password_len = strlen(pPassword); 144 | mPassword = new char[password_len + 1]; 145 | strncpy(mPassword, pPassword, password_len); 146 | mPassword[password_len] = '\0'; 147 | } 148 | 149 | void Credential::setRecommendedAuthOption( 150 | RecommendedAuthenticationMethod authOption) { 151 | mRecommendedAuthMethod = authOption; 152 | } 153 | -------------------------------------------------------------------------------- /src/credential.h: -------------------------------------------------------------------------------- 1 | #ifndef CREDENTIAL_H 2 | #define CREDENTIAL_H 3 | 4 | #ifdef _WIN32 5 | #ifdef SMTPCLIENT_STATIC 6 | #define CREDENTIAL_API 7 | #elif defined(SMTPCLIENT_EXPORTS) 8 | #define CREDENTIAL_API __declspec(dllexport) 9 | #else 10 | #define CREDENTIAL_API __declspec(dllimport) 11 | #endif 12 | #else 13 | #define CREDENTIAL_API 14 | #endif 15 | 16 | namespace jed_utils { 17 | /** 18 | * @brief The RecommendedAuthenticationMethod contains all possible 19 | * authentication options that developers can indicate for a set 20 | * of credentials. 21 | */ 22 | enum RecommendedAuthenticationMethod { 23 | kImplicit = 0, 24 | kPlain, 25 | kLogin, 26 | kXOauth2, 27 | kPlainClientToken, 28 | kOAuthBearer, 29 | kXOAuth 30 | }; 31 | 32 | 33 | /** @brief The Credential class is used to authenticate with the 34 | * SMTP server. 35 | */ 36 | class CREDENTIAL_API Credential { 37 | public: 38 | /** 39 | * @brief Construct a new Credential. 40 | * @param pUsername The user name. 41 | * @param pPassword The password. 42 | */ 43 | Credential(const char *pUsername, const char *pPassword); 44 | 45 | /** 46 | * @brief Construct a new Credential. 47 | * @param pUsername The user name. 48 | * @param pPassword The password. 49 | */ 50 | Credential(const char *pUsername, 51 | const char *pPassword, 52 | RecommendedAuthenticationMethod authOption); 53 | 54 | /** The destructor og Credential */ 55 | virtual ~Credential(); 56 | 57 | /** Credential copy constructor */ 58 | Credential(const Credential& other); 59 | 60 | /** Credential copy assignment operator */ 61 | Credential& operator=(const Credential& other); 62 | 63 | /** Credential move constructor */ 64 | Credential(Credential&& other) noexcept; 65 | 66 | /** Credential move assignment operator */ 67 | Credential& operator=(Credential&& other) noexcept; 68 | 69 | /** Return the username. */ 70 | const char *getUsername() const; 71 | 72 | /** Return the password. */ 73 | const char *getPassword() const; 74 | 75 | /** Returns the recommended authentication option 76 | * currently set for the credentials. */ 77 | RecommendedAuthenticationMethod getRecommendedAuthOption() const; 78 | 79 | /** 80 | * @brief Set the user name. 81 | * @param pUsername A char array pointer of the user name. 82 | */ 83 | void setUsername(const char *pUsername); 84 | 85 | /** 86 | * @brief Set the password. 87 | * @param pPassword A char array pointer of the password. 88 | */ 89 | void setPassword(const char *pPassword); 90 | 91 | /** 92 | * @brief Changes the recommended authentication method for this set 93 | * of credentials. 94 | * @param authOption The recommended authentication option that we should 95 | * use while authenticating the client using the current set of credentials. 96 | */ 97 | void setRecommendedAuthOption(RecommendedAuthenticationMethod authOption); 98 | 99 | private: 100 | char *mUsername; 101 | char *mPassword; 102 | RecommendedAuthenticationMethod mRecommendedAuthMethod; 103 | }; 104 | } // namespace jed_utils 105 | 106 | #endif // CREDENTIAL_H 107 | -------------------------------------------------------------------------------- /src/errorresolver.h: -------------------------------------------------------------------------------- 1 | #ifndef ERRORRESOLVER_H 2 | #define ERRORRESOLVER_H 3 | 4 | #ifdef _WIN32 5 | #ifdef SMTPCLIENT_STATIC 6 | #define ERRORRESOLVER_API 7 | #elif defined(SMTPCLIENT_EXPORTS) 8 | #define ERRORRESOLVER_API __declspec(dllexport) 9 | #else 10 | #define ERRORRESOLVER_API __declspec(dllimport) 11 | #endif 12 | #else 13 | #define ERRORRESOLVER_API 14 | #endif 15 | 16 | namespace jed_utils { 17 | /** @brief The ErrorResolver class is used to translate an error code 18 | * return be the sendMail method of the differents SMTP client classes 19 | * to a string representation of the error message. 20 | */ 21 | class ERRORRESOLVER_API ErrorResolver { 22 | public: 23 | /** 24 | * @brief Construct a new ErrorResolver. 25 | * @param pErrorCode The error code returned by the sendMail method 26 | * of the different smtp client classes. 27 | */ 28 | explicit ErrorResolver(int pErrorCode); 29 | 30 | /** The destructor of ErrorResolver */ 31 | virtual ~ErrorResolver(); 32 | 33 | /** ErrorResolver copy constructor */ 34 | ErrorResolver(const ErrorResolver& other); 35 | 36 | /** ErrorResolver copy assignment operator */ 37 | ErrorResolver& operator=(const ErrorResolver& other); 38 | 39 | /** ErrorResolver move constructor */ 40 | ErrorResolver(ErrorResolver&& other) noexcept; 41 | 42 | /** ErrorResolver move assignment operator */ 43 | ErrorResolver& operator=(ErrorResolver&& other) noexcept; 44 | 45 | /** Return the error code configured. */ 46 | int getErrorCode() const; 47 | 48 | /** Return the error message corresponding of the currently set 49 | * error code. 50 | */ 51 | const char *getErrorMessage() const; 52 | 53 | private: 54 | int mErrorCode; 55 | char *mErrorMessage = nullptr; 56 | }; 57 | } // namespace jed_utils 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/forcedsecuresmtpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "forcedsecuresmtpclient.h" 2 | #include 3 | #include 4 | #include "smtpclienterrors.h" 5 | #include "smtpserverstatuscodes.h" 6 | #include "socketerrors.h" 7 | #include "sslerrors.h" 8 | 9 | #ifdef _WIN32 10 | #include 11 | #include 12 | #include 13 | typedef SSIZE_T ssize_t; 14 | #include 15 | constexpr auto sleep = Sleep; 16 | #else 17 | #include 18 | #include 19 | #include 20 | #include /* BasicInput/Output streams */ 21 | #include 22 | #endif 23 | 24 | using namespace jed_utils; 25 | 26 | ForcedSecureSMTPClient::ForcedSecureSMTPClient(const char *pServerName, unsigned int pPort) 27 | : SecureSMTPClientBase(pServerName, pPort) { 28 | } 29 | 30 | // Assignment operator 31 | ForcedSecureSMTPClient& ForcedSecureSMTPClient::operator=(const ForcedSecureSMTPClient& other) { 32 | if (this != &other) { 33 | SecureSMTPClientBase::operator=(other); 34 | } 35 | return *this; 36 | } 37 | 38 | // Move constructor 39 | ForcedSecureSMTPClient::ForcedSecureSMTPClient(ForcedSecureSMTPClient&& other) noexcept 40 | : SecureSMTPClientBase(std::move(other)) { 41 | } 42 | 43 | // Move assignement operator 44 | ForcedSecureSMTPClient& ForcedSecureSMTPClient::operator=(ForcedSecureSMTPClient&& other) noexcept { 45 | if (this != &other) { 46 | SecureSMTPClientBase::operator=(std::move(other)); 47 | } 48 | return *this; 49 | } 50 | 51 | int ForcedSecureSMTPClient::establishConnectionWithServer() { 52 | int session_init_return_code = initializeSession(); 53 | if (session_init_return_code != 0) { 54 | return session_init_return_code; 55 | } 56 | 57 | int tls_start_return_code = startTLSNegotiation(); 58 | if (tls_start_return_code != 0) { 59 | return tls_start_return_code; 60 | } 61 | 62 | int server_greetings_return_code = checkServerGreetings(); 63 | if (server_greetings_return_code != STATUS_CODE_SERVICE_READY) { 64 | return server_greetings_return_code; 65 | } 66 | 67 | int client_initSecure_return_code = getServerSecureIdentification(); 68 | if (client_initSecure_return_code != STATUS_CODE_REQUESTED_MAIL_ACTION_OK_OR_COMPLETED) { 69 | return client_initSecure_return_code; 70 | } 71 | 72 | if (getCredentials() != nullptr) { 73 | int client_auth_return_code = authenticateClient(); 74 | if (client_auth_return_code != STATUS_CODE_AUTHENTICATION_SUCCEEDED) { 75 | return client_auth_return_code; 76 | } 77 | } 78 | return 0; 79 | } 80 | 81 | int ForcedSecureSMTPClient::checkServerGreetings() { 82 | char outbuf[SERVERRESPONSE_BUFFER_LENGTH]; 83 | unsigned int waitTime = 0; 84 | ssize_t bytes_received = 0; 85 | while ((bytes_received = BIO_read(getBIO(), outbuf, SERVERRESPONSE_BUFFER_LENGTH)) <= 0 && waitTime < getCommandTimeout()) { 86 | crossPlatformSleep(1); 87 | waitTime += 1; 88 | } 89 | if (waitTime < getCommandTimeout()) { 90 | outbuf[bytes_received-1] = '\0'; 91 | addCommunicationLogItem(outbuf, "s"); 92 | int status_code = extractReturnCode(outbuf); 93 | if (status_code == STATUS_CODE_SERVICE_READY) { 94 | addCommunicationLogItem("Connected!"); 95 | } 96 | return status_code; 97 | } 98 | return SOCKET_INIT_SESSION_CONNECT_TIMEOUT; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /src/forcedsecuresmtpclient.h: -------------------------------------------------------------------------------- 1 | #ifndef IMPLICITSSLSMTPCLIENT_H 2 | #define IMPLICITSSLSMTPCLIENT_H 3 | 4 | #include "securesmtpclientbase.h" 5 | 6 | #ifdef _WIN32 7 | #ifdef SMTPCLIENT_STATIC 8 | #define FORCEDSECURESMTPCLIENT_API 9 | #elif defined(SMTPCLIENT_EXPORTS) 10 | #define FORCEDSECURESMTPCLIENT_API __declspec(dllexport) 11 | #else 12 | #define FORCEDSECURESMTPCLIENT_API __declspec(dllimport) 13 | #endif 14 | #else 15 | #define FORCEDSECURESMTPCLIENT_API 16 | #endif 17 | 18 | namespace jed_utils { 19 | /** @brief The ForcedSecureSMTPClient is useful to communicate with legacy 20 | * systems which requires that the communication be encrypted from the 21 | * initial connection. The communication is usually done via port 465. 22 | */ 23 | class FORCEDSECURESMTPCLIENT_API ForcedSecureSMTPClient : public SecureSMTPClientBase { 24 | public: 25 | /** 26 | * @brief Construct a new ForcedSecureSMTPClient. 27 | * @param pServerName The name of the server. 28 | * Example: smtp.domainexample.com 29 | * @param pPort The server port number. 30 | * Example: 25, 465, 587 31 | */ 32 | ForcedSecureSMTPClient(const char *pServerName, unsigned int pPort); 33 | 34 | /** Destructor of the ForcedSecureSMTPClient. */ 35 | ~ForcedSecureSMTPClient() override = default; 36 | 37 | /** ForcedSecureSMTPClient copy constructor. */ 38 | ForcedSecureSMTPClient(const ForcedSecureSMTPClient& other) = default; 39 | 40 | /** ForcedSecureSMTPClient copy assignment operator. */ 41 | ForcedSecureSMTPClient& operator=(const ForcedSecureSMTPClient& other); 42 | 43 | /** ForcedSecureSMTPClient move constructor. */ 44 | ForcedSecureSMTPClient(ForcedSecureSMTPClient&& other) noexcept; 45 | 46 | /** ForcedSecureSMTPClient move assignment operator. */ 47 | ForcedSecureSMTPClient& operator=(ForcedSecureSMTPClient&& other) noexcept; 48 | 49 | protected: 50 | int establishConnectionWithServer() override; 51 | int checkServerGreetings() override; 52 | }; 53 | } // namespace jed_utils 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/htmlmessage.cpp: -------------------------------------------------------------------------------- 1 | #include "htmlmessage.h" 2 | 3 | using namespace jed_utils; 4 | 5 | HTMLMessage::HTMLMessage(const MessageAddress &pFrom, 6 | const MessageAddress &pTo, 7 | const char *pSubject, 8 | const char *pBody, 9 | const MessageAddress *pCc, 10 | const MessageAddress *pBcc, 11 | const Attachment *pAttachments, 12 | const size_t pAttachmentsSize) 13 | : Message(pFrom, pTo, pSubject, pBody, pCc, pBcc, pAttachments, pAttachmentsSize) { 14 | } 15 | 16 | HTMLMessage::HTMLMessage(const MessageAddress &pFrom, 17 | const MessageAddress pTo[], 18 | const size_t pToCount, 19 | const char *pSubject, 20 | const char *pBody, 21 | const MessageAddress pCc[], 22 | const size_t pCcCount, 23 | const MessageAddress pBcc[], 24 | const size_t pBccCount, 25 | const Attachment pAttachments[], 26 | const size_t pAttachmentsSize) 27 | : Message(pFrom, pTo, pToCount, pSubject, pBody, pCc, pCcCount, pBcc, pBccCount, pAttachments, pAttachmentsSize) { 28 | } 29 | 30 | const char *HTMLMessage::getMimeType() const { 31 | return "text/html"; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/htmlmessage.h: -------------------------------------------------------------------------------- 1 | #ifndef HTMLMESSAGE_H 2 | #define HTMLMESSAGE_H 3 | 4 | #include 5 | #include "message.h" 6 | 7 | #ifdef _WIN32 8 | #ifdef SMTPCLIENT_STATIC 9 | #define HTMLMESSAGE_API 10 | #elif defined(SMTPCLIENT_EXPORTS) 11 | #define HTMLMESSAGE_API __declspec(dllexport) 12 | #else 13 | #define HTMLMESSAGE_API __declspec(dllimport) 14 | #endif 15 | #else 16 | #define HTMLMESSAGE_API 17 | #endif 18 | 19 | namespace jed_utils { 20 | /** @brief The HTMLMessage class represents an email message in 21 | * HTML format. 22 | */ 23 | class HTMLMESSAGE_API HTMLMessage : public Message { 24 | public: 25 | /** 26 | * @brief Construct a new single recipient HTMLMessage. 27 | * @param pFrom The sender email address of the message. 28 | * @param pTo The recipient email address of the message. 29 | * @param pSubject The subject of the message. 30 | * @param pBody The content of the message. 31 | * @param pCc The carbon-copy recipient email address. 32 | * @param pBcc The blind carbon-copy recipient email address. 33 | * @param pAttachments The attachments array of the message 34 | * @param pAttachmentsSize The number of attachments in the array. 35 | */ 36 | HTMLMessage(const MessageAddress &pFrom, 37 | const MessageAddress &pTo, 38 | const char *pSubject, 39 | const char *pBody, 40 | const MessageAddress *pCc = nullptr, 41 | const MessageAddress *pBcc = nullptr, 42 | const Attachment *pAttachments = nullptr, 43 | size_t pAttachmentsSize = 0); 44 | 45 | /** 46 | * @brief Construct a new multiple recipients HTMLMessage. 47 | * @param pFrom The sender email address of the message. 48 | * @param pTo The recipients email address array of the message. 49 | * @param pToCount The number of recipients email address in the array 50 | * @param pSubject The subject of the message. 51 | * @param pBody The content of the message. 52 | * @param pCc The carbon-copy recipients email address array. 53 | * @param pCcCount The number of carbon-copy recipients email address in the array 54 | * @param pBcc The blind carbon-copy recipient email address. 55 | * @param pBccCount The number of blind carbon-copy recipients email address in the array 56 | * @param pAttachments The attachments array of the message 57 | * @param pAttachmentsSize The number of attachments in the array. 58 | */ 59 | HTMLMessage(const MessageAddress &pFrom, 60 | const MessageAddress pTo[], 61 | size_t pToCount, 62 | const char *pSubject, 63 | const char *pBody, 64 | const MessageAddress pCc[] = nullptr, 65 | size_t pCcCount = 0, 66 | const MessageAddress pBcc[] = nullptr, 67 | size_t pBccCount = 0, 68 | const Attachment pAttachments[] = nullptr, 69 | size_t pAttachmentsSize = 0); 70 | const char *getMimeType() const override; 71 | }; 72 | } // namespace jed_utils 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/message.h: -------------------------------------------------------------------------------- 1 | #ifndef MESSAGE_H 2 | #define MESSAGE_H 3 | 4 | #include 5 | #include 6 | #include "attachment.h" 7 | #include "messageaddress.h" 8 | 9 | #ifdef _WIN32 10 | #ifdef SMTPCLIENT_STATIC 11 | #define MESSAGE_API 12 | #elif defined(SMTPCLIENT_EXPORTS) 13 | #define MESSAGE_API __declspec(dllexport) 14 | #else 15 | #define MESSAGE_API __declspec(dllimport) 16 | #endif 17 | #else 18 | #define MESSAGE_API 19 | #endif 20 | 21 | namespace jed_utils { 22 | /** @brief The Message class represents the base class of an email message. */ 23 | class MESSAGE_API Message { 24 | public: 25 | /** 26 | * @brief Construct a new single recipient Message base class. 27 | * @param pFrom The sender email address of the message. 28 | * @param pTo The recipient email address of the message. 29 | * @param pSubject The subject of the message. 30 | * @param pBody The content of the message. 31 | * @param pCc The carbon-copy recipient email address. 32 | * @param pBcc The blind carbon-copy recipient email address. 33 | * @param pAttachments The attachments array of the message 34 | * @param pAttachmentsSize The number of attachments in the array. 35 | */ 36 | Message(const MessageAddress &pFrom, 37 | const MessageAddress &pTo, 38 | const char *pSubject, 39 | const char *pBody, 40 | const MessageAddress *pCc = nullptr, 41 | const MessageAddress *pBcc = nullptr, 42 | const Attachment pAttachments[] = nullptr, 43 | size_t pAttachmentsSize = 0); 44 | 45 | /** 46 | * @brief Construct a new multiple recipients Message base class. 47 | * @param pFrom The sender email address of the message. 48 | * @param pTo The recipients email address array of the message. 49 | * @param pToCount The number of recipients email address in the array 50 | * @param pSubject The subject of the message. 51 | * @param pBody The content of the message. 52 | * @param pCc The carbon-copy recipients email address array. 53 | * @param pCcCount The number of carbon-copy recipients email address in the array 54 | * @param pBcc The blind carbon-copy recipient email address. 55 | * @param pBccCount The number of blind carbon-copy recipients email address in the array 56 | * @param pAttachments The attachments array of the message 57 | * @param pAttachmentsSize The number of attachments in the array. 58 | */ 59 | Message(const MessageAddress &pFrom, 60 | const MessageAddress pTo[], 61 | size_t pToCount, 62 | const char *pSubject, 63 | const char *pBody, 64 | const MessageAddress pCc[] = nullptr, 65 | size_t pCcCount = 0, 66 | const MessageAddress pBcc[] = nullptr, 67 | size_t pBccCount = 0, 68 | const Attachment pAttachments[] = nullptr, 69 | size_t pAttachmentsSize = 0); 70 | 71 | /** The destructor of the Message */ 72 | virtual ~Message(); 73 | 74 | /** Message copy constructor. */ 75 | Message(const Message &other); 76 | 77 | /** Message copy assignment operator. */ 78 | Message& operator=(const Message &other); 79 | 80 | /** Message move constructor. */ 81 | Message(Message &&other) noexcept; 82 | 83 | /** Message move assignment operator. */ 84 | Message& operator=(Message &&other) noexcept; 85 | 86 | /** Return the string MIME type of the message (Pure virtual function). */ 87 | virtual const char *getMimeType() const = 0; 88 | 89 | /** Return the Sender MessageAddress of the message. */ 90 | const MessageAddress &getFrom() const; 91 | 92 | /** Return the recipient MessageAddress array of the message */ 93 | MessageAddress **getTo() const; 94 | 95 | /** Return the number message recipients in the array. */ 96 | size_t getToCount() const; 97 | 98 | /** Return the subject of the message. */ 99 | const char *getSubject() const; 100 | 101 | /** Return the body of the message. */ 102 | const char *getBody() const; 103 | 104 | /** Return the carbon-copy recipient MessageAddress array of the message */ 105 | MessageAddress **getCc() const; 106 | 107 | /** Return the number of message carbon-copy recipients in the array. */ 108 | size_t getCcCount() const; 109 | 110 | /** Return the blind carbon-copy recipient MessageAddress array of the message */ 111 | MessageAddress **getBcc() const; 112 | 113 | /** Return the number of message blind carbon-copy recipients in the array. */ 114 | size_t getBccCount() const; 115 | 116 | /** Return the attachment array of the message */ 117 | Attachment **getAttachments() const; 118 | 119 | /** Return the number of message attachments in the array. */ 120 | size_t getAttachmentsCount() const; 121 | 122 | private: 123 | MessageAddress mFrom; 124 | MessageAddress **mTo; 125 | size_t mToCount; 126 | MessageAddress **mCc; 127 | size_t mCCCount; 128 | MessageAddress **mBcc; 129 | size_t mBCCCount; 130 | char *mSubject; 131 | char *mBody; 132 | Attachment **mAttachments; 133 | size_t mAttachmentCount; 134 | }; 135 | } // namespace jed_utils 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /src/messageaddress.cpp: -------------------------------------------------------------------------------- 1 | #include "messageaddress.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "stringutils.h" 7 | 8 | using namespace jed_utils; 9 | 10 | MessageAddress::MessageAddress(const char *pEmailAddress, const char *pDisplayName) 11 | : mEmailAddress(nullptr), mDisplayName(nullptr) { 12 | std::string email_address { pEmailAddress }; 13 | // Check if the email address is not empty or white spaces 14 | if (email_address.length() == 0 || StringUtils::trim(email_address).empty()) { 15 | throw std::invalid_argument("pEmailAddress"); 16 | } 17 | // Check is the email address is valid 18 | if (!isEmailAddressValid(email_address)) { 19 | throw std::invalid_argument("pEmailAddress"); 20 | } 21 | 22 | size_t email_len = strlen(pEmailAddress); 23 | mEmailAddress = new char[email_len+1]; 24 | strncpy(mEmailAddress, pEmailAddress, email_len); 25 | mEmailAddress[email_len] = '\0'; 26 | 27 | size_t name_len = strlen(pDisplayName); 28 | mDisplayName = new char[name_len+1]; 29 | strncpy(mDisplayName, pDisplayName, name_len); 30 | mDisplayName[name_len] = '\0'; 31 | } 32 | 33 | MessageAddress::~MessageAddress() { 34 | delete[] mEmailAddress; 35 | delete[] mDisplayName; 36 | } 37 | 38 | // Copy constructor 39 | MessageAddress::MessageAddress(const MessageAddress& other) 40 | : mEmailAddress(new char[strlen(other.mEmailAddress) + 1]), 41 | mDisplayName(new char[strlen(other.mDisplayName) + 1]) { 42 | size_t email_len = strlen(other.mEmailAddress); 43 | strncpy(mEmailAddress, other.mEmailAddress, email_len); 44 | mEmailAddress[email_len] = '\0'; 45 | size_t name_len = strlen(other.mDisplayName); 46 | strncpy(mDisplayName, other.mDisplayName, name_len); 47 | mDisplayName[name_len] = '\0'; 48 | } 49 | 50 | // Assignment operator 51 | MessageAddress& MessageAddress::operator=(const MessageAddress& other) { 52 | if (this != &other) { 53 | delete[] mEmailAddress; 54 | delete[] mDisplayName; 55 | // mEmailAddress 56 | size_t email_len = strlen(other.mEmailAddress); 57 | mEmailAddress = new char[email_len + 1]; 58 | strncpy(mEmailAddress, other.mEmailAddress, email_len); 59 | mEmailAddress[email_len] = '\0'; 60 | // mDisplayName 61 | size_t name_len = strlen(other.mDisplayName); 62 | mDisplayName = new char[name_len + 1]; 63 | strncpy(mDisplayName, other.mDisplayName, name_len); 64 | mDisplayName[name_len] = '\0'; 65 | } 66 | return *this; 67 | } 68 | 69 | // Move constructor 70 | MessageAddress::MessageAddress(MessageAddress&& other) noexcept 71 | : mEmailAddress(other.mEmailAddress), 72 | mDisplayName(other.mDisplayName) { 73 | // Release the data pointer from the source object so that the destructor 74 | // does not free the memory multiple times. 75 | other.mEmailAddress = nullptr; 76 | other.mDisplayName = nullptr; 77 | } 78 | 79 | // Move assignement operator 80 | MessageAddress& MessageAddress::operator=(MessageAddress&& other) noexcept { 81 | if (this != &other) { 82 | delete[] mEmailAddress; 83 | delete[] mDisplayName; 84 | // Copy the data pointer and its length from the source object. 85 | mEmailAddress = other.mEmailAddress; 86 | mDisplayName = other.mDisplayName; 87 | // Release the data pointer from the source object so that 88 | // the destructor does not free the memory multiple times. 89 | other.mEmailAddress = nullptr; 90 | other.mDisplayName = nullptr; 91 | } 92 | return *this; 93 | } 94 | 95 | bool MessageAddress::operator==(const MessageAddress &pMsgComp) const { 96 | return (strcmp(mEmailAddress, pMsgComp.mEmailAddress) == 0 && 97 | strcmp(mDisplayName, pMsgComp.mDisplayName) == 0); 98 | } 99 | 100 | MessageAddress::operator std::string() const { 101 | std::ostringstream retval; 102 | std::string display_name { mDisplayName }; 103 | if (display_name.empty()) { 104 | retval << mEmailAddress; 105 | } else { 106 | retval << mDisplayName << " <" << mEmailAddress << ">"; 107 | } 108 | return retval.str(); 109 | } 110 | 111 | const char *MessageAddress::getEmailAddress() const { 112 | return mEmailAddress; 113 | } 114 | 115 | const char *MessageAddress::getDisplayName() const { 116 | return mDisplayName; 117 | } 118 | 119 | bool MessageAddress::isEmailAddressValid(const std::string &pEmailAddress) const { 120 | std::regex emailPattern("^[_a-z0-9-]+(.[_a-z0-9-]+)*@[a-z0-9-]+(.[a-z0-9-]+)*(.[a-z]{2,4})$"); 121 | return regex_match(StringUtils::toLower(pEmailAddress), emailPattern); 122 | } 123 | 124 | -------------------------------------------------------------------------------- /src/messageaddress.h: -------------------------------------------------------------------------------- 1 | #ifndef MESSAGEADDRESS_H 2 | #define MESSAGEADDRESS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef _WIN32 9 | #ifdef SMTPCLIENT_STATIC 10 | #define MESSAGEADDRESS_API 11 | #elif defined(SMTPCLIENT_EXPORTS) 12 | #define MESSAGEADDRESS_API __declspec(dllexport) 13 | #else 14 | #define MESSAGEADDRESS_API __declspec(dllimport) 15 | #endif 16 | #else 17 | #define MESSAGEADDRESS_API 18 | #endif 19 | 20 | namespace jed_utils { 21 | /** @brief The MessageAddress class represents a sender or a recipient address containing 22 | * the email address and the display name. 23 | * Example : joeblow@domainexample.com Joe Blow 24 | */ 25 | class MESSAGEADDRESS_API MessageAddress { 26 | public: 27 | /** 28 | * @brief Construct a new MessageAddress. 29 | * @param pEmailAddress The email address. The prefix appears to the left of the @ symbol. 30 | * The domain appears to the right of the @ symbol. 31 | * @param pDisplayName The display name of the address that will appear in 32 | * the message. Example : Joe Blow 33 | */ 34 | explicit MessageAddress(const char *pEmailAddress, const char *pDisplayName = ""); 35 | 36 | /** Destructor of the MessageAddress */ 37 | virtual ~MessageAddress(); 38 | 39 | /** MessageAddress copy constructor. */ 40 | MessageAddress(const MessageAddress& other); 41 | 42 | /** MessageAddress copy assignment operator. */ 43 | MessageAddress& operator=(const MessageAddress& other); 44 | 45 | /** MessageAddress move constructor. */ 46 | MessageAddress(MessageAddress&& other) noexcept; 47 | 48 | /** MessageAddress move assignment operator. */ 49 | MessageAddress& operator=(MessageAddress&& other) noexcept; 50 | 51 | /** MessageAddress equality operator. */ 52 | bool operator==(const MessageAddress &pMsgComp) const; 53 | 54 | /** MessageAddress implicit string conversion operator. */ 55 | explicit operator std::string() const; 56 | 57 | /** Return the email address. */ 58 | const char *getEmailAddress() const; 59 | 60 | /** Return the display name. */ 61 | const char *getDisplayName() const; 62 | 63 | friend class message; 64 | 65 | private: 66 | char *mEmailAddress = nullptr; 67 | char *mDisplayName = nullptr; 68 | bool isEmailAddressValid(const std::string &pEmailAddress) const; 69 | }; 70 | } // namespace jed_utils 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /src/opportunisticsecuresmtpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "opportunisticsecuresmtpclient.h" 2 | #include 3 | #include 4 | #include 5 | #include "smtpserverstatuscodes.h" 6 | #include "socketerrors.h" 7 | #include "stringutils.h" 8 | 9 | #ifdef _WIN32 10 | #include 11 | #include 12 | #include 13 | typedef SSIZE_T ssize_t; 14 | #include 15 | constexpr auto sleep = Sleep; 16 | #else 17 | #include 18 | #include 19 | #include 20 | #include /* BasicInput/Output streams */ 21 | #include 22 | #endif 23 | 24 | using namespace jed_utils; 25 | 26 | OpportunisticSecureSMTPClient::OpportunisticSecureSMTPClient(const char *pServerName, unsigned int pPort) 27 | : SecureSMTPClientBase(pServerName, pPort) { 28 | } 29 | 30 | // Assignment operator 31 | OpportunisticSecureSMTPClient& OpportunisticSecureSMTPClient::operator=(const OpportunisticSecureSMTPClient& other) { 32 | if (this != &other) { 33 | SecureSMTPClientBase::operator=(other); 34 | } 35 | return *this; 36 | } 37 | 38 | // Move constructor 39 | OpportunisticSecureSMTPClient::OpportunisticSecureSMTPClient(OpportunisticSecureSMTPClient&& other) noexcept 40 | : SecureSMTPClientBase(std::move(other)) { 41 | } 42 | 43 | // Move assignement operator 44 | OpportunisticSecureSMTPClient& OpportunisticSecureSMTPClient::operator=(OpportunisticSecureSMTPClient&& other) noexcept { 45 | if (this != &other) { 46 | SecureSMTPClientBase::operator=(std::move(other)); 47 | } 48 | return *this; 49 | } 50 | 51 | int OpportunisticSecureSMTPClient::establishConnectionWithServer() { 52 | int session_init_return_code = initializeSession(); 53 | if (session_init_return_code != 0) { 54 | return session_init_return_code; 55 | } 56 | 57 | int server_greetings_return_code = checkServerGreetings(); 58 | if (server_greetings_return_code != STATUS_CODE_SERVICE_READY) { 59 | return server_greetings_return_code; 60 | } 61 | 62 | int client_init_return_code = sendServerIdentification(); 63 | if (client_init_return_code != STATUS_CODE_REQUESTED_MAIL_ACTION_OK_OR_COMPLETED) { 64 | return client_init_return_code; 65 | } 66 | 67 | if (isStartTLSSupported(getLastServerResponse())) { 68 | addCommunicationLogItem("Info: STARTTLS is available by the server, the communication will be encrypted."); 69 | int tls_init_return_code = upgradeToSecureConnection(); 70 | if (tls_init_return_code != STATUS_CODE_SERVICE_READY) { 71 | return tls_init_return_code; 72 | } 73 | int tls_start_return_code = startTLSNegotiation(); 74 | if (tls_start_return_code != 0) { 75 | return tls_start_return_code; 76 | } 77 | int client_initSecure_return_code = getServerSecureIdentification(); 78 | if (client_initSecure_return_code != STATUS_CODE_REQUESTED_MAIL_ACTION_OK_OR_COMPLETED) { 79 | return client_initSecure_return_code; 80 | } 81 | if (getCredentials() != nullptr) { 82 | int client_auth_return_code = authenticateClient(); 83 | if (client_auth_return_code != STATUS_CODE_AUTHENTICATION_SUCCEEDED) { 84 | return client_auth_return_code; 85 | } 86 | } 87 | } else { 88 | addCommunicationLogItem("Info: STARTTLS is not an available option by the server, the communication will then remain unencrypted."); 89 | setKeepUsingBaseSendCommands(true); 90 | } 91 | return 0; 92 | } 93 | 94 | int OpportunisticSecureSMTPClient::upgradeToSecureConnection() { 95 | std::string start_tls_cmd { "STARTTLS\r\n" }; 96 | addCommunicationLogItem(start_tls_cmd.c_str()); 97 | return sendRawCommand(start_tls_cmd.c_str(), 98 | SOCKET_INIT_CLIENT_SEND_STARTTLS_ERROR, 99 | SOCKET_INIT_CLIENT_SEND_STARTTLS_TIMEOUT); 100 | } 101 | 102 | 103 | bool OpportunisticSecureSMTPClient::isStartTLSSupported(const char *pServerResponse) { 104 | if (pServerResponse == nullptr) { 105 | return false; 106 | } 107 | std::string serverResponse { pServerResponse }; 108 | if (serverResponse.length() == 0 || StringUtils::trim(serverResponse).empty()) { 109 | return false; 110 | } 111 | 112 | const std::string STARTTLS_LINE { "250-STARTTLS" }; 113 | // See RFC 5321 - Last line has a space instead of an hyphen 114 | const std::string STARTTLS_LAST_LINE { "250 STARTTLS" }; 115 | return serverResponse.find(STARTTLS_LINE) != std::string::npos || 116 | serverResponse.find(STARTTLS_LAST_LINE) != std::string::npos; 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/opportunisticsecuresmtpclient.h: -------------------------------------------------------------------------------- 1 | #ifndef OPPORTUNISTICSECURESMTPCLIENT_H 2 | #define OPPORTUNISTICSECURESMTPCLIENT_H 3 | 4 | #include "securesmtpclientbase.h" 5 | 6 | #ifdef _WIN32 7 | #ifdef SMTPCLIENT_STATIC 8 | #define OPPORTUNISTICSECURESMTPCLIENT_API 9 | #elif defined(SMTPCLIENT_EXPORTS) 10 | #define OPPORTUNISTICSECURESMTPCLIENT_API __declspec(dllexport) 11 | #else 12 | #define OPPORTUNISTICSECURESMTPCLIENT_API __declspec(dllimport) 13 | #endif 14 | #else 15 | #define OPPORTUNISTICSECURESMTPCLIENT_API 16 | #endif 17 | 18 | namespace jed_utils { 19 | /** @brief The OpportunisticSecureSMTPClient should be your default choice for 20 | * communicating with modern SMTP servers. The communication is usually done 21 | * via port 587. 22 | */ 23 | class OPPORTUNISTICSECURESMTPCLIENT_API OpportunisticSecureSMTPClient : public SecureSMTPClientBase { 24 | public: 25 | /** 26 | * @brief Construct a new OpportunisticSecureSMTPClient. 27 | * @param pServerName The name of the server. 28 | * Example: smtp.domainexample.com 29 | * @param pPort The server port number. 30 | * Example: 25, 465, 587 31 | */ 32 | OpportunisticSecureSMTPClient(const char *pServerName, unsigned int pPort); 33 | 34 | /** Destructor of the OpportunisticSecureSMTPClient. */ 35 | ~OpportunisticSecureSMTPClient() override = default; 36 | 37 | /** OpportunisticSecureSMTPClient copy constructor. */ 38 | OpportunisticSecureSMTPClient(const OpportunisticSecureSMTPClient& other) = default; 39 | 40 | /** OpportunisticSecureSMTPClient copy assignment operator. */ 41 | OpportunisticSecureSMTPClient& operator=(const OpportunisticSecureSMTPClient& other); 42 | 43 | /** OpportunisticSecureSMTPClient move constructor. */ 44 | OpportunisticSecureSMTPClient(OpportunisticSecureSMTPClient&& other) noexcept; 45 | 46 | /** OpportunisticSecureSMTPClient move assignment operator. */ 47 | OpportunisticSecureSMTPClient& operator=(OpportunisticSecureSMTPClient&& other) noexcept; 48 | 49 | protected: 50 | int establishConnectionWithServer() override; 51 | int upgradeToSecureConnection(); 52 | static bool isStartTLSSupported(const char *pServerResponse); 53 | }; 54 | } // namespace jed_utils 55 | 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/plaintextmessage.cpp: -------------------------------------------------------------------------------- 1 | #include "plaintextmessage.h" 2 | 3 | using namespace jed_utils; 4 | 5 | PlaintextMessage::PlaintextMessage(const MessageAddress &pFrom, 6 | const MessageAddress &pTo, 7 | const char *pSubject, 8 | const char *pBody, 9 | const MessageAddress *pCc, 10 | const MessageAddress *pBcc, 11 | const Attachment *pAttachments, 12 | const size_t pAttachmentsSize) 13 | : Message(pFrom, pTo, pSubject, pBody, pCc, pBcc, pAttachments, pAttachmentsSize) { 14 | } 15 | 16 | PlaintextMessage::PlaintextMessage(const MessageAddress &pFrom, 17 | const MessageAddress pTo[], 18 | const size_t pToCount, 19 | const char *pSubject, 20 | const char *pBody, 21 | const MessageAddress pCc[], 22 | const size_t pCcCount, 23 | const MessageAddress pBcc[], 24 | const size_t pBccCount, 25 | const Attachment pAttachments[], 26 | const size_t pAttachmentsSize) 27 | : Message(pFrom, pTo, pToCount, pSubject, pBody, pCc, pCcCount, pBcc, pBccCount, pAttachments, pAttachmentsSize) { 28 | } 29 | 30 | const char *PlaintextMessage::getMimeType() const { 31 | return "text/plain"; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/plaintextmessage.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAINTEXTMESSAGE_H 2 | #define PLAINTEXTMESSAGE_H 3 | 4 | #include 5 | #include "message.h" 6 | 7 | #ifdef _WIN32 8 | #ifdef SMTPCLIENT_STATIC 9 | #define PLAINTEXTMESSAGE_API 10 | #elif defined(SMTPCLIENT_EXPORTS) 11 | #define PLAINTEXTMESSAGE_API __declspec(dllexport) 12 | #else 13 | #define PLAINTEXTMESSAGE_API __declspec(dllimport) 14 | #endif 15 | #else 16 | #define PLAINTEXTMESSAGE_API 17 | #endif 18 | 19 | namespace jed_utils { 20 | /** @brief The PlaintextMessage class represents an email message in 21 | * plain text format. 22 | */ 23 | class PLAINTEXTMESSAGE_API PlaintextMessage : public Message { 24 | public: 25 | /** 26 | * @brief Construct a new single recipient PlaintextMessage. 27 | * @param pFrom The sender email address of the message. 28 | * @param pTo The recipient email address of the message. 29 | * @param pSubject The subject of the message. 30 | * @param pBody The content of the message. 31 | * @param pCc The carbon-copy recipient email address. 32 | * @param pBcc The blind carbon-copy recipient email address. 33 | * @param pAttachments The attachments array of the message 34 | * @param pAttachmentsSize The number of attachments in the array. 35 | */ 36 | PlaintextMessage(const MessageAddress &pFrom, 37 | const MessageAddress &pTo, 38 | const char *pSubject, 39 | const char *pBody, 40 | const MessageAddress *pCc = nullptr, 41 | const MessageAddress *pBcc = nullptr, 42 | const Attachment *pAttachments = nullptr, 43 | size_t pAttachmentsSize = 0); 44 | 45 | /** 46 | * @brief Construct a new multiple recipients PlaintextMessage. 47 | * @param pFrom The sender email address of the message. 48 | * @param pTo The recipients email address array of the message. 49 | * @param pToCount The number of recipients email address in the array 50 | * @param pSubject The subject of the message. 51 | * @param pBody The content of the message. 52 | * @param pCc The carbon-copy recipients email address array. 53 | * @param pCcCount The number of carbon-copy recipients email address in the array 54 | * @param pBcc The blind carbon-copy recipient email address. 55 | * @param pBccCount The number of blind carbon-copy recipients email address in the array 56 | * @param pAttachments The attachments array of the message 57 | * @param pAttachmentsSize The number of attachments in the array. 58 | */ 59 | PlaintextMessage(const MessageAddress &pFrom, 60 | const MessageAddress pTo[], 61 | size_t pToCount, 62 | const char *pSubject, 63 | const char *pBody, 64 | const MessageAddress pCc[] = nullptr, 65 | size_t pCcCount = 0, 66 | const MessageAddress pBcc[] = nullptr, 67 | size_t pBccCount = 0, 68 | const Attachment pAttachments[] = nullptr, 69 | size_t pAttachmentsSize = 0); 70 | const char *getMimeType() const override; 71 | }; 72 | } // namespace jed_utils 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/securesmtpclientbase.h: -------------------------------------------------------------------------------- 1 | #ifndef SECURESMTPCLIENTBASE_H 2 | #define SECURESMTPCLIENTBASE_H 3 | 4 | #include 5 | #include "smtpclientbase.h" 6 | 7 | #ifdef _WIN32 8 | #ifdef SMTPCLIENT_STATIC 9 | #define SECURESMTPCLIENTBASE_API 10 | #elif defined(SMTPCLIENT_EXPORTS) 11 | #define SECURESMTPCLIENTBASE_API __declspec(dllexport) 12 | #else 13 | #define SECURESMTPCLIENTBASE_API __declspec(dllimport) 14 | #endif 15 | #else 16 | #define SECURESMTPCLIENTBASE_API 17 | #endif 18 | 19 | namespace jed_utils { 20 | /** @brief The SecureSMTPClientBase represents the base class for SMTP client 21 | * that will use encryption for communication. 22 | */ 23 | class SECURESMTPCLIENTBASE_API SecureSMTPClientBase : public SMTPClientBase { 24 | public: 25 | /** 26 | * @brief Construct a new SecureSMTPClientBase. 27 | * @param pServerName The name of the server. 28 | * Example: smtp.domainexample.com 29 | * @param pPort The server port number. 30 | * Example: 25, 465, 587 31 | */ 32 | SecureSMTPClientBase(const char *pServerName, unsigned int pPort); 33 | 34 | /** Destructor of the SecureSMTPClientBase. */ 35 | ~SecureSMTPClientBase(); 36 | 37 | /** SecureSMTPClientBase copy constructor. */ 38 | SecureSMTPClientBase(const SecureSMTPClientBase& other); 39 | 40 | /** SecureSMTPClientBase copy assignment operator. */ 41 | SecureSMTPClientBase& operator=(const SecureSMTPClientBase& other); 42 | 43 | /** SecureSMTPClientBase move constructor. */ 44 | SecureSMTPClientBase(SecureSMTPClientBase&& other) noexcept; 45 | 46 | /** SecureSMTPClientBase move assignment operator. */ 47 | SecureSMTPClientBase& operator=(SecureSMTPClientBase&& other) noexcept; 48 | 49 | /** Return the accept self certificate flag. */ 50 | bool getAcceptSelfSignedCert() const; 51 | 52 | /** 53 | * @brief Set the accept self signed certificate 54 | * @param pValue Indicate if self signed certificate is accepted. 55 | * Default: false 56 | */ 57 | void setAcceptSelfSignedCert(bool pValue); 58 | 59 | protected: 60 | // Methods 61 | void cleanup() override; 62 | BIO *getBIO() const; 63 | // Methods used to establish the connection with server 64 | int getServerSecureIdentification(); 65 | int startTLSNegotiation(); 66 | void initializeSSLContext(); 67 | // Methods to send commands to the server 68 | int sendCommand(const char *pCommand, int pErrorCode) override; 69 | int sendCommandWithFeedback(const char *pCommand, int pErrorCode, int pTimeoutCode) override; 70 | int getServerReply() override; 71 | 72 | private: 73 | // Attributes used to communicate with the server 74 | BIO *mBIO; 75 | SSL_CTX *mCTX; 76 | SSL *mSSL; 77 | bool mAcceptSelfSignedCert; 78 | }; 79 | } // namespace jed_utils 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /src/serverauthoptions.h: -------------------------------------------------------------------------------- 1 | #ifndef SERVERAUTHOPTIONS_H 2 | #define SERVERAUTHOPTIONS_H 3 | 4 | namespace jed_utils { 5 | struct ServerAuthOptions { 6 | bool Plain = false; 7 | bool Login = false; 8 | bool XOAuth2 = false; 9 | bool Plain_ClientToken = false; 10 | bool OAuthBearer = false; 11 | bool XOAuth = false; 12 | }; 13 | } // namespace jed_utils 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/serveroptionsanalyzer.cpp: -------------------------------------------------------------------------------- 1 | #include "serveroptionsanalyzer.h" 2 | 3 | using namespace jed_utils; 4 | 5 | // Trim from start 6 | bool ServerOptionsAnalyzer::containsAllOptions(const std::string &optionsStr) { 7 | std::string sanitizedOptionsStr = [&optionsStr]() { 8 | if (optionsStr.size() >= 2 && optionsStr.substr(optionsStr.size() - 2) == "\r\n") { 9 | return optionsStr.substr(0, optionsStr.size() - 2); 10 | } else { 11 | return optionsStr; 12 | } 13 | }(); 14 | size_t lastLineStart = sanitizedOptionsStr.rfind("\r\n"); 15 | if (lastLineStart == std::string::npos) { 16 | lastLineStart = 0; 17 | } else { 18 | lastLineStart += 2; // Move past the \r\n 19 | } 20 | if (sanitizedOptionsStr.substr(lastLineStart, 4) == "250 ") { 21 | return true; 22 | } 23 | return false; 24 | } 25 | -------------------------------------------------------------------------------- /src/serveroptionsanalyzer.h: -------------------------------------------------------------------------------- 1 | #ifndef SERVEROPTIONSANALYZER_H 2 | #define SERVEROPTIONSANALYZER_H 3 | 4 | #include 5 | 6 | #ifdef _WIN32 7 | #ifdef SMTPCLIENT_STATIC 8 | #define SERVEROPTIONSANALYZER_API 9 | #elif defined(SMTPCLIENT_EXPORTS) 10 | #define SERVEROPTIONSANALYZER_API __declspec(dllexport) 11 | #else 12 | #define SERVEROPTIONSANALYZER_API __declspec(dllimport) 13 | #endif 14 | #else 15 | #define SERVEROPTIONSANALYZER_API 16 | #endif 17 | 18 | namespace jed_utils { 19 | /** @brief The ServerOptionsAnalyzer class provides utility fonctions to 20 | * analyzer the SMTP options that are available by the server. 21 | */ 22 | class SERVEROPTIONSANALYZER_API ServerOptionsAnalyzer { 23 | public: 24 | /** 25 | * @brief Indicate if the server has returned all it's available options 26 | * or of there still a reply with other options to come. 27 | * @param optionsStr The string that contains the option list returned by 28 | * the SMTP server. 29 | */ 30 | static bool containsAllOptions(const std::string &optionsStr); 31 | }; 32 | } // namespace jed_utils 33 | 34 | 35 | #endif // SERVEROPTIONSANALYZER_H 36 | -------------------------------------------------------------------------------- /src/smtpclient.cpp: -------------------------------------------------------------------------------- 1 | #include "smtpclient.h" 2 | #include "smtpserverstatuscodes.h" 3 | #ifdef _WIN32 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | #include 10 | #include 11 | #endif 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace jed_utils; 17 | 18 | SmtpClient::SmtpClient(const char *pServerName, unsigned int pPort) 19 | : SMTPClientBase(pServerName, pPort) { 20 | } 21 | 22 | SmtpClient::~SmtpClient() { 23 | SmtpClient::cleanup(); 24 | } 25 | 26 | // Assignment operator 27 | SmtpClient& SmtpClient::operator=(const SmtpClient &other) { 28 | if (this != &other) { 29 | SMTPClientBase::operator=(other); 30 | } 31 | return *this; 32 | } 33 | 34 | // Move constructor 35 | SmtpClient::SmtpClient(SmtpClient &&other) noexcept 36 | : SMTPClientBase(std::move(other)) { 37 | } 38 | 39 | // Move assignement 40 | SmtpClient& SmtpClient::operator=(SmtpClient &&other) noexcept { 41 | if (this != &other) { 42 | SMTPClientBase::operator=(std::move(other)); 43 | } 44 | return *this; 45 | } 46 | 47 | void SmtpClient::cleanup() { 48 | if (getBatchMode() && mIsConnected) { 49 | sendQuitCommand(); 50 | } 51 | mIsConnected = false; 52 | int socket { getSocketFileDescriptor() }; 53 | if (socket != 0) { 54 | #ifdef _WIN32 55 | shutdown(socket, SD_BOTH); 56 | closesocket(socket); 57 | #else 58 | close(socket); 59 | #endif 60 | } 61 | clearSocketFileDescriptor(); 62 | #ifdef _WIN32 63 | if (isWSAStarted() && WSACleanup() != 0) { 64 | int wsa_retVal = WSAGetLastError(); 65 | std::stringstream ssError; 66 | setLastSocketErrNo(wsa_retVal); 67 | ssError << "WSACleanup failed with error: " << wsa_retVal; 68 | addCommunicationLogItem(ssError.str().c_str()); 69 | } 70 | setWSAStopped(); 71 | #endif 72 | } 73 | 74 | int SmtpClient::establishConnectionWithServer() { 75 | int session_init_return_code = initializeSession(); 76 | if (session_init_return_code != 0) { 77 | return session_init_return_code; 78 | } 79 | 80 | int server_greetings_return_code = checkServerGreetings(); 81 | if (server_greetings_return_code != STATUS_CODE_SERVICE_READY) { 82 | return server_greetings_return_code; 83 | } 84 | 85 | int client_init_return_code = sendServerIdentification(); 86 | if (client_init_return_code != STATUS_CODE_REQUESTED_MAIL_ACTION_OK_OR_COMPLETED) { 87 | return client_init_return_code; 88 | } 89 | 90 | if (getCredentials() != nullptr) { 91 | int client_auth_return_code = authenticateClient(); 92 | if (client_auth_return_code != STATUS_CODE_AUTHENTICATION_SUCCEEDED) { 93 | return client_auth_return_code; 94 | } 95 | } 96 | 97 | return 0; 98 | } 99 | 100 | int SmtpClient::sendCommand(const char *pCommand, int pErrorCode) { 101 | return sendRawCommand(pCommand, pErrorCode); 102 | } 103 | 104 | int SmtpClient::sendCommandWithFeedback(const char *pCommand, int pErrorCode, int pTimeoutCode) { 105 | return sendRawCommand(pCommand, pErrorCode, pTimeoutCode); 106 | } 107 | 108 | int SmtpClient::getServerReply() { 109 | return getRawCommandReply(); 110 | } 111 | -------------------------------------------------------------------------------- /src/smtpclient.h: -------------------------------------------------------------------------------- 1 | #ifndef SMTPCLIENT_H 2 | #define SMTPCLIENT_H 3 | 4 | #include "smtpclientbase.h" 5 | 6 | #ifdef _WIN32 7 | #ifdef SMTPCLIENT_STATIC 8 | #define SMTPCLIENT_API 9 | #elif defined(SMTPCLIENT_EXPORTS) 10 | #define SMTPCLIENT_API __declspec(dllexport) 11 | #else 12 | #define SMTPCLIENT_API __declspec(dllimport) 13 | #endif 14 | #else 15 | #define SMTPCLIENT_API 16 | #endif 17 | 18 | namespace jed_utils { 19 | /** @brief The SmtpClient should be used to communicate with internal relay servers. 20 | * This client doesn't provided encryption for communication. 21 | * The communication is usually done via port 25. 22 | */ 23 | class SMTPCLIENT_API SmtpClient : public SMTPClientBase { 24 | public: 25 | /** 26 | * @brief Construct a new SmtpClient. 27 | * @param pServerName The name of the server. 28 | * Example: smtp.domainexample.com 29 | * @param pPort The server port number. 30 | * Example: 25, 465, 587 31 | */ 32 | SmtpClient(const char *pServerName, unsigned int pPort); 33 | 34 | /** Destructor of the SmtpClient. */ 35 | ~SmtpClient() override; 36 | 37 | /** SmtpClient copy constructor. */ 38 | SmtpClient(const SmtpClient &other) = default; 39 | 40 | /** SmtpClient copy assignment operator. */ 41 | SmtpClient& operator=(const SmtpClient &other); 42 | 43 | /** SmtpClient move constructor. */ 44 | SmtpClient(SmtpClient &&other) noexcept; 45 | 46 | /** SmtpClient move assignment operator. */ 47 | SmtpClient& operator=(SmtpClient &&other) noexcept; 48 | 49 | protected: 50 | void cleanup() override; 51 | // Methods used to establish the connection with server 52 | int establishConnectionWithServer() override; 53 | // Methods to send commands to the server 54 | int sendCommand(const char *pCommand, int pErrorCode) override; 55 | int sendCommandWithFeedback(const char *pCommand, int pErrorCode, int pTimeoutCode) override; 56 | int getServerReply() override; 57 | }; 58 | } // namespace jed_utils 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /src/smtpclientbase.h: -------------------------------------------------------------------------------- 1 | #ifndef SMTPCLIENTBASE_H 2 | #define SMTPCLIENTBASE_H 3 | 4 | #include 5 | #include 6 | #include "attachment.h" 7 | #include "credential.h" 8 | #include "message.h" 9 | #include "messageaddress.h" 10 | #include "serverauthoptions.h" 11 | 12 | #ifdef _WIN32 13 | #ifdef SMTPCLIENT_STATIC 14 | #define SMTPCLIENTBASE_API 15 | #elif defined(SMTPCLIENT_EXPORTS) 16 | #define SMTPCLIENTBASE_API __declspec(dllexport) 17 | #else 18 | #define SMTPCLIENTBASE_API __declspec(dllimport) 19 | #endif 20 | #else 21 | #define SMTPCLIENTBASE_API 22 | #endif 23 | 24 | /** The max length of the communication log */ 25 | #define INITIAL_COMM_LOG_LENGTH 4096 26 | 27 | /** The max length of the server response buffer */ 28 | #define SERVERRESPONSE_BUFFER_LENGTH 1024 29 | 30 | namespace jed_utils { 31 | /** @brief The SMTPClientBase represents the base class for all SMTP clients 32 | * that will or will not use encryption for communication. 33 | */ 34 | class SMTPCLIENTBASE_API SMTPClientBase { 35 | public: 36 | /** 37 | * @brief Construct a new SMTPClientBase. 38 | * @param pServerName The name of the server. 39 | * Example: smtp.domainexample.com 40 | * @param pPort The server port number. 41 | * Example: 25, 465, 587 42 | */ 43 | SMTPClientBase(const char *pServerName, unsigned int pPort); 44 | 45 | /** Destructor of the SMTPClientBase. */ 46 | virtual ~SMTPClientBase(); 47 | 48 | /** SMTPClientBase copy constructor. */ 49 | SMTPClientBase(const SMTPClientBase& other); 50 | 51 | /** SMTPClientBase copy assignment operator. */ 52 | SMTPClientBase& operator=(const SMTPClientBase& other); 53 | 54 | /** SMTPClientBase move constructor. */ 55 | SMTPClientBase(SMTPClientBase&& other) noexcept; 56 | 57 | /** SMTPClientBase move assignment operator. */ 58 | SMTPClientBase& operator=(SMTPClientBase&& other) noexcept; 59 | 60 | /** Return the server name. */ 61 | const char *getServerName() const; 62 | 63 | /** Return the server port number. */ 64 | unsigned int getServerPort() const; 65 | 66 | /** Return the batch mode enable flag. */ 67 | bool getBatchMode() const; 68 | 69 | /** Return the command timeout in seconds. */ 70 | unsigned int getCommandTimeout() const; 71 | 72 | /** Return the communication log produced by the sendMail method. */ 73 | const char *getCommunicationLog() const; 74 | 75 | /** Return the credentials configured. */ 76 | const Credential *getCredentials() const; 77 | 78 | /** 79 | * @brief Set the server name. 80 | * @param pServerName A char array pointer of the server name. 81 | * Example: smtp.domainexample.com 82 | */ 83 | void setServerName(const char *pServerName); 84 | 85 | /** 86 | * @brief Set the server port number. 87 | * @param pPort The port number. 88 | * Example: 25, 465, 587 89 | */ 90 | void setServerPort(unsigned int pPort); 91 | 92 | /** 93 | * @brief Set the batch mode flag. 94 | * @param pEnabled Indicate if the batch mode is enabled. 95 | * Default: false 96 | */ 97 | void setBatchMode(bool pEnabled); 98 | 99 | /** 100 | * @brief Set the command timeout in seconds. 101 | * @param pTimeOutInSeconds The timeout in seconds. 102 | * Default: 3 seconds 103 | */ 104 | void setCommandTimeout(unsigned int pTimeOutInSeconds); 105 | 106 | /** 107 | * @brief Set the credentials. 108 | * @param pCredential The credential containing the username and the password. 109 | */ 110 | void setCredentials(const Credential &pCredential); 111 | 112 | /** 113 | * @brief Indicate if the class will keep using base send command even 114 | * if a child class as overriden the sendCommand and sendCommandWithFeedback. 115 | * 116 | * This is used for example if you are using a secure client class but 117 | * the STARTTLS feature is not available. The communication will then 118 | * remain unsecured. 119 | * @param pValue True to enable this behavior, false for the default 120 | */ 121 | void setKeepUsingBaseSendCommands(bool pValue); 122 | 123 | /** 124 | * @brief Retreive the error message string that correspond to 125 | * the error code provided. 126 | * @return A pointer to an allocated char array that pointed to the 127 | * error message. The user is responsible to delete this pointer after 128 | * usage. 129 | */ 130 | static char *getErrorMessage(int errorCode); 131 | 132 | /** 133 | * @brief This is the reentrant version of the getErrorMessage method 134 | * @param errorCode The error code return by the SMTP client. 135 | * @param errorMessagePtr A pointer to an allocated char array 136 | * @param maxLength The size of the allocated char array. 137 | * @return Return 0 for success, -1 if an error occurred and a positive 138 | * number representing the number of characters copied to errorMessagePtr 139 | * if the message was longer than that allocated char array. 140 | * 141 | * Retreive the error message string that correspond to the error code 142 | * provided. 143 | */ 144 | static int getErrorMessage_r(int errorCode, 145 | char *errorMessagePtr, 146 | const size_t maxLength); 147 | 148 | int sendMail(const Message &pMsg); 149 | 150 | protected: 151 | bool mIsConnected; 152 | virtual void cleanup() = 0; 153 | int getSocketFileDescriptor() const; 154 | void clearSocketFileDescriptor(); 155 | const char *getLastServerResponse() const; 156 | void setLastSocketErrNo(int lastError); 157 | void setAuthenticationOptions(ServerAuthOptions *authOptions); 158 | // Methods used to establish the connection with server 159 | int initializeSession(); 160 | #ifdef _WIN32 161 | int initializeSessionWinSock(); 162 | int setSocketToNonBlockingWinSock(); 163 | int setSocketToBlockingWinSock(); 164 | bool isWSAStarted(); 165 | void setWSAStopped(); 166 | void addWSAMessageToCommunicationLog(const int errorCode); 167 | void doWSACleanup(); 168 | #else 169 | int initializeSessionPOSIX(); 170 | int setSocketToNonBlockingPOSIX(); 171 | int setSocketToBlockingPOSIX(); 172 | #endif 173 | int setSocketToNonBlocking(); 174 | int setSocketToBlocking(); 175 | int sendServerIdentification(); 176 | virtual int establishConnectionWithServer() = 0; 177 | virtual int checkServerGreetings(); 178 | // Methods to send commands to the server 179 | void setLastServerResponse(const char *pResponse); 180 | int sendRawCommand(const char *pCommand, int pErrorCode); 181 | int sendRawCommand(const char *pCommand, int pErrorCode, int pTimeoutCode); 182 | int getRawCommandReply(); 183 | virtual int sendCommand(const char *pCommand, int pErrorCode) = 0; 184 | virtual int sendCommandWithFeedback(const char *pCommand, int pErrorCode, int pTimeoutCode) = 0; 185 | virtual int getServerReply() = 0; 186 | int sendQuitCommand(); 187 | void crossPlatformSleep(unsigned int seconds); 188 | // Methods used for authentication 189 | int authenticateClient(); 190 | int authenticateWithMethodPlain(); 191 | int authenticateWithMethodLogin(); 192 | int authenticatedWithMethodXOauth2(); 193 | // Methods to send an email 194 | int setMailRecipients(const Message &pMsg); 195 | int addMailRecipients(jed_utils::MessageAddress **list, size_t count, const int RECIPIENT_OK); 196 | int setMailHeaders(const Message &pMsg); 197 | int addMailHeader(const char *field, const char *value, int pErrorCode); 198 | int setMailBody(const Message &pMsg); 199 | 200 | void addCommunicationLogItem(const char *pItem, const char *pPrefix = "c"); 201 | static std::string createAttachmentsText(const std::vector &pAttachments); 202 | static int extractReturnCode(const char *pOutput); 203 | static ServerAuthOptions *extractAuthenticationOptions(const char *pEhloOutput); 204 | static std::string generateHeaderAddressValues(const std::vector &pList); 205 | 206 | private: 207 | char *mServerName; 208 | unsigned int mPort; 209 | bool mBatchMode; 210 | char *mCommunicationLog; 211 | size_t mCommunicationLogSize = 0; 212 | char *mLastServerResponse; 213 | unsigned int mCommandTimeOut; 214 | int mLastSocketErrNo; 215 | ServerAuthOptions *mAuthOptions; 216 | Credential *mCredential; 217 | int mSock = 0; 218 | #ifdef _WIN32 219 | bool mWSAStarted = false; 220 | #endif 221 | 222 | // This field indicate the class will keep using base send command even if a child class 223 | // as overriden the sendCommand and sendCommandWithFeedback. 224 | // This is used for example if you are using a secure client class but the STARTTLS 225 | // feature is not available. The communication will then remain unsecured. 226 | bool mKeepUsingBaseSendCommands; 227 | 228 | int (SMTPClientBase::*sendCommandPtr)(const char *pCommand, int pErrorCode); 229 | int (SMTPClientBase::*sendCommandWithFeedbackPtr)(const char *pCommand, int pErrorCode, int pTimeoutCode); 230 | }; 231 | } // namespace jed_utils 232 | 233 | #endif 234 | -------------------------------------------------------------------------------- /src/smtpclienterrors.h: -------------------------------------------------------------------------------- 1 | #ifndef SMTPCLIENTERRORS_H 2 | #define SMTPCLIENTERRORS_H 3 | 4 | // Authenticate error codes 5 | const int CLIENT_AUTHENTICATE_ERROR = -81; 6 | const int CLIENT_AUTHENTICATE_TIMEOUT = -82; 7 | const int CLIENT_AUTHENTICATE_NONEED = -83; 8 | const int CLIENT_AUTHENTICATION_METHOD_NOTSUPPORTED = -84; 9 | 10 | // Send Mail error codes 11 | const int CLIENT_SENDMAIL_MAILFROM_ERROR = -85; 12 | const int CLIENT_SENDMAIL_MAILFROM_TIMEOUT = -86; 13 | const int CLIENT_SENDMAIL_RCPTTO_ERROR = -87; 14 | const int CLIENT_SENDMAIL_RCPTTO_TIMEOUT = -88; 15 | const int CLIENT_SENDMAIL_DATA_ERROR = -89; 16 | const int CLIENT_SENDMAIL_DATA_TIMEOUT = -90; 17 | const int CLIENT_SENDMAIL_HEADERFROM_ERROR = -91; 18 | const int CLIENT_SENDMAIL_HEADERTOANDCC_ERROR = -92; 19 | const int CLIENT_SENDMAIL_HEADERSUBJECT_ERROR = -93; 20 | const int CLIENT_SENDMAIL_HEADERCONTENTTYPE_ERROR = -94; 21 | const int CLIENT_SENDMAIL_BODYPART_ERROR = -95; 22 | const int CLIENT_SENDMAIL_BODY_ERROR = -96; 23 | const int CLIENT_SENDMAIL_END_DATA_ERROR = -97; 24 | const int CLIENT_SENDMAIL_END_DATA_TIMEOUT = -98; 25 | const int CLIENT_SENDMAIL_QUIT_ERROR = -99; 26 | 27 | // SMTP standard error code 28 | const int SMTPSERVER_AUTHENTICATIONREQUIRED_ERROR = 530; 29 | const int SMTPSERVER_AUTHENTICATIONTOOWEAK_ERROR = 534; 30 | const int SMTPSERVER_CREDENTIALSINVALID_ERROR = 535; 31 | const int SMTPSERVER_ENCRYPTIONREQUIREDFORAUTH_ERROR = 538; 32 | 33 | // SMTP non standard error code 34 | const int SMTPSERVER_AUTHENTICATION_UNSUCCESSFUL_GMAIL_ERROR = 334; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/smtpserverstatuscodes.h: -------------------------------------------------------------------------------- 1 | #ifndef SMTPSERVERSTATUSCODES_H 2 | #define SMTPSERVERSTATUSCODES_H 3 | 4 | const int STATUS_CODE_SERVICE_READY = 220; 5 | const int STATUS_CODE_AUTHENTICATION_SUCCEEDED = 235; 6 | const int STATUS_CODE_REQUESTED_MAIL_ACTION_OK_OR_COMPLETED = 250; 7 | const int STATUS_CODE_SERVER_CHALLENGE = 334; 8 | const int STATUS_CODE_START_MAIL_INPUT = 354; 9 | 10 | #endif -------------------------------------------------------------------------------- /src/socketerrors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Init Session error codes 4 | const int SOCKET_INIT_SESSION_CREATION_ERROR = -1; 5 | const int SOCKET_INIT_SESSION_CONNECT_ERROR = -2; 6 | const int SOCKET_INIT_SESSION_CONNECT_TIMEOUT = -3; 7 | const int SOCKET_INIT_SESSION_WINSOCKET_STARTUP_ERROR = -4; 8 | const int SOCKET_INIT_SESSION_WINSOCKET_GETADDRINFO_ERROR = -5; 9 | const int SOCKET_INIT_SESSION_GETHOSTBYNAME_ERROR = -6; 10 | const int SOCKET_INIT_SESSION_FCNTL_GET_ERROR = -7; 11 | const int SOCKET_INIT_SESSION_FCNTL_SET_ERROR = -8; 12 | const int SOCKET_INIT_SESSION_GET_SOCKET_OPTIONS_ERROR = -9; 13 | const int SOCKET_INIT_SESSION_DELAYED_CONNECTION_ERROR = -10; 14 | 15 | // Init Client error codes 16 | const int SOCKET_INIT_CLIENT_SEND_EHLO_ERROR = -20; 17 | const int SOCKET_INIT_CLIENT_SEND_EHLO_TIMEOUT = -21; 18 | 19 | // Init TLS error codes 20 | const int SOCKET_INIT_CLIENT_SEND_STARTTLS_ERROR = -30; 21 | const int SOCKET_INIT_CLIENT_SEND_STARTTLS_TIMEOUT = -31; 22 | -------------------------------------------------------------------------------- /src/sslerrors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Start TLS error codes 4 | const int SSL_CLIENT_STARTTLS_INITSSLCTX_ERROR = -51; 5 | const int SSL_CLIENT_STARTTLS_BIONEWSSLCONNECT_ERROR = -52; 6 | const int SSL_CLIENT_STARTTLS_WIN_CERTOPENSYSTEMSTORE_ERROR = -53; 7 | const int SSL_CLIENT_STARTTLS_CTX_SET_DEFAULT_VERIFY_PATHS_ERROR = -54; 8 | const int SSL_CLIENT_STARTTLS_BIO_CONNECT_ERROR = -55; 9 | const int SSL_CLIENT_STARTTLS_BIO_HANDSHAKE_ERROR = -56; 10 | const int SSL_CLIENT_STARTTLS_GET_CERTIFICATE_ERROR = -57; 11 | const int SSL_CLIENT_STARTTLS_VERIFY_RESULT_ERROR = -58; 12 | 13 | // Init Session error codes 14 | const int SSL_CLIENT_INITSECURECLIENT_ERROR = -59; 15 | const int SSL_CLIENT_INITSECURECLIENT_TIMEOUT = -60; 16 | -------------------------------------------------------------------------------- /src/sslsmtpclient.h: -------------------------------------------------------------------------------- 1 | #ifndef SSLSMTPCLIENT_H 2 | #define SSLSMTPCLIENT_H 3 | 4 | // This header file has been kept for compatiblity purposes 5 | 6 | #include "opportunisticsecuresmtpclient.h" 7 | 8 | namespace jed_utils { 9 | typedef OpportunisticSecureSMTPClient SSLSmtpClient; 10 | } // namespace jed_utils 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/stringutils.cpp: -------------------------------------------------------------------------------- 1 | #include "stringutils.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace jed_utils; 7 | 8 | // Trim from start 9 | std::string StringUtils::trimLeft(const std::string &pString) { 10 | std::string s = pString; 11 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { 12 | return !static_cast(std::isspace(ch)); 13 | })); 14 | return s; 15 | } 16 | 17 | // Trim from end 18 | std::string StringUtils::trimRight(const std::string &pString) { 19 | std::string s = pString; 20 | s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { 21 | return !static_cast(std::isspace(ch)); 22 | }).base(), s.end()); 23 | return s; 24 | } 25 | 26 | // trim from both ends 27 | std::string StringUtils::trim(const std::string &pString) { 28 | std::string s = pString; 29 | s = trimLeft(s); 30 | s = trimRight(s); 31 | return s; 32 | } 33 | 34 | std::string StringUtils::toLower(const std::string &pString) { 35 | std::string transformed_string { pString }; 36 | std::transform(pString.begin(), pString.end(), transformed_string.begin(), 37 | [](unsigned char c){ return static_cast(std::tolower(c)); }); 38 | return transformed_string; 39 | } 40 | 41 | std::string StringUtils::toUpper(const std::string &pString) { 42 | std::string transformed_string { pString }; 43 | std::transform(pString.begin(), pString.end(), transformed_string.begin(), 44 | [](unsigned char c){ return static_cast(std::toupper(c)); }); 45 | return transformed_string; 46 | } 47 | -------------------------------------------------------------------------------- /src/stringutils.h: -------------------------------------------------------------------------------- 1 | #ifndef STRINGUTILS_H 2 | #define STRINGUTILS_H 3 | 4 | #include 5 | 6 | #ifdef _WIN32 7 | #ifdef SMTPCLIENT_STATIC 8 | #define STRINGUTILS_API 9 | #elif defined(SMTPCLIENT_EXPORTS) 10 | #define STRINGUTILS_API __declspec(dllexport) 11 | #else 12 | #define STRINGUTILS_API __declspec(dllimport) 13 | #endif 14 | #else 15 | #define STRINGUTILS_API 16 | #endif 17 | 18 | namespace jed_utils { 19 | /** @brief The StringUtils class provides string utility fonctions 20 | */ 21 | class STRINGUTILS_API StringUtils { 22 | public: 23 | /** 24 | * @brief Remove space at the beginning of a string and return the result. 25 | * @param pString The string that will be processed. 26 | */ 27 | static std::string trimLeft(const std::string &pString); 28 | 29 | /** 30 | * @brief Remove space at the end of a string and return the result. 31 | * @param pString The string that will be processed. 32 | */ 33 | static std::string trimRight(const std::string &pString); 34 | 35 | /** 36 | * @brief Remove space at both the beginning and end of a string and return the result. 37 | * @param pString The string that will be processed. 38 | */ 39 | static std::string trim(const std::string &pString); 40 | 41 | /** 42 | * @brief Convert a string to lowercase and return the result. 43 | * @param pString The string that will be processed. 44 | */ 45 | static std::string toLower(const std::string &pString); 46 | 47 | /** 48 | * @brief Convert a string to uppercase and return the result. 49 | * @param pString The string that will be processed. 50 | */ 51 | static std::string toUpper(const std::string &pString); 52 | }; 53 | } // namespace jed_utils 54 | 55 | 56 | #endif // STRINGUTILS_H 57 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/credential_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/credential.h" 2 | #include "../../src/cpp/credential.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace jed_utils; 8 | 9 | template 10 | class MultiCredentialFixture : public ::testing::Test { 11 | public: 12 | MultiCredentialFixture() 13 | : att("test", "123") { 14 | } 15 | T att; 16 | }; 17 | 18 | using MyTypes = ::testing::Types; 19 | TYPED_TEST_SUITE(MultiCredentialFixture, MyTypes); 20 | 21 | TEST(Credential, Constructor_NullUsername) { 22 | try { 23 | Credential cred(nullptr, "123"); 24 | FAIL(); 25 | } 26 | catch(std::invalid_argument &err) { 27 | ASSERT_STREQ("Username cannot be null or empty", err.what()); 28 | } 29 | } 30 | 31 | TYPED_TEST(MultiCredentialFixture, Constructor_EmptyUsername) { 32 | try { 33 | TypeParam cred("", "123"); 34 | FAIL(); 35 | } 36 | catch(std::invalid_argument &err) { 37 | ASSERT_STREQ("Username cannot be null or empty", err.what()); 38 | } 39 | } 40 | 41 | TYPED_TEST(MultiCredentialFixture, Constructor_OnlySpacesUsername) { 42 | try { 43 | TypeParam cred(" ", "123"); 44 | FAIL(); 45 | } 46 | catch(std::invalid_argument &err) { 47 | ASSERT_STREQ("Username cannot be null or empty", err.what()); 48 | } 49 | } 50 | 51 | TEST(Credential, Constructor_NullPassword) { 52 | try { 53 | Credential cred("test", nullptr); 54 | FAIL(); 55 | } 56 | catch(std::invalid_argument &err) { 57 | ASSERT_STREQ("Password cannot be null or empty", err.what()); 58 | } 59 | } 60 | 61 | TYPED_TEST(MultiCredentialFixture, Constructor_EmptyPassword) { 62 | try { 63 | TypeParam cred("test", ""); 64 | FAIL(); 65 | } 66 | catch(std::invalid_argument &err) { 67 | ASSERT_STREQ("Password cannot be null or empty", err.what()); 68 | } 69 | } 70 | 71 | TYPED_TEST(MultiCredentialFixture, Constructor_ValidOnlySpacesPassword) { 72 | TypeParam cred("Test3", " "); 73 | ASSERT_EQ("Test3", std::string(cred.getUsername())); 74 | ASSERT_EQ(" ", std::string(cred.getPassword())); 75 | } 76 | 77 | TYPED_TEST(MultiCredentialFixture, CopyConstructor_CredentialCopyConstructorValid) { 78 | TypeParam *cred1 = new TypeParam("test", "123"); 79 | TypeParam cred2(*cred1); 80 | delete cred1; 81 | ASSERT_EQ("test", std::string(cred2.getUsername())); 82 | ASSERT_EQ("123", std::string(cred2.getPassword())); 83 | } 84 | 85 | TYPED_TEST(MultiCredentialFixture, CopyAssignment_CredentialCopyAssignmentValid) { 86 | TypeParam *cred1 = new TypeParam("test", "123"); 87 | TypeParam cred2("aaa", "bbb"); 88 | cred2 = *cred1; 89 | delete cred1; 90 | ASSERT_EQ("test", std::string(cred2.getUsername())); 91 | ASSERT_EQ("123", std::string(cred2.getPassword())); 92 | } 93 | 94 | TYPED_TEST(MultiCredentialFixture, MoveConstructor_CredentialMoveConstructorValid) { 95 | TypeParam cred1("test", "123"); 96 | TypeParam cred2(std::move(cred1)); 97 | ASSERT_EQ("test", std::string(cred2.getUsername())); 98 | ASSERT_EQ("123", std::string(cred2.getPassword())); 99 | } 100 | 101 | TYPED_TEST(MultiCredentialFixture, MoveAssignment_CredentialMoveAssignmentValid) { 102 | TypeParam cred1("test", "123"); 103 | TypeParam cred2("aaa", "bbb"); 104 | cred2 = std::move(cred1); 105 | ASSERT_EQ("test", std::string(cred2.getUsername())); 106 | ASSERT_EQ("123", std::string(cred2.getPassword())); 107 | } 108 | 109 | TYPED_TEST(MultiCredentialFixture, getUsername_ValidUsername) { 110 | TypeParam cred("Test3", "123"); 111 | ASSERT_EQ("Test3", std::string(cred.getUsername())); 112 | } 113 | 114 | TYPED_TEST(MultiCredentialFixture, getPassword_ValidPassword) { 115 | TypeParam cred("Test3", "123"); 116 | ASSERT_EQ("123", std::string(cred.getPassword())); 117 | } 118 | 119 | TYPED_TEST(MultiCredentialFixture, setUsername_ValidUsername) { 120 | TypeParam cred("Test3", "123"); 121 | cred.setUsername("Test4"); 122 | ASSERT_EQ("Test4", std::string(cred.getUsername())); 123 | } 124 | 125 | TEST(Credential, setUsername_NullUsername) { 126 | Credential cred("Test", "123"); 127 | try { 128 | cred.setUsername(nullptr); 129 | FAIL(); 130 | } 131 | catch(std::invalid_argument &err) { 132 | ASSERT_STREQ("Username cannot be null or empty", err.what()); 133 | } 134 | } 135 | 136 | TYPED_TEST(MultiCredentialFixture, setUsername_EmptyUsername) { 137 | TypeParam cred("Test", "123"); 138 | try { 139 | cred.setUsername(""); 140 | FAIL(); 141 | } 142 | catch(std::invalid_argument &err) { 143 | ASSERT_STREQ("Username cannot be null or empty", err.what()); 144 | } 145 | } 146 | 147 | TYPED_TEST(MultiCredentialFixture, setUsername_OnlySpacesUsername) { 148 | TypeParam cred("Test", "123"); 149 | try { 150 | cred.setUsername(" "); 151 | FAIL(); 152 | } 153 | catch(std::invalid_argument &err) { 154 | ASSERT_STREQ("Username cannot be null or empty", err.what()); 155 | } 156 | } 157 | 158 | TYPED_TEST(MultiCredentialFixture, setPassword_ValidPassword) { 159 | TypeParam cred("Test3", "123"); 160 | cred.setPassword("432"); 161 | ASSERT_EQ("432", std::string(cred.getPassword())); 162 | } 163 | 164 | TEST(Credential, setPassword_NullPassword) { 165 | Credential cred("Test", "123"); 166 | try { 167 | cred.setPassword(nullptr); 168 | FAIL(); 169 | } 170 | catch(std::invalid_argument &err) { 171 | ASSERT_STREQ("Password cannot be null or empty", err.what()); 172 | } 173 | } 174 | 175 | TYPED_TEST(MultiCredentialFixture, setPassword_EmptyPassword) { 176 | TypeParam cred("Test", "123"); 177 | try { 178 | cred.setPassword(""); 179 | FAIL(); 180 | } 181 | catch(std::invalid_argument &err) { 182 | ASSERT_STREQ("Password cannot be null or empty", err.what()); 183 | } 184 | } 185 | 186 | TYPED_TEST(MultiCredentialFixture, setPassword_ValidOnlySpacesPassword) { 187 | TypeParam cred("Test3", "123"); 188 | cred.setPassword(" "); 189 | ASSERT_EQ(" ", std::string(cred.getPassword())); 190 | } 191 | 192 | TYPED_TEST(MultiCredentialFixture, RecommendedAuthenticationModeImplicit) { 193 | TypeParam cred{"Test", "123"}; 194 | ASSERT_EQ("Test", std::string{cred.getUsername()}); 195 | ASSERT_EQ("123", std::string{cred.getPassword()}); 196 | ASSERT_EQ(RecommendedAuthenticationMethod::kImplicit, 197 | cred.getRecommendedAuthOption()); 198 | 199 | std::vector methods{ 200 | RecommendedAuthenticationMethod::kPlain, 201 | RecommendedAuthenticationMethod::kLogin, 202 | RecommendedAuthenticationMethod::kXOauth2, 203 | RecommendedAuthenticationMethod::kPlainClientToken, 204 | RecommendedAuthenticationMethod::kOAuthBearer, 205 | RecommendedAuthenticationMethod::kXOAuth, 206 | }; 207 | 208 | std::for_each(methods.cbegin(), methods.cend(), 209 | [cred](auto method) mutable { 210 | cred.setRecommendedAuthOption(method); 211 | ASSERT_EQ(method, cred.getRecommendedAuthOption()); 212 | 213 | TypeParam newCred{cred}; 214 | ASSERT_EQ(method, newCred.getRecommendedAuthOption()); 215 | 216 | TypeParam newCred2 = cred; 217 | ASSERT_EQ(method, newCred2.getRecommendedAuthOption()); 218 | 219 | TypeParam movedCred{std::move(newCred)}; 220 | ASSERT_EQ(method, movedCred.getRecommendedAuthOption()); 221 | 222 | TypeParam movedCred2 = std::move(movedCred); 223 | ASSERT_EQ(method, movedCred.getRecommendedAuthOption()); 224 | }); 225 | } 226 | 227 | TYPED_TEST(MultiCredentialFixture, RecommendedAuthenticationModeSpecified) { 228 | std::vector methods{ 229 | RecommendedAuthenticationMethod::kPlain, 230 | RecommendedAuthenticationMethod::kLogin, 231 | RecommendedAuthenticationMethod::kXOauth2, 232 | RecommendedAuthenticationMethod::kPlainClientToken, 233 | RecommendedAuthenticationMethod::kOAuthBearer, 234 | RecommendedAuthenticationMethod::kXOAuth, 235 | }; 236 | 237 | std::for_each(methods.cbegin(), methods.cend(), 238 | [](auto method) { 239 | TypeParam cred{"Test", "123", method}; 240 | ASSERT_EQ("Test", std::string{cred.getUsername()}); 241 | ASSERT_EQ("123", std::string{cred.getPassword()}); 242 | ASSERT_EQ(method, cred.getRecommendedAuthOption()); 243 | }); 244 | } 245 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/htmlmessage_cpp_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/cpp/htmlmessage.hpp" 2 | #include 3 | 4 | using namespace jed_utils::cpp; 5 | 6 | namespace jed_utils_unittest { 7 | namespace cpp_htmlmessage { 8 | 9 | TEST(HTMLMessage_Constructor, WithValidArgs_ReturnSuccess) { 10 | HTMLMessage msg(MessageAddress("from@from.com"), 11 | { MessageAddress("to1@to.com"), MessageAddress("to2@to.com") }, 12 | "Subject", 13 | "Body", 14 | { MessageAddress("cc1@cc.com"), MessageAddress("cc2@cc.com") }, 15 | { MessageAddress("bcc1@bcc.com"), MessageAddress("bcc2@bcc.com") }, 16 | { Attachment("file1.png"), Attachment("file2.png") }); 17 | ASSERT_EQ("from@from.com", msg.getFrom().getEmailAddress()); 18 | ASSERT_EQ("to1@to.com", msg.getTo()[0].getEmailAddress()); 19 | ASSERT_EQ("to2@to.com", msg.getTo()[1].getEmailAddress()); 20 | ASSERT_EQ("Subject", msg.getSubject()); 21 | ASSERT_EQ("Body", msg.getBody()); 22 | ASSERT_EQ("cc1@cc.com", msg.getCc()[0].getEmailAddress()); 23 | ASSERT_EQ("cc2@cc.com", msg.getCc()[1].getEmailAddress()); 24 | ASSERT_EQ("bcc1@bcc.com", msg.getBcc()[0].getEmailAddress()); 25 | ASSERT_EQ("bcc2@bcc.com", msg.getBcc()[1].getEmailAddress()); 26 | ASSERT_EQ("file1.png", msg.getAttachments()[0].getFilename()); 27 | ASSERT_EQ("file2.png", msg.getAttachments()[1].getFilename()); 28 | ASSERT_EQ(2, msg.getAttachmentsCount()); 29 | } 30 | 31 | TEST(HTMLMessage_GetMimeType, ReturnTextHTML) { 32 | HTMLMessage msg(MessageAddress("from@from.com"), 33 | { MessageAddress("to@to.com") }, 34 | "Subject", 35 | "Body"); 36 | ASSERT_EQ("text/html", msg.getMimeType()); 37 | } 38 | 39 | TEST(HTMLMessage_ConversionToStdHtmlMessage, WithSimpleArgs_ReturnSuccess) { 40 | HTMLMessage msg(MessageAddress("from@from.com"), 41 | { MessageAddress("to1@to.com"), MessageAddress("to2@to.com") }, 42 | "Subject", 43 | "Body"); 44 | auto stdMsg = static_cast(msg); 45 | ASSERT_STREQ("from@from.com", stdMsg.getFrom().getEmailAddress()); 46 | ASSERT_STREQ("to1@to.com", stdMsg.getTo()[0]->getEmailAddress()); 47 | ASSERT_STREQ("to2@to.com", stdMsg.getTo()[1]->getEmailAddress()); 48 | ASSERT_STREQ("Subject", stdMsg.getSubject()); 49 | ASSERT_STREQ("Body", stdMsg.getBody()); 50 | } 51 | 52 | TEST(HTMLMessage_ConversionToStdHtmlMessage, WithExtendedArgs_ReturnSuccess) { 53 | HTMLMessage msg(MessageAddress("from@from.com"), 54 | { MessageAddress("to1@to.com"), MessageAddress("to2@to.com") }, 55 | "Subject", 56 | "Body", 57 | { MessageAddress("cc1@cc.com"), MessageAddress("cc2@cc.com") }, 58 | { MessageAddress("bcc1@bcc.com"), MessageAddress("bcc2@bcc.com") }, 59 | { Attachment("file1.png"), Attachment("file2.png") }); 60 | auto stdMsg = static_cast(msg); 61 | ASSERT_STREQ("from@from.com", stdMsg.getFrom().getEmailAddress()); 62 | ASSERT_STREQ("to1@to.com", stdMsg.getTo()[0]->getEmailAddress()); 63 | ASSERT_STREQ("to2@to.com", stdMsg.getTo()[1]->getEmailAddress()); 64 | ASSERT_STREQ("Subject", stdMsg.getSubject()); 65 | ASSERT_STREQ("Body", stdMsg.getBody()); 66 | ASSERT_STREQ("cc1@cc.com", stdMsg.getCc()[0]->getEmailAddress()); 67 | ASSERT_STREQ("cc2@cc.com", stdMsg.getCc()[1]->getEmailAddress()); 68 | ASSERT_STREQ("bcc1@bcc.com", stdMsg.getBcc()[0]->getEmailAddress()); 69 | ASSERT_STREQ("bcc2@bcc.com", stdMsg.getBcc()[1]->getEmailAddress()); 70 | ASSERT_STREQ("file1.png", stdMsg.getAttachments()[0]->getFilename()); 71 | ASSERT_STREQ("file2.png", stdMsg.getAttachments()[1]->getFilename()); 72 | ASSERT_EQ(2, stdMsg.getAttachmentsCount()); 73 | } 74 | 75 | } // namespace cpp_htmlmessage 76 | } // namespace jed_utils_unittest 77 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/main.cpp: -------------------------------------------------------------------------------- 1 | // SMTPClient_Test.cpp : Defines the entry point for the console application. 2 | #include 3 | #include 4 | #include 5 | #include "../../src/smtpclient.h" 6 | 7 | using namespace jed_utils; 8 | 9 | int main(int argc, char**argv) { 10 | ::testing::InitGoogleTest(&argc, argv); 11 | return RUN_ALL_TESTS(); 12 | } 13 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/message_cpp_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/cpp/message.hpp" 2 | #include 3 | 4 | using namespace jed_utils::cpp; 5 | 6 | namespace jed_utils_unittest { 7 | namespace cpp_message { 8 | 9 | 10 | class FakeMessage : public Message { 11 | public: 12 | FakeMessage(const MessageAddress &pFrom, 13 | const std::vector &pTo, 14 | const std::string &pSubject, 15 | const std::string &pBody, 16 | const std::vector &pCc = {}, 17 | const std::vector &pBcc = {}, 18 | const std::vector &pAttachments = {}) 19 | : Message(pFrom, pTo, pSubject, pBody, pCc, pBcc, pAttachments) { 20 | } 21 | 22 | std::string getMimeType() const override { return ""; } 23 | }; 24 | 25 | void validateMinimumDataMessage(const Message &msg) { 26 | ASSERT_EQ("test@test.com", msg.getFrom().getEmailAddress()); 27 | ASSERT_EQ(1, msg.getToCount()); 28 | ASSERT_EQ("test2@test.com", msg.getTo()[0].getEmailAddress()); 29 | ASSERT_EQ("", msg.getSubject()); 30 | ASSERT_EQ("", msg.getBody()); 31 | ASSERT_EQ(0, msg.getCcCount()); 32 | ASSERT_EQ(0, msg.getBccCount()); 33 | ASSERT_EQ(0, msg.getAttachmentsCount()); 34 | } 35 | 36 | FakeMessage getFakeMessageSample1() { 37 | return FakeMessage(MessageAddress("from@from.com"), 38 | { MessageAddress("to@to.com") }, 39 | "Subject", 40 | "Body", 41 | { MessageAddress("cc@cc.com") }, 42 | { MessageAddress("bcc@bcc.com") }, 43 | { Attachment("file1.png"), Attachment("file2.png") }); 44 | } 45 | 46 | FakeMessage getFakeMessageSample2() { 47 | return FakeMessage(MessageAddress("from@from.com"), 48 | { MessageAddress("to1@to.com"), MessageAddress("to2@to.com") }, 49 | "Subject", 50 | "Body", 51 | { MessageAddress("cc1@cc.com"), MessageAddress("cc2@cc.com") }, 52 | { MessageAddress("bcc1@bcc.com"), MessageAddress("bcc2@bcc.com") }, 53 | { Attachment("file1.png"), Attachment("file2.png") }); 54 | } 55 | 56 | void validateFakeMessageSample1(const FakeMessage &msg) { 57 | ASSERT_EQ("from@from.com", msg.getFrom().getEmailAddress()); 58 | ASSERT_EQ("to@to.com", msg.getTo()[0].getEmailAddress()); 59 | ASSERT_EQ("Subject", msg.getSubject()); 60 | ASSERT_EQ("Body", msg.getBody()); 61 | ASSERT_EQ("cc@cc.com", msg.getCc()[0].getEmailAddress()); 62 | ASSERT_EQ("bcc@bcc.com", msg.getBcc()[0].getEmailAddress()); 63 | ASSERT_EQ("file1.png", msg.getAttachments()[0].getFilename()); 64 | ASSERT_EQ("file2.png", msg.getAttachments()[1].getFilename()); 65 | ASSERT_EQ(2, msg.getAttachmentsCount()); 66 | } 67 | 68 | void validateFakeMessageSample2(const FakeMessage &msg) { 69 | ASSERT_EQ("from@from.com", msg.getFrom().getEmailAddress()); 70 | ASSERT_EQ("to1@to.com", msg.getTo()[0].getEmailAddress()); 71 | ASSERT_EQ("to2@to.com", msg.getTo()[1].getEmailAddress()); 72 | ASSERT_EQ("Subject", msg.getSubject()); 73 | ASSERT_EQ("Body", msg.getBody()); 74 | ASSERT_EQ("cc1@cc.com", msg.getCc()[0].getEmailAddress()); 75 | ASSERT_EQ("cc2@cc.com", msg.getCc()[1].getEmailAddress()); 76 | ASSERT_EQ("bcc1@bcc.com", msg.getBcc()[0].getEmailAddress()); 77 | ASSERT_EQ("bcc2@bcc.com", msg.getBcc()[1].getEmailAddress()); 78 | ASSERT_EQ("file1.png", msg.getAttachments()[0].getFilename()); 79 | ASSERT_EQ("file2.png", msg.getAttachments()[1].getFilename()); 80 | ASSERT_EQ(2, msg.getAttachmentsCount()); 81 | } 82 | 83 | TEST(Message_constructor, WithSingleToRecipientAndMinimumData_Success) { 84 | FakeMessage msg(MessageAddress("test@test.com"), 85 | { MessageAddress("test2@test.com") }, 86 | "", 87 | ""); 88 | validateMinimumDataMessage(msg); 89 | } 90 | 91 | TEST(Message_constructor, WithCompleteInfos_Success) { 92 | auto msg = getFakeMessageSample1(); 93 | validateFakeMessageSample1(msg); 94 | } 95 | 96 | TEST(Message_constructor, WithExtendedInfos_Success) { 97 | auto msg = getFakeMessageSample2(); 98 | validateFakeMessageSample2(msg); 99 | } 100 | 101 | TEST(Message_constructor, WithEmptyToArgs_ThrowInvalidArgumentException) { 102 | try { 103 | FakeMessage msg(MessageAddress("from@from.com"), 104 | {}, 105 | "TestSubject", 106 | "TestBody"); 107 | FAIL(); 108 | } 109 | catch(std::invalid_argument &err) { 110 | ASSERT_STREQ("To cannot be empty", err.what()); 111 | } 112 | } 113 | 114 | TEST(Message_CopyConstructor, MessageCopyConstructorValid) { 115 | auto msg1 = getFakeMessageSample2(); 116 | FakeMessage msg2(msg1); 117 | validateFakeMessageSample2(msg2); 118 | } 119 | 120 | TEST(Message_CopyAssignment, MessageCopyAssignmentValid) { 121 | auto msg1 = getFakeMessageSample2(); 122 | auto msg2 = getFakeMessageSample1(); 123 | msg2 = msg1; 124 | validateFakeMessageSample2(msg2); 125 | } 126 | 127 | TEST(Message_MoveConstructor, MessageMoveConstructorValid) { 128 | auto msg1 = getFakeMessageSample2(); 129 | FakeMessage msg2(std::move(msg1)); 130 | validateFakeMessageSample2(msg2); 131 | } 132 | 133 | TEST(Message_MoveAssignment, MessageMoveAssignmentValid) { 134 | auto msg1 = getFakeMessageSample2(); 135 | FakeMessage msg2 = getFakeMessageSample1(); 136 | msg2 = std::move(msg1); 137 | validateFakeMessageSample2(msg2); 138 | } 139 | 140 | } // namespace cpp_message 141 | } // namespace jed_utils_unittest 142 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/message_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/message.h" 2 | #include 3 | 4 | using namespace jed_utils; 5 | 6 | class FakeMessage : public Message { 7 | public: 8 | FakeMessage(const MessageAddress &pFrom, 9 | const MessageAddress &pTo, 10 | const char *pSubject, 11 | const char *pBody, 12 | const MessageAddress *pCc = nullptr, 13 | const MessageAddress *pBcc = nullptr, 14 | const Attachment pAttachments[] = nullptr, 15 | size_t pAttachmentsSize = 0) 16 | : Message(pFrom, pTo, pSubject, pBody, pCc, pBcc, pAttachments, pAttachmentsSize) { 17 | } 18 | 19 | FakeMessage(const MessageAddress &pFrom, 20 | const MessageAddress pTo[], 21 | size_t pToCount, 22 | const char *pSubject, 23 | const char *pBody, 24 | const MessageAddress pCc[] = nullptr, 25 | size_t pCcCount = 0, 26 | const MessageAddress pBcc[] = nullptr, 27 | size_t pBccCount = 0, 28 | const Attachment pAttachments[] = nullptr, 29 | size_t pAttachmentsSize = 0) 30 | : Message(pFrom, pTo, pToCount, pSubject, pBody, pCc, pCcCount, 31 | pBcc, pBccCount, pAttachments, pAttachmentsSize) { 32 | } 33 | 34 | const char *getMimeType() const override { return nullptr; } 35 | }; 36 | 37 | void validateMinimumDataMessage(const Message &msg) { 38 | ASSERT_STREQ("test@test.com", msg.getFrom().getEmailAddress()); 39 | ASSERT_EQ(1, msg.getToCount()); 40 | ASSERT_STREQ("test2@test.com", msg.getTo()[0]->getEmailAddress()); 41 | ASSERT_STREQ("", msg.getSubject()); 42 | ASSERT_STREQ("", msg.getBody()); 43 | ASSERT_EQ(0, msg.getCcCount()); 44 | ASSERT_EQ(nullptr, msg.getCc()); 45 | ASSERT_EQ(0, msg.getBccCount()); 46 | ASSERT_EQ(nullptr, msg.getBcc()); 47 | ASSERT_EQ(0, msg.getAttachmentsCount()); 48 | ASSERT_EQ(nullptr, msg.getAttachments()); 49 | } 50 | 51 | FakeMessage getFakeMessageSample1() { 52 | MessageAddress *cc = new MessageAddress("cc@cc.com"); 53 | MessageAddress *bcc = new MessageAddress("bcc@bcc.com"); 54 | Attachment att[] { Attachment("file1.png"), Attachment("file2.png") }; 55 | return FakeMessage(MessageAddress("from@from.com"), 56 | MessageAddress("to@to.com"), 57 | "Subject", 58 | "Body", 59 | cc, 60 | bcc, 61 | att, 2); 62 | } 63 | 64 | FakeMessage getFakeMessageSample2() { 65 | MessageAddress to[2] { MessageAddress("to1@to.com"), MessageAddress("to2@to.com") }; 66 | MessageAddress cc[2] = { MessageAddress("cc1@cc.com"), MessageAddress("cc2@cc.com") }; 67 | MessageAddress bcc[2] = { MessageAddress("bcc1@bcc.com"), MessageAddress("bcc2@bcc.com") }; 68 | Attachment att[] { Attachment("file1.png"), Attachment("file2.png") }; 69 | return FakeMessage(MessageAddress("from@from.com"), 70 | to, 2, 71 | "Subject", 72 | "Body", 73 | cc, 2, 74 | bcc, 2, 75 | att, 2); 76 | } 77 | 78 | void validateFakeMessageSample1(const FakeMessage &msg) { 79 | ASSERT_STREQ("from@from.com", msg.getFrom().getEmailAddress()); 80 | ASSERT_STREQ("to@to.com", msg.getTo()[0]->getEmailAddress()); 81 | ASSERT_STREQ("Subject", msg.getSubject()); 82 | ASSERT_STREQ("Body", msg.getBody()); 83 | ASSERT_STREQ("cc@cc.com", msg.getCc()[0]->getEmailAddress()); 84 | ASSERT_STREQ("bcc@bcc.com", msg.getBcc()[0]->getEmailAddress()); 85 | ASSERT_STREQ("file1.png", msg.getAttachments()[0]->getFilename()); 86 | ASSERT_STREQ("file2.png", msg.getAttachments()[1]->getFilename()); 87 | ASSERT_EQ(2, msg.getAttachmentsCount()); 88 | } 89 | 90 | void validateFakeMessageSample2(const FakeMessage &msg) { 91 | ASSERT_STREQ("from@from.com", msg.getFrom().getEmailAddress()); 92 | ASSERT_STREQ("to1@to.com", msg.getTo()[0]->getEmailAddress()); 93 | ASSERT_STREQ("to2@to.com", msg.getTo()[1]->getEmailAddress()); 94 | ASSERT_STREQ("Subject", msg.getSubject()); 95 | ASSERT_STREQ("Body", msg.getBody()); 96 | ASSERT_STREQ("cc1@cc.com", msg.getCc()[0]->getEmailAddress()); 97 | ASSERT_STREQ("cc2@cc.com", msg.getCc()[1]->getEmailAddress()); 98 | ASSERT_STREQ("bcc1@bcc.com", msg.getBcc()[0]->getEmailAddress()); 99 | ASSERT_STREQ("bcc2@bcc.com", msg.getBcc()[1]->getEmailAddress()); 100 | ASSERT_STREQ("file1.png", msg.getAttachments()[0]->getFilename()); 101 | ASSERT_STREQ("file2.png", msg.getAttachments()[1]->getFilename()); 102 | ASSERT_EQ(2, msg.getAttachmentsCount()); 103 | } 104 | 105 | TEST(Message_constructor, WithSingleToRecipientAndMinimumData_Success) { 106 | FakeMessage msg(MessageAddress("test@test.com"), 107 | MessageAddress("test2@test.com"), 108 | "", 109 | ""); 110 | validateMinimumDataMessage(msg); 111 | } 112 | 113 | TEST(Message_constructor, WithSingleToRecipientAndNullSubject_Success) { 114 | FakeMessage msg(MessageAddress("test@test.com"), 115 | MessageAddress("test2@test.com"), 116 | nullptr, 117 | ""); 118 | validateMinimumDataMessage(msg); 119 | } 120 | 121 | TEST(Message_constructor, WithSingleToRecipientAndNullBody_Success) { 122 | FakeMessage msg(MessageAddress("test@test.com"), 123 | MessageAddress("test2@test.com"), 124 | "", 125 | nullptr); 126 | validateMinimumDataMessage(msg); 127 | } 128 | 129 | TEST(Message_constructor, WithCompleteInfos_Success) { 130 | auto msg = getFakeMessageSample1(); 131 | validateFakeMessageSample1(msg); 132 | } 133 | 134 | TEST(Message_constructor, WithExtendedInfos_Success) { 135 | auto msg = getFakeMessageSample2(); 136 | validateFakeMessageSample2(msg); 137 | } 138 | 139 | TEST(Message_CopyConstructor, MessageCopyConstructorValid) { 140 | auto msg1 = getFakeMessageSample2(); 141 | FakeMessage msg2(msg1); 142 | validateFakeMessageSample2(msg2); 143 | } 144 | 145 | TEST(Message_CopyAssignment, MessageCopyAssignmentValid) { 146 | auto msg1 = getFakeMessageSample2(); 147 | auto msg2 = getFakeMessageSample1(); 148 | msg2 = msg1; 149 | validateFakeMessageSample2(msg2); 150 | } 151 | 152 | TEST(Message_MoveConstructor, MessageMoveConstructorValid) { 153 | auto msg1 = getFakeMessageSample2(); 154 | FakeMessage msg2(std::move(msg1)); 155 | validateFakeMessageSample2(msg2); 156 | } 157 | 158 | TEST(Message_MoveAssignment, MessageMoveAssignmentValid) { 159 | auto msg1 = getFakeMessageSample2(); 160 | FakeMessage msg2 = getFakeMessageSample1(); 161 | msg2 = std::move(msg1); 162 | validateFakeMessageSample2(msg2); 163 | } 164 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/messageaddress_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/messageaddress.h" 2 | #include "../../src/cpp/messageaddress.hpp" 3 | #include 4 | #include 5 | 6 | using namespace jed_utils; 7 | 8 | template 9 | class MultiMessageAddressFixture : public ::testing::Test { 10 | public: 11 | MultiMessageAddressFixture() 12 | : msg_add("test@domain", "Test Address") { 13 | } 14 | T msg_add; 15 | }; 16 | 17 | using MyTypes = ::testing::Types; 18 | TYPED_TEST_SUITE(MultiMessageAddressFixture, MyTypes); 19 | 20 | TYPED_TEST(MultiMessageAddressFixture, constructor_EmptyEmailAddrInvalidArgument) { 21 | // Test with empty filename 22 | try { 23 | TypeParam msg_add("", ""); 24 | FAIL(); 25 | } 26 | catch (std::invalid_argument) { 27 | } 28 | } 29 | 30 | TYPED_TEST(MultiMessageAddressFixture, constructor_EmptyEmailAddressValidDNThrowInvalidArgument) { 31 | try { 32 | TypeParam msg_add("", "Test Address"); 33 | FAIL(); 34 | } 35 | catch(std::invalid_argument) { 36 | } 37 | } 38 | 39 | TYPED_TEST(MultiMessageAddressFixture, constructor_WhiteSpacesEmailAddrInvalidArgument) { 40 | // Test with empty filename 41 | try { 42 | TypeParam msg_add(" ", ""); 43 | FAIL(); 44 | } 45 | catch (std::invalid_argument) { 46 | } 47 | } 48 | 49 | TYPED_TEST(MultiMessageAddressFixture, constructor_OnlyPrefixEmailAddrInvalidArgument) { 50 | // Test with empty filename 51 | try { 52 | TypeParam msg_add("test", ""); 53 | FAIL(); 54 | } 55 | catch (std::invalid_argument) { 56 | } 57 | } 58 | 59 | TYPED_TEST(MultiMessageAddressFixture, constructor_OnlyAtSignEmailAddrInvalidArgument) { 60 | // Test with empty filename 61 | try { 62 | TypeParam msg_add("@", ""); 63 | FAIL(); 64 | } 65 | catch (std::invalid_argument) { 66 | } 67 | } 68 | 69 | TYPED_TEST(MultiMessageAddressFixture, constructor_NoSuffixEmailAddrInvalidArgument) { 70 | // Test with empty filename 71 | try { 72 | TypeParam msg_add("test@", ""); 73 | FAIL(); 74 | } 75 | catch (std::invalid_argument) { 76 | } 77 | } 78 | 79 | TYPED_TEST(MultiMessageAddressFixture, constructor_NoExtensionEmailAddrValidParam) { 80 | TypeParam msg_add("test@domain", "Test Address"); 81 | ASSERT_EQ("test@domain", std::string(msg_add.getEmailAddress())); 82 | ASSERT_EQ("Test Address", std::string(msg_add.getDisplayName())); 83 | } 84 | 85 | TYPED_TEST(MultiMessageAddressFixture, constructor_ValidParam) { 86 | TypeParam msg_add("test@domain.com", "Test Address"); 87 | ASSERT_EQ("test@domain.com", std::string(msg_add.getEmailAddress())); 88 | ASSERT_EQ("Test Address", std::string(msg_add.getDisplayName())); 89 | } 90 | 91 | TYPED_TEST(MultiMessageAddressFixture, constructor_ValidParamWithUpperCaseCharEmailAddress) { 92 | TypeParam msg_add("myaddress@gmail.com", "Test Address"); 93 | ASSERT_EQ("myaddress@gmail.com", std::string(msg_add.getEmailAddress())); 94 | ASSERT_EQ("Test Address", std::string(msg_add.getDisplayName())); 95 | } 96 | 97 | TYPED_TEST(MultiMessageAddressFixture, CopyConstructor_MessageAddressCopyConstructorValid) { 98 | TypeParam msg_add1("myaddress@gmail.com", "Test Address"); 99 | TypeParam msg_add2(msg_add1); 100 | ASSERT_EQ("myaddress@gmail.com", std::string(msg_add1.getEmailAddress())); 101 | ASSERT_EQ("Test Address", std::string(msg_add1.getDisplayName())); 102 | ASSERT_EQ("myaddress@gmail.com", std::string(msg_add2.getEmailAddress())); 103 | ASSERT_EQ("Test Address", std::string(msg_add2.getDisplayName())); 104 | } 105 | 106 | TYPED_TEST(MultiMessageAddressFixture, CopyAssignment_MessageAddressCopyAssignmentValid) { 107 | TypeParam msg_add1("test@ccc.com", "123"); 108 | TypeParam msg_add2("aaa@test.com", "bbb"); 109 | msg_add2 = msg_add1; 110 | ASSERT_EQ("test@ccc.com", std::string(msg_add1.getEmailAddress())); 111 | ASSERT_EQ("123", std::string(msg_add1.getDisplayName())); 112 | ASSERT_EQ("test@ccc.com", std::string(msg_add2.getEmailAddress())); 113 | ASSERT_EQ("123", std::string(msg_add2.getDisplayName())); 114 | } 115 | 116 | TYPED_TEST(MultiMessageAddressFixture, MoveConstructor_MessageAddressMoveConstructorValid) { 117 | TypeParam msg_add1("test@aaa.com", "123"); 118 | TypeParam msg_add2(std::move(msg_add1)); 119 | ASSERT_EQ("test@aaa.com", std::string(msg_add2.getEmailAddress())); 120 | ASSERT_EQ("123", std::string(msg_add2.getDisplayName())); 121 | } 122 | 123 | TYPED_TEST(MultiMessageAddressFixture, MoveAssignment_MessageAddressMoveAssignmentValid) { 124 | TypeParam msg_add1("test@aaa.com", "123"); 125 | TypeParam msg_add2("aaa@bbb.com", "bbb"); 126 | msg_add2 = std::move(msg_add1); 127 | ASSERT_EQ("test@aaa.com", std::string(msg_add2.getEmailAddress())); 128 | ASSERT_EQ("123", std::string(msg_add2.getDisplayName())); 129 | } 130 | 131 | TYPED_TEST(MultiMessageAddressFixture, get_email_address_validDomain) { 132 | TypeParam msg_add("test@domain.com", "Test Address"); 133 | ASSERT_EQ("test@domain.com", std::string(msg_add.getEmailAddress())); 134 | } 135 | 136 | TYPED_TEST(MultiMessageAddressFixture, get_display_name_validDN) { 137 | TypeParam msg_add("test@domain.com", "Test Address"); 138 | ASSERT_EQ("Test Address", std::string(msg_add.getDisplayName())); 139 | } 140 | 141 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/opportunisticsecuresmtpclient_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/opportunisticsecuresmtpclient.h" 2 | #include "../../src/cpp/opportunisticsecuresmtpclient.hpp" 3 | #include 4 | 5 | using namespace jed_utils; 6 | 7 | class FakeOpportunisticSecureSMTPClient : public ::testing::Test, public OpportunisticSecureSMTPClient { 8 | public: 9 | FakeOpportunisticSecureSMTPClient() 10 | : OpportunisticSecureSMTPClient("127.0.0.1", 587) { 11 | } 12 | }; 13 | 14 | template 15 | class MultiOppSmtpClientFixture : public ::testing::Test { 16 | public: 17 | MultiOppSmtpClientFixture() 18 | : client("test", 587) { 19 | } 20 | T client; 21 | }; 22 | 23 | using MyTypes = ::testing::Types; 24 | TYPED_TEST_SUITE(MultiOppSmtpClientFixture, MyTypes); 25 | 26 | TEST(OpportunisticSecureSMTPClient_Constructor, NullServerName_ThrowInvalidArgument) { 27 | try { 28 | OpportunisticSecureSMTPClient client(nullptr, 587); 29 | FAIL(); 30 | } 31 | catch(std::invalid_argument &err) { 32 | ASSERT_STREQ("Server name cannot be null or empty", err.what()); 33 | } 34 | } 35 | 36 | TEST(OpportunisticSecureSMTPClient_Constructor, EmptyServerName_ThrowInvalidArgument) { 37 | try { 38 | OpportunisticSecureSMTPClient client("", 587); 39 | FAIL(); 40 | } 41 | catch(std::invalid_argument &err) { 42 | ASSERT_STREQ("Server name cannot be null or empty", err.what()); 43 | } 44 | } 45 | 46 | TEST(OpportunisticSecureSMTPClient_Constructor, OnlySpacesServerName_ThrowInvalidArgument) { 47 | try { 48 | OpportunisticSecureSMTPClient client(" ", 587); 49 | FAIL(); 50 | } 51 | catch(std::invalid_argument &err) { 52 | ASSERT_STREQ("Server name cannot be null or empty", err.what()); 53 | } 54 | } 55 | 56 | TEST(OpportunisticSecureSMTPClient_Constructor, ValidArguments_ReturnSuccess) { 57 | OpportunisticSecureSMTPClient client("test", 587); 58 | ASSERT_STREQ("test", client.getServerName()); 59 | ASSERT_EQ(587, client.getServerPort()); 60 | } 61 | 62 | TEST(OpportunisticSecureSMTPClient_CopyConstructor, OpportunisticSecureSMTPClientCopyConstructorValid) { 63 | OpportunisticSecureSMTPClient* client1 = new OpportunisticSecureSMTPClient("server1", 123); 64 | client1->setCredentials(Credential("user1", "pass1")); 65 | client1->setCommandTimeout(8); 66 | OpportunisticSecureSMTPClient client2(*client1); 67 | delete client1; 68 | ASSERT_STREQ("server1", client2.getServerName()); 69 | ASSERT_EQ(123, client2.getServerPort()); 70 | ASSERT_EQ(8, client2.getCommandTimeout()); 71 | ASSERT_STREQ("user1", client2.getCredentials()->getUsername()); 72 | ASSERT_STREQ("pass1", client2.getCredentials()->getPassword()); 73 | } 74 | 75 | TEST(OpportunisticSecureSMTPClient_CopyAssignment, OpportunisticSecureSMTPClientCopyAssignmentValid) { 76 | OpportunisticSecureSMTPClient* client1 = new OpportunisticSecureSMTPClient("test", 123); 77 | client1->setCredentials(Credential("user1", "pass1")); 78 | client1->setCommandTimeout(8); 79 | OpportunisticSecureSMTPClient client2("aaa", 456); 80 | client2 = *client1; 81 | delete client1; 82 | ASSERT_STREQ("test", client2.getServerName()); 83 | ASSERT_EQ(123, client2.getServerPort()); 84 | ASSERT_EQ(8, client2.getCommandTimeout()); 85 | ASSERT_STREQ("user1", client2.getCredentials()->getUsername()); 86 | ASSERT_STREQ("pass1", client2.getCredentials()->getPassword()); 87 | } 88 | 89 | TEST(OpportunisticSecureSMTPClient_MoveConstructor, OpportunisticSecureSMTPClientMoveConstructorValid) { 90 | OpportunisticSecureSMTPClient client1("test", 123); 91 | client1.setCredentials(Credential("user1", "pass1")); 92 | client1.setCommandTimeout(8); 93 | OpportunisticSecureSMTPClient client2(std::move(client1)); 94 | ASSERT_STREQ("test", client2.getServerName()); 95 | ASSERT_EQ(123, client2.getServerPort()); 96 | ASSERT_EQ(8, client2.getCommandTimeout()); 97 | ASSERT_STREQ("user1", client2.getCredentials()->getUsername()); 98 | ASSERT_STREQ("pass1", client2.getCredentials()->getPassword()); 99 | } 100 | 101 | TEST(OpportunisticSecureSMTPClient_MoveAssignment, OpportunisticSecureSMTPClientMoveAssignmentValid) { 102 | OpportunisticSecureSMTPClient client1("test", 123); 103 | client1.setCredentials(Credential("user1", "pass1")); 104 | client1.setCommandTimeout(8); 105 | OpportunisticSecureSMTPClient client2("aaa", 456); 106 | client2 = std::move(client1); 107 | ASSERT_STREQ("test", client2.getServerName()); 108 | ASSERT_EQ(123, client2.getServerPort()); 109 | ASSERT_EQ(8, client2.getCommandTimeout()); 110 | ASSERT_STREQ("user1", client2.getCredentials()->getUsername()); 111 | ASSERT_STREQ("pass1", client2.getCredentials()->getPassword()); 112 | } 113 | 114 | TEST_F(FakeOpportunisticSecureSMTPClient, isStartTLSSupported_WithNullServerReponse_ReturnFalse) { 115 | ASSERT_FALSE(isStartTLSSupported(nullptr)); 116 | } 117 | 118 | TEST_F(FakeOpportunisticSecureSMTPClient, isStartTLSSupported_WithEmptyServerReponse_ReturnFalse) { 119 | ASSERT_FALSE(isStartTLSSupported("")); 120 | } 121 | 122 | TEST_F(FakeOpportunisticSecureSMTPClient, isStartTLSSupported_WithWhiteSpacesServerReponse_ReturnFalse) { 123 | ASSERT_FALSE(isStartTLSSupported(" ")); 124 | } 125 | 126 | TEST_F(FakeOpportunisticSecureSMTPClient, isStartTLSSupported_WithServerReponseContainingSTARTTLS_ReturnTrue) { 127 | ASSERT_TRUE(isStartTLSSupported("250-SIZE 35882577\r\n" 128 | "250-8BITMIME\r\n" 129 | "250-STARTTLS\r\n" 130 | "250-ENHANCEDSTATUSCODES\r\n" 131 | "250-PIPELINING\r\n" 132 | "250-CHUNKING\r\n" 133 | "250 SMTPUTF8")); 134 | } 135 | 136 | TEST_F(FakeOpportunisticSecureSMTPClient, isStartTLSSupported_WithServerReponseContainingSTARTTLSFirstLine_ReturnTrue) { 137 | ASSERT_TRUE(isStartTLSSupported("250-STARTTLS\r\n" 138 | "250-SIZE 35882577\r\n" 139 | "250-8BITMIME\r\n" 140 | "250-ENHANCEDSTATUSCODES\r\n" 141 | "250-PIPELINING\r\n" 142 | "250-CHUNKING\r\n" 143 | "250 SMTPUTF8")); 144 | } 145 | 146 | TEST_F(FakeOpportunisticSecureSMTPClient, isStartTLSSupported_WithServerReponseContainingSTARTTLSLastLine_ReturnTrue) { 147 | ASSERT_TRUE(isStartTLSSupported("250-SIZE 35882577\r\n" 148 | "250-8BITMIME\r\n" 149 | "250-ENHANCEDSTATUSCODES\r\n" 150 | "250-PIPELINING\r\n" 151 | "250-CHUNKING\r\n" 152 | "250-SMTPUTF8\r\n" 153 | "250 STARTTLS")); 154 | } 155 | 156 | TEST_F(FakeOpportunisticSecureSMTPClient, isStartTLSSupported_WithServerReponseContainingSTARTTLSAsOnlyLine_ReturnTrue) { 157 | ASSERT_TRUE(isStartTLSSupported("250 STARTTLS")); 158 | } 159 | 160 | TEST_F(FakeOpportunisticSecureSMTPClient, isStartTLSSupported_WithoutServerReponseContainingSTARTTLS_ReturnFalse) { 161 | ASSERT_FALSE(isStartTLSSupported("250-SIZE 35882577\r\n" 162 | "250-8BITMIME\r\n" 163 | "250-ENHANCEDSTATUSCODES\r\n" 164 | "250-PIPELINING\r\n" 165 | "250-CHUNKING\r\n" 166 | "250 SMTPUTF8")); 167 | } 168 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/plaintextmessage_cpp_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/cpp/plaintextmessage.hpp" 2 | #include 3 | 4 | using namespace jed_utils::cpp; 5 | 6 | namespace jed_utils_unittest { 7 | namespace cpp_plaintextmessage { 8 | 9 | TEST(PlaintextMessage_Constructor, WithValidArgs_ReturnSuccess) { 10 | PlaintextMessage msg(MessageAddress("from@from.com"), 11 | { MessageAddress("to1@to.com"), MessageAddress("to2@to.com") }, 12 | "Subject", 13 | "Body", 14 | { MessageAddress("cc1@cc.com"), MessageAddress("cc2@cc.com") }, 15 | { MessageAddress("bcc1@bcc.com"), MessageAddress("bcc2@bcc.com") }, 16 | { Attachment("file1.png"), Attachment("file2.png") }); 17 | ASSERT_EQ("from@from.com", msg.getFrom().getEmailAddress()); 18 | ASSERT_EQ("to1@to.com", msg.getTo()[0].getEmailAddress()); 19 | ASSERT_EQ("to2@to.com", msg.getTo()[1].getEmailAddress()); 20 | ASSERT_EQ("Subject", msg.getSubject()); 21 | ASSERT_EQ("Body", msg.getBody()); 22 | ASSERT_EQ("cc1@cc.com", msg.getCc()[0].getEmailAddress()); 23 | ASSERT_EQ("cc2@cc.com", msg.getCc()[1].getEmailAddress()); 24 | ASSERT_EQ("bcc1@bcc.com", msg.getBcc()[0].getEmailAddress()); 25 | ASSERT_EQ("bcc2@bcc.com", msg.getBcc()[1].getEmailAddress()); 26 | ASSERT_EQ("file1.png", msg.getAttachments()[0].getFilename()); 27 | ASSERT_EQ("file2.png", msg.getAttachments()[1].getFilename()); 28 | ASSERT_EQ(2, msg.getAttachmentsCount()); 29 | } 30 | 31 | TEST(PlaintextMessage_GetMimeType, ReturnTextPlaintext) { 32 | PlaintextMessage msg(MessageAddress("from@from.com"), 33 | { MessageAddress("to@to.com") }, 34 | "Subject", 35 | "Body"); 36 | ASSERT_EQ("text/plain", msg.getMimeType()); 37 | } 38 | 39 | TEST(PlaintextMessage_ConversionToStdPlaintextMessage, WithExtendedArgs_ReturnSuccess) { 40 | PlaintextMessage msg(MessageAddress("from@from.com"), 41 | { MessageAddress("to1@to.com"), MessageAddress("to2@to.com") }, 42 | "Subject", 43 | "Body", 44 | { MessageAddress("cc1@cc.com"), MessageAddress("cc2@cc.com") }, 45 | { MessageAddress("bcc1@bcc.com"), MessageAddress("bcc2@bcc.com") }, 46 | { Attachment("file1.png"), Attachment("file2.png") }); 47 | auto stdMsg = static_cast(msg); 48 | ASSERT_STREQ("from@from.com", stdMsg.getFrom().getEmailAddress()); 49 | ASSERT_STREQ("to1@to.com", stdMsg.getTo()[0]->getEmailAddress()); 50 | ASSERT_STREQ("to2@to.com", stdMsg.getTo()[1]->getEmailAddress()); 51 | ASSERT_STREQ("Subject", stdMsg.getSubject()); 52 | ASSERT_STREQ("Body", stdMsg.getBody()); 53 | ASSERT_STREQ("cc1@cc.com", stdMsg.getCc()[0]->getEmailAddress()); 54 | ASSERT_STREQ("cc2@cc.com", stdMsg.getCc()[1]->getEmailAddress()); 55 | ASSERT_STREQ("bcc1@bcc.com", stdMsg.getBcc()[0]->getEmailAddress()); 56 | ASSERT_STREQ("bcc2@bcc.com", stdMsg.getBcc()[1]->getEmailAddress()); 57 | ASSERT_STREQ("file1.png", stdMsg.getAttachments()[0]->getFilename()); 58 | ASSERT_STREQ("file2.png", stdMsg.getAttachments()[1]->getFilename()); 59 | ASSERT_EQ(2, stdMsg.getAttachmentsCount()); 60 | } 61 | 62 | } // namespace cpp_plainmessage 63 | } // namespace jed_utils_unittest 64 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/plaintextmessage_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../../src/plaintextmessage.h" 5 | 6 | using namespace jed_utils; 7 | 8 | class SimpleMessage : public ::testing::Test { 9 | public: 10 | SimpleMessage() { 11 | msg = new PlaintextMessage(MessageAddress("myfromaddress@test.com", "Test Address Display"), 12 | MessageAddress("youraddress@domain.com"), 13 | "This is a test (Subject)", 14 | "Hello\nHow are you?"); 15 | } 16 | 17 | ~SimpleMessage() { 18 | if (msg) { 19 | delete msg; 20 | } 21 | } 22 | 23 | PlaintextMessage *msg; 24 | }; 25 | 26 | class CompleteMessageWithSimpleConstructor : public ::testing::Test { 27 | public: 28 | CompleteMessageWithSimpleConstructor() { 29 | Attachment att[2] { Attachment("C:\\Temp\\test.png", "test image.png"), Attachment("C:\\Temp\\test2.png", "test image2.png") }; 30 | MessageAddress cc[1] { MessageAddress("myccaddress@domain.com", "myCCName") }; 31 | MessageAddress bcc[1] { MessageAddress("mybccaddress@domain.com", "myBCCName") }; 32 | msg = new PlaintextMessage(MessageAddress("myfromaddress@test.com", "Test Address Display"), 33 | MessageAddress("youraddress@domain.com", "DisplayNameTo"), 34 | "This is a test (Subject)", 35 | "Hello\nHow are you?", 36 | cc, 37 | bcc, 38 | att); 39 | } 40 | 41 | ~CompleteMessageWithSimpleConstructor() { 42 | if (msg) { 43 | delete msg; 44 | } 45 | } 46 | 47 | PlaintextMessage *msg; 48 | }; 49 | 50 | TEST(plainTextMessage_SimpleConstructor, validParams) { 51 | PlaintextMessage(MessageAddress("myfromaddress@test.com", "Test Address Display"), 52 | MessageAddress("youraddress@domain.com"), 53 | "This is a test (Subject)", 54 | "Hello\nHow are you?"); 55 | } 56 | 57 | TEST(plainTextMessage_ExtendedConstructor, validParams) { 58 | MessageAddress addr_to[1] { MessageAddress("youraddress@domain.com") }; 59 | PlaintextMessage(MessageAddress("myfromaddress@test.com", "Test Address Display"), 60 | *addr_to, 61 | "This is a test (Subject)", 62 | "Hello\nHow are you?"); 63 | } 64 | 65 | // Simple Message fixture tests 66 | TEST_F(SimpleMessage, getFromReturnValid) { 67 | ASSERT_EQ(msg->getFrom(), MessageAddress("myfromaddress@test.com", "Test Address Display")); 68 | } 69 | 70 | TEST_F(SimpleMessage, getToCountReturnValid) { 71 | ASSERT_EQ(msg->getToCount(), 1); 72 | } 73 | 74 | TEST_F(SimpleMessage, getToReturnValid) { 75 | ASSERT_EQ(msg->getToCount(), 1); 76 | ASSERT_EQ(*msg->getTo()[0], MessageAddress("youraddress@domain.com")); 77 | } 78 | 79 | TEST_F(SimpleMessage, getSubjectReturnValid) { 80 | ASSERT_STREQ("This is a test (Subject)", msg->getSubject()); 81 | } 82 | 83 | TEST_F(SimpleMessage, getBodyReturnValid) { 84 | ASSERT_STREQ("Hello\nHow are you?", msg->getBody()); 85 | } 86 | 87 | TEST_F(SimpleMessage, getCcCountReturnValid) { 88 | ASSERT_EQ(msg->getCcCount(), 0); 89 | } 90 | 91 | TEST_F(SimpleMessage, getCcReturnNull) { 92 | ASSERT_EQ(nullptr, msg->getCc()); 93 | } 94 | 95 | TEST_F(SimpleMessage, getAttachmentsCountReturn0) { 96 | ASSERT_EQ(msg->getAttachmentsCount(), 0); 97 | } 98 | 99 | TEST_F(SimpleMessage, getAttachmentsPtrReturnNull) { 100 | ASSERT_EQ(nullptr, msg->getAttachments()); 101 | } 102 | 103 | TEST_F(SimpleMessage, getMimeTypeReturnValidPlainText) { 104 | ASSERT_STREQ("text/plain", msg->getMimeType()); 105 | } 106 | 107 | // End Simple Message fixture tests 108 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/serveroptionsanalyzer_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../src/serveroptionsanalyzer.h" 3 | 4 | using namespace jed_utils; 5 | 6 | TEST(ServerOptionsAnalyzer_ContainsAllOptions, WithEmpty_ReturnFalse) { 7 | ASSERT_FALSE(ServerOptionsAnalyzer::containsAllOptions("")); 8 | } 9 | 10 | TEST(ServerOptionsAnalyzer_ContainsAllOptions, With250HypenSTARTTLS_ReturnFALSE) { 11 | ASSERT_FALSE(ServerOptionsAnalyzer::containsAllOptions("250-STARTTLS")); 12 | } 13 | 14 | TEST(ServerOptionsAnalyzer_ContainsAllOptions, With250SpaceSTARTTLS_ReturnTrue) { 15 | ASSERT_TRUE(ServerOptionsAnalyzer::containsAllOptions("250 STARTTLS")); 16 | } 17 | 18 | TEST(ServerOptionsAnalyzer_ContainsAllOptions, With250HypenSTARTTLSrn250SpaceSize_ReturnTrue) { 19 | ASSERT_TRUE(ServerOptionsAnalyzer::containsAllOptions("250-STARTTLS\r\n250 SIZE 53477376")); 20 | } 21 | 22 | TEST(ServerOptionsAnalyzer_ContainsAllOptions, With250HypenSTARTTLSrn250HypenSize_ReturnFalse) { 23 | ASSERT_FALSE(ServerOptionsAnalyzer::containsAllOptions("250-STARTTLS\r\n250-SIZE 53477376")); 24 | } 25 | 26 | TEST(ServerOptionsAnalyzer_ContainsAllOptions, With250HypenSTARTTLSrn250HypenSizern_ReturnTrue) { 27 | ASSERT_TRUE(ServerOptionsAnalyzer::containsAllOptions("250-STARTTLS\r\n250 SIZE 53477376\r\n")); 28 | } 29 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/smtpclient_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include "../../src/smtpclient.h" 2 | #include "../../src/cpp/smtpclient.hpp" 3 | #include 4 | 5 | using namespace jed_utils; 6 | 7 | class FakeSMTPClient : public ::testing::Test, public SmtpClient { 8 | public: 9 | FakeSMTPClient() 10 | : SmtpClient("127.0.0.1", 587) { 11 | } 12 | }; 13 | 14 | TEST(SmtpClient_Constructor, NullServerName_ThrowInvalidArgument) { 15 | try { 16 | SmtpClient client(nullptr, 587); 17 | FAIL(); 18 | } 19 | catch(std::invalid_argument &err) { 20 | ASSERT_STREQ("Server name cannot be null or empty", err.what()); 21 | } 22 | } 23 | 24 | TEST(SmtpClient_Constructor, EmptyServerName_ThrowInvalidArgument) { 25 | try { 26 | SmtpClient client("", 587); 27 | FAIL(); 28 | } 29 | catch(std::invalid_argument &err) { 30 | ASSERT_STREQ("Server name cannot be null or empty", err.what()); 31 | } 32 | } 33 | 34 | TEST(SmtpClient_Constructor, OnlySpacesServerName_ThrowInvalidArgument) { 35 | try { 36 | SmtpClient client(" ", 587); 37 | FAIL(); 38 | } 39 | catch(std::invalid_argument &err) { 40 | ASSERT_STREQ("Server name cannot be null or empty", err.what()); 41 | } 42 | } 43 | 44 | TEST(SmtpClient_Constructor, ValidArguments_ReturnSuccess) { 45 | SmtpClient client("test", 587); 46 | ASSERT_STREQ("test", client.getServerName()); 47 | ASSERT_EQ(587, client.getServerPort()); 48 | } 49 | 50 | TEST(SmtpClient_CopyConstructor, SmtpClientCopyConstructorValid) { 51 | SmtpClient* client1 = new SmtpClient("server1", 123); 52 | client1->setCredentials(Credential("user1", "pass1")); 53 | client1->setCommandTimeout(8); 54 | SmtpClient client2(*client1); 55 | delete client1; 56 | ASSERT_STREQ("server1", client2.getServerName()); 57 | ASSERT_EQ(123, client2.getServerPort()); 58 | ASSERT_EQ(8, client2.getCommandTimeout()); 59 | ASSERT_STREQ("user1", client2.getCredentials()->getUsername()); 60 | ASSERT_STREQ("pass1", client2.getCredentials()->getPassword()); 61 | } 62 | 63 | TEST(SmtpClient_CopyAssignment, SmtpClientCopyAssignmentValid) { 64 | SmtpClient* client1 = new SmtpClient("test", 123); 65 | client1->setCredentials(Credential("user1", "pass1")); 66 | client1->setCommandTimeout(8); 67 | SmtpClient client2("aaa", 456); 68 | client2 = *client1; 69 | delete client1; 70 | ASSERT_STREQ("test", client2.getServerName()); 71 | ASSERT_EQ(123, client2.getServerPort()); 72 | ASSERT_EQ(8, client2.getCommandTimeout()); 73 | ASSERT_STREQ("user1", client2.getCredentials()->getUsername()); 74 | ASSERT_STREQ("pass1", client2.getCredentials()->getPassword()); 75 | } 76 | 77 | TEST(SmtpClient_MoveConstructor, SmtpClientMoveConstructorValid) { 78 | SmtpClient client1("test", 123); 79 | client1.setCredentials(Credential("user1", "pass1")); 80 | client1.setCommandTimeout(8); 81 | SmtpClient client2(std::move(client1)); 82 | ASSERT_STREQ("test", client2.getServerName()); 83 | ASSERT_EQ(123, client2.getServerPort()); 84 | ASSERT_EQ(8, client2.getCommandTimeout()); 85 | ASSERT_STREQ("user1", client2.getCredentials()->getUsername()); 86 | ASSERT_STREQ("pass1", client2.getCredentials()->getPassword()); 87 | } 88 | 89 | TEST(SmtpClient_MoveAssignment, SmtpClientMoveAssignmentValid) { 90 | SmtpClient client1("test", 123); 91 | client1.setCredentials(Credential("user1", "pass1")); 92 | client1.setCommandTimeout(8); 93 | SmtpClient client2("aaa", 456); 94 | client2 = std::move(client1); 95 | ASSERT_STREQ("test", client2.getServerName()); 96 | ASSERT_EQ(123, client2.getServerPort()); 97 | ASSERT_EQ(8, client2.getCommandTimeout()); 98 | ASSERT_STREQ("user1", client2.getCredentials()->getUsername()); 99 | ASSERT_STREQ("pass1", client2.getCredentials()->getPassword()); 100 | } 101 | -------------------------------------------------------------------------------- /test/smtpclient_unittest/stringutils_unittest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../../src/stringutils.h" 3 | 4 | using namespace jed_utils; 5 | 6 | TEST(TrimLeft, TrimOneSpace) { 7 | std::string s = " "; 8 | ASSERT_EQ("", StringUtils::trimLeft(s)); 9 | } 10 | 11 | TEST(TrimLeft, TrimTwoSpaces) { 12 | std::string s = " "; 13 | ASSERT_EQ("", StringUtils::trimLeft(s)); 14 | } 15 | 16 | TEST(TrimLeft, TrimWordOneSpaceLeft) { 17 | std::string s = " Test "; 18 | ASSERT_EQ("Test ", StringUtils::trimLeft(s)); 19 | } 20 | 21 | TEST(TrimLeft, TrimWordTwoSpacesLeft) { 22 | std::string s = " Test "; 23 | ASSERT_EQ("Test ", StringUtils::trimLeft(s)); 24 | } 25 | 26 | TEST(TrimLeft, TrimTwoWordsTwoSpacesLeft) { 27 | std::string s = " Test 123 "; 28 | ASSERT_EQ("Test 123 ", StringUtils::trimLeft(s)); 29 | } 30 | 31 | TEST(TrimRight, TrimOneSpace) { 32 | std::string s = " "; 33 | ASSERT_EQ("", StringUtils::trimRight(s)); 34 | } 35 | 36 | TEST(TrimRight, TrimTwoSpaces) { 37 | std::string s = " "; 38 | ASSERT_EQ("", StringUtils::trimRight(s)); 39 | } 40 | 41 | TEST(TrimRight, TrimWordOneSpaceRight) { 42 | std::string s = " Test "; 43 | ASSERT_EQ(" Test", StringUtils::trimRight(s)); 44 | } 45 | 46 | TEST(TrimRight, TrimWordTwoSpacesRight) { 47 | std::string s = " Test "; 48 | ASSERT_EQ(" Test", StringUtils::trimRight(s)); 49 | } 50 | 51 | TEST(TrimRight, TrimTwoWordsTwoSpacesRight) { 52 | std::string s = " Test 123 "; 53 | ASSERT_EQ(" Test 123", StringUtils::trimRight(s)); 54 | } 55 | 56 | TEST(TrimBoth, TrimOneSpace) { 57 | std::string s = " "; 58 | ASSERT_EQ("", StringUtils::trim(s)); 59 | } 60 | 61 | TEST(TrimBoth, TrimTwoSpaces) { 62 | std::string s = " "; 63 | ASSERT_EQ("", StringUtils::trim(s)); 64 | } 65 | 66 | TEST(TrimBoth, TrimOneWordTwoSpacesLeftAndRight) { 67 | std::string s = " Test "; 68 | ASSERT_EQ("Test", StringUtils::trim(s)); 69 | } 70 | 71 | TEST(TrimBoth, TrimTwoWordsOneSpaceLeftAndRight) { 72 | std::string s = " Test 123 "; 73 | ASSERT_EQ("Test 123", StringUtils::trim(s)); 74 | } 75 | 76 | TEST(TrimBoth, TrimTwoWordsTwoSpacesLeftAndRight) { 77 | std::string s = " Test 123 "; 78 | ASSERT_EQ("Test 123", StringUtils::trim(s)); 79 | } 80 | 81 | TEST(ToLower, EmptyString) { 82 | ASSERT_EQ("", StringUtils::toLower("")); 83 | } 84 | 85 | TEST(ToLower, OnlySpaces) { 86 | ASSERT_EQ(" ", StringUtils::toLower(" ")); 87 | } 88 | 89 | TEST(ToLower, AllCaps) { 90 | ASSERT_EQ("test", StringUtils::toLower("TEST")); 91 | } 92 | 93 | TEST(ToLower, AllLowerCase) { 94 | ASSERT_EQ("test", StringUtils::toLower("test")); 95 | } 96 | 97 | TEST(ToUpper, EmptyString) { 98 | ASSERT_EQ("", StringUtils::toUpper("")); 99 | } 100 | 101 | TEST(ToUpper, OnlySpaces) { 102 | ASSERT_EQ(" ", StringUtils::toUpper(" ")); 103 | } 104 | 105 | TEST(ToUpper, AllCaps) { 106 | ASSERT_EQ("TEST", StringUtils::toUpper("TEST")); 107 | } 108 | 109 | TEST(ToUpper, AllLowerCase) { 110 | ASSERT_EQ("TEST", StringUtils::toUpper("test")); 111 | } 112 | --------------------------------------------------------------------------------