|
|
1.1 paf 1: /** @file
2: Parser: SMTP sender.
3:
1.8 paf 4: Copyright (c) 2001, 2002 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.14 ! paf 10: static const char* IDENT_SMTP_C="$Date: 2002/08/01 11:41:14 $";
1.1 paf 11:
1.7 paf 12: #include "pa_exception.h"
1.1 paf 13: #include "smtp.h"
14:
15: SMTP::SMTP(Pool& pool, const String& aorigin_string) : Pooled(pool),
16: origin_string(aorigin_string) {
17: the_socket = 0;
18: in_index = 0;
19: out_index = 0;
20: in_buffer = 0;
21: in_buffer_total = 0;
22: out_buffer_total = 0;
23:
24: in_buffer = (char *)malloc(SOCKET_BUFFER_SIZE);
25: out_buffer = (char *)malloc(SOCKET_BUFFER_SIZE);
26:
27: last_winsock_error = 0;
28: }
29:
30:
31: // ---------------------------------------------------------------------------
32: void SMTP::
1.2 paf 33: ConnectToHost(const char *hostname, const char *service)
1.1 paf 34: {
35: struct sockaddr_in sa_in;
36: int our_port;
37:
38: if( !ResolveService(service, &our_port) )
39: {
40: if( !ResolveHostname(hostname, &sa_in) )
41: {
42: sa_in.sin_family = AF_INET;
43: sa_in.sin_port = our_port;
44:
45: if( !GetAndSetTheSocket(&the_socket) )
46: {
47: if( !GetConnection(the_socket, &sa_in) )
48: {
49: MiscSocketSetup(the_socket, &fds, &timeout);
50:
51: return;
52: }
53: }
54: }
55: }
56:
57: CloseConnect();
58:
1.10 paf 59: throw Exception("smtp.connect",
1.1 paf 60: &origin_string,
61: "connect to %s:%s failed",
62: hostname, service);
63: }
64:
65: //---------------------------------------------------------------------------
66: // returns 0 if all is OK
67: int SMTP::
68: GetBuffer(int wait)
69: {
70: int retval;
71: int bytes_read = 0;
72: unsigned long ready_to_read = 0;
73:
74: // Use select to see if data is waiting...
75: FD_ZERO(&fds);
76: FD_SET(the_socket, &fds);
77:
78: // if wait is set, we are polling, return immediately
79: if( wait )
80: {
81: timeout.tv_sec = 0;
82: }
83: else
84: {
85: timeout.tv_sec = 30;
86: }
87:
88: if( SOCKET_ERROR == (retval = select(0, &fds, NULL, NULL, &timeout)) )
89: {
1.2 paf 90: //char what_error[256];
1.1 paf 91: int error_code = WSAGetLastError();
92:
93: if( error_code == WSAEINPROGRESS && wait )
94: {
95: return WAIT_A_BIT;
96: }
97:
1.2 paf 98: /*
1.1 paf 99: wsprintf(what_error,
100: "GetBuffer() unexpected error from select: %d",
101: error_code);
102:
103: ShowError(what_error);
1.2 paf 104: */
1.1 paf 105: }
106:
107: // if we don't want to wait
108: if( !retval && wait )
109: {
110: return WAIT_A_BIT;
111: }
112:
113: // we have data waiting...
114: bytes_read = recv(the_socket,
115: in_buffer,
116: SOCKET_BUFFER_SIZE,
117: 0);
118:
119: // just in case.
120: if( 0 == bytes_read )
121: {
122: // connection terminated (semi-) gracefully by the other side
123: return WSAENOTCONN;
124: }
125:
126: if( SOCKET_ERROR == bytes_read )
127: {
1.2 paf 128: //char what_error[256];
1.1 paf 129: int ws_error = WSAGetLastError();
130:
131: switch( ws_error )
132: {
133: // all these indicate loss of connection (are there more?)
134: case WSAENOTCONN:
135: case WSAENETDOWN:
136: case WSAENETUNREACH:
137: case WSAENETRESET:
138: case WSAECONNABORTED:
139: case WSAECONNRESET:
140: return WSAENOTCONN;
141:
142: case WSAEWOULDBLOCK:
143: return WAIT_A_BIT;
144:
145: default:
1.2 paf 146: /*wsprintf(what_error,
1.1 paf 147: "GetBuffer() unexpected error: %d",
148: ws_error);
149: ShowError(what_error);
1.2 paf 150: */
151: break;
1.1 paf 152: }
153: }
154:
155: // reset buffer indices.
156: in_buffer_total = bytes_read;
157: in_index = 0;
158:
159: return 0;
160: }
161:
162: //---------------------------------------------------------------------------
163: // returns 0 if all is OK
164: int SMTP::
165: GetChar(int wait, char *ch)
166: {
167: int retval = 0;
168:
169: if( in_index >= in_buffer_total )
170: {
171: if( 0 != (retval = GetBuffer(wait)) )
172: return retval;
173: }
174: *ch = in_buffer[in_index++];
175:
176: return 0;
177: }
178:
179: //----------------------------------------------------------------------
180: int SMTP::
181: get_line( void )
182: {
183: char ch = '.';
184: char in_data[MAXOUTLINE];
185: char *index;
186:
187: index = in_data;
188:
189: while( ch != '\n' )
190: {
191: if( 0 != GetChar(0, &ch) )
192: {
193: return -1;
194: }
195: else
196: {
197: *index = ch;
198: index++;
199: }
200: }
201:
202: if( in_data[3] == '-' )
203: return get_line();
204: else {
205: char *error_pos;
206: return strtol(in_data, &error_pos, 0);
207: }
208: }
209:
210: //---------------------------------------------------------------------------
211: // returns 0 if all is OK
212: void SMTP::
213: SendLine(const char *data, unsigned long length)
214: {
215: int num_sent;
216:
217: FD_ZERO(&fds);
218: FD_SET(the_socket, &fds);
219:
220: timeout.tv_sec = 30;
221:
222: while( length > 0 )
223: {
224: if( SOCKET_ERROR == select(0, NULL, &fds, NULL, &timeout) )
1.10 paf 225: throw Exception("smtp.execute",
1.1 paf 226: &origin_string,
227: "connection::put_data() unexpected error from select: %d",
228: WSAGetLastError());
229:
230: num_sent = send(the_socket,
231: data,
232: length > 1024 ? 1024 : (int)length,
233: 0);
234:
235: if( SOCKET_ERROR == num_sent )
236: {
237: int ws_error = WSAGetLastError();
238:
239: switch( ws_error )
240: {
241: // this is the only error we really expect to see.
242: case WSAENOTCONN:
243: return;
244:
245: // seems that we can still get a block
246: case WSAEWOULDBLOCK:
247: break;
248:
249: default:
1.10 paf 250: throw Exception("smtp.execute",
1.1 paf 251: &origin_string,
252: "connection::put_data() unexpected error from send(): %d",
253: ws_error);
254: }
255: }
256: else
257: {
258: length -= num_sent;
259: data += num_sent;
260: }
261: }
262: }
263:
264: //---------------------------------------------------------------------------
265: // returns 0 if all is OK
266: void SMTP::
267: SendBuffer(const char *data, unsigned long length)
268: {
269: DWORD retval = 0;
270: unsigned int sorta_sent = 0;
271:
272: while( length )
273: {
274: if( (out_index + length) < SOCKET_BUFFER_SIZE )
275: {
276: // we won't overflow, simply copy into the buffer
277: memcpy(out_buffer + out_index, data, length);
278: out_index += length;
279: length = 0;
280: }
281: else
282: {
283: unsigned int orphaned_chunk = SOCKET_BUFFER_SIZE - out_index;
284:
285: // we will overflow, handle it
286: memcpy(out_buffer + out_index, data, orphaned_chunk);
287:
288: // send this buffer...
289: SendLine(out_buffer, SOCKET_BUFFER_SIZE);
290:
291: length -= orphaned_chunk;
292: out_index = 0;
293: data += orphaned_chunk;
294: }
295: }
296: }
297:
298: //---------------------------------------------------------------------------
299: // returns 0 if all is OK
300: void SMTP::
301: FlushBuffer()
302: {
303: SendLine(out_buffer, out_index);
304: out_index = 0;
305: }
306:
307: //---------------------------------------------------------------------------
308: BOOL SMTP::
309: CloseConnect()
310: {
311: if( closesocket(the_socket) == SOCKET_ERROR )
312: return FALSE;
313:
314: return TRUE;
315: }
316:
317: //----------------------------------------------------------------------
318: void SMTP::
319: SendSmtpError(const char * message)
320: {
321: SendLine("QUIT\r\n", 6);
322: CloseConnect();
323:
1.10 paf 324: throw Exception("smtp.execute",
1.1 paf 325: &origin_string,
326: "failed: %s", message);
327: }
328:
329: //----------------------------------------------------------------------
330: // returns 0 if all is OK
331: // returns 20, 21, 22, 23, 24, 25 if SendBuffer() fails
332: // returns 26 if FlushBuffer() fails
333: void SMTP::
334: transform_and_send_edit_data(const char * editptr )
335: {
336: const char *index;
337: const char *header_end;
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::
393: send_data(const char * message)
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.2 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.1 paf 410: &origin_string,
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.2 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.1 paf 461: &origin_string,
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.2 paf 504: Send(const char *server, const char *service, const char *msg, char *from, char *to)
1.1 paf 505: {
1.11 paf 506: prepare_message( from, to, server, service);
1.1 paf 507:
508: send_data(msg);
509:
510: SendLine("QUIT\r\n", 6 );
511: CloseConnect();
512: }