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

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

E-mail: