Annotation of parser3/src/lib/smtp/smtp.C, revision 1.3

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: }

E-mail: