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 | }
|
---|