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