|
|
1.1 paf 1: /** @file
2: Parser: SMTP sender.
3:
1.12 moko 4: Copyright (c) 2001-2015 Art. Lebedev Studio (http://www.artlebedev.com)
1.1 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
6:
7: Parts of the code here is based upon an early gensock and blat
8: */
9:
10: #include "pa_exception.h"
11: #include "smtp.h"
1.4 paf 12:
1.13 ! moko 13: volatile const char * IDENT_SMTP_C="$Id: smtp.C,v 1.12 2015/10/26 01:21:57 moko Exp $" IDENT_SMTP_H;
1.9 moko 14:
1.13 ! moko 15: // pa_common.C
1.4 paf 16: #undef snprintf
1.13 ! moko 17: extern int pa_snprintf(char *, size_t, const char* , ...);
! 18: #define snprintf pa_snprintf
1.1 paf 19:
20: //#define DEBUG_SHOW
21:
22: SMTP::SMTP() {
23: the_socket = 0;
24: in_index = 0;
25: out_index = 0;
26: in_buffer = 0;
27: in_buffer_total = 0;
28: out_buffer_total = 0;
29:
1.11 moko 30: in_buffer = (char *)pa_malloc(SOCKET_BUFFER_SIZE);
31: out_buffer = (char *)pa_malloc(SOCKET_BUFFER_SIZE);
1.1 paf 32:
33: last_winsock_error = 0;
34: }
35:
36: SMTP::~SMTP() {
1.11 moko 37: pa_free(in_buffer);
38: pa_free(out_buffer);
1.1 paf 39: }
40:
41:
42: // ---------------------------------------------------------------------------
43: void SMTP::
44: ConnectToHost(const char* hostname, const char* service)
45: {
46: struct sockaddr_in sa_in;
1.6 paf 47: memset(&sa_in, 0, sizeof(sa_in));
1.1 paf 48: u_short our_port;
49:
50: if( !ResolveService(service, &our_port) )
51: {
52: if( !ResolveHostname(hostname, &sa_in) )
53: {
54: sa_in.sin_family = AF_INET;
55: sa_in.sin_port = (unsigned short)our_port;
56:
57: if( !GetAndSetTheSocket(&the_socket) )
58: {
59: if( !GetConnection(the_socket, &sa_in) )
60: {
61: MiscSocketSetup(the_socket, &fds, &timeout);
62:
63: return;
64: }
65: }
66: }
67: }
68:
69: CloseConnect();
70:
71: throw Exception("smtp.connect",
72: 0,
73: "connect to %s:%s failed",
74: hostname, service);
75: }
76:
77: //---------------------------------------------------------------------------
78: // returns 0 if all is OK
79: int SMTP::
80: GetBuffer(int wait)
81: {
82: int retval;
83: int bytes_read = 0;
84:
85: // Use select to see if data is waiting...
86: FD_ZERO(&fds);
87: FD_SET(the_socket, &fds);
88:
89: // if wait is set, we are polling, return immediately
90: if( wait )
91: {
92: timeout.tv_sec = 0;
93: }
94: else
95: {
96: timeout.tv_sec = 30;
97: }
98:
1.7 paf 99: if( (retval = select(1+the_socket, &fds, NULL, NULL, &timeout))<0 )
1.1 paf 100: {
1.10 moko 101: #ifdef _MSC_VER
1.1 paf 102: int error_code = WSAGetLastError();
103:
104: if( error_code == WSAEINPROGRESS && wait )
105: {
106: return WAIT_A_BIT;
107: }
1.3 paf 108: #else
109: if( errno == EAGAIN && wait )
110: return WAIT_A_BIT;
111: #endif
1.1 paf 112: }
113:
114: // if we don't want to wait
115: if( !retval && wait )
116: {
117: return WAIT_A_BIT;
118: }
119:
120: // we have data waiting...
121: bytes_read = recv(the_socket,
122: in_buffer,
123: SOCKET_BUFFER_SIZE,
124: 0);
125:
126: // just in case.
127: if( 0 == bytes_read )
128: {
129: // connection terminated (semi-) gracefully by the other side
130: return WSAENOTCONN;
131: }
132:
1.3 paf 133: if( bytes_read <0 )
1.1 paf 134: {
135: //char what_error[256];
136: int ws_error = WSAGetLastError();
137:
138: switch( ws_error )
139: {
140: // all these indicate loss of connection (are there more?)
141: case WSAENOTCONN:
142: case WSAENETDOWN:
143: case WSAENETUNREACH:
144: case WSAENETRESET:
145: case WSAECONNABORTED:
146: case WSAECONNRESET:
147: return WSAENOTCONN;
148:
149: case WSAEWOULDBLOCK:
150: return WAIT_A_BIT;
151:
152: default:
153: /*wsprintf(what_error,
154: "GetBuffer() unexpected error: %d",
155: ws_error);
156: ShowError(what_error);
157: */
158: break;
159: }
160: }
161:
162: // reset buffer indices.
163: in_buffer_total = bytes_read;
164: in_index = 0;
165:
166: return 0;
167: }
168:
169: //---------------------------------------------------------------------------
170: // returns 0 if all is OK
171: int SMTP::
172: GetChar(int wait, char *ch)
173: {
174: int retval = 0;
175:
176: if( in_index >= in_buffer_total )
177: {
178: if( 0 != (retval = GetBuffer(wait)) )
179: return retval;
180: }
181: *ch = in_buffer[in_index++];
182:
183: return 0;
184: }
185:
186: //----------------------------------------------------------------------
187: int SMTP::
188: get_line( void )
189: {
190: char ch = '.';
191: char in_data[MAXOUTLINE];
192: char *index;
193:
194: index = in_data;
195:
196: while( ch != '\n' )
197: {
198: if( 0 != GetChar(0, &ch) )
199: {
200: return -1;
201: }
202: else
203: {
204: *index = ch;
205: index++;
206: }
207: }
208:
209: if( in_data[3] == '-' )
210: return get_line();
211: else {
212: char *error_pos;
213: return strtol(in_data, &error_pos, 0);
214: }
215: }
216:
217: //---------------------------------------------------------------------------
218: // returns 0 if all is OK
219: void SMTP::
220: SendLine(const char* data, unsigned long length)
221: {
222: int num_sent;
223:
224: FD_ZERO(&fds);
225: FD_SET(the_socket, &fds);
226:
227: timeout.tv_sec = 30;
228:
229: while( length > 0 )
230: {
1.7 paf 231: if( select(1+the_socket, NULL, &fds, NULL, &timeout)<0 )
1.1 paf 232: throw Exception("smtp.execute",
233: 0,
234: "connection::put_data() unexpected error from select: %d",
235: WSAGetLastError());
236:
237: num_sent = send(the_socket,
238: data,
239: length > 1024 ? 1024 : (int)length,
240: 0);
241:
1.3 paf 242: if( num_sent<0 )
1.1 paf 243: {
244: int ws_error = WSAGetLastError();
245:
246: switch( ws_error )
247: {
248: // this is the only error we really expect to see.
249: case WSAENOTCONN:
250: return;
251:
252: // seems that we can still get a block
253: case WSAEWOULDBLOCK:
254: break;
255:
256: default:
257: throw Exception("smtp.execute",
258: 0,
259: "connection::put_data() unexpected error from send(): %d",
260: ws_error);
261: }
262: }
263: else
264: {
265: length -= num_sent;
266: data += num_sent;
267: }
268: }
269: }
270:
271: //---------------------------------------------------------------------------
272: // returns 0 if all is OK
273: void SMTP::
274: SendBuffer(const char* data, unsigned long length)
275: {
276: while( length )
277: {
278: if( (out_index + length) < SOCKET_BUFFER_SIZE )
279: {
280: // we won't overflow, simply copy into the buffer
281: memcpy(out_buffer + out_index, data, length);
282: out_index += length;
283: length = 0;
284: }
285: else
286: {
287: unsigned int orphaned_chunk = SOCKET_BUFFER_SIZE - out_index;
288:
289: // we will overflow, handle it
290: memcpy(out_buffer + out_index, data, orphaned_chunk);
291:
292: // send this buffer...
293: SendLine(out_buffer, SOCKET_BUFFER_SIZE);
294:
295: length -= orphaned_chunk;
296: out_index = 0;
297: data += orphaned_chunk;
298: }
299: }
300: }
301:
302: //---------------------------------------------------------------------------
303: // returns 0 if all is OK
304: void SMTP::
305: FlushBuffer()
306: {
307: SendLine(out_buffer, out_index);
308: out_index = 0;
309: }
310:
311: //---------------------------------------------------------------------------
1.3 paf 312: bool SMTP::
1.1 paf 313: CloseConnect()
314: {
1.3 paf 315: if( closesocket(the_socket) <0 )
316: return false;
1.1 paf 317:
1.3 paf 318: return true;
1.1 paf 319: }
320:
321: //----------------------------------------------------------------------
322: void SMTP::
323: SendSmtpError(const char* message)
324: {
325: SendLine("QUIT\r\n", 6);
326: CloseConnect();
327:
328: throw Exception("smtp.execute",
329: 0,
330: "failed: %s", message);
331: }
332:
333: //----------------------------------------------------------------------
334: // returns 0 if all is OK
335: // returns 20, 21, 22, 23, 24, 25 if SendBuffer() fails
336: // returns 26 if FlushBuffer() fails
337: void SMTP::
338: transform_and_send_edit_data(const char* editptr )
339: {
340: const char *index;
341: char previous_char = 'x';
342: unsigned int send_len;
1.3 paf 343: bool done = false;
1.1 paf 344:
1.3 paf 345: send_len = strlen(editptr);
1.1 paf 346: index = editptr;
347:
348: while( !done )
349: {
350: // room for extra char for double dot on end case
351: while( (unsigned int)(index - editptr) < send_len )
352: {
353: switch( *index )
354: {
355: case '.':
356: if( previous_char == '\n' )
357: SendBuffer(index, 1); // send _two_ dots...
358: SendBuffer(index, 1);
359: break;
360:
361: case '\n': // x\n -> \r\n
362: if( previous_char != '\r' ) {
363: SendBuffer("\r", 1);
364: SendBuffer(index, 1);
365: }
366: break;
367:
368: default:
369: SendBuffer(index, 1);
370: break;
371: }
372: previous_char = *index;
373: index++;
374: }
375:
376: if( (unsigned int)(index - editptr) == send_len )
1.3 paf 377: done = true;
1.1 paf 378: }
379:
380: // this handles the case where the user doesn't end the last
381: // line with a <return>
382:
383: if( editptr[send_len-1] != '\n' )
384: SendBuffer("\r\n.\r\n", 5);
385: else
386: SendBuffer(".\r\n", 3);
387: /* now make sure it's all sent... */
388: FlushBuffer();
389: }
390:
391: //----------------------------------------------------------------------
392: // returns 0 if all is OK
393: // returns 16 if any get_line()'s fail
394: // returns 20, 21, 22, 23, 24, 25, 26 if transform_and_send_edit_data() fails
395: void SMTP::
396: send_data(const char* message)
397: {
398: transform_and_send_edit_data(message);
399: if( 250 != get_line() )
400: SendSmtpError("Message not accepted by server");
401: }
402:
403: //----------------------------------------------------------------------
404: // returns 0 if all is OK
405: // returns 50, 51, 52 if fails
406: void SMTP::
407: open_socket( const char* server, const char* service )
408: {
409: ConnectToHost(server, service);
410:
411: if( gethostname(my_hostname, sizeof(my_hostname)) )
412: throw Exception("smtp.connect",
413: 0,
414: "lookup of '%s' failed", my_hostname);
415: }
416:
417: //----------------------------------------------------------------------
418: // returns 0 if all is OK
419: // returns 50, 51, 52 if open_socket() fails
420: // returns 10, 11, 12, 13, 14, 15 if any get_line()'s fail
421: void SMTP::
422: prepare_message(char *from, char *to, const char* server, const char* service)
423: {
424: char out_data[MAXOUTLINE];
425: char *ptr;
426: int len;
427: int startLen;
428:
429: open_socket(server, service);
430:
431: if( 220 != get_line() )
432: SendSmtpError("SMTP server error");
433:
1.3 paf 434: snprintf(out_data, sizeof(out_data), "HELO %s\r\n", my_hostname );
435: SendLine(out_data, strlen(out_data) );
1.1 paf 436:
437: if( 250 != get_line() )
438: SendSmtpError("SMTP server error");
439:
1.3 paf 440: snprintf(out_data, sizeof(out_data), "MAIL From: <%s>\r\n", from);
441: SendLine(out_data, strlen(out_data) );
1.1 paf 442:
443: if( 250 != get_line() )
444: SendSmtpError("The mail server doesn't like the sender name, have you set your mail address correctly?");
445:
446: // do a series of RCPT lines for each name in address line
447: for( ptr = to; *ptr; ptr += len + 1 )
448: {
449: // if there's only one token left, then len will = startLen,
450: // and we'll iterate once only
1.3 paf 451: startLen = strlen(ptr);
1.1 paf 452: if( (len = strcspn(ptr, " ,\n\t\r")) != startLen )
453: {
454: ptr[len] = '\0'; // replace delim with NULL char
455: while( strchr (" ,\n\t\r", ptr[len+1]) ) // eat white space
456: ptr[len++] = '\0';
457: }
458:
1.3 paf 459: snprintf(out_data, sizeof(out_data), "RCPT To: <%s>\r\n", ptr);
460: SendLine(out_data, strlen(out_data) );
1.1 paf 461:
462: if( 250 != get_line() )
463: throw Exception("smtp.execute",
464: 0,
465: "The mail server doesn't like the name %s. Have you set the 'To: ' field correctly?",
466: ptr);
467:
468: if( len == startLen ) // last token, we're done
469: break;
470: }
471:
1.3 paf 472: snprintf(out_data, sizeof(out_data), "DATA\r\n");
473: SendLine(out_data, strlen(out_data));
1.1 paf 474:
475: if( 354 != get_line() )
476: SendSmtpError("Mail server error accepting message data");
477: }
478:
479:
480: //----------------------------------------------------------------------------
481: //----------------------------------------------------------------------------
482: // returns 0 if all is OK
483: // returns 1 if MakeSmtpHeader() fails
484: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
485: void SMTP::
486: Send(const char* server, const char* service, const char* msg, char *from, char *to)
487: {
488: #ifdef DEBUG_SHOW
489: throw Exception("paf.debug",0,"from=%s|to=%s|msg=%s", from,to,msg);
490: #endif
491:
492: prepare_message( from, to, server, service);
493:
494: send_data(msg);
495:
496: SendLine("QUIT\r\n", 6 );
497: CloseConnect();
498: }