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