Webserver+3d print
Embed:
(wiki syntax)
Show/hide line numbers
smtp_client.c
Go to the documentation of this file.
00001 /** 00002 * @file smtp_client.c 00003 * @brief SMTP client (Simple Mail Transfer Protocol) 00004 * 00005 * @section License 00006 * 00007 * Copyright (C) 2010-2017 Oryx Embedded SARL. All rights reserved. 00008 * 00009 * This file is part of CycloneTCP Open. 00010 * 00011 * This program is free software; you can redistribute it and/or 00012 * modify it under the terms of the GNU General Public License 00013 * as published by the Free Software Foundation; either version 2 00014 * of the License, or (at your option) any later version. 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU General Public License for more details. 00020 * 00021 * You should have received a copy of the GNU General Public License 00022 * along with this program; if not, write to the Free Software Foundation, 00023 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00024 * 00025 * @section Description 00026 * 00027 * SMTP is designed as a mail transport and delivery protocol. Refer to 00028 * the following RFCs for complete details: 00029 * - RFC 5321: Simple Mail Transfer Protocol 00030 * - RFC 4954: SMTP Service Extension for Authentication 00031 * - RFC 3207: SMTP Service Extension for Secure SMTP over TLS 00032 * 00033 * @author Oryx Embedded SARL (www.oryx-embedded.com) 00034 * @version 1.7.6 00035 **/ 00036 00037 //Switch to the appropriate trace level 00038 #define TRACE_LEVEL SMTP_TRACE_LEVEL 00039 00040 //Dependencies 00041 #include <string.h> 00042 #include <ctype.h> 00043 #include <stdlib.h> 00044 #include "core/net.h" 00045 #include "smtp/smtp_client.h" 00046 #include "core/socket.h" 00047 #include "str.h" 00048 #include "debug.h" 00049 00050 //Check TCP/IP stack configuration 00051 #if (SMTP_CLIENT_SUPPORT == ENABLED) 00052 00053 00054 /** 00055 * @brief Send a mail to the specified recipients 00056 * @param[in] authInfo Authentication information 00057 * @param[in] mail Mail contents 00058 * @return Error code 00059 **/ 00060 00061 error_t smtpSendMail(const SmtpAuthInfo *authInfo, const SmtpMail *mail) 00062 { 00063 error_t error; 00064 uint_t i; 00065 uint_t replyCode; 00066 IpAddr serverIpAddr; 00067 SmtpClientContext *context; 00068 00069 //Check parameters 00070 if(authInfo == NULL || mail == NULL) 00071 return ERROR_INVALID_PARAMETER; 00072 //Make sure the server name is valid 00073 if(authInfo->serverName == NULL) 00074 return ERROR_INVALID_PARAMETER; 00075 00076 //Debug message 00077 TRACE_INFO("Sending a mail to %s port %" PRIu16 "...\r\n", 00078 authInfo->serverName, authInfo->serverPort); 00079 00080 //The specified SMTP server can be either an IP or a host name 00081 error = getHostByName(authInfo->interface, 00082 authInfo->serverName, &serverIpAddr, 0); 00083 //Unable to resolve server name? 00084 if(error) 00085 return ERROR_NAME_RESOLUTION_FAILED; 00086 00087 //Allocate a memory buffer to hold the SMTP client context 00088 context = osAllocMem(sizeof(SmtpClientContext)); 00089 //Failed to allocate memory? 00090 if(context == NULL) 00091 return ERROR_OUT_OF_MEMORY; 00092 00093 //Open a TCP socket 00094 context->socket = socketOpen(SOCKET_TYPE_STREAM, SOCKET_IP_PROTO_TCP); 00095 //Failed to open socket? 00096 if(!context->socket) 00097 { 00098 //Free previously allocated resources 00099 osFreeMem(context); 00100 //Report an error 00101 return ERROR_OPEN_FAILED; 00102 } 00103 00104 #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) 00105 //Do not use SSL/TLS for the moment 00106 context->tlsContext = NULL; 00107 #endif 00108 00109 //Start of exception handling block 00110 do 00111 { 00112 //Bind the socket to a particular network interface? 00113 if(authInfo->interface) 00114 { 00115 //Associate the socket with the relevant interface 00116 error = socketBindToInterface(context->socket, authInfo->interface); 00117 //Any error to report? 00118 if(error) 00119 break; 00120 } 00121 00122 //Set timeout for blocking operations 00123 error = socketSetTimeout(context->socket, SMTP_CLIENT_DEFAULT_TIMEOUT); 00124 //Any error to report? 00125 if(error) 00126 break; 00127 00128 //Connect to the SMTP server 00129 error = socketConnect(context->socket, &serverIpAddr, authInfo->serverPort); 00130 //Connection to server failed? 00131 if(error) 00132 break; 00133 00134 #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) 00135 //Open a secure SSL/TLS session? 00136 if(authInfo->useTls) 00137 { 00138 //Initialize TLS context 00139 context->tlsContext = tlsInit(); 00140 //Initialization failed? 00141 if(context->tlsContext == NULL) 00142 { 00143 //Unable to allocate memory 00144 error = ERROR_OUT_OF_MEMORY; 00145 //Stop immediately 00146 break; 00147 } 00148 00149 //Bind TLS to the relevant socket 00150 error = tlsSetSocket(context->tlsContext, context->socket); 00151 //Any error to report? 00152 if(error) 00153 break; 00154 00155 //Select client operation mode 00156 error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT); 00157 //Any error to report? 00158 if(error) 00159 break; 00160 00161 //Set the PRNG algorithm to be used 00162 error = tlsSetPrng(context->tlsContext, authInfo->prngAlgo, authInfo->prngContext); 00163 //Any error to report? 00164 if(error) 00165 break; 00166 00167 //Perform TLS handshake 00168 error = tlsConnect(context->tlsContext); 00169 //Failed to established a TLS session? 00170 if(error) 00171 break; 00172 } 00173 #endif 00174 00175 //Wait for the connection greeting reply 00176 error = smtpSendCommand(context, NULL, &replyCode, NULL); 00177 //Any communication error to report? 00178 if(error) 00179 break; 00180 00181 //Check whether the greeting message was properly received 00182 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00183 { 00184 //An unexpected response was received... 00185 error = ERROR_UNEXPECTED_RESPONSE; 00186 //Stop immediately 00187 break; 00188 } 00189 00190 //Clear security features 00191 context->authLoginSupported = FALSE; 00192 context->authPlainSupported = FALSE; 00193 context->authCramMd5Supported = FALSE; 00194 context->startTlsSupported = FALSE; 00195 00196 //Send EHLO command and parse server response 00197 error = smtpSendCommand(context, "EHLO [127.0.0.1]\r\n", 00198 &replyCode, smtpEhloReplyCallback); 00199 //Any communication error to report? 00200 if(error) 00201 break; 00202 00203 //Check SMTP response code 00204 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00205 { 00206 //An unexpected response was received... 00207 error = ERROR_UNEXPECTED_RESPONSE; 00208 //Stop immediately 00209 break; 00210 } 00211 00212 #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) 00213 //Check whether the STARTTLS command is supported 00214 if(context->startTlsSupported && !context->tlsContext) 00215 { 00216 //Send STARTTLS command 00217 error = smtpSendCommand(context, "STARTTLS\r\n", &replyCode, NULL); 00218 //Any communication error to report? 00219 if(error) 00220 break; 00221 00222 //Check SMTP response code 00223 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00224 { 00225 //An unexpected response was received... 00226 error = ERROR_UNEXPECTED_RESPONSE; 00227 //Stop immediately 00228 break; 00229 } 00230 00231 //Initialize TLS context 00232 context->tlsContext = tlsInit(); 00233 //Initialization failed? 00234 if(context->tlsContext == NULL) 00235 { 00236 //Unable to allocate memory 00237 error = ERROR_OUT_OF_MEMORY; 00238 //Stop immediately 00239 break; 00240 } 00241 00242 //Bind TLS to the relevant socket 00243 error = tlsSetSocket(context->tlsContext, context->socket); 00244 //Any error to report? 00245 if(error) 00246 break; 00247 00248 //Select client operation mode 00249 error = tlsSetConnectionEnd(context->tlsContext, TLS_CONNECTION_END_CLIENT); 00250 //Any error to report? 00251 if(error) 00252 break; 00253 00254 //Set the PRNG algorithm to be used 00255 error = tlsSetPrng(context->tlsContext, authInfo->prngAlgo, authInfo->prngContext); 00256 //Any error to report? 00257 if(error) 00258 break; 00259 00260 //Perform TLS handshake 00261 error = tlsConnect(context->tlsContext); 00262 //Failed to established a TLS session? 00263 if(error) 00264 break; 00265 00266 //Clear security features 00267 context->authLoginSupported = FALSE; 00268 context->authPlainSupported = FALSE; 00269 context->authCramMd5Supported = FALSE; 00270 00271 //Send EHLO command and parse server response 00272 error = smtpSendCommand(context, "EHLO [127.0.0.1]\r\n", 00273 &replyCode, smtpEhloReplyCallback); 00274 //Any communication error to report? 00275 if(error) 00276 break; 00277 00278 //Check SMTP response code 00279 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00280 { 00281 //An unexpected response was received... 00282 error = ERROR_UNEXPECTED_RESPONSE; 00283 //Stop immediately 00284 break; 00285 } 00286 } 00287 #endif 00288 00289 //Authentication requires a valid user name and password 00290 if(authInfo->userName && authInfo->password) 00291 { 00292 #if (SMTP_CLIENT_LOGIN_AUTH_SUPPORT == ENABLED) 00293 //LOGIN authentication mechanism supported? 00294 if(context->authLoginSupported) 00295 { 00296 //Perform LOGIN authentication 00297 error = smtpSendAuthLogin(context, authInfo); 00298 //Authentication failed? 00299 if(error) 00300 break; 00301 } 00302 else 00303 #endif 00304 #if (SMTP_CLIENT_PLAIN_AUTH_SUPPORT == ENABLED) 00305 //PLAIN authentication mechanism supported? 00306 if(context->authPlainSupported) 00307 { 00308 //Perform PLAIN authentication 00309 error = smtpSendAuthPlain(context, authInfo); 00310 //Authentication failed? 00311 if(error) 00312 break; 00313 } 00314 else 00315 #endif 00316 #if (SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT == ENABLED) 00317 //CRAM-MD5 authentication mechanism supported? 00318 if(context->authCramMd5Supported) 00319 { 00320 //Perform CRAM-MD5 authentication 00321 error = smtpSendAuthCramMd5(context, authInfo); 00322 //Authentication failed? 00323 if(error) 00324 break; 00325 } 00326 else 00327 #endif 00328 //No authentication mechanism supported? 00329 { 00330 //Skip authentication step 00331 } 00332 } 00333 00334 //Format the MAIL FROM command (a null return path must be accepted) 00335 if(mail->from.addr) 00336 sprintf(context->buffer, "MAIL FROM:<%s>\r\n", mail->from.addr); 00337 else 00338 strcpy(context->buffer, "MAIL FROM:<>\r\n"); 00339 00340 //Send the command to the server 00341 error = smtpSendCommand(context, context->buffer, &replyCode, NULL); 00342 //Any communication error to report? 00343 if(error) 00344 break; 00345 00346 //Check SMTP response code 00347 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00348 { 00349 //An unexpected response was received... 00350 error = ERROR_UNEXPECTED_RESPONSE; 00351 //Stop immediately 00352 break; 00353 } 00354 00355 //Format the RCPT TO command 00356 for(i = 0; i < mail->recipientCount; i++) 00357 { 00358 //Skip recipient addresses that are not valid 00359 if(!mail->recipients[i].addr) 00360 continue; 00361 00362 //Format the RCPT TO command 00363 sprintf(context->buffer, "RCPT TO:<%s>\r\n", mail->recipients[i].addr); 00364 //Send the command to the server 00365 error = smtpSendCommand(context, context->buffer, &replyCode, NULL); 00366 //Any communication error to report? 00367 if(error) 00368 break; 00369 00370 //Check SMTP response code 00371 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00372 { 00373 //An unexpected response was received... 00374 error = ERROR_UNEXPECTED_RESPONSE; 00375 //Stop immediately 00376 break; 00377 } 00378 } 00379 00380 //Propagate exception if necessary 00381 if(error) 00382 break; 00383 00384 //Send message body 00385 error = smtpSendData(context, mail); 00386 //Any error to report? 00387 if(error) 00388 break; 00389 00390 //End of exception handling block 00391 } while(0); 00392 00393 //Check status code 00394 if(error == NO_ERROR || 00395 error == ERROR_UNEXPECTED_RESPONSE || 00396 error == ERROR_AUTHENTICATION_FAILED) 00397 { 00398 //Properly disconnect from the SMTP server 00399 smtpSendCommand(context, "QUIT\r\n", &replyCode, NULL); 00400 } 00401 00402 #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) 00403 if(context->tlsContext != NULL) 00404 { 00405 //Gracefully close SSL/TLS session 00406 tlsShutdown(context->tlsContext); 00407 //Release SSL/TLS context 00408 tlsFree(context->tlsContext); 00409 } 00410 #endif 00411 00412 //Close socket 00413 socketClose(context->socket); 00414 //Clean up previously allocated resources 00415 osFreeMem(context); 00416 00417 //Return status code 00418 return error; 00419 } 00420 00421 00422 /** 00423 * @brief Callback function to parse EHLO response 00424 * @param[in] context SMTP client context 00425 * @param[in] replyLine Response line 00426 * @param[in] replyCode Response code 00427 * @return Error code 00428 **/ 00429 00430 error_t smtpEhloReplyCallback(SmtpClientContext *context, 00431 char_t *replyLine, uint_t replyCode) 00432 { 00433 char_t *p; 00434 char_t *token; 00435 00436 //The line must be at least 4 characters long 00437 if(strlen(replyLine) < 4) 00438 return NO_ERROR; 00439 00440 //Skip the response code and the separator 00441 replyLine += 4; 00442 00443 //Get the first keyword 00444 token = strtok_r(replyLine, " ", &p); 00445 //Check whether the response line is empty 00446 if(token == NULL) 00447 return ERROR_INVALID_SYNTAX; 00448 00449 //The AUTH keyword contains a space-separated list of 00450 //names of available authentication mechanisms 00451 if(!strcasecmp(token, "AUTH")) 00452 { 00453 //Process the rest of the line 00454 while(1) 00455 { 00456 //Get the next keyword 00457 token = strtok_r(NULL, " ", &p); 00458 //Unable to find the next token? 00459 if(token == NULL) 00460 break; 00461 00462 //LOGIN authentication mechanism supported? 00463 if(!strcasecmp(token, "LOGIN")) 00464 context->authLoginSupported = TRUE; 00465 //PLAIN authentication mechanism supported? 00466 else if(!strcasecmp(token, "PLAIN")) 00467 context->authPlainSupported = TRUE; 00468 //CRAM-MD5 authentication mechanism supported? 00469 else if(!strcasecmp(token, "CRAM-MD5")) 00470 context->authCramMd5Supported = TRUE; 00471 } 00472 } 00473 //The STARTTLS keyword is used to tell the SMTP client 00474 //that the SMTP server allows use of TLS 00475 else if(!strcasecmp(token, "STARTTLS")) 00476 { 00477 //STARTTLS use is allowed 00478 context->startTlsSupported = TRUE; 00479 } 00480 00481 //Successful processing 00482 return NO_ERROR; 00483 } 00484 00485 00486 /** 00487 * @brief Authentication using LOGIN mechanism 00488 * @param[in] context SMTP client context 00489 * @param[in] authInfo Authentication information 00490 * @return Error code 00491 **/ 00492 00493 error_t smtpSendAuthLogin(SmtpClientContext *context, const SmtpAuthInfo *authInfo) 00494 { 00495 #if (SMTP_CLIENT_LOGIN_AUTH_SUPPORT == ENABLED) 00496 error_t error; 00497 uint_t replyCode; 00498 00499 //Send AUTH LOGIN command 00500 error = smtpSendCommand(context, "AUTH LOGIN\r\n", &replyCode, NULL); 00501 00502 //Any communication error to report? 00503 if(error) 00504 return error; 00505 //Check SMTP reply code 00506 if(!SMTP_REPLY_CODE_3YZ(replyCode)) 00507 return ERROR_AUTHENTICATION_FAILED; 00508 00509 //Encode the user name with Base64 algorithm 00510 base64Encode(authInfo->userName, strlen(authInfo->userName), context->buffer, NULL); 00511 //Add a line feed 00512 strcat(context->buffer, "\r\n"); 00513 00514 //Send the resulting string 00515 error = smtpSendCommand(context, context->buffer, &replyCode, NULL); 00516 //Any communication error to report? 00517 if(error) 00518 return error; 00519 00520 //Check SMTP reply code 00521 if(!SMTP_REPLY_CODE_3YZ(replyCode)) 00522 return ERROR_AUTHENTICATION_FAILED; 00523 00524 //Encode the password with Base64 algorithm 00525 base64Encode(authInfo->password, strlen(authInfo->password), context->buffer, NULL); 00526 //Add a line feed 00527 strcat(context->buffer, "\r\n"); 00528 00529 //Send the resulting string 00530 error = smtpSendCommand(context, context->buffer, &replyCode, NULL); 00531 //Any communication error to report? 00532 if(error) 00533 return error; 00534 00535 //Check SMTP reply code 00536 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00537 return ERROR_AUTHENTICATION_FAILED; 00538 00539 //Successful authentication 00540 return NO_ERROR; 00541 #else 00542 //LOGIN authentication is not supported 00543 return ERROR_AUTHENTICATION_FAILED; 00544 #endif 00545 } 00546 00547 00548 /** 00549 * @brief Authentication using PLAIN mechanism 00550 * @param[in] context SMTP client context 00551 * @param[in] authInfo Authentication information 00552 * @return Error code 00553 **/ 00554 00555 error_t smtpSendAuthPlain(SmtpClientContext *context, const SmtpAuthInfo *authInfo) 00556 { 00557 #if (SMTP_CLIENT_PLAIN_AUTH_SUPPORT == ENABLED) 00558 error_t error; 00559 uint_t n; 00560 uint_t replyCode; 00561 00562 //Authorization identity 00563 strcpy(context->buffer, authInfo->userName); 00564 n = strlen(authInfo->userName) + 1; 00565 //Authentication identity 00566 strcpy(context->buffer + n, authInfo->userName); 00567 n += strlen(authInfo->userName) + 1; 00568 //Password 00569 strcpy(context->buffer + n, authInfo->password); 00570 n += strlen(authInfo->password); 00571 00572 //Base64 encoding 00573 base64Encode(context->buffer, n, context->buffer2, NULL); 00574 //Format the AUTH PLAIN command 00575 sprintf(context->buffer, "AUTH PLAIN %s\r\n", context->buffer2); 00576 00577 //Send the command to the server 00578 error = smtpSendCommand(context, context->buffer, &replyCode, NULL); 00579 //Any communication error to report? 00580 if(error) 00581 return error; 00582 00583 //Check SMTP reply code 00584 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00585 return ERROR_AUTHENTICATION_FAILED; 00586 00587 //Successful authentication 00588 return NO_ERROR; 00589 #else 00590 //PLAIN authentication is not supported 00591 return ERROR_AUTHENTICATION_FAILED; 00592 #endif 00593 } 00594 00595 00596 /** 00597 * @brief Authentication using CRAM-MD5 mechanism 00598 * @param[in] context SMTP client context 00599 * @param[in] authInfo Authentication information 00600 * @return Error code 00601 **/ 00602 00603 error_t smtpSendAuthCramMd5(SmtpClientContext *context, const SmtpAuthInfo *authInfo) 00604 { 00605 #if (SMTP_CLIENT_CRAM_MD5_AUTH_SUPPORT == ENABLED) 00606 //Hex conversion table 00607 static const char_t hexDigit[] = 00608 { 00609 '0', '1', '2', '3', '4', '5', '6', '7', 00610 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 00611 }; 00612 00613 //Local variables 00614 error_t error; 00615 uint_t n; 00616 uint_t replyCode; 00617 00618 //Alias pointers 00619 uint8_t *challenge = (uint8_t *) context->buffer2; 00620 uint8_t *digest = (uint8_t *) context->buffer; 00621 char_t *textDigest = (char_t *) context->buffer2; 00622 00623 //Send AUTH CRAM-MD5 command 00624 error = smtpSendCommand(context, "AUTH CRAM-MD5\r\n", &replyCode, NULL); 00625 //Any communication error to report? 00626 if(error) 00627 return error; 00628 00629 //Check SMTP reply code 00630 if(!SMTP_REPLY_CODE_3YZ(replyCode)) 00631 return ERROR_AUTHENTICATION_FAILED; 00632 00633 //Compute the length of the response 00634 n = strlen(context->buffer); 00635 //Unexpected response from the SMTP server? 00636 if(n <= 4) 00637 return ERROR_INVALID_SYNTAX; 00638 00639 //Decrypt the Base64 encoded challenge 00640 error = base64Decode(context->buffer + 4, n - 4, challenge, &n); 00641 //Decoding failed? 00642 if(error) 00643 return error; 00644 00645 //Compute HMAC using MD5 00646 error = hmacCompute(MD5_HASH_ALGO, authInfo->password, 00647 strlen(authInfo->password), challenge, n, digest); 00648 //HMAC computation failed? 00649 if(error) 00650 return error; 00651 00652 //Convert the digest to text 00653 for(n = 0; n < MD5_DIGEST_SIZE; n++) 00654 { 00655 //Convert upper nibble 00656 textDigest[n * 2] = hexDigit[(digest[n] >> 4) & 0x0F]; 00657 //Then convert lower nibble 00658 textDigest[n * 2 + 1] = hexDigit[digest[n] & 0x0F]; 00659 } 00660 00661 //Properly terminate the string 00662 textDigest[MD5_DIGEST_SIZE * 2] = '\0'; 00663 //Concatenate the user name and the text representation of the digest 00664 sprintf(context->buffer, "%s %s", authInfo->userName, textDigest); 00665 //Encode the resulting string with Base64 algorithm 00666 base64Encode(context->buffer, strlen(context->buffer), context->buffer2, NULL); 00667 //Add a line feed 00668 strcat(context->buffer2, "\r\n"); 00669 00670 //Transmit the Base64 encoded string 00671 error = smtpSendCommand(context, context->buffer2, &replyCode, NULL); 00672 //Any communication error to report? 00673 if(error) 00674 return error; 00675 00676 //Check SMTP reply code 00677 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00678 return ERROR_AUTHENTICATION_FAILED; 00679 00680 //Successful authentication 00681 return NO_ERROR; 00682 #else 00683 //CRAM-MD5 authentication is not supported 00684 return ERROR_AUTHENTICATION_FAILED; 00685 #endif 00686 } 00687 00688 00689 /** 00690 * @brief Send message body 00691 * @param[in] context SMTP client context 00692 * @param[in] mail Mail contents 00693 * @return Error code 00694 **/ 00695 00696 error_t smtpSendData(SmtpClientContext *context, const SmtpMail *mail) 00697 { 00698 error_t error; 00699 bool_t first; 00700 uint_t i; 00701 uint_t replyCode; 00702 char_t *p; 00703 00704 //Send DATA command 00705 error = smtpSendCommand(context, "DATA\r\n", &replyCode, NULL); 00706 //Any communication error to report? 00707 if(error) 00708 return error; 00709 00710 //Check SMTP reply code 00711 if(!SMTP_REPLY_CODE_3YZ(replyCode)) 00712 return ERROR_UNEXPECTED_RESPONSE; 00713 00714 //Point to the beginning of the buffer 00715 p = context->buffer; 00716 00717 //Current date and time 00718 if(mail->dateTime && mail->dateTime[0] != '\0') 00719 p += sprintf(p, "Date: %s\r\n", mail->dateTime); 00720 00721 //Sender address 00722 if(mail->from.addr) 00723 { 00724 //A friendly name may be associated with the sender address 00725 if(mail->from.name && mail->from.name[0] != '\0') 00726 p += sprintf(p, "From: \"%s\" <%s>\r\n", mail->from.name, mail->from.addr); 00727 else 00728 p += sprintf(p, "From: %s\r\n", mail->from.addr); 00729 } 00730 00731 //Recipients 00732 for(i = 0, first = TRUE; i < mail->recipientCount; i++) 00733 { 00734 //Skip recipient addresses that are not valid 00735 if(!mail->recipients[i].addr) 00736 continue; 00737 00738 //Check recipient type 00739 if(mail->recipients[i].type & SMTP_RCPT_TYPE_TO) 00740 { 00741 //The first item of the list requires special processing 00742 p += sprintf(p, first ? "To: " : ", "); 00743 00744 //A friendly name may be associated with the address 00745 if(mail->recipients[i].name && mail->recipients[i].name[0] != '\0') 00746 p += sprintf(p, "\"%s\" <%s>", mail->recipients[i].name, mail->recipients[i].addr); 00747 else 00748 p += sprintf(p, "%s", mail->recipients[i].addr); 00749 00750 //Prepare to add a new item to the list 00751 first = FALSE; 00752 } 00753 } 00754 00755 //Properly terminate the line with CRLF 00756 if(!first) 00757 p += sprintf(p, "\r\n"); 00758 00759 //Carbon copy 00760 for(i = 0, first = TRUE; i < mail->recipientCount; i++) 00761 { 00762 //Skip recipient addresses that are not valid 00763 if(!mail->recipients[i].addr) 00764 continue; 00765 00766 //Check recipient type 00767 if(mail->recipients[i].type & SMTP_RCPT_TYPE_CC) 00768 { 00769 //The first item of the list requires special processing 00770 p += sprintf(p, first ? "Cc: " : ", "); 00771 00772 //A friendly name may be associated with the address 00773 if(mail->recipients[i].name && mail->recipients[i].name[0] != '\0') 00774 p += sprintf(p, "\"%s\" <%s>", mail->recipients[i].name, mail->recipients[i].addr); 00775 else 00776 p += sprintf(p, "%s", mail->recipients[i].addr); 00777 00778 //Prepare to add a new item to the list 00779 first = FALSE; 00780 } 00781 } 00782 00783 //Properly terminate the line with CRLF 00784 if(!first) 00785 p += sprintf(p, "\r\n"); 00786 00787 //Subject 00788 if(mail->subject) 00789 p += sprintf(p, "Subject: %s\r\n", mail->subject); 00790 00791 //The header and the body are separated by an empty line 00792 sprintf(p, "\r\n"); 00793 00794 //Debug message 00795 TRACE_DEBUG(context->buffer); 00796 TRACE_DEBUG(mail->body); 00797 TRACE_DEBUG("\r\n.\r\n"); 00798 00799 //Send message header 00800 error = smtpWrite(context, context->buffer, strlen(context->buffer), 0); 00801 //Any communication error to report? 00802 if(error) 00803 return error; 00804 00805 //Send message body 00806 error = smtpWrite(context, mail->body, strlen(mail->body), 0); 00807 //Any communication error to report? 00808 if(error) 00809 return error; 00810 00811 //Indicate the end of the mail data by sending a line containing only a "." 00812 error = smtpSendCommand(context, "\r\n.\r\n", &replyCode, NULL); 00813 //Any communication error to report? 00814 if(error) 00815 return error; 00816 00817 //Check SMTP reply code 00818 if(!SMTP_REPLY_CODE_2YZ(replyCode)) 00819 return ERROR_UNEXPECTED_RESPONSE; 00820 00821 //Successful operation 00822 return NO_ERROR; 00823 } 00824 00825 00826 /** 00827 * @brief Send SMTP command and wait for a reply 00828 * @param[in] context SMTP client context 00829 * @param[in] command Command line 00830 * @param[out] replyCode SMTP server reply code 00831 * @param[in] callback Optional callback to parse each line of the reply 00832 * @return Error code 00833 **/ 00834 00835 error_t smtpSendCommand(SmtpClientContext *context, const char_t *command, 00836 uint_t *replyCode, SmtpReplyCallback callback) 00837 { 00838 error_t error; 00839 size_t length; 00840 char_t *line; 00841 00842 //Any command line to send? 00843 if(command) 00844 { 00845 //Debug message 00846 TRACE_DEBUG("SMTP client: %s", command); 00847 00848 //Send the command to the SMTP server 00849 error = smtpWrite(context, command, strlen(command), SOCKET_FLAG_WAIT_ACK); 00850 //Failed to send command? 00851 if(error) 00852 return error; 00853 } 00854 00855 //Multiline replies are allowed for any command 00856 while(1) 00857 { 00858 //Wait for a response from the server 00859 error = smtpRead(context, context->buffer, 00860 SMTP_CLIENT_MAX_LINE_LENGTH - 1, &length, SOCKET_FLAG_BREAK_CRLF); 00861 00862 //The remote server did not respond as expected? 00863 if(error) 00864 return error; 00865 00866 //Properly terminate the string with a NULL character 00867 context->buffer[length] = '\0'; 00868 //Remove all leading and trailing whitespace from the response 00869 line = strTrimWhitespace(context->buffer); 00870 00871 //Debug message 00872 TRACE_DEBUG("SMTP server: %s\r\n", line); 00873 00874 //Check the length of the response 00875 if(strlen(line) < 3) 00876 return ERROR_INVALID_SYNTAX; 00877 //All replies begin with a three digit numeric code 00878 if(!isdigit((uint8_t) line[0]) || !isdigit((uint8_t) line[1]) || !isdigit((uint8_t) line[2])) 00879 return ERROR_INVALID_SYNTAX; 00880 //A hyphen or a space character must follow the response code 00881 if(line[3] != '-' && line[3] != ' ' && line[3] != '\0') 00882 return ERROR_INVALID_SYNTAX; 00883 00884 //Get the server response code 00885 *replyCode = strtoul(line, NULL, 10); 00886 00887 //Any callback function to call? 00888 if(callback) 00889 { 00890 //Invoke callback function to parse the response line 00891 error = callback(context, line, *replyCode); 00892 //Check status code 00893 if(error) 00894 return error; 00895 } 00896 00897 //A hyphen follows the response code for all but the last line 00898 if(line[3] != '-') 00899 break; 00900 } 00901 00902 //Successful processing 00903 return NO_ERROR; 00904 } 00905 00906 00907 /** 00908 * @brief Send data to the SMTP server 00909 * @param[in] context SMTP client context 00910 * @param[in] data Pointer to a buffer containing the data to be transmitted 00911 * @param[in] length Number of bytes to be transmitted 00912 * @param[in] flags Set of flags that influences the behavior of this function 00913 **/ 00914 00915 error_t smtpWrite(SmtpClientContext *context, const void *data, size_t length, uint_t flags) 00916 { 00917 #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) 00918 //Check whether a secure connection is being used 00919 if(context->tlsContext != NULL) 00920 { 00921 //Use SSL/TLS to transmit data to the SMTP server 00922 return tlsWrite(context->tlsContext, data, length, NULL, flags); 00923 } 00924 else 00925 #endif 00926 { 00927 //Transmit data to the SMTP server 00928 return socketSend(context->socket, data, length, NULL, flags); 00929 } 00930 } 00931 00932 00933 /** 00934 * @brief Receive data from the SMTP server 00935 * @param[in] context SMTP client context 00936 * @param[out] data Buffer into which received data will be placed 00937 * @param[in] size Maximum number of bytes that can be received 00938 * @param[out] received Actual number of bytes that have been received 00939 * @param[in] flags Set of flags that influences the behavior of this function 00940 * @return Error code 00941 **/ 00942 00943 error_t smtpRead(SmtpClientContext *context, void *data, size_t size, size_t *received, uint_t flags) 00944 { 00945 #if (SMTP_CLIENT_TLS_SUPPORT == ENABLED) 00946 //Check whether a secure connection is being used 00947 if(context->tlsContext != NULL) 00948 { 00949 //Use SSL/TLS to receive data from the SMTP server 00950 return tlsRead(context->tlsContext, data, size, received, flags); 00951 } 00952 else 00953 #endif 00954 { 00955 //Receive data from the SMTP server 00956 return socketReceive(context->socket, data, size, received, flags); 00957 } 00958 } 00959 00960 #endif 00961
Generated on Tue Jul 12 2022 17:10:16 by
