source: AE/RequestHelp/src/quickmail/smtpsocket.c@ 1100

Last change on this file since 1100 was 1005, checked in by alloc, 10 years ago

RequestHelp initial commit

File size: 6.6 KB
Line 
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)
29static const char* ERRMSG_MEMORY_ALLOCATION_ERROR = "Memory allocation error";
30
31////////////////////////////////////////////////////////////////////////
32
33SOCKET 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
73void socket_close (SOCKET sock)
74{
75#ifndef _WIN32
76 shutdown(sock, 2);
77#else
78 closesocket(sock);
79#endif
80}
81
82int 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
100int 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
116char* 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
172int 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
191int 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}
Note: See TracBrowser for help on using the repository browser.