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

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

E-mail: