|
|
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.4 ! paf 10: static const char * const IDENT_SMTP_C="$Date: 2004/02/24 10:36:16 $";
1.1 paf 11:
12: #include "pa_exception.h"
13: #include "smtp.h"
1.4 ! paf 14:
! 15: #undef snprintf
! 16: // pa_common.C
! 17: extern int __snprintf(char *, size_t, const char* , ...);
! 18: #define snprintf __snprintf
1.1 paf 19:
20: //#define DEBUG_SHOW
21:
22: SMTP::SMTP() {
23: the_socket = 0;
24: in_index = 0;
25: out_index = 0;
26: in_buffer = 0;
27: in_buffer_total = 0;
28: out_buffer_total = 0;
29:
30: in_buffer = (char *)malloc(SOCKET_BUFFER_SIZE);
31: out_buffer = (char *)malloc(SOCKET_BUFFER_SIZE);
32:
33: last_winsock_error = 0;
34: }
35:
36: SMTP::~SMTP() {
37: free(in_buffer);
38: free(out_buffer);
39: }
40:
41:
42: // ---------------------------------------------------------------------------
43: void SMTP::
44: ConnectToHost(const char* hostname, const char* service)
45: {
46: struct sockaddr_in sa_in;
47: u_short our_port;
48:
49: if( !ResolveService(service, &our_port) )
50: {
51: if( !ResolveHostname(hostname, &sa_in) )
52: {
53: sa_in.sin_family = AF_INET;
54: sa_in.sin_port = (unsigned short)our_port;
55:
56: if( !GetAndSetTheSocket(&the_socket) )
57: {
58: if( !GetConnection(the_socket, &sa_in) )
59: {
60: MiscSocketSetup(the_socket, &fds, &timeout);
61:
62: return;
63: }
64: }
65: }
66: }
67:
68: CloseConnect();
69:
70: throw Exception("smtp.connect",
71: 0,
72: "connect to %s:%s failed",
73: hostname, service);
74: }
75:
76: //---------------------------------------------------------------------------
77: // returns 0 if all is OK
78: int SMTP::
79: GetBuffer(int wait)
80: {
81: int retval;
82: int bytes_read = 0;
83:
84: // Use select to see if data is waiting...
85: FD_ZERO(&fds);
86: FD_SET(the_socket, &fds);
87:
88: // if wait is set, we are polling, return immediately
89: if( wait )
90: {
91: timeout.tv_sec = 0;
92: }
93: else
94: {
95: timeout.tv_sec = 30;
96: }
97:
1.3 paf 98: if( (retval = select(0, &fds, NULL, NULL, &timeout))<0 )
1.1 paf 99: {
1.3 paf 100: #ifdef WIN32
1.1 paf 101: int error_code = WSAGetLastError();
102:
103: if( error_code == WSAEINPROGRESS && wait )
104: {
105: return WAIT_A_BIT;
106: }
1.3 paf 107: #else
108: if( errno == EAGAIN && wait )
109: return WAIT_A_BIT;
110: #endif
1.1 paf 111: }
112:
113: // if we don't want to wait
114: if( !retval && wait )
115: {
116: return WAIT_A_BIT;
117: }
118:
119: // we have data waiting...
120: bytes_read = recv(the_socket,
121: in_buffer,
122: SOCKET_BUFFER_SIZE,
123: 0);
124:
125: // just in case.
126: if( 0 == bytes_read )
127: {
128: // connection terminated (semi-) gracefully by the other side
129: return WSAENOTCONN;
130: }
131:
1.3 paf 132: if( bytes_read <0 )
1.1 paf 133: {
134: //char what_error[256];
135: int ws_error = WSAGetLastError();
136:
137: switch( ws_error )
138: {
139: // all these indicate loss of connection (are there more?)
140: case WSAENOTCONN:
141: case WSAENETDOWN:
142: case WSAENETUNREACH:
143: case WSAENETRESET:
144: case WSAECONNABORTED:
145: case WSAECONNRESET:
146: return WSAENOTCONN;
147:
148: case WSAEWOULDBLOCK:
149: return WAIT_A_BIT;
150:
151: default:
152: /*wsprintf(what_error,
153: "GetBuffer() unexpected error: %d",
154: ws_error);
155: ShowError(what_error);
156: */
157: break;
158: }
159: }
160:
161: // reset buffer indices.
162: in_buffer_total = bytes_read;
163: in_index = 0;
164:
165: return 0;
166: }
167:
168: //---------------------------------------------------------------------------
169: // returns 0 if all is OK
170: int SMTP::
171: GetChar(int wait, char *ch)
172: {
173: int retval = 0;
174:
175: if( in_index >= in_buffer_total )
176: {
177: if( 0 != (retval = GetBuffer(wait)) )
178: return retval;
179: }
180: *ch = in_buffer[in_index++];
181:
182: return 0;
183: }
184:
185: //----------------------------------------------------------------------
186: int SMTP::
187: get_line( void )
188: {
189: char ch = '.';
190: char in_data[MAXOUTLINE];
191: char *index;
192:
193: index = in_data;
194:
195: while( ch != '\n' )
196: {
197: if( 0 != GetChar(0, &ch) )
198: {
199: return -1;
200: }
201: else
202: {
203: *index = ch;
204: index++;
205: }
206: }
207:
208: if( in_data[3] == '-' )
209: return get_line();
210: else {
211: char *error_pos;
212: return strtol(in_data, &error_pos, 0);
213: }
214: }
215:
216: //---------------------------------------------------------------------------
217: // returns 0 if all is OK
218: void SMTP::
219: SendLine(const char* data, unsigned long length)
220: {
221: int num_sent;
222:
223: FD_ZERO(&fds);
224: FD_SET(the_socket, &fds);
225:
226: timeout.tv_sec = 30;
227:
228: while( length > 0 )
229: {
1.3 paf 230: if( select(0, NULL, &fds, NULL, &timeout)<0 )
1.1 paf 231: throw Exception("smtp.execute",
232: 0,
233: "connection::put_data() unexpected error from select: %d",
234: WSAGetLastError());
235:
236: num_sent = send(the_socket,
237: data,
238: length > 1024 ? 1024 : (int)length,
239: 0);
240:
1.3 paf 241: if( num_sent<0 )
1.1 paf 242: {
243: int ws_error = WSAGetLastError();
244:
245: switch( ws_error )
246: {
247: // this is the only error we really expect to see.
248: case WSAENOTCONN:
249: return;
250:
251: // seems that we can still get a block
252: case WSAEWOULDBLOCK:
253: break;
254:
255: default:
256: throw Exception("smtp.execute",
257: 0,
258: "connection::put_data() unexpected error from send(): %d",
259: ws_error);
260: }
261: }
262: else
263: {
264: length -= num_sent;
265: data += num_sent;
266: }
267: }
268: }
269:
270: //---------------------------------------------------------------------------
271: // returns 0 if all is OK
272: void SMTP::
273: SendBuffer(const char* data, unsigned long length)
274: {
275: while( length )
276: {
277: if( (out_index + length) < SOCKET_BUFFER_SIZE )
278: {
279: // we won't overflow, simply copy into the buffer
280: memcpy(out_buffer + out_index, data, length);
281: out_index += length;
282: length = 0;
283: }
284: else
285: {
286: unsigned int orphaned_chunk = SOCKET_BUFFER_SIZE - out_index;
287:
288: // we will overflow, handle it
289: memcpy(out_buffer + out_index, data, orphaned_chunk);
290:
291: // send this buffer...
292: SendLine(out_buffer, SOCKET_BUFFER_SIZE);
293:
294: length -= orphaned_chunk;
295: out_index = 0;
296: data += orphaned_chunk;
297: }
298: }
299: }
300:
301: //---------------------------------------------------------------------------
302: // returns 0 if all is OK
303: void SMTP::
304: FlushBuffer()
305: {
306: SendLine(out_buffer, out_index);
307: out_index = 0;
308: }
309:
310: //---------------------------------------------------------------------------
1.3 paf 311: bool SMTP::
1.1 paf 312: CloseConnect()
313: {
1.3 paf 314: if( closesocket(the_socket) <0 )
315: return false;
1.1 paf 316:
1.3 paf 317: return true;
1.1 paf 318: }
319:
320: //----------------------------------------------------------------------
321: void SMTP::
322: SendSmtpError(const char* message)
323: {
324: SendLine("QUIT\r\n", 6);
325: CloseConnect();
326:
327: throw Exception("smtp.execute",
328: 0,
329: "failed: %s", message);
330: }
331:
332: //----------------------------------------------------------------------
333: // returns 0 if all is OK
334: // returns 20, 21, 22, 23, 24, 25 if SendBuffer() fails
335: // returns 26 if FlushBuffer() fails
336: void SMTP::
337: transform_and_send_edit_data(const char* editptr )
338: {
339: const char *index;
340: char previous_char = 'x';
341: unsigned int send_len;
1.3 paf 342: bool done = false;
1.1 paf 343:
1.3 paf 344: send_len = strlen(editptr);
1.1 paf 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' )
356: SendBuffer(index, 1); // send _two_ dots...
357: SendBuffer(index, 1);
358: break;
359:
360: case '\n': // x\n -> \r\n
361: if( previous_char != '\r' ) {
362: SendBuffer("\r", 1);
363: SendBuffer(index, 1);
364: }
365: break;
366:
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 )
1.3 paf 376: done = true;
1.1 paf 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::
406: open_socket( const char* server, const char* service )
407: {
408: ConnectToHost(server, service);
409:
410: if( gethostname(my_hostname, sizeof(my_hostname)) )
411: throw Exception("smtp.connect",
412: 0,
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::
421: prepare_message(char *from, char *to, const char* server, const char* service)
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:
1.3 paf 433: snprintf(out_data, sizeof(out_data), "HELO %s\r\n", my_hostname );
434: SendLine(out_data, strlen(out_data) );
1.1 paf 435:
436: if( 250 != get_line() )
437: SendSmtpError("SMTP server error");
438:
1.3 paf 439: snprintf(out_data, sizeof(out_data), "MAIL From: <%s>\r\n", from);
440: SendLine(out_data, strlen(out_data) );
1.1 paf 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
1.3 paf 450: startLen = strlen(ptr);
1.1 paf 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:
1.3 paf 458: snprintf(out_data, sizeof(out_data), "RCPT To: <%s>\r\n", ptr);
459: SendLine(out_data, strlen(out_data) );
1.1 paf 460:
461: if( 250 != get_line() )
462: throw Exception("smtp.execute",
463: 0,
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:
1.3 paf 471: snprintf(out_data, sizeof(out_data), "DATA\r\n");
472: SendLine(out_data, strlen(out_data));
1.1 paf 473:
474: if( 354 != get_line() )
475: SendSmtpError("Mail server error accepting message data");
476: }
477:
478:
479: //----------------------------------------------------------------------------
480: //----------------------------------------------------------------------------
481: // returns 0 if all is OK
482: // returns 1 if MakeSmtpHeader() fails
483: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
484: void SMTP::
485: Send(const char* server, const char* service, const char* msg, char *from, char *to)
486: {
487: #ifdef DEBUG_SHOW
488: throw Exception("paf.debug",0,"from=%s|to=%s|msg=%s", from,to,msg);
489: #endif
490:
491: prepare_message( from, to, server, service);
492:
493: send_data(msg);
494:
495: SendLine("QUIT\r\n", 6 );
496: CloseConnect();
497: }