Annotation of parser3/src/classes/smtp/smtp.C, revision 1.1
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: $Id: mail.C,v 1.3 2001/04/07 12:13:13 paf Exp $
! 9:
! 10:
! 11: Parts of the code here is based upon an early gensock and blat
! 12: */
! 13:
! 14: #include "smtp.h"
! 15: #include "pa_exception.h"
! 16:
! 17: SMTP::SMTP(Pool& pool, const String& aorigin_string) : Pooled(pool),
! 18: origin_string(aorigin_string) {
! 19: the_socket = 0;
! 20: in_index = 0;
! 21: out_index = 0;
! 22: in_buffer = 0;
! 23: in_buffer_total = 0;
! 24: out_buffer_total = 0;
! 25:
! 26: in_buffer = (char *)malloc(SOCKET_BUFFER_SIZE);
! 27: out_buffer = (char *)malloc(SOCKET_BUFFER_SIZE);
! 28:
! 29: last_winsock_error = 0;
! 30: }
! 31:
! 32:
! 33: // ---------------------------------------------------------------------------
! 34: void SMTP::
! 35: ConnectToHost(char *hostname, char *service)
! 36: {
! 37: struct sockaddr_in sa_in;
! 38: int our_port;
! 39:
! 40: if( !ResolveService(service, &our_port) )
! 41: {
! 42: if( !ResolveHostname(hostname, &sa_in) )
! 43: {
! 44: sa_in.sin_family = AF_INET;
! 45: sa_in.sin_port = our_port;
! 46:
! 47: if( !GetAndSetTheSocket(&the_socket) )
! 48: {
! 49: if( !GetConnection(the_socket, &sa_in) )
! 50: {
! 51: MiscSocketSetup(the_socket, &fds, &timeout);
! 52:
! 53: return;
! 54: }
! 55: }
! 56: }
! 57: }
! 58:
! 59: CloseConnect();
! 60:
! 61: THROW(0, 0,
! 62: &origin_string,
! 63: "connect to %s:%s failed",
! 64: hostname, service);
! 65: }
! 66:
! 67: //---------------------------------------------------------------------------
! 68: // returns 0 if all is OK
! 69: int SMTP::
! 70: GetBuffer(int wait)
! 71: {
! 72: int retval;
! 73: int bytes_read = 0;
! 74: unsigned long ready_to_read = 0;
! 75:
! 76: // Use select to see if data is waiting...
! 77: FD_ZERO(&fds);
! 78: FD_SET(the_socket, &fds);
! 79:
! 80: // if wait is set, we are polling, return immediately
! 81: if( wait )
! 82: {
! 83: timeout.tv_sec = 0;
! 84: }
! 85: else
! 86: {
! 87: timeout.tv_sec = 30;
! 88: }
! 89:
! 90: if( SOCKET_ERROR == (retval = select(0, &fds, NULL, NULL, &timeout)) )
! 91: {
! 92: char what_error[256];
! 93: int error_code = WSAGetLastError();
! 94:
! 95: if( error_code == WSAEINPROGRESS && wait )
! 96: {
! 97: return WAIT_A_BIT;
! 98: }
! 99:
! 100: wsprintf(what_error,
! 101: "GetBuffer() unexpected error from select: %d",
! 102: error_code);
! 103:
! 104: ShowError(what_error);
! 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: {
! 128: char what_error[256];
! 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:
! 146: wsprintf(what_error,
! 147: "GetBuffer() unexpected error: %d",
! 148: ws_error);
! 149: ShowError(what_error);
! 150: }
! 151: }
! 152:
! 153: // reset buffer indices.
! 154: in_buffer_total = bytes_read;
! 155: in_index = 0;
! 156:
! 157: return 0;
! 158: }
! 159:
! 160: //---------------------------------------------------------------------------
! 161: // returns 0 if all is OK
! 162: int SMTP::
! 163: GetChar(int wait, char *ch)
! 164: {
! 165: int retval = 0;
! 166:
! 167: if( in_index >= in_buffer_total )
! 168: {
! 169: if( 0 != (retval = GetBuffer(wait)) )
! 170: return retval;
! 171: }
! 172: *ch = in_buffer[in_index++];
! 173:
! 174: return 0;
! 175: }
! 176:
! 177: //----------------------------------------------------------------------
! 178: int SMTP::
! 179: get_line( void )
! 180: {
! 181: char ch = '.';
! 182: char in_data[MAXOUTLINE];
! 183: char *index;
! 184:
! 185: index = in_data;
! 186:
! 187: while( ch != '\n' )
! 188: {
! 189: if( 0 != GetChar(0, &ch) )
! 190: {
! 191: return -1;
! 192: }
! 193: else
! 194: {
! 195: *index = ch;
! 196: index++;
! 197: }
! 198: }
! 199:
! 200: if( in_data[3] == '-' )
! 201: return get_line();
! 202: else {
! 203: char *error_pos;
! 204: return strtol(in_data, &error_pos, 0);
! 205: }
! 206: }
! 207:
! 208: //---------------------------------------------------------------------------
! 209: // returns 0 if all is OK
! 210: void SMTP::
! 211: SendLine(const char *data, unsigned long length)
! 212: {
! 213: int num_sent;
! 214:
! 215: FD_ZERO(&fds);
! 216: FD_SET(the_socket, &fds);
! 217:
! 218: timeout.tv_sec = 30;
! 219:
! 220: while( length > 0 )
! 221: {
! 222: if( SOCKET_ERROR == select(0, NULL, &fds, NULL, &timeout) )
! 223: THROW(0, 0,
! 224: &origin_string,
! 225: "connection::put_data() unexpected error from select: %d",
! 226: WSAGetLastError());
! 227:
! 228: num_sent = send(the_socket,
! 229: data,
! 230: length > 1024 ? 1024 : (int)length,
! 231: 0);
! 232:
! 233: if( SOCKET_ERROR == num_sent )
! 234: {
! 235: int ws_error = WSAGetLastError();
! 236:
! 237: switch( ws_error )
! 238: {
! 239: // this is the only error we really expect to see.
! 240: case WSAENOTCONN:
! 241: return;
! 242:
! 243: // seems that we can still get a block
! 244: case WSAEWOULDBLOCK:
! 245: break;
! 246:
! 247: default:
! 248: THROW(0, 0,
! 249: &origin_string,
! 250: "connection::put_data() unexpected error from send(): %d",
! 251: ws_error);
! 252: }
! 253: }
! 254: else
! 255: {
! 256: length -= num_sent;
! 257: data += num_sent;
! 258: }
! 259: }
! 260: }
! 261:
! 262: //---------------------------------------------------------------------------
! 263: // returns 0 if all is OK
! 264: void SMTP::
! 265: SendBuffer(const char *data, unsigned long length)
! 266: {
! 267: DWORD retval = 0;
! 268: unsigned int sorta_sent = 0;
! 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: //---------------------------------------------------------------------------
! 306: BOOL SMTP::
! 307: CloseConnect()
! 308: {
! 309: if( closesocket(the_socket) == SOCKET_ERROR )
! 310: return FALSE;
! 311:
! 312: return TRUE;
! 313: }
! 314:
! 315: //----------------------------------------------------------------------
! 316: void SMTP::
! 317: SendSmtpError(const char * message)
! 318: {
! 319: SendLine("QUIT\r\n", 6);
! 320: CloseConnect();
! 321:
! 322: THROW(0, 0,
! 323: &origin_string,
! 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: const char *header_end;
! 336: char previous_char = 'x';
! 337: unsigned int send_len;
! 338: BOOL done = 0;
! 339:
! 340: send_len = lstrlen(editptr);
! 341: index = editptr;
! 342:
! 343: header_end = strstr(editptr, "\r\n\r\n");
! 344:
! 345: while( !done )
! 346: {
! 347: // room for extra char for double dot on end case
! 348: while( (unsigned int)(index - editptr) < send_len )
! 349: {
! 350: switch( *index )
! 351: {
! 352: case '.':
! 353: if( previous_char == '\n' )
! 354: {
! 355: /* send _two_ dots... */
! 356: SendBuffer(index, 1);
! 357: }
! 358: SendBuffer(index, 1);
! 359: break;
! 360:
! 361: case '\r':
! 362: // watch for soft-breaks in the header, and ignore them
! 363: if( index < header_end && (strncmp(index, "\r\r\n", 3) == 0) )
! 364: index += 2;
! 365: else
! 366: if( previous_char != '\r' )
! 367: SendBuffer(index, 1);
! 368: // soft line-break (see EM_FMTLINES), skip extra CR */
! 369: break;
! 370: default:
! 371: SendBuffer(index, 1);
! 372: break;
! 373: }
! 374: previous_char = *index;
! 375: index++;
! 376: }
! 377:
! 378: if( (unsigned int)(index - editptr) == send_len )
! 379: done = 1;
! 380: }
! 381:
! 382: // this handles the case where the user doesn't end the last
! 383: // line with a <return>
! 384:
! 385: if( editptr[send_len-1] != '\n' )
! 386: SendBuffer("\r\n.\r\n", 5);
! 387: else
! 388: SendBuffer(".\r\n", 3);
! 389: /* now make sure it's all sent... */
! 390: FlushBuffer();
! 391: }
! 392:
! 393: //----------------------------------------------------------------------
! 394: // returns 0 if all is OK
! 395: // returns 16 if any get_line()'s fail
! 396: // returns 20, 21, 22, 23, 24, 25, 26 if transform_and_send_edit_data() fails
! 397: void SMTP::
! 398: send_data(const char * message)
! 399: {
! 400: transform_and_send_edit_data(message);
! 401: if( 250 != get_line() )
! 402: SendSmtpError("Message not accepted by server");
! 403: }
! 404:
! 405: //----------------------------------------------------------------------
! 406: // returns 0 if all is OK
! 407: // returns 50, 51, 52 if fails
! 408: void SMTP::
! 409: open_socket( char *server, char *service )
! 410: {
! 411: ConnectToHost(server, service);
! 412:
! 413: if( gethostname(my_hostname, sizeof(my_hostname)) )
! 414: THROW(0, 0,
! 415: &origin_string,
! 416: "lookup of '%s' failed", my_hostname);
! 417: }
! 418:
! 419: //----------------------------------------------------------------------
! 420: // returns 0 if all is OK
! 421: // returns 50, 51, 52 if open_socket() fails
! 422: // returns 10, 11, 12, 13, 14, 15 if any get_line()'s fail
! 423: void SMTP::
! 424: prepare_message(char *from, char *to, char *server, char *service)
! 425: {
! 426: char out_data[MAXOUTLINE];
! 427: char *ptr;
! 428: int len;
! 429: int startLen;
! 430:
! 431: open_socket(server, service);
! 432:
! 433: if( 220 != get_line() )
! 434: SendSmtpError("SMTP server error");
! 435:
! 436: wsprintf(out_data, "HELO %s\r\n", my_hostname );
! 437: SendLine(out_data, lstrlen(out_data) );
! 438:
! 439: if( 250 != get_line() )
! 440: SendSmtpError("SMTP server error");
! 441:
! 442: wsprintf(out_data, "MAIL From: <%s>\r\n", from);
! 443: SendLine(out_data, lstrlen(out_data) );
! 444:
! 445: if( 250 != get_line() )
! 446: SendSmtpError("The mail server doesn't like the sender name, have you set your mail address correctly?");
! 447:
! 448: // do a series of RCPT lines for each name in address line
! 449: for( ptr = to; *ptr; ptr += len + 1 )
! 450: {
! 451: // if there's only one token left, then len will = startLen,
! 452: // and we'll iterate once only
! 453: startLen = lstrlen(ptr);
! 454: if( (len = strcspn(ptr, " ,\n\t\r")) != startLen )
! 455: {
! 456: ptr[len] = '\0'; // replace delim with NULL char
! 457: while( strchr (" ,\n\t\r", ptr[len+1]) ) // eat white space
! 458: ptr[len++] = '\0';
! 459: }
! 460:
! 461: wsprintf(out_data, "RCPT To: <%s>\r\n", ptr);
! 462: SendLine(out_data, lstrlen(out_data) );
! 463:
! 464: if( 250 != get_line() )
! 465: THROW(0, 0,
! 466: &origin_string,
! 467: "The mail server doesn't like the name %s. Have you set the 'To: ' field correctly?",
! 468: ptr);
! 469:
! 470: if( len == startLen ) // last token, we're done
! 471: break;
! 472: }
! 473:
! 474: wsprintf(out_data, "DATA\r\n");
! 475: SendLine(out_data, lstrlen(out_data));
! 476:
! 477: if( 354 != get_line() )
! 478: SendSmtpError("Mail server error accepting message data");
! 479: }
! 480:
! 481:
! 482: static char *lsplit(char *string, char delim) {
! 483: if(string) {
! 484: char *v=strchr(string, delim);
! 485: if(v) {
! 486: *v=0;
! 487: return v+1;
! 488: }
! 489: }
! 490: return 0;
! 491: }
! 492: static char *rsplit(char *string, char delim) {
! 493: if(string) {
! 494: char *v=strrchr(string, delim);
! 495: if(v) {
! 496: *v=0;
! 497: return v+1;
! 498: }
! 499: }
! 500: return NULL;
! 501: }
! 502: static char *extractEmail(char *email) {
! 503: lsplit(email, '>'); lsplit(email, '\x0D');lsplit(email, '\x0A');
! 504: char *next=rsplit(email, '<');
! 505: if(next) email=next;
! 506: return email;
! 507: }
! 508:
! 509: //----------------------------------------------------------------------------
! 510: //----------------------------------------------------------------------------
! 511: // returns 0 if all is OK
! 512: // returns 1 if MakeSmtpHeader() fails
! 513: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
! 514: void SMTP::
! 515: Send(char *server, char *service, const char *msg, char *from, char *to)
! 516: {
! 517: prepare_message( extractEmail(from), extractEmail(to), server, service);
! 518:
! 519: send_data(msg);
! 520:
! 521: SendLine("QUIT\r\n", 6 );
! 522: CloseConnect();
! 523: }
E-mail: