Annotation of parser3/src/classes/smtp/smtp.C, revision 1.21

1.1       paf         1: /** @file
                      2:        Parser: SMTP sender.
                      3: 
1.21    ! paf         4:        Copyright (c) 2001-2003 ArtLebedev Group (http://www.artlebedev.com)
1.9       paf         5:        Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1       paf         6: 
                      7:        Parts of the code here is based upon an early gensock and blat
                      8: */
1.12      paf         9: 
1.21    ! paf        10: static const char* IDENT_SMTP_C="$Date: 2003/03/24 13:01:22 $";
1.1       paf        11: 
1.7       paf        12: #include "pa_exception.h"
1.1       paf        13: #include "smtp.h"
                     14: 
1.17      paf        15: //#define DEBUG_SHOW
                     16: 
1.21    ! paf        17: SMTP::SMTP() {
1.1       paf        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: 
1.21    ! paf        31: SMTP::~SMTP() {
        !            32:        free(in_buffer);
        !            33:     free(out_buffer);
        !            34: }
        !            35: 
1.1       paf        36: 
                     37: // ---------------------------------------------------------------------------
                     38: void SMTP:: 
1.21    ! paf        39: ConnectToHost(const char* hostname, const char* service)
1.1       paf        40: {
                     41:     struct sockaddr_in sa_in;
                     42:     int                                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   = 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: 
1.10      paf        65:     throw Exception("smtp.connect",
1.21    ! paf        66:                0,
1.1       paf        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:     unsigned long   ready_to_read = 0;
                     79: 
                     80:     // Use select to see if data is waiting...
                     81:     FD_ZERO(&fds);
                     82:     FD_SET(the_socket, &fds);
                     83: 
                     84:     // if wait is set, we are polling, return immediately
                     85:     if( wait ) 
                     86:     {
                     87:         timeout.tv_sec = 0;
                     88:     }
                     89:     else 
                     90:     {
                     91:         timeout.tv_sec = 30;
                     92:     }
                     93: 
                     94:     if( SOCKET_ERROR == (retval = select(0, &fds, NULL, NULL, &timeout)) )
                     95:     {
1.2       paf        96:         //char    what_error[256];
1.1       paf        97:         int     error_code = WSAGetLastError();
                     98: 
                     99:         if( error_code == WSAEINPROGRESS && wait ) 
                    100:         {
                    101:             return WAIT_A_BIT;
                    102:         }
                    103: 
1.2       paf       104:                /*
1.1       paf       105:         wsprintf(what_error,
                    106:                    "GetBuffer() unexpected error from select: %d",
                    107:                    error_code);
                    108: 
                    109:         ShowError(what_error);
1.2       paf       110:                */
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: 
                    132:     if( SOCKET_ERROR == bytes_read ) 
                    133:     {
1.2       paf       134:         //char    what_error[256];
1.1       paf       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:
1.2       paf       152:                 /*wsprintf(what_error,
1.1       paf       153:                                "GetBuffer() unexpected error: %d",
                    154:                                ws_error);
                    155:                 ShowError(what_error);
1.2       paf       156:                                */
                    157:                                break;
1.1       paf       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:: 
1.21    ! paf       219: SendLine(const char* data, unsigned long length)
1.1       paf       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:     {
                    230:         if( SOCKET_ERROR == select(0, NULL, &fds, NULL, &timeout) ) 
1.10      paf       231:             throw Exception("smtp.execute",
1.21    ! paf       232:                                0,
1.1       paf       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: 
                    241:         if( SOCKET_ERROR == num_sent ) 
                    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:
1.10      paf       256:                                        throw Exception("smtp.execute",
1.21    ! paf       257:                                                0,
1.1       paf       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:: 
1.21    ! paf       273: SendBuffer(const char* data, unsigned long length)
1.1       paf       274: {
                    275:     DWORD             retval = 0;
                    276:     unsigned int    sorta_sent = 0;
                    277: 
                    278:     while( length )
                    279:     {
                    280:         if( (out_index + length) < SOCKET_BUFFER_SIZE ) 
                    281:         {
                    282:             // we won't overflow, simply copy into the buffer
                    283:             memcpy(out_buffer + out_index, data, length);
                    284:             out_index += length;
                    285:             length = 0;
                    286:         }
                    287:         else 
                    288:         {
                    289:             unsigned int orphaned_chunk = SOCKET_BUFFER_SIZE - out_index;
                    290: 
                    291:             // we will overflow, handle it
                    292:             memcpy(out_buffer + out_index, data, orphaned_chunk);
                    293: 
                    294:             // send this buffer...
                    295:             SendLine(out_buffer, SOCKET_BUFFER_SIZE); 
                    296: 
                    297:             length -= orphaned_chunk;
                    298:             out_index = 0;
                    299:             data += orphaned_chunk;
                    300:         }
                    301:     }
                    302: }
                    303: 
                    304: //---------------------------------------------------------------------------
                    305: // returns 0 if all is OK
                    306: void SMTP:: 
                    307: FlushBuffer()
                    308: {
                    309:     SendLine(out_buffer, out_index);
                    310:        out_index = 0;
                    311: }
                    312: 
                    313: //---------------------------------------------------------------------------
                    314: BOOL SMTP:: 
                    315: CloseConnect()
                    316: {
                    317:     if( closesocket(the_socket) == SOCKET_ERROR )
                    318:         return FALSE;
                    319: 
                    320:     return TRUE;
                    321: }
                    322: 
                    323: //----------------------------------------------------------------------
                    324: void SMTP::
1.21    ! paf       325: SendSmtpError(const char*  message)
1.1       paf       326: {
                    327:        SendLine("QUIT\r\n", 6);
                    328:        CloseConnect();
                    329: 
1.10      paf       330:        throw Exception("smtp.execute",
1.21    ! paf       331:                0,
1.1       paf       332:                "failed: %s", message);
                    333: }
                    334: 
                    335: //----------------------------------------------------------------------
                    336: // returns 0 if all is OK
                    337: // returns 20, 21, 22, 23, 24, 25 if SendBuffer() fails
                    338: // returns 26 if FlushBuffer() fails
                    339: void SMTP::
1.21    ! paf       340: transform_and_send_edit_data(const char*  editptr )
1.1       paf       341: {
                    342:        const char      *index;
                    343:        char            previous_char = 'x';
                    344:        unsigned int    send_len;
                    345:        BOOL            done = 0;
                    346: 
                    347:        send_len = lstrlen(editptr);
                    348:        index = editptr;
                    349: 
                    350:        while( !done )
                    351:        {
                    352:                // room for extra char for double dot on end case
                    353:                while( (unsigned int)(index - editptr) < send_len )
                    354:                {
                    355:                        switch( *index )
                    356:                        {
                    357:                                case '.':
                    358:                                        if( previous_char == '\n' )
1.14      paf       359:                                        SendBuffer(index, 1); // send _two_ dots...
1.1       paf       360:                                        SendBuffer(index, 1);
                    361:                                        break;
                    362: 
1.14      paf       363:                                case '\n': // x\n -> \r\n
                    364:                                        if( previous_char != '\r' ) {
                    365:                                                SendBuffer("\r", 1);
                    366:                                                SendBuffer(index, 1);
                    367:                                        }
1.1       paf       368:                                        break;
1.14      paf       369: 
1.1       paf       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::
1.21    ! paf       398: send_data(const char*  message)
1.1       paf       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::
1.21    ! paf       409: open_socket( const char* server, const char* service )
1.1       paf       410: {
                    411:        ConnectToHost(server, service);
                    412: 
                    413:     if( gethostname(my_hostname, sizeof(my_hostname)) )
1.10      paf       414:                throw Exception("smtp.connect",
1.21    ! paf       415:                        0,
1.1       paf       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::
1.21    ! paf       424: prepare_message(char *from, char *to, const char* server, const char* service)
1.1       paf       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() )
1.10      paf       465:                        throw Exception("smtp.execute",
1.21    ! paf       466:                                0,
1.1       paf       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: 
                    503: //----------------------------------------------------------------------------
                    504: //----------------------------------------------------------------------------
                    505: // returns 0 if all is OK
                    506: // returns 1 if MakeSmtpHeader() fails
                    507: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
                    508: void SMTP::
1.21    ! paf       509: Send(const char* server, const char* service, const char* msg, char *from, char *to)
1.1       paf       510: {
1.17      paf       511: #ifdef DEBUG_SHOW
1.18      paf       512:        throw Exception("paf.debug",0,"from=%s|to=%s|msg=%s", from,to,msg);
1.17      paf       513: #endif
1.15      paf       514: 
1.11      paf       515:     prepare_message( from, to, server, service);
1.1       paf       516: 
                    517:     send_data(msg);
                    518: 
                    519:        SendLine("QUIT\r\n", 6 );
                    520:        CloseConnect();
                    521: }

E-mail: