|
|
1.1 paf 1: /** @file
2: Parser: SMTP sender.
3:
1.2 paf 4: Copyright (c) 2001-2004 ArtLebedev Group (http://www.artlebedev.com)
1.1 paf 5: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
6:
7: Parts of the code here is based upon an early gensock and blat
8: */
9:
1.3 ! paf 10: static const char * const IDENT_SMTP_C="$Date: 2004/02/11 15:33:15 $";
1.1 paf 11:
12: #include "pa_exception.h"
13: #include "smtp.h"
14:
15: //#define DEBUG_SHOW
16:
17: SMTP::SMTP() {
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: SMTP::~SMTP() {
32: free(in_buffer);
33: free(out_buffer);
34: }
35:
36:
37: // ---------------------------------------------------------------------------
38: void SMTP::
39: ConnectToHost(const char* hostname, const char* service)
40: {
41: struct sockaddr_in sa_in;
42: u_short 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 = (unsigned short)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:
65: throw Exception("smtp.connect",
66: 0,
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:
79: // Use select to see if data is waiting...
80: FD_ZERO(&fds);
81: FD_SET(the_socket, &fds);
82:
83: // if wait is set, we are polling, return immediately
84: if( wait )
85: {
86: timeout.tv_sec = 0;
87: }
88: else
89: {
90: timeout.tv_sec = 30;
91: }
92:
1.3 ! paf 93: if( (retval = select(0, &fds, NULL, NULL, &timeout))<0 )
1.1 paf 94: {
1.3 ! paf 95: #ifdef WIN32
1.1 paf 96: int error_code = WSAGetLastError();
97:
98: if( error_code == WSAEINPROGRESS && wait )
99: {
100: return WAIT_A_BIT;
101: }
1.3 ! paf 102: #else
! 103: if( errno == EAGAIN && wait )
! 104: return WAIT_A_BIT;
! 105: #endif
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:
1.3 ! paf 127: if( bytes_read <0 )
1.1 paf 128: {
129: //char what_error[256];
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:
147: /*wsprintf(what_error,
148: "GetBuffer() unexpected error: %d",
149: ws_error);
150: ShowError(what_error);
151: */
152: break;
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::
214: SendLine(const char* data, unsigned long length)
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: {
1.3 ! paf 225: if( select(0, NULL, &fds, NULL, &timeout)<0 )
1.1 paf 226: throw Exception("smtp.execute",
227: 0,
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:
1.3 ! paf 236: if( num_sent<0 )
1.1 paf 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:
251: throw Exception("smtp.execute",
252: 0,
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::
268: SendBuffer(const char* data, unsigned long length)
269: {
270: while( length )
271: {
272: if( (out_index + length) < SOCKET_BUFFER_SIZE )
273: {
274: // we won't overflow, simply copy into the buffer
275: memcpy(out_buffer + out_index, data, length);
276: out_index += length;
277: length = 0;
278: }
279: else
280: {
281: unsigned int orphaned_chunk = SOCKET_BUFFER_SIZE - out_index;
282:
283: // we will overflow, handle it
284: memcpy(out_buffer + out_index, data, orphaned_chunk);
285:
286: // send this buffer...
287: SendLine(out_buffer, SOCKET_BUFFER_SIZE);
288:
289: length -= orphaned_chunk;
290: out_index = 0;
291: data += orphaned_chunk;
292: }
293: }
294: }
295:
296: //---------------------------------------------------------------------------
297: // returns 0 if all is OK
298: void SMTP::
299: FlushBuffer()
300: {
301: SendLine(out_buffer, out_index);
302: out_index = 0;
303: }
304:
305: //---------------------------------------------------------------------------
1.3 ! paf 306: bool SMTP::
1.1 paf 307: CloseConnect()
308: {
1.3 ! paf 309: if( closesocket(the_socket) <0 )
! 310: return false;
1.1 paf 311:
1.3 ! paf 312: return true;
1.1 paf 313: }
314:
315: //----------------------------------------------------------------------
316: void SMTP::
317: SendSmtpError(const char* message)
318: {
319: SendLine("QUIT\r\n", 6);
320: CloseConnect();
321:
322: throw Exception("smtp.execute",
323: 0,
324: "failed: %s", message);
325: }
326:
327: //----------------------------------------------------------------------
328: // returns 0 if all is OK
329: // returns 20, 21, 22, 23, 24, 25 if SendBuffer() fails
330: // returns 26 if FlushBuffer() fails
331: void SMTP::
332: transform_and_send_edit_data(const char* editptr )
333: {
334: const char *index;
335: char previous_char = 'x';
336: unsigned int send_len;
1.3 ! paf 337: bool done = false;
1.1 paf 338:
1.3 ! paf 339: send_len = strlen(editptr);
1.1 paf 340: index = editptr;
341:
342: while( !done )
343: {
344: // room for extra char for double dot on end case
345: while( (unsigned int)(index - editptr) < send_len )
346: {
347: switch( *index )
348: {
349: case '.':
350: if( previous_char == '\n' )
351: SendBuffer(index, 1); // send _two_ dots...
352: SendBuffer(index, 1);
353: break;
354:
355: case '\n': // x\n -> \r\n
356: if( previous_char != '\r' ) {
357: SendBuffer("\r", 1);
358: SendBuffer(index, 1);
359: }
360: break;
361:
362: default:
363: SendBuffer(index, 1);
364: break;
365: }
366: previous_char = *index;
367: index++;
368: }
369:
370: if( (unsigned int)(index - editptr) == send_len )
1.3 ! paf 371: done = true;
1.1 paf 372: }
373:
374: // this handles the case where the user doesn't end the last
375: // line with a <return>
376:
377: if( editptr[send_len-1] != '\n' )
378: SendBuffer("\r\n.\r\n", 5);
379: else
380: SendBuffer(".\r\n", 3);
381: /* now make sure it's all sent... */
382: FlushBuffer();
383: }
384:
385: //----------------------------------------------------------------------
386: // returns 0 if all is OK
387: // returns 16 if any get_line()'s fail
388: // returns 20, 21, 22, 23, 24, 25, 26 if transform_and_send_edit_data() fails
389: void SMTP::
390: send_data(const char* message)
391: {
392: transform_and_send_edit_data(message);
393: if( 250 != get_line() )
394: SendSmtpError("Message not accepted by server");
395: }
396:
397: //----------------------------------------------------------------------
398: // returns 0 if all is OK
399: // returns 50, 51, 52 if fails
400: void SMTP::
401: open_socket( const char* server, const char* service )
402: {
403: ConnectToHost(server, service);
404:
405: if( gethostname(my_hostname, sizeof(my_hostname)) )
406: throw Exception("smtp.connect",
407: 0,
408: "lookup of '%s' failed", my_hostname);
409: }
410:
411: //----------------------------------------------------------------------
412: // returns 0 if all is OK
413: // returns 50, 51, 52 if open_socket() fails
414: // returns 10, 11, 12, 13, 14, 15 if any get_line()'s fail
415: void SMTP::
416: prepare_message(char *from, char *to, const char* server, const char* service)
417: {
418: char out_data[MAXOUTLINE];
419: char *ptr;
420: int len;
421: int startLen;
422:
423: open_socket(server, service);
424:
425: if( 220 != get_line() )
426: SendSmtpError("SMTP server error");
427:
1.3 ! paf 428: snprintf(out_data, sizeof(out_data), "HELO %s\r\n", my_hostname );
! 429: SendLine(out_data, strlen(out_data) );
1.1 paf 430:
431: if( 250 != get_line() )
432: SendSmtpError("SMTP server error");
433:
1.3 ! paf 434: snprintf(out_data, sizeof(out_data), "MAIL From: <%s>\r\n", from);
! 435: SendLine(out_data, strlen(out_data) );
1.1 paf 436:
437: if( 250 != get_line() )
438: SendSmtpError("The mail server doesn't like the sender name, have you set your mail address correctly?");
439:
440: // do a series of RCPT lines for each name in address line
441: for( ptr = to; *ptr; ptr += len + 1 )
442: {
443: // if there's only one token left, then len will = startLen,
444: // and we'll iterate once only
1.3 ! paf 445: startLen = strlen(ptr);
1.1 paf 446: if( (len = strcspn(ptr, " ,\n\t\r")) != startLen )
447: {
448: ptr[len] = '\0'; // replace delim with NULL char
449: while( strchr (" ,\n\t\r", ptr[len+1]) ) // eat white space
450: ptr[len++] = '\0';
451: }
452:
1.3 ! paf 453: snprintf(out_data, sizeof(out_data), "RCPT To: <%s>\r\n", ptr);
! 454: SendLine(out_data, strlen(out_data) );
1.1 paf 455:
456: if( 250 != get_line() )
457: throw Exception("smtp.execute",
458: 0,
459: "The mail server doesn't like the name %s. Have you set the 'To: ' field correctly?",
460: ptr);
461:
462: if( len == startLen ) // last token, we're done
463: break;
464: }
465:
1.3 ! paf 466: snprintf(out_data, sizeof(out_data), "DATA\r\n");
! 467: SendLine(out_data, strlen(out_data));
1.1 paf 468:
469: if( 354 != get_line() )
470: SendSmtpError("Mail server error accepting message data");
471: }
472:
473:
474: //----------------------------------------------------------------------------
475: //----------------------------------------------------------------------------
476: // returns 0 if all is OK
477: // returns 1 if MakeSmtpHeader() fails
478: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
479: void SMTP::
480: Send(const char* server, const char* service, const char* msg, char *from, char *to)
481: {
482: #ifdef DEBUG_SHOW
483: throw Exception("paf.debug",0,"from=%s|to=%s|msg=%s", from,to,msg);
484: #endif
485:
486: prepare_message( from, to, server, service);
487:
488: send_data(msg);
489:
490: SendLine("QUIT\r\n", 6 );
491: CloseConnect();
492: }