| 1 | /* | 
 
 
 
 
 | 2 | This file is part of libquickmail. | 
 
 
 
 
 | 3 |  | 
 
 
 
 
 | 4 | libquickmail is free software: you can redistribute it and/or modify | 
 
 
 
 
 | 5 | it under the terms of the GNU General Public License as published by | 
 
 
 
 
 | 6 | the Free Software Foundation, either version 3 of the License, or | 
 
 
 
 
 | 7 | (at your option) any later version. | 
 
 
 
 
 | 8 |  | 
 
 
 
 
 | 9 | libquickmail is distributed in the hope that it will be useful, | 
 
 
 
 
 | 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 
 
 
 
 | 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 
 
 
 
 | 12 | GNU General Public License for more details. | 
 
 
 
 
 | 13 |  | 
 
 
 
 
 | 14 | You should have received a copy of the GNU General Public License | 
 
 
 
 
 | 15 | along with libquickmail.  If not, see <http://www.gnu.org/licenses/>. | 
 
 
 
 
 | 16 | */ | 
 
 
 
 
 | 17 |  | 
 
 
 
 
 | 18 | #include "smtpsocket.h" | 
 
 
 
 
 | 19 | #include <stdlib.h> | 
 
 
 
 
 | 20 | #include <ctype.h> | 
 
 
 
 
 | 21 | #include <string.h> | 
 
 
 
 
 | 22 | #if _MSC_VER | 
 
 
 
 
 | 23 | #define va_copy(dst,src) ((dst) = (src)) | 
 
 
 
 
 | 24 | #endif | 
 
 
 
 
 | 25 |  | 
 
 
 
 
 | 26 | //////////////////////////////////////////////////////////////////////// | 
 
 
 
 
 | 27 |  | 
 
 
 
 
 | 28 | #define DEBUG_ERROR(errmsg) | 
 
 
 
 
 | 29 | static const char* ERRMSG_MEMORY_ALLOCATION_ERROR = "Memory allocation error"; | 
 
 
 
 
 | 30 |  | 
 
 
 
 
 | 31 | //////////////////////////////////////////////////////////////////////// | 
 
 
 
 
 | 32 |  | 
 
 
 
 
 | 33 | SOCKET socket_open (const char* smtpserver, unsigned int smtpport, char** errmsg) | 
 
 
 
 
 | 34 | { | 
 
 
 
 
 | 35 | struct in_addr ipv4addr; | 
 
 
 
 
 | 36 | SOCKET sock; | 
 
 
 
 
 | 37 | struct sockaddr_in remote_sock_addr; | 
 
 
 
 
 | 38 | static const struct linger linger_option = {-1, 2};   //linger 2 seconds when disconnecting | 
 
 
 
 
 | 39 | //determine IPv4 address of SMTP server | 
 
 
 
 
 | 40 | ipv4addr.s_addr = inet_addr(smtpserver); | 
 
 
 
 
 | 41 | if (ipv4addr.s_addr == INADDR_NONE) { | 
 
 
 
 
 | 42 | struct hostent* addr; | 
 
 
 
 
 | 43 | if ((addr = gethostbyname(smtpserver)) != NULL && (addr->h_addrtype == AF_INET && addr->h_length >= 1 && ((struct in_addr*)addr->h_addr)->s_addr != 0)) | 
 
 
 
 
 | 44 | memcpy(&ipv4addr, addr->h_addr, sizeof(ipv4addr)); | 
 
 
 
 
 | 45 | } | 
 
 
 
 
 | 46 | if (ipv4addr.s_addr == INADDR_NONE) { | 
 
 
 
 
 | 47 | if (errmsg) | 
 
 
 
 
 | 48 | *errmsg = "Unable to resolve SMTP server host name"; | 
 
 
 
 
 | 49 | return INVALID_SOCKET; | 
 
 
 
 
 | 50 | } | 
 
 
 
 
 | 51 | //create socket | 
 
 
 
 
 | 52 | sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | 
 
 
 
 
 | 53 | if (sock == INVALID_SOCKET) { | 
 
 
 
 
 | 54 | if (errmsg) | 
 
 
 
 
 | 55 | *errmsg = "Error creating socket for SMTP connection"; | 
 
 
 
 
 | 56 | return INVALID_SOCKET; | 
 
 
 
 
 | 57 | } | 
 
 
 
 
 | 58 | //connect | 
 
 
 
 
 | 59 | remote_sock_addr.sin_family = AF_INET; | 
 
 
 
 
 | 60 | remote_sock_addr.sin_port = htons(smtpport); | 
 
 
 
 
 | 61 | remote_sock_addr.sin_addr.s_addr = ipv4addr.s_addr; | 
 
 
 
 
 | 62 | if (connect(sock, (struct sockaddr*)&remote_sock_addr, sizeof(remote_sock_addr)) != 0) { | 
 
 
 
 
 | 63 | if (errmsg) | 
 
 
 
 
 | 64 | *errmsg = "Error connecting to SMTP server"; | 
 
 
 
 
 | 65 | socket_close(sock); | 
 
 
 
 
 | 66 | return INVALID_SOCKET; | 
 
 
 
 
 | 67 | } | 
 
 
 
 
 | 68 | //set linger option | 
 
 
 
 
 | 69 | setsockopt(sock, SOL_SOCKET, SO_LINGER, (const char*)&linger_option, sizeof(linger_option)); | 
 
 
 
 
 | 70 | return sock; | 
 
 
 
 
 | 71 | } | 
 
 
 
 
 | 72 |  | 
 
 
 
 
 | 73 | void socket_close (SOCKET sock) | 
 
 
 
 
 | 74 | { | 
 
 
 
 
 | 75 | #ifndef _WIN32 | 
 
 
 
 
 | 76 | shutdown(sock, 2); | 
 
 
 
 
 | 77 | #else | 
 
 
 
 
 | 78 | closesocket(sock); | 
 
 
 
 
 | 79 | #endif | 
 
 
 
 
 | 80 | } | 
 
 
 
 
 | 81 |  | 
 
 
 
 
 | 82 | int socket_send (SOCKET sock, const char* buf, int len) | 
 
 
 
 
 | 83 | { | 
 
 
 
 
 | 84 | int total_sent = 0; | 
 
 
 
 
 | 85 | int l = 0; | 
 
 
 
 
 | 86 | if (sock == 0 || !buf) | 
 
 
 
 
 | 87 | return 0; | 
 
 
 
 
 | 88 | if (len < 0) | 
 
 
 
 
 | 89 | len = strlen(buf); | 
 
 
 
 
 | 90 | while (len > 0 && (l = send(sock, buf, len, 0)) < len) { | 
 
 
 
 
 | 91 | if (l == SOCKET_ERROR || l > len) | 
 
 
 
 
 | 92 | return (total_sent > 0 ? total_sent : -1); | 
 
 
 
 
 | 93 | total_sent += l; | 
 
 
 
 
 | 94 | buf += l; | 
 
 
 
 
 | 95 | len -= l; | 
 
 
 
 
 | 96 | } | 
 
 
 
 
 | 97 | return total_sent + l; | 
 
 
 
 
 | 98 | } | 
 
 
 
 
 | 99 |  | 
 
 
 
 
 | 100 | int socket_data_waiting (SOCKET sock, int timeoutseconds) | 
 
 
 
 
 | 101 | { | 
 
 
 
 
 | 102 | fd_set rfds; | 
 
 
 
 
 | 103 | struct timeval tv; | 
 
 
 
 
 | 104 | if (sock == 0) | 
 
 
 
 
 | 105 | return 0; | 
 
 
 
 
 | 106 | //make a set with only this socket | 
 
 
 
 
 | 107 | FD_ZERO(&rfds); | 
 
 
 
 
 | 108 | FD_SET(sock, &rfds); | 
 
 
 
 
 | 109 | //make a timeval with the supplied timeout | 
 
 
 
 
 | 110 | tv.tv_sec = timeoutseconds; | 
 
 
 
 
 | 111 | tv.tv_usec = 0; | 
 
 
 
 
 | 112 | //check the socket | 
 
 
 
 
 | 113 | return (select(1, &rfds, NULL, NULL, &tv) > 0); | 
 
 
 
 
 | 114 | } | 
 
 
 
 
 | 115 |  | 
 
 
 
 
 | 116 | char* socket_receive_smtp (SOCKET sock) | 
 
 
 
 
 | 117 | { | 
 
 
 
 
 | 118 | char* buf = NULL; | 
 
 
 
 
 | 119 | int bufsize = READ_BUFFER_CHUNK_SIZE; | 
 
 
 
 
 | 120 | int pos = 0; | 
 
 
 
 
 | 121 | int linestart; | 
 
 
 
 
 | 122 | int n; | 
 
 
 
 
 | 123 | if ((buf = (char*)malloc(bufsize)) == NULL) { | 
 
 
 
 
 | 124 | DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR) | 
 
 
 
 
 | 125 | return NULL; | 
 
 
 
 
 | 126 | } | 
 
 
 
 
 | 127 | do { | 
 
 
 
 
 | 128 | //insert line break if response is multiple lines | 
 
 
 
 
 | 129 | if (pos > 0) { | 
 
 
 
 
 | 130 | buf[pos++] = '\n'; | 
 
 
 
 
 | 131 | if (pos >= bufsize) { | 
 
 
 
 
 | 132 | char* newbuf; | 
 
 
 
 
 | 133 | if ((newbuf = (char*)realloc(buf, bufsize + READ_BUFFER_CHUNK_SIZE)) == NULL) { | 
 
 
 
 
 | 134 | free(buf); | 
 
 
 
 
 | 135 | DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR) | 
 
 
 
 
 | 136 | return NULL; | 
 
 
 
 
 | 137 | } | 
 
 
 
 
 | 138 | buf = newbuf; | 
 
 
 
 
 | 139 | bufsize += READ_BUFFER_CHUNK_SIZE; | 
 
 
 
 
 | 140 | } | 
 
 
 
 
 | 141 | } | 
 
 
 
 
 | 142 | //add each character read until it is a line break | 
 
 
 
 
 | 143 | linestart = pos; | 
 
 
 
 
 | 144 | while ((n = recv(sock, buf + pos, 1, 0)) == 1) { | 
 
 
 
 
 | 145 | //detect optional carriage return (CR) | 
 
 
 
 
 | 146 | if (buf[pos] == '\r') | 
 
 
 
 
 | 147 | if (recv(sock, buf + pos, 1, 0) < 1) | 
 
 
 
 
 | 148 | return NULL; | 
 
 
 
 
 | 149 | //detect line feed (LF) | 
 
 
 
 
 | 150 | if (buf[pos] == '\n') | 
 
 
 
 
 | 151 | break; | 
 
 
 
 
 | 152 | //enlarge buffer if necessary | 
 
 
 
 
 | 153 | if (++pos >= bufsize) { | 
 
 
 
 
 | 154 | char* newbuf; | 
 
 
 
 
 | 155 | if ((newbuf = (char*)realloc(buf, bufsize + READ_BUFFER_CHUNK_SIZE)) == NULL) { | 
 
 
 
 
 | 156 | free(buf); | 
 
 
 
 
 | 157 | DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR) | 
 
 
 
 
 | 158 | return NULL; | 
 
 
 
 
 | 159 | } | 
 
 
 
 
 | 160 | buf = newbuf; | 
 
 
 
 
 | 161 | bufsize += READ_BUFFER_CHUNK_SIZE; | 
 
 
 
 
 | 162 | } | 
 
 
 
 
 | 163 | } | 
 
 
 
 
 | 164 | //exit on error (e.g. if connection is closed) | 
 
 
 
 
 | 165 | if (n < 1) | 
 
 
 
 
 | 166 | return NULL; | 
 
 
 
 
 | 167 | } while (!isdigit(buf[linestart]) || !isdigit(buf[linestart + 1]) || !isdigit(buf[linestart + 2]) || buf[linestart + 3] != ' '); | 
 
 
 
 
 | 168 | buf[pos] = 0; | 
 
 
 
 
 | 169 | return buf; | 
 
 
 
 
 | 170 | } | 
 
 
 
 
 | 171 |  | 
 
 
 
 
 | 172 | int socket_get_smtp_code (SOCKET sock, char** message) | 
 
 
 
 
 | 173 | { | 
 
 
 
 
 | 174 | int code; | 
 
 
 
 
 | 175 | char* buf = socket_receive_smtp(sock); | 
 
 
 
 
 | 176 | if (!buf || strlen(buf) < 4 || (buf[3] != ' ' && buf[3] != '-')) { | 
 
 
 
 
 | 177 | free(buf); | 
 
 
 
 
 | 178 | return 999; | 
 
 
 
 
 | 179 | } | 
 
 
 
 
 | 180 | //get code | 
 
 
 
 
 | 181 | buf[3] = 0; | 
 
 
 
 
 | 182 | code = atoi(buf); | 
 
 
 
 
 | 183 | //get error message (if needed) | 
 
 
 
 
 | 184 | if (message /*&& code >= 400*/) | 
 
 
 
 
 | 185 | *message = strdup(buf + 4); | 
 
 
 
 
 | 186 | //clean up and return | 
 
 
 
 
 | 187 | free(buf); | 
 
 
 
 
 | 188 | return code; | 
 
 
 
 
 | 189 | } | 
 
 
 
 
 | 190 |  | 
 
 
 
 
 | 191 | int socket_smtp_command (SOCKET sock, FILE* debuglog, const char* template, ...) | 
 
 
 
 
 | 192 | { | 
 
 
 
 
 | 193 | char* message; | 
 
 
 
 
 | 194 | int statuscode; | 
 
 
 
 
 | 195 | //send command (if one is supplied) | 
 
 
 
 
 | 196 | if (template) { | 
 
 
 
 
 | 197 | va_list ap; | 
 
 
 
 
 | 198 | va_list aq; | 
 
 
 
 
 | 199 | char* cmd; | 
 
 
 
 
 | 200 | int cmdlen; | 
 
 
 
 
 | 201 | va_start(ap, template); | 
 
 
 
 
 | 202 | //construct command to send | 
 
 
 
 
 | 203 | va_copy(aq, ap); | 
 
 
 
 
 | 204 | cmdlen = vsnprintf(NULL, 0, template, aq); | 
 
 
 
 
 | 205 | va_end(aq); | 
 
 
 
 
 | 206 | if ((cmd = (char*)malloc(cmdlen + 3)) == NULL) { | 
 
 
 
 
 | 207 | DEBUG_ERROR(ERRMSG_MEMORY_ALLOCATION_ERROR) | 
 
 
 
 
 | 208 | if (debuglog) | 
 
 
 
 
 | 209 | fprintf(debuglog, ERRMSG_MEMORY_ALLOCATION_ERROR); | 
 
 
 
 
 | 210 | va_end(ap); | 
 
 
 
 
 | 211 | return 999; | 
 
 
 
 
 | 212 | } | 
 
 
 
 
 | 213 | vsnprintf(cmd, cmdlen + 1, template, ap); | 
 
 
 
 
 | 214 | //log command to send | 
 
 
 
 
 | 215 | if (debuglog) | 
 
 
 
 
 | 216 | fprintf(debuglog, "SMTP> %s\n", cmd); | 
 
 
 
 
 | 217 | //append CR+LF | 
 
 
 
 
 | 218 | strcpy(cmd + cmdlen, "\r\n"); | 
 
 
 
 
 | 219 | cmdlen += 2; | 
 
 
 
 
 | 220 | //send command | 
 
 
 
 
 | 221 | statuscode = socket_send(sock, cmd, cmdlen); | 
 
 
 
 
 | 222 | //clean up | 
 
 
 
 
 | 223 | free(cmd); | 
 
 
 
 
 | 224 | va_end(ap); | 
 
 
 
 
 | 225 | if (statuscode < 0) | 
 
 
 
 
 | 226 | return 999; | 
 
 
 
 
 | 227 | } | 
 
 
 
 
 | 228 | //receive result | 
 
 
 
 
 | 229 | message = NULL; | 
 
 
 
 
 | 230 | statuscode = socket_get_smtp_code(sock, &message); | 
 
 
 
 
 | 231 | if (debuglog) | 
 
 
 
 
 | 232 | fprintf(debuglog, "SMTP< %i %s\n", statuscode, (message ? message : "")); | 
 
 
 
 
 | 233 | free(message); | 
 
 
 
 
 | 234 | return statuscode; | 
 
 
 
 
 | 235 | } |