[1005] | 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 | }
|
---|