Webserver+3d print
cyclone_tcp/smtp/smtp_client.c@0:8918a71cdbe9, 2017-02-04 (annotated)
- Committer:
- Sergunb
- Date:
- Sat Feb 04 18:15:49 2017 +0000
- Revision:
- 0:8918a71cdbe9
nothing else
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Sergunb | 0:8918a71cdbe9 | 1 | /** |
Sergunb | 0:8918a71cdbe9 | 2 | * @file smtp_client.c |
Sergunb | 0:8918a71cdbe9 | 3 | * @brief SMTP client (Simple Mail Transfer Protocol) |
Sergunb | 0:8918a71cdbe9 | 4 | * |
Sergunb | 0:8918a71cdbe9 | 5 | * @section License |
Sergunb | 0:8918a71cdbe9 | 6 | * |
Sergunb | 0:8918a71cdbe9 | 7 | * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. |
Sergunb | 0:8918a71cdbe9 | 8 | * |
Sergunb | 0:8918a71cdbe9 | 9 | * This file is part of CycloneTCP Open. |
Sergunb | 0:8918a71cdbe9 | 10 | * |
Sergunb | 0:8918a71cdbe9 | 11 | * This program is free software; you can redistribute it and/or |
Sergunb | 0:8918a71cdbe9 | 12 | * modify it under the terms of the GNU General Public License |
Sergunb | 0:8918a71cdbe9 | 13 | * as published by the Free Software Foundation; either version 2 |
Sergunb | 0:8918a71cdbe9 | 14 | * of the License, or (at your option) any later version. |
Sergunb | 0:8918a71cdbe9 | 15 | * |
Sergunb | 0:8918a71cdbe9 | 16 | * This program is distributed in the hope that it will be useful, |
Sergunb | 0:8918a71cdbe9 | 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
Sergunb | 0:8918a71cdbe9 | 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
Sergunb | 0:8918a71cdbe9 | 19 | * GNU General Public License for more details. |
Sergunb | 0:8918a71cdbe9 | 20 | * |
Sergunb | 0:8918a71cdbe9 | 21 | * You should have received a copy of the GNU General Public License |
Sergunb | 0:8918a71cdbe9 | 22 | * along with this program; if not, write to the Free Software Foundation, |
Sergunb | 0:8918a71cdbe9 | 23 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
Sergunb | 0:8918a71cdbe9 | 24 | * |
Sergunb | 0:8918a71cdbe9 | 25 | * @section Description |
Sergunb | 0:8918a71cdbe9 | 26 | * |
Sergunb | 0:8918a71cdbe9 | 27 | * SMTP is designed as a mail transport and delivery protocol. Refer to |
Sergunb | 0:8918a71cdbe9 | 28 | * the following RFCs for complete details: |
Sergunb | 0:8918a71cdbe9 | 29 | * - RFC 5321: Simple Mail Transfer Protocol |
Sergunb | 0:8918a71cdbe9 | 30 | * - RFC 4954: SMTP Service Extension for Authentication |
Sergunb | 0:8918a71cdbe9 | 31 | * - RFC 3207: SMTP Service Extension for Secure SMTP over TLS |
Sergunb | 0:8918a71cdbe9 | 32 | * |
Sergunb | 0:8918a71cdbe9 | 33 | * @author Oryx Embedded SARL (www.oryx-embedded.com) |
Sergunb | 0:8918a71cdbe9 | 34 | * @version 1.7.6 |
Sergunb | 0:8918a71cdbe9 | 35 | **/ |
Sergunb | 0:8918a71cdbe9 | 36 | |
Sergunb | 0:8918a71cdbe9 | 37 | //Switch to the appropriate trace level |
Sergunb | 0:8918a71cdbe9 | 38 | #define TRACE_LEVEL SMTP_TRACE_LEVEL |
Sergunb | 0:8918a71cdbe9 | 39 | |
Sergunb | 0:8918a71cdbe9 | 40 | //Dependencies |
Sergunb | 0:8918a71cdbe9 | 41 | #include <string.h> |
Sergunb | 0:8918a71cdbe9 | 42 | #include <ctype.h> |
Sergunb | 0:8918a71cdbe9 | 43 | #include <stdlib.h> |
Sergunb | 0:8918a71cdbe9 | 44 | #include "core/net.h" |
Sergunb | 0:8918a71cdbe9 | 45 | #include "smtp/smtp_client.h" |
Sergunb | 0:8918a71cdbe9 | 46 | #include "core/socket.h" |
Sergunb | 0:8918a71cdbe9 | 47 | #include "str.h" |
Sergunb | 0:8918a71cdbe9 | 48 | #include "debug.h" |
Sergunb | 0:8918a71cdbe9 | 49 | |
Sergunb | 0:8918a71cdbe9 | 50 | //Check TCP/IP stack configuration |
Sergunb | 0:8918a71cdbe9 | 51 | #if (SMTP_CLIENT_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 52 | |
Sergunb | 0:8918a71cdbe9 | 53 | |
Sergunb | 0:8918a71cdbe9 | 54 | /** |
Sergunb | 0:8918a71cdbe9 | 55 | * @brief Send a mail to the specified recipients |
Sergunb | 0:8918a71cdbe9 | 56 | * @param[in] authInfo Authentication information |
Sergunb | 0:8918a71cdbe9 | 57 | * @param[in] mail Mail contents |
Sergunb | 0:8918a71cdbe9 | 58 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 59 | **/ |
Sergunb | 0:8918a71cdbe9 | 60 | |
Sergunb | 0:8918a71cdbe9 | 61 | error_t smtpSendMail(const SmtpAuthInfo *authInfo, const SmtpMail *mail) |
Sergunb | 0:8918a71cdbe9 | 62 | { |
Sergunb | 0:8918a71cdbe9 | 63 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 64 | uint_t i; |
Sergunb | 0:8918a71cdbe9 | 65 | uint_t replyCode; |
Sergunb | 0:8918a71cdbe9 | 66 | IpAddr serverIpAddr; |
Sergunb | 0:8918a71cdbe9 | 67 | SmtpClientContext *context; |
Sergunb | 0:8918a71cdbe9 | 68 | |
Sergunb | 0:8918a71cdbe9 | 69 | //Check parameters |
Sergunb | 0:8918a71cdbe9 | 70 | if(authInfo == NULL || mail == NULL) |
Sergunb | 0:8918a71cdbe9 | 71 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 72 | //Make sure the server name is valid |
Sergunb | 0:8918a71cdbe9 | 73 | if(authInfo->serverName == NULL) |
Sergunb | 0:8918a71cdbe9 | 74 | return ERROR_INVALID_PARAMETER; |
Sergunb | 0:8918a71cdbe9 | 75 | |
Sergunb | 0:8918a71cdbe9 | 76 | //Debug message |
Sergunb | 0:8918a71cdbe9 | 77 | TRACE_INFO("Sending a mail to %s port %" PRIu16 "...\r\n", |
Sergunb | 0:8918a71cdbe9 | 78 | authInfo->serverName, authInfo->serverPort); |
Sergunb | 0:8918a71cdbe9 | 79 | |
Sergunb | 0:8918a71cdbe9 | 80 | //The specified SMTP server can be either an IP or a host name |
Sergunb | 0:8918a71cdbe9 | 81 | error = getHostByName(authInfo->interface, |
Sergunb | 0:8918a71cdbe9 | 82 | authInfo->serverName, &serverIpAddr, 0); |
Sergunb | 0:8918a71cdbe9 | 83 | //Unable to resolve server name? |
Sergunb | 0:8918a71cdbe9 | 84 | if(error) |
Sergunb | 0:8918a71cdbe9 | 85 | return ERROR_NAME_RESOLUTION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 86 | |
Sergunb | 0:8918a71cdbe9 | 87 | //Allocate a memory buffer to hold the SMTP client context |
Sergunb | 0:8918a71cdbe9 | 88 | context = osAllocMem(sizeof(SmtpClientContext)); |
Sergunb | 0:8918a71cdbe9 | 89 | //Failed to allocate memory? |
Sergunb | 0:8918a71cdbe9 | 90 | if(context == NULL) |
Sergunb | 0:8918a71cdbe9 | 91 | return ERROR_OUT_OF_MEMORY; |
Sergunb | 0:8918a71cdbe9 | 92 | |
Sergunb | 0:8918a71cdbe9 | 93 | //Open a TCP socket |
Sergunb | 0:8918a71cdbe9 | 94 | context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); |
Sergunb | 0:8918a71cdbe9 | 95 | //Failed to open socket? |
Sergunb | 0:8918a71cdbe9 | 96 | if(!context->socket) |
Sergunb | 0:8918a71cdbe9 | 97 | { |
Sergunb | 0:8918a71cdbe9 | 98 | //Free previously allocated resources |
Sergunb | 0:8918a71cdbe9 | 99 | osFreeMem(context); |
Sergunb | 0:8918a71cdbe9 | 100 | //Report an error |
Sergunb | 0:8918a71cdbe9 | 101 | return ERROR_OPEN_FAILED; |
Sergunb | 0:8918a71cdbe9 | 102 | } |
Sergunb | 0:8918a71cdbe9 | 103 | |
Sergunb | 0:8918a71cdbe9 | 104 | #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 105 | //Do not use SSL/TLS for the moment |
Sergunb | 0:8918a71cdbe9 | 106 | context->tlsContext = NULL; |
Sergunb | 0:8918a71cdbe9 | 107 | #endif |
Sergunb | 0:8918a71cdbe9 | 108 | |
Sergunb | 0:8918a71cdbe9 | 109 | //Start of exception handling block |
Sergunb | 0:8918a71cdbe9 | 110 | do |
Sergunb | 0:8918a71cdbe9 | 111 | { |
Sergunb | 0:8918a71cdbe9 | 112 | //Bind the socket to a particular network interface? |
Sergunb | 0:8918a71cdbe9 | 113 | if(authInfo->interface) |
Sergunb | 0:8918a71cdbe9 | 114 | { |
Sergunb | 0:8918a71cdbe9 | 115 | //Associate the socket with the relevant interface |
Sergunb | 0:8918a71cdbe9 | 116 | error = socketBindToInterface(context->socket, authInfo->interface); |
Sergunb | 0:8918a71cdbe9 | 117 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 118 | if(error) |
Sergunb | 0:8918a71cdbe9 | 119 | break; |
Sergunb | 0:8918a71cdbe9 | 120 | } |
Sergunb | 0:8918a71cdbe9 | 121 | |
Sergunb | 0:8918a71cdbe9 | 122 | //Set timeout for blocking operations |
Sergunb | 0:8918a71cdbe9 | 123 | error = socketSetTimeout(context->socket, SMTP_CLIENT_DEFAULT_TIMEOUT); |
Sergunb | 0:8918a71cdbe9 | 124 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 125 | if(error) |
Sergunb | 0:8918a71cdbe9 | 126 | break; |
Sergunb | 0:8918a71cdbe9 | 127 | |
Sergunb | 0:8918a71cdbe9 | 128 | //Connect to the SMTP server |
Sergunb | 0:8918a71cdbe9 | 129 | error = socketConnect(context->socket, &serverIpAddr, authInfo->serverPort); |
Sergunb | 0:8918a71cdbe9 | 130 | //Connection to server failed? |
Sergunb | 0:8918a71cdbe9 | 131 | if(error) |
Sergunb | 0:8918a71cdbe9 | 132 | break; |
Sergunb | 0:8918a71cdbe9 | 133 | |
Sergunb | 0:8918a71cdbe9 | 134 | #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 135 | //Open a secure SSL/TLS session? |
Sergunb | 0:8918a71cdbe9 | 136 | if(authInfo->useTls) |
Sergunb | 0:8918a71cdbe9 | 137 | { |
Sergunb | 0:8918a71cdbe9 | 138 | //Initialize TLS context |
Sergunb | 0:8918a71cdbe9 | 139 | context->tlsContext = tlsInit(); |
Sergunb | 0:8918a71cdbe9 | 140 | //Initialization failed? |
Sergunb | 0:8918a71cdbe9 | 141 | if(context->tlsContext == NULL) |
Sergunb | 0:8918a71cdbe9 | 142 | { |
Sergunb | 0:8918a71cdbe9 | 143 | //Unable to allocate memory |
Sergunb | 0:8918a71cdbe9 | 144 | error = ERROR_OUT_OF_MEMORY; |
Sergunb | 0:8918a71cdbe9 | 145 | //Stop immediately |
Sergunb | 0:8918a71cdbe9 | 146 | break; |
Sergunb | 0:8918a71cdbe9 | 147 | } |
Sergunb | 0:8918a71cdbe9 | 148 | |
Sergunb | 0:8918a71cdbe9 | 149 | //Bind TLS to the relevant socket |
Sergunb | 0:8918a71cdbe9 | 150 | error = tlsSetSocket(context->tlsContext, context->socket); |
Sergunb | 0:8918a71cdbe9 | 151 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 152 | if(error) |
Sergunb | 0:8918a71cdbe9 | 153 | break; |
Sergunb | 0:8918a71cdbe9 | 154 | |
Sergunb | 0:8918a71cdbe9 | 155 | //Select client operation mode |
Sergunb | 0:8918a71cdbe9 | 156 | error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT); |
Sergunb | 0:8918a71cdbe9 | 157 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 158 | if(error) |
Sergunb | 0:8918a71cdbe9 | 159 | break; |
Sergunb | 0:8918a71cdbe9 | 160 | |
Sergunb | 0:8918a71cdbe9 | 161 | //Set the PRNG algorithm to be used |
Sergunb | 0:8918a71cdbe9 | 162 | error = tlsSetPrng(context->tlsContext, authInfo->prngAlgo, authInfo->prngContext); |
Sergunb | 0:8918a71cdbe9 | 163 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 164 | if(error) |
Sergunb | 0:8918a71cdbe9 | 165 | break; |
Sergunb | 0:8918a71cdbe9 | 166 | |
Sergunb | 0:8918a71cdbe9 | 167 | //Perform TLS handshake |
Sergunb | 0:8918a71cdbe9 | 168 | error = tlsConnect(context->tlsContext); |
Sergunb | 0:8918a71cdbe9 | 169 | //Failed to established a TLS session? |
Sergunb | 0:8918a71cdbe9 | 170 | if(error) |
Sergunb | 0:8918a71cdbe9 | 171 | break; |
Sergunb | 0:8918a71cdbe9 | 172 | } |
Sergunb | 0:8918a71cdbe9 | 173 | #endif |
Sergunb | 0:8918a71cdbe9 | 174 | |
Sergunb | 0:8918a71cdbe9 | 175 | //Wait for the connection greeting reply |
Sergunb | 0:8918a71cdbe9 | 176 | error = smtpSendCommand(context, NULL, &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 177 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 178 | if(error) |
Sergunb | 0:8918a71cdbe9 | 179 | break; |
Sergunb | 0:8918a71cdbe9 | 180 | |
Sergunb | 0:8918a71cdbe9 | 181 | //Check whether the greeting message was properly received |
Sergunb | 0:8918a71cdbe9 | 182 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 183 | { |
Sergunb | 0:8918a71cdbe9 | 184 | //An unexpected response was received... |
Sergunb | 0:8918a71cdbe9 | 185 | error = ERROR_UNEXPECTED_RESPONSE; |
Sergunb | 0:8918a71cdbe9 | 186 | //Stop immediately |
Sergunb | 0:8918a71cdbe9 | 187 | break; |
Sergunb | 0:8918a71cdbe9 | 188 | } |
Sergunb | 0:8918a71cdbe9 | 189 | |
Sergunb | 0:8918a71cdbe9 | 190 | //Clear security features |
Sergunb | 0:8918a71cdbe9 | 191 | context->authLoginSupported = FALSE; |
Sergunb | 0:8918a71cdbe9 | 192 | context->authPlainSupported = FALSE; |
Sergunb | 0:8918a71cdbe9 | 193 | context->authCramMd5Supported = FALSE; |
Sergunb | 0:8918a71cdbe9 | 194 | context->startTlsSupported = FALSE; |
Sergunb | 0:8918a71cdbe9 | 195 | |
Sergunb | 0:8918a71cdbe9 | 196 | //Send EHLO command and parse server response |
Sergunb | 0:8918a71cdbe9 | 197 | error = smtpSendCommand(context, "EHLO [127.0.0.1]\r\n", |
Sergunb | 0:8918a71cdbe9 | 198 | &replyCode, smtpEhloReplyCallback); |
Sergunb | 0:8918a71cdbe9 | 199 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 200 | if(error) |
Sergunb | 0:8918a71cdbe9 | 201 | break; |
Sergunb | 0:8918a71cdbe9 | 202 | |
Sergunb | 0:8918a71cdbe9 | 203 | //Check SMTP response code |
Sergunb | 0:8918a71cdbe9 | 204 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 205 | { |
Sergunb | 0:8918a71cdbe9 | 206 | //An unexpected response was received... |
Sergunb | 0:8918a71cdbe9 | 207 | error = ERROR_UNEXPECTED_RESPONSE; |
Sergunb | 0:8918a71cdbe9 | 208 | //Stop immediately |
Sergunb | 0:8918a71cdbe9 | 209 | break; |
Sergunb | 0:8918a71cdbe9 | 210 | } |
Sergunb | 0:8918a71cdbe9 | 211 | |
Sergunb | 0:8918a71cdbe9 | 212 | #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 213 | //Check whether the STARTTLS command is supported |
Sergunb | 0:8918a71cdbe9 | 214 | if(context->startTlsSupported && !context->tlsContext) |
Sergunb | 0:8918a71cdbe9 | 215 | { |
Sergunb | 0:8918a71cdbe9 | 216 | //Send STARTTLS command |
Sergunb | 0:8918a71cdbe9 | 217 | error = smtpSendCommand(context, "STARTTLS\r\n", &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 218 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 219 | if(error) |
Sergunb | 0:8918a71cdbe9 | 220 | break; |
Sergunb | 0:8918a71cdbe9 | 221 | |
Sergunb | 0:8918a71cdbe9 | 222 | //Check SMTP response code |
Sergunb | 0:8918a71cdbe9 | 223 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 224 | { |
Sergunb | 0:8918a71cdbe9 | 225 | //An unexpected response was received... |
Sergunb | 0:8918a71cdbe9 | 226 | error = ERROR_UNEXPECTED_RESPONSE; |
Sergunb | 0:8918a71cdbe9 | 227 | //Stop immediately |
Sergunb | 0:8918a71cdbe9 | 228 | break; |
Sergunb | 0:8918a71cdbe9 | 229 | } |
Sergunb | 0:8918a71cdbe9 | 230 | |
Sergunb | 0:8918a71cdbe9 | 231 | //Initialize TLS context |
Sergunb | 0:8918a71cdbe9 | 232 | context->tlsContext = tlsInit(); |
Sergunb | 0:8918a71cdbe9 | 233 | //Initialization failed? |
Sergunb | 0:8918a71cdbe9 | 234 | if(context->tlsContext == NULL) |
Sergunb | 0:8918a71cdbe9 | 235 | { |
Sergunb | 0:8918a71cdbe9 | 236 | //Unable to allocate memory |
Sergunb | 0:8918a71cdbe9 | 237 | error = ERROR_OUT_OF_MEMORY; |
Sergunb | 0:8918a71cdbe9 | 238 | //Stop immediately |
Sergunb | 0:8918a71cdbe9 | 239 | break; |
Sergunb | 0:8918a71cdbe9 | 240 | } |
Sergunb | 0:8918a71cdbe9 | 241 | |
Sergunb | 0:8918a71cdbe9 | 242 | //Bind TLS to the relevant socket |
Sergunb | 0:8918a71cdbe9 | 243 | error = tlsSetSocket(context->tlsContext, context->socket); |
Sergunb | 0:8918a71cdbe9 | 244 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 245 | if(error) |
Sergunb | 0:8918a71cdbe9 | 246 | break; |
Sergunb | 0:8918a71cdbe9 | 247 | |
Sergunb | 0:8918a71cdbe9 | 248 | //Select client operation mode |
Sergunb | 0:8918a71cdbe9 | 249 | error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT); |
Sergunb | 0:8918a71cdbe9 | 250 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 251 | if(error) |
Sergunb | 0:8918a71cdbe9 | 252 | break; |
Sergunb | 0:8918a71cdbe9 | 253 | |
Sergunb | 0:8918a71cdbe9 | 254 | //Set the PRNG algorithm to be used |
Sergunb | 0:8918a71cdbe9 | 255 | error = tlsSetPrng(context->tlsContext, authInfo->prngAlgo, authInfo->prngContext); |
Sergunb | 0:8918a71cdbe9 | 256 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 257 | if(error) |
Sergunb | 0:8918a71cdbe9 | 258 | break; |
Sergunb | 0:8918a71cdbe9 | 259 | |
Sergunb | 0:8918a71cdbe9 | 260 | //Perform TLS handshake |
Sergunb | 0:8918a71cdbe9 | 261 | error = tlsConnect(context->tlsContext); |
Sergunb | 0:8918a71cdbe9 | 262 | //Failed to established a TLS session? |
Sergunb | 0:8918a71cdbe9 | 263 | if(error) |
Sergunb | 0:8918a71cdbe9 | 264 | break; |
Sergunb | 0:8918a71cdbe9 | 265 | |
Sergunb | 0:8918a71cdbe9 | 266 | //Clear security features |
Sergunb | 0:8918a71cdbe9 | 267 | context->authLoginSupported = FALSE; |
Sergunb | 0:8918a71cdbe9 | 268 | context->authPlainSupported = FALSE; |
Sergunb | 0:8918a71cdbe9 | 269 | context->authCramMd5Supported = FALSE; |
Sergunb | 0:8918a71cdbe9 | 270 | |
Sergunb | 0:8918a71cdbe9 | 271 | //Send EHLO command and parse server response |
Sergunb | 0:8918a71cdbe9 | 272 | error = smtpSendCommand(context, "EHLO [127.0.0.1]\r\n", |
Sergunb | 0:8918a71cdbe9 | 273 | &replyCode, smtpEhloReplyCallback); |
Sergunb | 0:8918a71cdbe9 | 274 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 275 | if(error) |
Sergunb | 0:8918a71cdbe9 | 276 | break; |
Sergunb | 0:8918a71cdbe9 | 277 | |
Sergunb | 0:8918a71cdbe9 | 278 | //Check SMTP response code |
Sergunb | 0:8918a71cdbe9 | 279 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 280 | { |
Sergunb | 0:8918a71cdbe9 | 281 | //An unexpected response was received... |
Sergunb | 0:8918a71cdbe9 | 282 | error = ERROR_UNEXPECTED_RESPONSE; |
Sergunb | 0:8918a71cdbe9 | 283 | //Stop immediately |
Sergunb | 0:8918a71cdbe9 | 284 | break; |
Sergunb | 0:8918a71cdbe9 | 285 | } |
Sergunb | 0:8918a71cdbe9 | 286 | } |
Sergunb | 0:8918a71cdbe9 | 287 | #endif |
Sergunb | 0:8918a71cdbe9 | 288 | |
Sergunb | 0:8918a71cdbe9 | 289 | //Authentication requires a valid user name and password |
Sergunb | 0:8918a71cdbe9 | 290 | if(authInfo->userName && authInfo->password) |
Sergunb | 0:8918a71cdbe9 | 291 | { |
Sergunb | 0:8918a71cdbe9 | 292 | #if (SMTP_CLIENT_LOGIN_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 293 | //LOGIN authentication mechanism supported? |
Sergunb | 0:8918a71cdbe9 | 294 | if(context->authLoginSupported) |
Sergunb | 0:8918a71cdbe9 | 295 | { |
Sergunb | 0:8918a71cdbe9 | 296 | //Perform LOGIN authentication |
Sergunb | 0:8918a71cdbe9 | 297 | error = smtpSendAuthLogin(context, authInfo); |
Sergunb | 0:8918a71cdbe9 | 298 | //Authentication failed? |
Sergunb | 0:8918a71cdbe9 | 299 | if(error) |
Sergunb | 0:8918a71cdbe9 | 300 | break; |
Sergunb | 0:8918a71cdbe9 | 301 | } |
Sergunb | 0:8918a71cdbe9 | 302 | else |
Sergunb | 0:8918a71cdbe9 | 303 | #endif |
Sergunb | 0:8918a71cdbe9 | 304 | #if (SMTP_CLIENT_PLAIN_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 305 | //PLAIN authentication mechanism supported? |
Sergunb | 0:8918a71cdbe9 | 306 | if(context->authPlainSupported) |
Sergunb | 0:8918a71cdbe9 | 307 | { |
Sergunb | 0:8918a71cdbe9 | 308 | //Perform PLAIN authentication |
Sergunb | 0:8918a71cdbe9 | 309 | error = smtpSendAuthPlain(context, authInfo); |
Sergunb | 0:8918a71cdbe9 | 310 | //Authentication failed? |
Sergunb | 0:8918a71cdbe9 | 311 | if(error) |
Sergunb | 0:8918a71cdbe9 | 312 | break; |
Sergunb | 0:8918a71cdbe9 | 313 | } |
Sergunb | 0:8918a71cdbe9 | 314 | else |
Sergunb | 0:8918a71cdbe9 | 315 | #endif |
Sergunb | 0:8918a71cdbe9 | 316 | #if (SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 317 | //CRAM-MD5 authentication mechanism supported? |
Sergunb | 0:8918a71cdbe9 | 318 | if(context->authCramMd5Supported) |
Sergunb | 0:8918a71cdbe9 | 319 | { |
Sergunb | 0:8918a71cdbe9 | 320 | //Perform CRAM-MD5 authentication |
Sergunb | 0:8918a71cdbe9 | 321 | error = smtpSendAuthCramMd5(context, authInfo); |
Sergunb | 0:8918a71cdbe9 | 322 | //Authentication failed? |
Sergunb | 0:8918a71cdbe9 | 323 | if(error) |
Sergunb | 0:8918a71cdbe9 | 324 | break; |
Sergunb | 0:8918a71cdbe9 | 325 | } |
Sergunb | 0:8918a71cdbe9 | 326 | else |
Sergunb | 0:8918a71cdbe9 | 327 | #endif |
Sergunb | 0:8918a71cdbe9 | 328 | //No authentication mechanism supported? |
Sergunb | 0:8918a71cdbe9 | 329 | { |
Sergunb | 0:8918a71cdbe9 | 330 | //Skip authentication step |
Sergunb | 0:8918a71cdbe9 | 331 | } |
Sergunb | 0:8918a71cdbe9 | 332 | } |
Sergunb | 0:8918a71cdbe9 | 333 | |
Sergunb | 0:8918a71cdbe9 | 334 | //Format the MAIL FROM command (a null return path must be accepted) |
Sergunb | 0:8918a71cdbe9 | 335 | if(mail->from.addr) |
Sergunb | 0:8918a71cdbe9 | 336 | sprintf(context->buffer, "MAIL FROM:<%s>\r\n", mail->from.addr); |
Sergunb | 0:8918a71cdbe9 | 337 | else |
Sergunb | 0:8918a71cdbe9 | 338 | strcpy(context->buffer, "MAIL FROM:<>\r\n"); |
Sergunb | 0:8918a71cdbe9 | 339 | |
Sergunb | 0:8918a71cdbe9 | 340 | //Send the command to the server |
Sergunb | 0:8918a71cdbe9 | 341 | error = smtpSendCommand(context, context->buffer, &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 342 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 343 | if(error) |
Sergunb | 0:8918a71cdbe9 | 344 | break; |
Sergunb | 0:8918a71cdbe9 | 345 | |
Sergunb | 0:8918a71cdbe9 | 346 | //Check SMTP response code |
Sergunb | 0:8918a71cdbe9 | 347 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 348 | { |
Sergunb | 0:8918a71cdbe9 | 349 | //An unexpected response was received... |
Sergunb | 0:8918a71cdbe9 | 350 | error = ERROR_UNEXPECTED_RESPONSE; |
Sergunb | 0:8918a71cdbe9 | 351 | //Stop immediately |
Sergunb | 0:8918a71cdbe9 | 352 | break; |
Sergunb | 0:8918a71cdbe9 | 353 | } |
Sergunb | 0:8918a71cdbe9 | 354 | |
Sergunb | 0:8918a71cdbe9 | 355 | //Format the RCPT TO command |
Sergunb | 0:8918a71cdbe9 | 356 | for(i = 0; i < mail->recipientCount; i++) |
Sergunb | 0:8918a71cdbe9 | 357 | { |
Sergunb | 0:8918a71cdbe9 | 358 | //Skip recipient addresses that are not valid |
Sergunb | 0:8918a71cdbe9 | 359 | if(!mail->recipients[i].addr) |
Sergunb | 0:8918a71cdbe9 | 360 | continue; |
Sergunb | 0:8918a71cdbe9 | 361 | |
Sergunb | 0:8918a71cdbe9 | 362 | //Format the RCPT TO command |
Sergunb | 0:8918a71cdbe9 | 363 | sprintf(context->buffer, "RCPT TO:<%s>\r\n", mail->recipients[i].addr); |
Sergunb | 0:8918a71cdbe9 | 364 | //Send the command to the server |
Sergunb | 0:8918a71cdbe9 | 365 | error = smtpSendCommand(context, context->buffer, &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 366 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 367 | if(error) |
Sergunb | 0:8918a71cdbe9 | 368 | break; |
Sergunb | 0:8918a71cdbe9 | 369 | |
Sergunb | 0:8918a71cdbe9 | 370 | //Check SMTP response code |
Sergunb | 0:8918a71cdbe9 | 371 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 372 | { |
Sergunb | 0:8918a71cdbe9 | 373 | //An unexpected response was received... |
Sergunb | 0:8918a71cdbe9 | 374 | error = ERROR_UNEXPECTED_RESPONSE; |
Sergunb | 0:8918a71cdbe9 | 375 | //Stop immediately |
Sergunb | 0:8918a71cdbe9 | 376 | break; |
Sergunb | 0:8918a71cdbe9 | 377 | } |
Sergunb | 0:8918a71cdbe9 | 378 | } |
Sergunb | 0:8918a71cdbe9 | 379 | |
Sergunb | 0:8918a71cdbe9 | 380 | //Propagate exception if necessary |
Sergunb | 0:8918a71cdbe9 | 381 | if(error) |
Sergunb | 0:8918a71cdbe9 | 382 | break; |
Sergunb | 0:8918a71cdbe9 | 383 | |
Sergunb | 0:8918a71cdbe9 | 384 | //Send message body |
Sergunb | 0:8918a71cdbe9 | 385 | error = smtpSendData(context, mail); |
Sergunb | 0:8918a71cdbe9 | 386 | //Any error to report? |
Sergunb | 0:8918a71cdbe9 | 387 | if(error) |
Sergunb | 0:8918a71cdbe9 | 388 | break; |
Sergunb | 0:8918a71cdbe9 | 389 | |
Sergunb | 0:8918a71cdbe9 | 390 | //End of exception handling block |
Sergunb | 0:8918a71cdbe9 | 391 | } while(0); |
Sergunb | 0:8918a71cdbe9 | 392 | |
Sergunb | 0:8918a71cdbe9 | 393 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 394 | if(error == NO_ERROR || |
Sergunb | 0:8918a71cdbe9 | 395 | error == ERROR_UNEXPECTED_RESPONSE || |
Sergunb | 0:8918a71cdbe9 | 396 | error == ERROR_AUTHENTICATION_FAILED) |
Sergunb | 0:8918a71cdbe9 | 397 | { |
Sergunb | 0:8918a71cdbe9 | 398 | //Properly disconnect from the SMTP server |
Sergunb | 0:8918a71cdbe9 | 399 | smtpSendCommand(context, "QUIT\r\n", &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 400 | } |
Sergunb | 0:8918a71cdbe9 | 401 | |
Sergunb | 0:8918a71cdbe9 | 402 | #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 403 | if(context->tlsContext != NULL) |
Sergunb | 0:8918a71cdbe9 | 404 | { |
Sergunb | 0:8918a71cdbe9 | 405 | //Gracefully close SSL/TLS session |
Sergunb | 0:8918a71cdbe9 | 406 | tlsShutdown(context->tlsContext); |
Sergunb | 0:8918a71cdbe9 | 407 | //Release SSL/TLS context |
Sergunb | 0:8918a71cdbe9 | 408 | tlsFree(context->tlsContext); |
Sergunb | 0:8918a71cdbe9 | 409 | } |
Sergunb | 0:8918a71cdbe9 | 410 | #endif |
Sergunb | 0:8918a71cdbe9 | 411 | |
Sergunb | 0:8918a71cdbe9 | 412 | //Close socket |
Sergunb | 0:8918a71cdbe9 | 413 | socketClose(context->socket); |
Sergunb | 0:8918a71cdbe9 | 414 | //Clean up previously allocated resources |
Sergunb | 0:8918a71cdbe9 | 415 | osFreeMem(context); |
Sergunb | 0:8918a71cdbe9 | 416 | |
Sergunb | 0:8918a71cdbe9 | 417 | //Return status code |
Sergunb | 0:8918a71cdbe9 | 418 | return error; |
Sergunb | 0:8918a71cdbe9 | 419 | } |
Sergunb | 0:8918a71cdbe9 | 420 | |
Sergunb | 0:8918a71cdbe9 | 421 | |
Sergunb | 0:8918a71cdbe9 | 422 | /** |
Sergunb | 0:8918a71cdbe9 | 423 | * @brief Callback function to parse EHLO response |
Sergunb | 0:8918a71cdbe9 | 424 | * @param[in] context SMTP client context |
Sergunb | 0:8918a71cdbe9 | 425 | * @param[in] replyLine Response line |
Sergunb | 0:8918a71cdbe9 | 426 | * @param[in] replyCode Response code |
Sergunb | 0:8918a71cdbe9 | 427 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 428 | **/ |
Sergunb | 0:8918a71cdbe9 | 429 | |
Sergunb | 0:8918a71cdbe9 | 430 | error_t smtpEhloReplyCallback(SmtpClientContext *context, |
Sergunb | 0:8918a71cdbe9 | 431 | char_t *replyLine, uint_t replyCode) |
Sergunb | 0:8918a71cdbe9 | 432 | { |
Sergunb | 0:8918a71cdbe9 | 433 | char_t *p; |
Sergunb | 0:8918a71cdbe9 | 434 | char_t *token; |
Sergunb | 0:8918a71cdbe9 | 435 | |
Sergunb | 0:8918a71cdbe9 | 436 | //The line must be at least 4 characters long |
Sergunb | 0:8918a71cdbe9 | 437 | if(strlen(replyLine) < 4) |
Sergunb | 0:8918a71cdbe9 | 438 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 439 | |
Sergunb | 0:8918a71cdbe9 | 440 | //Skip the response code and the separator |
Sergunb | 0:8918a71cdbe9 | 441 | replyLine += 4; |
Sergunb | 0:8918a71cdbe9 | 442 | |
Sergunb | 0:8918a71cdbe9 | 443 | //Get the first keyword |
Sergunb | 0:8918a71cdbe9 | 444 | token = strtok_r(replyLine, " ", &p); |
Sergunb | 0:8918a71cdbe9 | 445 | //Check whether the response line is empty |
Sergunb | 0:8918a71cdbe9 | 446 | if(token == NULL) |
Sergunb | 0:8918a71cdbe9 | 447 | return ERROR_INVALID_SYNTAX; |
Sergunb | 0:8918a71cdbe9 | 448 | |
Sergunb | 0:8918a71cdbe9 | 449 | //The AUTH keyword contains a space-separated list of |
Sergunb | 0:8918a71cdbe9 | 450 | //names of available authentication mechanisms |
Sergunb | 0:8918a71cdbe9 | 451 | if(!strcasecmp(token, "AUTH")) |
Sergunb | 0:8918a71cdbe9 | 452 | { |
Sergunb | 0:8918a71cdbe9 | 453 | //Process the rest of the line |
Sergunb | 0:8918a71cdbe9 | 454 | while(1) |
Sergunb | 0:8918a71cdbe9 | 455 | { |
Sergunb | 0:8918a71cdbe9 | 456 | //Get the next keyword |
Sergunb | 0:8918a71cdbe9 | 457 | token = strtok_r(NULL, " ", &p); |
Sergunb | 0:8918a71cdbe9 | 458 | //Unable to find the next token? |
Sergunb | 0:8918a71cdbe9 | 459 | if(token == NULL) |
Sergunb | 0:8918a71cdbe9 | 460 | break; |
Sergunb | 0:8918a71cdbe9 | 461 | |
Sergunb | 0:8918a71cdbe9 | 462 | //LOGIN authentication mechanism supported? |
Sergunb | 0:8918a71cdbe9 | 463 | if(!strcasecmp(token, "LOGIN")) |
Sergunb | 0:8918a71cdbe9 | 464 | context->authLoginSupported = TRUE; |
Sergunb | 0:8918a71cdbe9 | 465 | //PLAIN authentication mechanism supported? |
Sergunb | 0:8918a71cdbe9 | 466 | else if(!strcasecmp(token, "PLAIN")) |
Sergunb | 0:8918a71cdbe9 | 467 | context->authPlainSupported = TRUE; |
Sergunb | 0:8918a71cdbe9 | 468 | //CRAM-MD5 authentication mechanism supported? |
Sergunb | 0:8918a71cdbe9 | 469 | else if(!strcasecmp(token, "CRAM-MD5")) |
Sergunb | 0:8918a71cdbe9 | 470 | context->authCramMd5Supported = TRUE; |
Sergunb | 0:8918a71cdbe9 | 471 | } |
Sergunb | 0:8918a71cdbe9 | 472 | } |
Sergunb | 0:8918a71cdbe9 | 473 | //The STARTTLS keyword is used to tell the SMTP client |
Sergunb | 0:8918a71cdbe9 | 474 | //that the SMTP server allows use of TLS |
Sergunb | 0:8918a71cdbe9 | 475 | else if(!strcasecmp(token, "STARTTLS")) |
Sergunb | 0:8918a71cdbe9 | 476 | { |
Sergunb | 0:8918a71cdbe9 | 477 | //STARTTLS use is allowed |
Sergunb | 0:8918a71cdbe9 | 478 | context->startTlsSupported = TRUE; |
Sergunb | 0:8918a71cdbe9 | 479 | } |
Sergunb | 0:8918a71cdbe9 | 480 | |
Sergunb | 0:8918a71cdbe9 | 481 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 482 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 483 | } |
Sergunb | 0:8918a71cdbe9 | 484 | |
Sergunb | 0:8918a71cdbe9 | 485 | |
Sergunb | 0:8918a71cdbe9 | 486 | /** |
Sergunb | 0:8918a71cdbe9 | 487 | * @brief Authentication using LOGIN mechanism |
Sergunb | 0:8918a71cdbe9 | 488 | * @param[in] context SMTP client context |
Sergunb | 0:8918a71cdbe9 | 489 | * @param[in] authInfo Authentication information |
Sergunb | 0:8918a71cdbe9 | 490 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 491 | **/ |
Sergunb | 0:8918a71cdbe9 | 492 | |
Sergunb | 0:8918a71cdbe9 | 493 | error_t smtpSendAuthLogin(SmtpClientContext *context, const SmtpAuthInfo *authInfo) |
Sergunb | 0:8918a71cdbe9 | 494 | { |
Sergunb | 0:8918a71cdbe9 | 495 | #if (SMTP_CLIENT_LOGIN_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 496 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 497 | uint_t replyCode; |
Sergunb | 0:8918a71cdbe9 | 498 | |
Sergunb | 0:8918a71cdbe9 | 499 | //Send AUTH LOGIN command |
Sergunb | 0:8918a71cdbe9 | 500 | error = smtpSendCommand(context, "AUTH LOGIN\r\n", &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 501 | |
Sergunb | 0:8918a71cdbe9 | 502 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 503 | if(error) |
Sergunb | 0:8918a71cdbe9 | 504 | return error; |
Sergunb | 0:8918a71cdbe9 | 505 | //Check SMTP reply code |
Sergunb | 0:8918a71cdbe9 | 506 | if(!SMTP_REPLY_CODE_3YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 507 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 508 | |
Sergunb | 0:8918a71cdbe9 | 509 | //Encode the user name with Base64 algorithm |
Sergunb | 0:8918a71cdbe9 | 510 | base64Encode(authInfo->userName, strlen(authInfo->userName), context->buffer, NULL); |
Sergunb | 0:8918a71cdbe9 | 511 | //Add a line feed |
Sergunb | 0:8918a71cdbe9 | 512 | strcat(context->buffer, "\r\n"); |
Sergunb | 0:8918a71cdbe9 | 513 | |
Sergunb | 0:8918a71cdbe9 | 514 | //Send the resulting string |
Sergunb | 0:8918a71cdbe9 | 515 | error = smtpSendCommand(context, context->buffer, &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 516 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 517 | if(error) |
Sergunb | 0:8918a71cdbe9 | 518 | return error; |
Sergunb | 0:8918a71cdbe9 | 519 | |
Sergunb | 0:8918a71cdbe9 | 520 | //Check SMTP reply code |
Sergunb | 0:8918a71cdbe9 | 521 | if(!SMTP_REPLY_CODE_3YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 522 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 523 | |
Sergunb | 0:8918a71cdbe9 | 524 | //Encode the password with Base64 algorithm |
Sergunb | 0:8918a71cdbe9 | 525 | base64Encode(authInfo->password, strlen(authInfo->password), context->buffer, NULL); |
Sergunb | 0:8918a71cdbe9 | 526 | //Add a line feed |
Sergunb | 0:8918a71cdbe9 | 527 | strcat(context->buffer, "\r\n"); |
Sergunb | 0:8918a71cdbe9 | 528 | |
Sergunb | 0:8918a71cdbe9 | 529 | //Send the resulting string |
Sergunb | 0:8918a71cdbe9 | 530 | error = smtpSendCommand(context, context->buffer, &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 531 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 532 | if(error) |
Sergunb | 0:8918a71cdbe9 | 533 | return error; |
Sergunb | 0:8918a71cdbe9 | 534 | |
Sergunb | 0:8918a71cdbe9 | 535 | //Check SMTP reply code |
Sergunb | 0:8918a71cdbe9 | 536 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 537 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 538 | |
Sergunb | 0:8918a71cdbe9 | 539 | //Successful authentication |
Sergunb | 0:8918a71cdbe9 | 540 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 541 | #else |
Sergunb | 0:8918a71cdbe9 | 542 | //LOGIN authentication is not supported |
Sergunb | 0:8918a71cdbe9 | 543 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 544 | #endif |
Sergunb | 0:8918a71cdbe9 | 545 | } |
Sergunb | 0:8918a71cdbe9 | 546 | |
Sergunb | 0:8918a71cdbe9 | 547 | |
Sergunb | 0:8918a71cdbe9 | 548 | /** |
Sergunb | 0:8918a71cdbe9 | 549 | * @brief Authentication using PLAIN mechanism |
Sergunb | 0:8918a71cdbe9 | 550 | * @param[in] context SMTP client context |
Sergunb | 0:8918a71cdbe9 | 551 | * @param[in] authInfo Authentication information |
Sergunb | 0:8918a71cdbe9 | 552 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 553 | **/ |
Sergunb | 0:8918a71cdbe9 | 554 | |
Sergunb | 0:8918a71cdbe9 | 555 | error_t smtpSendAuthPlain(SmtpClientContext *context, const SmtpAuthInfo *authInfo) |
Sergunb | 0:8918a71cdbe9 | 556 | { |
Sergunb | 0:8918a71cdbe9 | 557 | #if (SMTP_CLIENT_PLAIN_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 558 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 559 | uint_t n; |
Sergunb | 0:8918a71cdbe9 | 560 | uint_t replyCode; |
Sergunb | 0:8918a71cdbe9 | 561 | |
Sergunb | 0:8918a71cdbe9 | 562 | //Authorization identity |
Sergunb | 0:8918a71cdbe9 | 563 | strcpy(context->buffer, authInfo->userName); |
Sergunb | 0:8918a71cdbe9 | 564 | n = strlen(authInfo->userName) + 1; |
Sergunb | 0:8918a71cdbe9 | 565 | //Authentication identity |
Sergunb | 0:8918a71cdbe9 | 566 | strcpy(context->buffer + n, authInfo->userName); |
Sergunb | 0:8918a71cdbe9 | 567 | n += strlen(authInfo->userName) + 1; |
Sergunb | 0:8918a71cdbe9 | 568 | //Password |
Sergunb | 0:8918a71cdbe9 | 569 | strcpy(context->buffer + n, authInfo->password); |
Sergunb | 0:8918a71cdbe9 | 570 | n += strlen(authInfo->password); |
Sergunb | 0:8918a71cdbe9 | 571 | |
Sergunb | 0:8918a71cdbe9 | 572 | //Base64 encoding |
Sergunb | 0:8918a71cdbe9 | 573 | base64Encode(context->buffer, n, context->buffer2, NULL); |
Sergunb | 0:8918a71cdbe9 | 574 | //Format the AUTH PLAIN command |
Sergunb | 0:8918a71cdbe9 | 575 | sprintf(context->buffer, "AUTH PLAIN %s\r\n", context->buffer2); |
Sergunb | 0:8918a71cdbe9 | 576 | |
Sergunb | 0:8918a71cdbe9 | 577 | //Send the command to the server |
Sergunb | 0:8918a71cdbe9 | 578 | error = smtpSendCommand(context, context->buffer, &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 579 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 580 | if(error) |
Sergunb | 0:8918a71cdbe9 | 581 | return error; |
Sergunb | 0:8918a71cdbe9 | 582 | |
Sergunb | 0:8918a71cdbe9 | 583 | //Check SMTP reply code |
Sergunb | 0:8918a71cdbe9 | 584 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 585 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 586 | |
Sergunb | 0:8918a71cdbe9 | 587 | //Successful authentication |
Sergunb | 0:8918a71cdbe9 | 588 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 589 | #else |
Sergunb | 0:8918a71cdbe9 | 590 | //PLAIN authentication is not supported |
Sergunb | 0:8918a71cdbe9 | 591 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 592 | #endif |
Sergunb | 0:8918a71cdbe9 | 593 | } |
Sergunb | 0:8918a71cdbe9 | 594 | |
Sergunb | 0:8918a71cdbe9 | 595 | |
Sergunb | 0:8918a71cdbe9 | 596 | /** |
Sergunb | 0:8918a71cdbe9 | 597 | * @brief Authentication using CRAM-MD5 mechanism |
Sergunb | 0:8918a71cdbe9 | 598 | * @param[in] context SMTP client context |
Sergunb | 0:8918a71cdbe9 | 599 | * @param[in] authInfo Authentication information |
Sergunb | 0:8918a71cdbe9 | 600 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 601 | **/ |
Sergunb | 0:8918a71cdbe9 | 602 | |
Sergunb | 0:8918a71cdbe9 | 603 | error_t smtpSendAuthCramMd5(SmtpClientContext *context, const SmtpAuthInfo *authInfo) |
Sergunb | 0:8918a71cdbe9 | 604 | { |
Sergunb | 0:8918a71cdbe9 | 605 | #if (SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 606 | //Hex conversion table |
Sergunb | 0:8918a71cdbe9 | 607 | static const char_t hexDigit[] = |
Sergunb | 0:8918a71cdbe9 | 608 | { |
Sergunb | 0:8918a71cdbe9 | 609 | '0', '1', '2', '3', '4', '5', '6', '7', |
Sergunb | 0:8918a71cdbe9 | 610 | '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' |
Sergunb | 0:8918a71cdbe9 | 611 | }; |
Sergunb | 0:8918a71cdbe9 | 612 | |
Sergunb | 0:8918a71cdbe9 | 613 | //Local variables |
Sergunb | 0:8918a71cdbe9 | 614 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 615 | uint_t n; |
Sergunb | 0:8918a71cdbe9 | 616 | uint_t replyCode; |
Sergunb | 0:8918a71cdbe9 | 617 | |
Sergunb | 0:8918a71cdbe9 | 618 | //Alias pointers |
Sergunb | 0:8918a71cdbe9 | 619 | uint8_t *challenge = (uint8_t *) context->buffer2; |
Sergunb | 0:8918a71cdbe9 | 620 | uint8_t *digest = (uint8_t *) context->buffer; |
Sergunb | 0:8918a71cdbe9 | 621 | char_t *textDigest = (char_t *) context->buffer2; |
Sergunb | 0:8918a71cdbe9 | 622 | |
Sergunb | 0:8918a71cdbe9 | 623 | //Send AUTH CRAM-MD5 command |
Sergunb | 0:8918a71cdbe9 | 624 | error = smtpSendCommand(context, "AUTH CRAM-MD5\r\n", &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 625 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 626 | if(error) |
Sergunb | 0:8918a71cdbe9 | 627 | return error; |
Sergunb | 0:8918a71cdbe9 | 628 | |
Sergunb | 0:8918a71cdbe9 | 629 | //Check SMTP reply code |
Sergunb | 0:8918a71cdbe9 | 630 | if(!SMTP_REPLY_CODE_3YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 631 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 632 | |
Sergunb | 0:8918a71cdbe9 | 633 | //Compute the length of the response |
Sergunb | 0:8918a71cdbe9 | 634 | n = strlen(context->buffer); |
Sergunb | 0:8918a71cdbe9 | 635 | //Unexpected response from the SMTP server? |
Sergunb | 0:8918a71cdbe9 | 636 | if(n <= 4) |
Sergunb | 0:8918a71cdbe9 | 637 | return ERROR_INVALID_SYNTAX; |
Sergunb | 0:8918a71cdbe9 | 638 | |
Sergunb | 0:8918a71cdbe9 | 639 | //Decrypt the Base64 encoded challenge |
Sergunb | 0:8918a71cdbe9 | 640 | error = base64Decode(context->buffer + 4, n - 4, challenge, &n); |
Sergunb | 0:8918a71cdbe9 | 641 | //Decoding failed? |
Sergunb | 0:8918a71cdbe9 | 642 | if(error) |
Sergunb | 0:8918a71cdbe9 | 643 | return error; |
Sergunb | 0:8918a71cdbe9 | 644 | |
Sergunb | 0:8918a71cdbe9 | 645 | //Compute HMAC using MD5 |
Sergunb | 0:8918a71cdbe9 | 646 | error = hmacCompute(MD5_HASH_ALGO, authInfo->password, |
Sergunb | 0:8918a71cdbe9 | 647 | strlen(authInfo->password), challenge, n, digest); |
Sergunb | 0:8918a71cdbe9 | 648 | //HMAC computation failed? |
Sergunb | 0:8918a71cdbe9 | 649 | if(error) |
Sergunb | 0:8918a71cdbe9 | 650 | return error; |
Sergunb | 0:8918a71cdbe9 | 651 | |
Sergunb | 0:8918a71cdbe9 | 652 | //Convert the digest to text |
Sergunb | 0:8918a71cdbe9 | 653 | for(n = 0; n < MD5_DIGEST_SIZE; n++) |
Sergunb | 0:8918a71cdbe9 | 654 | { |
Sergunb | 0:8918a71cdbe9 | 655 | //Convert upper nibble |
Sergunb | 0:8918a71cdbe9 | 656 | textDigest[n * 2] = hexDigit[(digest[n] >> 4) & 0x0F]; |
Sergunb | 0:8918a71cdbe9 | 657 | //Then convert lower nibble |
Sergunb | 0:8918a71cdbe9 | 658 | textDigest[n * 2 + 1] = hexDigit[digest[n] & 0x0F]; |
Sergunb | 0:8918a71cdbe9 | 659 | } |
Sergunb | 0:8918a71cdbe9 | 660 | |
Sergunb | 0:8918a71cdbe9 | 661 | //Properly terminate the string |
Sergunb | 0:8918a71cdbe9 | 662 | textDigest[MD5_DIGEST_SIZE * 2] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 663 | //Concatenate the user name and the text representation of the digest |
Sergunb | 0:8918a71cdbe9 | 664 | sprintf(context->buffer, "%s %s", authInfo->userName, textDigest); |
Sergunb | 0:8918a71cdbe9 | 665 | //Encode the resulting string with Base64 algorithm |
Sergunb | 0:8918a71cdbe9 | 666 | base64Encode(context->buffer, strlen(context->buffer), context->buffer2, NULL); |
Sergunb | 0:8918a71cdbe9 | 667 | //Add a line feed |
Sergunb | 0:8918a71cdbe9 | 668 | strcat(context->buffer2, "\r\n"); |
Sergunb | 0:8918a71cdbe9 | 669 | |
Sergunb | 0:8918a71cdbe9 | 670 | //Transmit the Base64 encoded string |
Sergunb | 0:8918a71cdbe9 | 671 | error = smtpSendCommand(context, context->buffer2, &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 672 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 673 | if(error) |
Sergunb | 0:8918a71cdbe9 | 674 | return error; |
Sergunb | 0:8918a71cdbe9 | 675 | |
Sergunb | 0:8918a71cdbe9 | 676 | //Check SMTP reply code |
Sergunb | 0:8918a71cdbe9 | 677 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 678 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 679 | |
Sergunb | 0:8918a71cdbe9 | 680 | //Successful authentication |
Sergunb | 0:8918a71cdbe9 | 681 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 682 | #else |
Sergunb | 0:8918a71cdbe9 | 683 | //CRAM-MD5 authentication is not supported |
Sergunb | 0:8918a71cdbe9 | 684 | return ERROR_AUTHENTICATION_FAILED; |
Sergunb | 0:8918a71cdbe9 | 685 | #endif |
Sergunb | 0:8918a71cdbe9 | 686 | } |
Sergunb | 0:8918a71cdbe9 | 687 | |
Sergunb | 0:8918a71cdbe9 | 688 | |
Sergunb | 0:8918a71cdbe9 | 689 | /** |
Sergunb | 0:8918a71cdbe9 | 690 | * @brief Send message body |
Sergunb | 0:8918a71cdbe9 | 691 | * @param[in] context SMTP client context |
Sergunb | 0:8918a71cdbe9 | 692 | * @param[in] mail Mail contents |
Sergunb | 0:8918a71cdbe9 | 693 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 694 | **/ |
Sergunb | 0:8918a71cdbe9 | 695 | |
Sergunb | 0:8918a71cdbe9 | 696 | error_t smtpSendData(SmtpClientContext *context, const SmtpMail *mail) |
Sergunb | 0:8918a71cdbe9 | 697 | { |
Sergunb | 0:8918a71cdbe9 | 698 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 699 | bool_t first; |
Sergunb | 0:8918a71cdbe9 | 700 | uint_t i; |
Sergunb | 0:8918a71cdbe9 | 701 | uint_t replyCode; |
Sergunb | 0:8918a71cdbe9 | 702 | char_t *p; |
Sergunb | 0:8918a71cdbe9 | 703 | |
Sergunb | 0:8918a71cdbe9 | 704 | //Send DATA command |
Sergunb | 0:8918a71cdbe9 | 705 | error = smtpSendCommand(context, "DATA\r\n", &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 706 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 707 | if(error) |
Sergunb | 0:8918a71cdbe9 | 708 | return error; |
Sergunb | 0:8918a71cdbe9 | 709 | |
Sergunb | 0:8918a71cdbe9 | 710 | //Check SMTP reply code |
Sergunb | 0:8918a71cdbe9 | 711 | if(!SMTP_REPLY_CODE_3YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 712 | return ERROR_UNEXPECTED_RESPONSE; |
Sergunb | 0:8918a71cdbe9 | 713 | |
Sergunb | 0:8918a71cdbe9 | 714 | //Point to the beginning of the buffer |
Sergunb | 0:8918a71cdbe9 | 715 | p = context->buffer; |
Sergunb | 0:8918a71cdbe9 | 716 | |
Sergunb | 0:8918a71cdbe9 | 717 | //Current date and time |
Sergunb | 0:8918a71cdbe9 | 718 | if(mail->dateTime && mail->dateTime[0] != '\0') |
Sergunb | 0:8918a71cdbe9 | 719 | p += sprintf(p, "Date: %s\r\n", mail->dateTime); |
Sergunb | 0:8918a71cdbe9 | 720 | |
Sergunb | 0:8918a71cdbe9 | 721 | //Sender address |
Sergunb | 0:8918a71cdbe9 | 722 | if(mail->from.addr) |
Sergunb | 0:8918a71cdbe9 | 723 | { |
Sergunb | 0:8918a71cdbe9 | 724 | //A friendly name may be associated with the sender address |
Sergunb | 0:8918a71cdbe9 | 725 | if(mail->from.name && mail->from.name[0] != '\0') |
Sergunb | 0:8918a71cdbe9 | 726 | p += sprintf(p, "From: \"%s\" <%s>\r\n", mail->from.name, mail->from.addr); |
Sergunb | 0:8918a71cdbe9 | 727 | else |
Sergunb | 0:8918a71cdbe9 | 728 | p += sprintf(p, "From: %s\r\n", mail->from.addr); |
Sergunb | 0:8918a71cdbe9 | 729 | } |
Sergunb | 0:8918a71cdbe9 | 730 | |
Sergunb | 0:8918a71cdbe9 | 731 | //Recipients |
Sergunb | 0:8918a71cdbe9 | 732 | for(i = 0, first = TRUE; i < mail->recipientCount; i++) |
Sergunb | 0:8918a71cdbe9 | 733 | { |
Sergunb | 0:8918a71cdbe9 | 734 | //Skip recipient addresses that are not valid |
Sergunb | 0:8918a71cdbe9 | 735 | if(!mail->recipients[i].addr) |
Sergunb | 0:8918a71cdbe9 | 736 | continue; |
Sergunb | 0:8918a71cdbe9 | 737 | |
Sergunb | 0:8918a71cdbe9 | 738 | //Check recipient type |
Sergunb | 0:8918a71cdbe9 | 739 | if(mail->recipients[i].type & SMTP_RCPT_TYPE_TO) |
Sergunb | 0:8918a71cdbe9 | 740 | { |
Sergunb | 0:8918a71cdbe9 | 741 | //The first item of the list requires special processing |
Sergunb | 0:8918a71cdbe9 | 742 | p += sprintf(p, first ? "To: " : ", "); |
Sergunb | 0:8918a71cdbe9 | 743 | |
Sergunb | 0:8918a71cdbe9 | 744 | //A friendly name may be associated with the address |
Sergunb | 0:8918a71cdbe9 | 745 | if(mail->recipients[i].name && mail->recipients[i].name[0] != '\0') |
Sergunb | 0:8918a71cdbe9 | 746 | p += sprintf(p, "\"%s\" <%s>", mail->recipients[i].name, mail->recipients[i].addr); |
Sergunb | 0:8918a71cdbe9 | 747 | else |
Sergunb | 0:8918a71cdbe9 | 748 | p += sprintf(p, "%s", mail->recipients[i].addr); |
Sergunb | 0:8918a71cdbe9 | 749 | |
Sergunb | 0:8918a71cdbe9 | 750 | //Prepare to add a new item to the list |
Sergunb | 0:8918a71cdbe9 | 751 | first = FALSE; |
Sergunb | 0:8918a71cdbe9 | 752 | } |
Sergunb | 0:8918a71cdbe9 | 753 | } |
Sergunb | 0:8918a71cdbe9 | 754 | |
Sergunb | 0:8918a71cdbe9 | 755 | //Properly terminate the line with CRLF |
Sergunb | 0:8918a71cdbe9 | 756 | if(!first) |
Sergunb | 0:8918a71cdbe9 | 757 | p += sprintf(p, "\r\n"); |
Sergunb | 0:8918a71cdbe9 | 758 | |
Sergunb | 0:8918a71cdbe9 | 759 | //Carbon copy |
Sergunb | 0:8918a71cdbe9 | 760 | for(i = 0, first = TRUE; i < mail->recipientCount; i++) |
Sergunb | 0:8918a71cdbe9 | 761 | { |
Sergunb | 0:8918a71cdbe9 | 762 | //Skip recipient addresses that are not valid |
Sergunb | 0:8918a71cdbe9 | 763 | if(!mail->recipients[i].addr) |
Sergunb | 0:8918a71cdbe9 | 764 | continue; |
Sergunb | 0:8918a71cdbe9 | 765 | |
Sergunb | 0:8918a71cdbe9 | 766 | //Check recipient type |
Sergunb | 0:8918a71cdbe9 | 767 | if(mail->recipients[i].type & SMTP_RCPT_TYPE_CC) |
Sergunb | 0:8918a71cdbe9 | 768 | { |
Sergunb | 0:8918a71cdbe9 | 769 | //The first item of the list requires special processing |
Sergunb | 0:8918a71cdbe9 | 770 | p += sprintf(p, first ? "Cc: " : ", "); |
Sergunb | 0:8918a71cdbe9 | 771 | |
Sergunb | 0:8918a71cdbe9 | 772 | //A friendly name may be associated with the address |
Sergunb | 0:8918a71cdbe9 | 773 | if(mail->recipients[i].name && mail->recipients[i].name[0] != '\0') |
Sergunb | 0:8918a71cdbe9 | 774 | p += sprintf(p, "\"%s\" <%s>", mail->recipients[i].name, mail->recipients[i].addr); |
Sergunb | 0:8918a71cdbe9 | 775 | else |
Sergunb | 0:8918a71cdbe9 | 776 | p += sprintf(p, "%s", mail->recipients[i].addr); |
Sergunb | 0:8918a71cdbe9 | 777 | |
Sergunb | 0:8918a71cdbe9 | 778 | //Prepare to add a new item to the list |
Sergunb | 0:8918a71cdbe9 | 779 | first = FALSE; |
Sergunb | 0:8918a71cdbe9 | 780 | } |
Sergunb | 0:8918a71cdbe9 | 781 | } |
Sergunb | 0:8918a71cdbe9 | 782 | |
Sergunb | 0:8918a71cdbe9 | 783 | //Properly terminate the line with CRLF |
Sergunb | 0:8918a71cdbe9 | 784 | if(!first) |
Sergunb | 0:8918a71cdbe9 | 785 | p += sprintf(p, "\r\n"); |
Sergunb | 0:8918a71cdbe9 | 786 | |
Sergunb | 0:8918a71cdbe9 | 787 | //Subject |
Sergunb | 0:8918a71cdbe9 | 788 | if(mail->subject) |
Sergunb | 0:8918a71cdbe9 | 789 | p += sprintf(p, "Subject: %s\r\n", mail->subject); |
Sergunb | 0:8918a71cdbe9 | 790 | |
Sergunb | 0:8918a71cdbe9 | 791 | //The header and the body are separated by an empty line |
Sergunb | 0:8918a71cdbe9 | 792 | sprintf(p, "\r\n"); |
Sergunb | 0:8918a71cdbe9 | 793 | |
Sergunb | 0:8918a71cdbe9 | 794 | //Debug message |
Sergunb | 0:8918a71cdbe9 | 795 | TRACE_DEBUG(context->buffer); |
Sergunb | 0:8918a71cdbe9 | 796 | TRACE_DEBUG(mail->body); |
Sergunb | 0:8918a71cdbe9 | 797 | TRACE_DEBUG("\r\n.\r\n"); |
Sergunb | 0:8918a71cdbe9 | 798 | |
Sergunb | 0:8918a71cdbe9 | 799 | //Send message header |
Sergunb | 0:8918a71cdbe9 | 800 | error = smtpWrite(context, context->buffer, strlen(context->buffer), 0); |
Sergunb | 0:8918a71cdbe9 | 801 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 802 | if(error) |
Sergunb | 0:8918a71cdbe9 | 803 | return error; |
Sergunb | 0:8918a71cdbe9 | 804 | |
Sergunb | 0:8918a71cdbe9 | 805 | //Send message body |
Sergunb | 0:8918a71cdbe9 | 806 | error = smtpWrite(context, mail->body, strlen(mail->body), 0); |
Sergunb | 0:8918a71cdbe9 | 807 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 808 | if(error) |
Sergunb | 0:8918a71cdbe9 | 809 | return error; |
Sergunb | 0:8918a71cdbe9 | 810 | |
Sergunb | 0:8918a71cdbe9 | 811 | //Indicate the end of the mail data by sending a line containing only a "." |
Sergunb | 0:8918a71cdbe9 | 812 | error = smtpSendCommand(context, "\r\n.\r\n", &replyCode, NULL); |
Sergunb | 0:8918a71cdbe9 | 813 | //Any communication error to report? |
Sergunb | 0:8918a71cdbe9 | 814 | if(error) |
Sergunb | 0:8918a71cdbe9 | 815 | return error; |
Sergunb | 0:8918a71cdbe9 | 816 | |
Sergunb | 0:8918a71cdbe9 | 817 | //Check SMTP reply code |
Sergunb | 0:8918a71cdbe9 | 818 | if(!SMTP_REPLY_CODE_2YZ(replyCode)) |
Sergunb | 0:8918a71cdbe9 | 819 | return ERROR_UNEXPECTED_RESPONSE; |
Sergunb | 0:8918a71cdbe9 | 820 | |
Sergunb | 0:8918a71cdbe9 | 821 | //Successful operation |
Sergunb | 0:8918a71cdbe9 | 822 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 823 | } |
Sergunb | 0:8918a71cdbe9 | 824 | |
Sergunb | 0:8918a71cdbe9 | 825 | |
Sergunb | 0:8918a71cdbe9 | 826 | /** |
Sergunb | 0:8918a71cdbe9 | 827 | * @brief Send SMTP command and wait for a reply |
Sergunb | 0:8918a71cdbe9 | 828 | * @param[in] context SMTP client context |
Sergunb | 0:8918a71cdbe9 | 829 | * @param[in] command Command line |
Sergunb | 0:8918a71cdbe9 | 830 | * @param[out] replyCode SMTP server reply code |
Sergunb | 0:8918a71cdbe9 | 831 | * @param[in] callback Optional callback to parse each line of the reply |
Sergunb | 0:8918a71cdbe9 | 832 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 833 | **/ |
Sergunb | 0:8918a71cdbe9 | 834 | |
Sergunb | 0:8918a71cdbe9 | 835 | error_t smtpSendCommand(SmtpClientContext *context, const char_t *command, |
Sergunb | 0:8918a71cdbe9 | 836 | uint_t *replyCode, SmtpReplyCallback callback) |
Sergunb | 0:8918a71cdbe9 | 837 | { |
Sergunb | 0:8918a71cdbe9 | 838 | error_t error; |
Sergunb | 0:8918a71cdbe9 | 839 | size_t length; |
Sergunb | 0:8918a71cdbe9 | 840 | char_t *line; |
Sergunb | 0:8918a71cdbe9 | 841 | |
Sergunb | 0:8918a71cdbe9 | 842 | //Any command line to send? |
Sergunb | 0:8918a71cdbe9 | 843 | if(command) |
Sergunb | 0:8918a71cdbe9 | 844 | { |
Sergunb | 0:8918a71cdbe9 | 845 | //Debug message |
Sergunb | 0:8918a71cdbe9 | 846 | TRACE_DEBUG("SMTP client: %s", command); |
Sergunb | 0:8918a71cdbe9 | 847 | |
Sergunb | 0:8918a71cdbe9 | 848 | //Send the command to the SMTP server |
Sergunb | 0:8918a71cdbe9 | 849 | error = smtpWrite(context, command, strlen(command), SOCKET_FLAG_WAIT_ACK); |
Sergunb | 0:8918a71cdbe9 | 850 | //Failed to send command? |
Sergunb | 0:8918a71cdbe9 | 851 | if(error) |
Sergunb | 0:8918a71cdbe9 | 852 | return error; |
Sergunb | 0:8918a71cdbe9 | 853 | } |
Sergunb | 0:8918a71cdbe9 | 854 | |
Sergunb | 0:8918a71cdbe9 | 855 | //Multiline replies are allowed for any command |
Sergunb | 0:8918a71cdbe9 | 856 | while(1) |
Sergunb | 0:8918a71cdbe9 | 857 | { |
Sergunb | 0:8918a71cdbe9 | 858 | //Wait for a response from the server |
Sergunb | 0:8918a71cdbe9 | 859 | error = smtpRead(context, context->buffer, |
Sergunb | 0:8918a71cdbe9 | 860 | SMTP_CLIENT_MAX_LINE_LENGTH - 1, &length, SOCKET_FLAG_BREAK_CRLF); |
Sergunb | 0:8918a71cdbe9 | 861 | |
Sergunb | 0:8918a71cdbe9 | 862 | //The remote server did not respond as expected? |
Sergunb | 0:8918a71cdbe9 | 863 | if(error) |
Sergunb | 0:8918a71cdbe9 | 864 | return error; |
Sergunb | 0:8918a71cdbe9 | 865 | |
Sergunb | 0:8918a71cdbe9 | 866 | //Properly terminate the string with a NULL character |
Sergunb | 0:8918a71cdbe9 | 867 | context->buffer[length] = '\0'; |
Sergunb | 0:8918a71cdbe9 | 868 | //Remove all leading and trailing whitespace from the response |
Sergunb | 0:8918a71cdbe9 | 869 | line = strTrimWhitespace(context->buffer); |
Sergunb | 0:8918a71cdbe9 | 870 | |
Sergunb | 0:8918a71cdbe9 | 871 | //Debug message |
Sergunb | 0:8918a71cdbe9 | 872 | TRACE_DEBUG("SMTP server: %s\r\n", line); |
Sergunb | 0:8918a71cdbe9 | 873 | |
Sergunb | 0:8918a71cdbe9 | 874 | //Check the length of the response |
Sergunb | 0:8918a71cdbe9 | 875 | if(strlen(line) < 3) |
Sergunb | 0:8918a71cdbe9 | 876 | return ERROR_INVALID_SYNTAX; |
Sergunb | 0:8918a71cdbe9 | 877 | //All replies begin with a three digit numeric code |
Sergunb | 0:8918a71cdbe9 | 878 | if(!isdigit((uint8_t) line[0]) || !isdigit((uint8_t) line[1]) || !isdigit((uint8_t) line[2])) |
Sergunb | 0:8918a71cdbe9 | 879 | return ERROR_INVALID_SYNTAX; |
Sergunb | 0:8918a71cdbe9 | 880 | //A hyphen or a space character must follow the response code |
Sergunb | 0:8918a71cdbe9 | 881 | if(line[3] != '-' && line[3] != ' ' && line[3] != '\0') |
Sergunb | 0:8918a71cdbe9 | 882 | return ERROR_INVALID_SYNTAX; |
Sergunb | 0:8918a71cdbe9 | 883 | |
Sergunb | 0:8918a71cdbe9 | 884 | //Get the server response code |
Sergunb | 0:8918a71cdbe9 | 885 | *replyCode = strtoul(line, NULL, 10); |
Sergunb | 0:8918a71cdbe9 | 886 | |
Sergunb | 0:8918a71cdbe9 | 887 | //Any callback function to call? |
Sergunb | 0:8918a71cdbe9 | 888 | if(callback) |
Sergunb | 0:8918a71cdbe9 | 889 | { |
Sergunb | 0:8918a71cdbe9 | 890 | //Invoke callback function to parse the response line |
Sergunb | 0:8918a71cdbe9 | 891 | error = callback(context, line, *replyCode); |
Sergunb | 0:8918a71cdbe9 | 892 | //Check status code |
Sergunb | 0:8918a71cdbe9 | 893 | if(error) |
Sergunb | 0:8918a71cdbe9 | 894 | return error; |
Sergunb | 0:8918a71cdbe9 | 895 | } |
Sergunb | 0:8918a71cdbe9 | 896 | |
Sergunb | 0:8918a71cdbe9 | 897 | //A hyphen follows the response code for all but the last line |
Sergunb | 0:8918a71cdbe9 | 898 | if(line[3] != '-') |
Sergunb | 0:8918a71cdbe9 | 899 | break; |
Sergunb | 0:8918a71cdbe9 | 900 | } |
Sergunb | 0:8918a71cdbe9 | 901 | |
Sergunb | 0:8918a71cdbe9 | 902 | //Successful processing |
Sergunb | 0:8918a71cdbe9 | 903 | return NO_ERROR; |
Sergunb | 0:8918a71cdbe9 | 904 | } |
Sergunb | 0:8918a71cdbe9 | 905 | |
Sergunb | 0:8918a71cdbe9 | 906 | |
Sergunb | 0:8918a71cdbe9 | 907 | /** |
Sergunb | 0:8918a71cdbe9 | 908 | * @brief Send data to the SMTP server |
Sergunb | 0:8918a71cdbe9 | 909 | * @param[in] context SMTP client context |
Sergunb | 0:8918a71cdbe9 | 910 | * @param[in] data Pointer to a buffer containing the data to be transmitted |
Sergunb | 0:8918a71cdbe9 | 911 | * @param[in] length Number of bytes to be transmitted |
Sergunb | 0:8918a71cdbe9 | 912 | * @param[in] flags Set of flags that influences the behavior of this function |
Sergunb | 0:8918a71cdbe9 | 913 | **/ |
Sergunb | 0:8918a71cdbe9 | 914 | |
Sergunb | 0:8918a71cdbe9 | 915 | error_t smtpWrite(SmtpClientContext *context, const void *data, size_t length, uint_t flags) |
Sergunb | 0:8918a71cdbe9 | 916 | { |
Sergunb | 0:8918a71cdbe9 | 917 | #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 918 | //Check whether a secure connection is being used |
Sergunb | 0:8918a71cdbe9 | 919 | if(context->tlsContext != NULL) |
Sergunb | 0:8918a71cdbe9 | 920 | { |
Sergunb | 0:8918a71cdbe9 | 921 | //Use SSL/TLS to transmit data to the SMTP server |
Sergunb | 0:8918a71cdbe9 | 922 | return tlsWrite(context->tlsContext, data, length, NULL, flags); |
Sergunb | 0:8918a71cdbe9 | 923 | } |
Sergunb | 0:8918a71cdbe9 | 924 | else |
Sergunb | 0:8918a71cdbe9 | 925 | #endif |
Sergunb | 0:8918a71cdbe9 | 926 | { |
Sergunb | 0:8918a71cdbe9 | 927 | //Transmit data to the SMTP server |
Sergunb | 0:8918a71cdbe9 | 928 | return socketSend(context->socket, data, length, NULL, flags); |
Sergunb | 0:8918a71cdbe9 | 929 | } |
Sergunb | 0:8918a71cdbe9 | 930 | } |
Sergunb | 0:8918a71cdbe9 | 931 | |
Sergunb | 0:8918a71cdbe9 | 932 | |
Sergunb | 0:8918a71cdbe9 | 933 | /** |
Sergunb | 0:8918a71cdbe9 | 934 | * @brief Receive data from the SMTP server |
Sergunb | 0:8918a71cdbe9 | 935 | * @param[in] context SMTP client context |
Sergunb | 0:8918a71cdbe9 | 936 | * @param[out] data Buffer into which received data will be placed |
Sergunb | 0:8918a71cdbe9 | 937 | * @param[in] size Maximum number of bytes that can be received |
Sergunb | 0:8918a71cdbe9 | 938 | * @param[out] received Actual number of bytes that have been received |
Sergunb | 0:8918a71cdbe9 | 939 | * @param[in] flags Set of flags that influences the behavior of this function |
Sergunb | 0:8918a71cdbe9 | 940 | * @return Error code |
Sergunb | 0:8918a71cdbe9 | 941 | **/ |
Sergunb | 0:8918a71cdbe9 | 942 | |
Sergunb | 0:8918a71cdbe9 | 943 | error_t smtpRead(SmtpClientContext *context, void *data, size_t size, size_t *received, uint_t flags) |
Sergunb | 0:8918a71cdbe9 | 944 | { |
Sergunb | 0:8918a71cdbe9 | 945 | #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) |
Sergunb | 0:8918a71cdbe9 | 946 | //Check whether a secure connection is being used |
Sergunb | 0:8918a71cdbe9 | 947 | if(context->tlsContext != NULL) |
Sergunb | 0:8918a71cdbe9 | 948 | { |
Sergunb | 0:8918a71cdbe9 | 949 | //Use SSL/TLS to receive data from the SMTP server |
Sergunb | 0:8918a71cdbe9 | 950 | return tlsRead(context->tlsContext, data, size, received, flags); |
Sergunb | 0:8918a71cdbe9 | 951 | } |
Sergunb | 0:8918a71cdbe9 | 952 | else |
Sergunb | 0:8918a71cdbe9 | 953 | #endif |
Sergunb | 0:8918a71cdbe9 | 954 | { |
Sergunb | 0:8918a71cdbe9 | 955 | //Receive data from the SMTP server |
Sergunb | 0:8918a71cdbe9 | 956 | return socketReceive(context->socket, data, size, received, flags); |
Sergunb | 0:8918a71cdbe9 | 957 | } |
Sergunb | 0:8918a71cdbe9 | 958 | } |
Sergunb | 0:8918a71cdbe9 | 959 | |
Sergunb | 0:8918a71cdbe9 | 960 | #endif |
Sergunb | 0:8918a71cdbe9 | 961 |