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