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