Annotation of parser3/src/lib/smtp/smtp.C, revision 1.1
1.1 ! paf 1: /** @file
! 2: Parser: SMTP sender.
! 3:
! 4: Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)
! 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:
! 10: static const char * const IDENT_SMTP_C="$Date: 2003/11/20 16:34:24 $";
! 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:
! 93: if( SOCKET_ERROR == (retval = select(0, &fds, NULL, NULL, &timeout)) )
! 94: {
! 95: //char what_error[256];
! 96: int error_code = WSAGetLastError();
! 97:
! 98: if( error_code == WSAEINPROGRESS && wait )
! 99: {
! 100: return WAIT_A_BIT;
! 101: }
! 102:
! 103: /*
! 104: wsprintf(what_error,
! 105: "GetBuffer() unexpected error from select: %d",
! 106: error_code);
! 107:
! 108: ShowError(what_error);
! 109: */
! 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:
! 131: if( SOCKET_ERROR == bytes_read )
! 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: {
! 229: if( SOCKET_ERROR == select(0, NULL, &fds, NULL, &timeout) )
! 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:
! 240: if( SOCKET_ERROR == num_sent )
! 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: //---------------------------------------------------------------------------
! 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:
! 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;
! 341: BOOL done = 0;
! 342:
! 343: send_len = lstrlen(editptr);
! 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 )
! 375: done = 1;
! 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:
! 432: wsprintf(out_data, "HELO %s\r\n", my_hostname );
! 433: SendLine(out_data, lstrlen(out_data) );
! 434:
! 435: if( 250 != get_line() )
! 436: SendSmtpError("SMTP server error");
! 437:
! 438: wsprintf(out_data, "MAIL From: <%s>\r\n", from);
! 439: SendLine(out_data, lstrlen(out_data) );
! 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
! 449: startLen = lstrlen(ptr);
! 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:
! 457: wsprintf(out_data, "RCPT To: <%s>\r\n", ptr);
! 458: SendLine(out_data, lstrlen(out_data) );
! 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:
! 470: wsprintf(out_data, "DATA\r\n");
! 471: SendLine(out_data, lstrlen(out_data));
! 472:
! 473: if( 354 != get_line() )
! 474: SendSmtpError("Mail server error accepting message data");
! 475: }
! 476:
! 477:
! 478: static char *lsplit(char *string, char delim) {
! 479: if(string) {
! 480: char *v=strchr(string, delim);
! 481: if(v) {
! 482: *v=0;
! 483: return v+1;
! 484: }
! 485: }
! 486: return 0;
! 487: }
! 488: static char *rsplit(char *string, char delim) {
! 489: if(string) {
! 490: char *v=strrchr(string, delim);
! 491: if(v) {
! 492: *v=0;
! 493: return v+1;
! 494: }
! 495: }
! 496: return NULL;
! 497: }
! 498:
! 499: //----------------------------------------------------------------------------
! 500: //----------------------------------------------------------------------------
! 501: // returns 0 if all is OK
! 502: // returns 1 if MakeSmtpHeader() fails
! 503: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
! 504: void SMTP::
! 505: Send(const char* server, const char* service, const char* msg, char *from, char *to)
! 506: {
! 507: #ifdef DEBUG_SHOW
! 508: throw Exception("paf.debug",0,"from=%s|to=%s|msg=%s", from,to,msg);
! 509: #endif
! 510:
! 511: prepare_message( from, to, server, service);
! 512:
! 513: send_data(msg);
! 514:
! 515: SendLine("QUIT\r\n", 6 );
! 516: CloseConnect();
! 517: }
E-mail: