|
|
1.1 paf 1: /** @file
2: Parser: SMTP sender.
3:
4: Copyright (c) 2001 ArtLebedev Group (http://www.artlebedev.com)
5:
6: Author: Alexander Petrosyan <paf@design.ru> (http://design.ru/paf)
7:
8: Parts of the code here is based upon an early gensock and blat
9: */
1.3 ! parser 10: static const char *RCSId="$Id: date.C,v 1.6 2001/09/04 10:50:19 parser Exp $";
1.1 paf 11:
12: #include "smtp.h"
13: #include "pa_exception.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:
59: THROW(0, 0,
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) )
225: THROW(0, 0,
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:
250: THROW(0, 0,
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:
324: THROW(0, 0,
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: header_end = strstr(editptr, "\r\n\r\n");
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' )
356: {
357: /* send _two_ dots... */
358: SendBuffer(index, 1);
359: }
360: SendBuffer(index, 1);
361: break;
362:
363: case '\r':
364: // watch for soft-breaks in the header, and ignore them
365: if( index < header_end && (strncmp(index, "\r\r\n", 3) == 0) )
366: index += 2;
367: else
368: if( previous_char != '\r' )
369: SendBuffer(index, 1);
370: // soft line-break (see EM_FMTLINES), skip extra CR */
371: break;
372: default:
373: SendBuffer(index, 1);
374: break;
375: }
376: previous_char = *index;
377: index++;
378: }
379:
380: if( (unsigned int)(index - editptr) == send_len )
381: done = 1;
382: }
383:
384: // this handles the case where the user doesn't end the last
385: // line with a <return>
386:
387: if( editptr[send_len-1] != '\n' )
388: SendBuffer("\r\n.\r\n", 5);
389: else
390: SendBuffer(".\r\n", 3);
391: /* now make sure it's all sent... */
392: FlushBuffer();
393: }
394:
395: //----------------------------------------------------------------------
396: // returns 0 if all is OK
397: // returns 16 if any get_line()'s fail
398: // returns 20, 21, 22, 23, 24, 25, 26 if transform_and_send_edit_data() fails
399: void SMTP::
400: send_data(const char * message)
401: {
402: transform_and_send_edit_data(message);
403: if( 250 != get_line() )
404: SendSmtpError("Message not accepted by server");
405: }
406:
407: //----------------------------------------------------------------------
408: // returns 0 if all is OK
409: // returns 50, 51, 52 if fails
410: void SMTP::
1.2 paf 411: open_socket( const char *server, const char *service )
1.1 paf 412: {
413: ConnectToHost(server, service);
414:
415: if( gethostname(my_hostname, sizeof(my_hostname)) )
416: THROW(0, 0,
417: &origin_string,
418: "lookup of '%s' failed", my_hostname);
419: }
420:
421: //----------------------------------------------------------------------
422: // returns 0 if all is OK
423: // returns 50, 51, 52 if open_socket() fails
424: // returns 10, 11, 12, 13, 14, 15 if any get_line()'s fail
425: void SMTP::
1.2 paf 426: prepare_message(char *from, char *to, const char *server, const char *service)
1.1 paf 427: {
428: char out_data[MAXOUTLINE];
429: char *ptr;
430: int len;
431: int startLen;
432:
433: open_socket(server, service);
434:
435: if( 220 != get_line() )
436: SendSmtpError("SMTP server error");
437:
438: wsprintf(out_data, "HELO %s\r\n", my_hostname );
439: SendLine(out_data, lstrlen(out_data) );
440:
441: if( 250 != get_line() )
442: SendSmtpError("SMTP server error");
443:
444: wsprintf(out_data, "MAIL From: <%s>\r\n", from);
445: SendLine(out_data, lstrlen(out_data) );
446:
447: if( 250 != get_line() )
448: SendSmtpError("The mail server doesn't like the sender name, have you set your mail address correctly?");
449:
450: // do a series of RCPT lines for each name in address line
451: for( ptr = to; *ptr; ptr += len + 1 )
452: {
453: // if there's only one token left, then len will = startLen,
454: // and we'll iterate once only
455: startLen = lstrlen(ptr);
456: if( (len = strcspn(ptr, " ,\n\t\r")) != startLen )
457: {
458: ptr[len] = '\0'; // replace delim with NULL char
459: while( strchr (" ,\n\t\r", ptr[len+1]) ) // eat white space
460: ptr[len++] = '\0';
461: }
462:
463: wsprintf(out_data, "RCPT To: <%s>\r\n", ptr);
464: SendLine(out_data, lstrlen(out_data) );
465:
466: if( 250 != get_line() )
467: THROW(0, 0,
468: &origin_string,
469: "The mail server doesn't like the name %s. Have you set the 'To: ' field correctly?",
470: ptr);
471:
472: if( len == startLen ) // last token, we're done
473: break;
474: }
475:
476: wsprintf(out_data, "DATA\r\n");
477: SendLine(out_data, lstrlen(out_data));
478:
479: if( 354 != get_line() )
480: SendSmtpError("Mail server error accepting message data");
481: }
482:
483:
484: static char *lsplit(char *string, char delim) {
485: if(string) {
486: char *v=strchr(string, delim);
487: if(v) {
488: *v=0;
489: return v+1;
490: }
491: }
492: return 0;
493: }
494: static char *rsplit(char *string, char delim) {
495: if(string) {
496: char *v=strrchr(string, delim);
497: if(v) {
498: *v=0;
499: return v+1;
500: }
501: }
502: return NULL;
503: }
504: static char *extractEmail(char *email) {
505: lsplit(email, '>'); lsplit(email, '\x0D');lsplit(email, '\x0A');
506: char *next=rsplit(email, '<');
507: if(next) email=next;
508: return email;
509: }
510:
511: //----------------------------------------------------------------------------
512: //----------------------------------------------------------------------------
513: // returns 0 if all is OK
514: // returns 1 if MakeSmtpHeader() fails
515: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
516: void SMTP::
1.2 paf 517: Send(const char *server, const char *service, const char *msg, char *from, char *to)
1.1 paf 518: {
519: prepare_message( extractEmail(from), extractEmail(to), server, service);
520:
521: send_data(msg);
522:
523: SendLine("QUIT\r\n", 6 );
524: CloseConnect();
525: }