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

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: 
1.2     ! paf         8:        $Id: smtp.C,v 1.1 2001/04/07 13:48:37 paf Exp $
1.1       paf         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:: 
1.2     ! paf        35: ConnectToHost(const char *hostname, const char *service)
1.1       paf        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:     {
1.2     ! paf        92:         //char    what_error[256];
1.1       paf        93:         int     error_code = WSAGetLastError();
                     94: 
                     95:         if( error_code == WSAEINPROGRESS && wait ) 
                     96:         {
                     97:             return WAIT_A_BIT;
                     98:         }
                     99: 
1.2     ! paf       100:                /*
1.1       paf       101:         wsprintf(what_error,
                    102:                    "GetBuffer() unexpected error from select: %d",
                    103:                    error_code);
                    104: 
                    105:         ShowError(what_error);
1.2     ! paf       106:                */
1.1       paf       107:     }
                    108: 
                    109:     // if we don't want to wait
                    110:     if( !retval && wait ) 
                    111:     {
                    112:         return WAIT_A_BIT;
                    113:     }
                    114: 
                    115:     // we have data waiting...
                    116:     bytes_read = recv(the_socket,
                    117:                                         in_buffer,
                    118:                                         SOCKET_BUFFER_SIZE,
                    119:                                         0);
                    120: 
                    121:     // just in case.
                    122:     if( 0 == bytes_read )
                    123:     {
                    124:         // connection terminated (semi-) gracefully by the other side
                    125:         return WSAENOTCONN;
                    126:     }
                    127: 
                    128:     if( SOCKET_ERROR == bytes_read ) 
                    129:     {
1.2     ! paf       130:         //char    what_error[256];
1.1       paf       131:         int     ws_error = WSAGetLastError();
                    132: 
                    133:         switch( ws_error ) 
                    134:         {
                    135:             // all these indicate loss of connection (are there more?)
                    136:             case WSAENOTCONN:
                    137:             case WSAENETDOWN:
                    138:             case WSAENETUNREACH:
                    139:             case WSAENETRESET:
                    140:             case WSAECONNABORTED:
                    141:             case WSAECONNRESET:
                    142:                 return WSAENOTCONN;
                    143: 
                    144:             case WSAEWOULDBLOCK:
                    145:                 return WAIT_A_BIT;
                    146: 
                    147:             default:
1.2     ! paf       148:                 /*wsprintf(what_error,
1.1       paf       149:                                "GetBuffer() unexpected error: %d",
                    150:                                ws_error);
                    151:                 ShowError(what_error);
1.2     ! paf       152:                                */
        !           153:                                break;
1.1       paf       154:         }
                    155:     }
                    156: 
                    157:     // reset buffer indices.
                    158:     in_buffer_total = bytes_read;
                    159:     in_index = 0;
                    160: 
                    161:     return 0;
                    162: }
                    163: 
                    164: //---------------------------------------------------------------------------
                    165: // returns 0 if all is OK
                    166: int SMTP:: 
                    167: GetChar(int wait, char *ch)
                    168: {
                    169:     int         retval = 0;
                    170: 
                    171:     if( in_index >= in_buffer_total )
                    172:     {
                    173:         if( 0 != (retval = GetBuffer(wait)) )
                    174:             return retval;
                    175:     }
                    176:     *ch = in_buffer[in_index++];
                    177: 
                    178:     return 0;
                    179: }
                    180: 
                    181: //----------------------------------------------------------------------
                    182: int SMTP::
                    183: get_line( void )
                    184: {
                    185:        char    ch = '.';
                    186:        char    in_data[MAXOUTLINE];
                    187:        char    *index;
                    188: 
                    189:        index = in_data;
                    190: 
                    191:        while( ch != '\n' )
                    192:        {
                    193:                if( 0 != GetChar(0, &ch) )
                    194:                {
                    195:                        return -1;
                    196:                }
                    197:                else
                    198:                {
                    199:                        *index = ch;
                    200:                        index++;
                    201:                }
                    202:        }
                    203: 
                    204:        if( in_data[3] == '-' ) 
                    205:                return get_line();
                    206:        else {
                    207:                char *error_pos;
                    208:                return strtol(in_data, &error_pos, 0);
                    209:        }
                    210: }
                    211: 
                    212: //---------------------------------------------------------------------------
                    213: // returns 0 if all is OK
                    214: void SMTP:: 
                    215: SendLine(const char *data, unsigned long length)
                    216: {
                    217:     int         num_sent;
                    218: 
                    219:     FD_ZERO(&fds);
                    220:     FD_SET(the_socket, &fds);
                    221: 
                    222:     timeout.tv_sec = 30;
                    223: 
                    224:     while( length > 0 ) 
                    225:     {
                    226:         if( SOCKET_ERROR == select(0, NULL, &fds, NULL, &timeout) ) 
                    227:             THROW(0, 0,
                    228:                                &origin_string,
                    229:                        "connection::put_data() unexpected error from select: %d",
                    230:                                        WSAGetLastError());
                    231: 
                    232:         num_sent = send(the_socket,
                    233:                                data,
                    234:                                length > 1024 ? 1024 : (int)length,
                    235:                                0);
                    236: 
                    237:         if( SOCKET_ERROR == num_sent ) 
                    238:         {
                    239:             int  ws_error = WSAGetLastError();
                    240:             
                    241:             switch( ws_error ) 
                    242:             {
                    243:                    // this is the only error we really expect to see.
                    244:                 case WSAENOTCONN:
                    245:                        return;
                    246: 
                    247:                    // seems that we can still get a block
                    248:                 case WSAEWOULDBLOCK:
                    249:                        break;
                    250: 
                    251:                 default:
                    252:                                        THROW(0, 0,
                    253:                                                &origin_string,
                    254:                                "connection::put_data() unexpected error from send(): %d",
                    255:                                                        ws_error);
                    256:             }
                    257:         }
                    258:         else 
                    259:         {
                    260:             length -= num_sent;
                    261:             data += num_sent;
                    262:         }
                    263:     }
                    264: }
                    265: 
                    266: //---------------------------------------------------------------------------
                    267: // returns 0 if all is OK
                    268: void SMTP:: 
                    269: SendBuffer(const char *data, unsigned long length)
                    270: {
                    271:     DWORD             retval = 0;
                    272:     unsigned int    sorta_sent = 0;
                    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(0, 0,
                    327:                &origin_string,
                    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:        const char      *header_end;
                    340:        char            previous_char = 'x';
                    341:        unsigned int    send_len;
                    342:        BOOL            done = 0;
                    343: 
                    344:        send_len = lstrlen(editptr);
                    345:        index = editptr;
                    346: 
                    347:        header_end = strstr(editptr, "\r\n\r\n");
                    348: 
                    349:        while( !done )
                    350:        {
                    351:                // room for extra char for double dot on end case
                    352:                while( (unsigned int)(index - editptr) < send_len )
                    353:                {
                    354:                        switch( *index )
                    355:                        {
                    356:                                case '.':
                    357:                                        if( previous_char == '\n' )
                    358:                     {
                    359:                                            /* send _two_ dots... */
                    360:                                        SendBuffer(index, 1);
                    361:                     }
                    362:                                        SendBuffer(index, 1);
                    363:                                        break;
                    364: 
                    365:                                case '\r':
                    366:                                        // watch for soft-breaks in the header, and ignore them
                    367:                                        if( index < header_end && (strncmp(index, "\r\r\n", 3) == 0) )
                    368:                                                index += 2;
                    369:                                        else
                    370:                                                if( previous_char != '\r' )
                    371:                                                        SendBuffer(index, 1);
                    372:                                        // soft line-break (see EM_FMTLINES), skip extra CR */
                    373:                                        break;
                    374:                                default:
                    375:                                        SendBuffer(index, 1);
                    376:                                        break;
                    377:                        }
                    378:                        previous_char = *index;
                    379:                        index++;
                    380:                }
                    381: 
                    382:                if( (unsigned int)(index - editptr) == send_len )
                    383:             done = 1;
                    384:        }
                    385: 
                    386:        // this handles the case where the user doesn't end the last
                    387:        // line with a <return>
                    388: 
                    389:        if( editptr[send_len-1] != '\n' )
                    390:                SendBuffer("\r\n.\r\n", 5);
                    391:        else
                    392:                SendBuffer(".\r\n", 3);
                    393:        /* now make sure it's all sent... */
                    394:        FlushBuffer();
                    395: }
                    396: 
                    397: //----------------------------------------------------------------------
                    398: // returns 0 if all is OK
                    399: // returns 16 if any get_line()'s fail
                    400: // returns 20, 21, 22, 23, 24, 25, 26 if transform_and_send_edit_data() fails
                    401: void SMTP::
                    402: send_data(const char * message)
                    403: {
                    404:        transform_and_send_edit_data(message);
                    405:        if( 250 != get_line() )
                    406:                SendSmtpError("Message not accepted by server");
                    407: }
                    408: 
                    409: //----------------------------------------------------------------------
                    410: // returns 0 if all is OK
                    411: // returns 50, 51, 52 if fails
                    412: void SMTP::
1.2     ! paf       413: open_socket( const char *server, const char *service )
1.1       paf       414: {
                    415:        ConnectToHost(server, service);
                    416: 
                    417:     if( gethostname(my_hostname, sizeof(my_hostname)) )
                    418:                THROW(0, 0,
                    419:                        &origin_string,
                    420:                        "lookup of '%s' failed", my_hostname);
                    421: }
                    422: 
                    423: //----------------------------------------------------------------------
                    424: // returns 0 if all is OK
                    425: // returns 50, 51, 52 if open_socket() fails
                    426: // returns 10, 11, 12, 13, 14, 15 if any get_line()'s fail
                    427: void SMTP::
1.2     ! paf       428: prepare_message(char *from, char *to, const char *server, const char *service)
1.1       paf       429: {
                    430:        char    out_data[MAXOUTLINE];
                    431:        char    *ptr;
                    432:        int             len;
                    433:        int             startLen;
                    434: 
                    435:        open_socket(server, service);
                    436: 
                    437:        if( 220 != get_line() )
                    438:                SendSmtpError("SMTP server error");
                    439: 
                    440:        wsprintf(out_data, "HELO %s\r\n", my_hostname );
                    441:        SendLine(out_data, lstrlen(out_data) );
                    442: 
                    443:        if( 250 != get_line() )
                    444:            SendSmtpError("SMTP server error");
                    445: 
                    446:        wsprintf(out_data, "MAIL From: <%s>\r\n", from);
                    447:        SendLine(out_data, lstrlen(out_data) );
                    448: 
                    449:        if( 250 != get_line() )
                    450:                SendSmtpError("The mail server doesn't like the sender name, have you set your mail address correctly?");
                    451: 
                    452:        // do a series of RCPT lines for each name in address line
                    453:        for( ptr = to; *ptr; ptr += len + 1 )
                    454:        {
                    455:                // if there's only one token left, then len will = startLen,
                    456:                // and we'll iterate once only
                    457:                startLen = lstrlen(ptr);
                    458:                if( (len = strcspn(ptr, " ,\n\t\r")) != startLen )
                    459:                {
                    460:                        ptr[len] = '\0';                        // replace delim with NULL char
                    461:                        while( strchr (" ,\n\t\r", ptr[len+1]) )        // eat white space
                    462:                                ptr[len++] = '\0';
                    463:                }
                    464: 
                    465:                wsprintf(out_data, "RCPT To: <%s>\r\n", ptr);
                    466:                SendLine(out_data, lstrlen(out_data) );
                    467: 
                    468:                if( 250 != get_line() )
                    469:                        THROW(0, 0,
                    470:                                &origin_string,
                    471:                                "The mail server doesn't like the name %s. Have you set the 'To: ' field correctly?", 
                    472:                                        ptr);
                    473: 
                    474:                if( len == startLen )   // last token, we're done
                    475:                        break;
                    476:        }
                    477: 
                    478:        wsprintf(out_data, "DATA\r\n");
                    479:        SendLine(out_data, lstrlen(out_data));
                    480: 
                    481:        if( 354 != get_line() )
                    482:                SendSmtpError("Mail server error accepting message data");
                    483: }
                    484: 
                    485: 
                    486: static char *lsplit(char *string, char delim) {
                    487:     if(string) {
                    488:                char *v=strchr(string, delim);
                    489:                if(v) {
                    490:                        *v=0;
                    491:                        return v+1;
                    492:                }
                    493:     }
                    494:     return 0;
                    495: }
                    496: static char *rsplit(char *string, char delim) {
                    497:     if(string) {
                    498:                char *v=strrchr(string, delim);
                    499:                if(v) {
                    500:                        *v=0;
                    501:                        return v+1;
                    502:                }
                    503:     }
                    504:     return NULL;       
                    505: }
                    506: static char *extractEmail(char *email) {
                    507:        lsplit(email, '>'); lsplit(email, '\x0D');lsplit(email, '\x0A');
                    508:        char *next=rsplit(email, '<');
                    509:        if(next) email=next;
                    510:        return email;
                    511: }
                    512: 
                    513: //----------------------------------------------------------------------------
                    514: //----------------------------------------------------------------------------
                    515: // returns 0 if all is OK
                    516: // returns 1 if MakeSmtpHeader() fails
                    517: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
                    518: void SMTP::
1.2     ! paf       519: Send(const char *server, const char *service, const char *msg, char *from, char *to)
1.1       paf       520: {
                    521:     prepare_message( extractEmail(from), extractEmail(to), server, service);
                    522: 
                    523:     send_data(msg);
                    524: 
                    525:        SendLine("QUIT\r\n", 6 );
                    526:        CloseConnect();
                    527: }

E-mail: