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

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: 
        !             8:        $Id: mail.C,v 1.3 2001/04/07 12:13:13 paf Exp $
        !             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:: 
        !            35: ConnectToHost(char *hostname, char *service)
        !            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:     {
        !            92:         char    what_error[256];
        !            93:         int     error_code = WSAGetLastError();
        !            94: 
        !            95:         if( error_code == WSAEINPROGRESS && wait ) 
        !            96:         {
        !            97:             return WAIT_A_BIT;
        !            98:         }
        !            99: 
        !           100:         wsprintf(what_error,
        !           101:                    "GetBuffer() unexpected error from select: %d",
        !           102:                    error_code);
        !           103: 
        !           104:         ShowError(what_error);
        !           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:     {
        !           128:         char    what_error[256];
        !           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:
        !           146:                 wsprintf(what_error,
        !           147:                                "GetBuffer() unexpected error: %d",
        !           148:                                ws_error);
        !           149:                 ShowError(what_error);
        !           150:         }
        !           151:     }
        !           152: 
        !           153:     // reset buffer indices.
        !           154:     in_buffer_total = bytes_read;
        !           155:     in_index = 0;
        !           156: 
        !           157:     return 0;
        !           158: }
        !           159: 
        !           160: //---------------------------------------------------------------------------
        !           161: // returns 0 if all is OK
        !           162: int SMTP:: 
        !           163: GetChar(int wait, char *ch)
        !           164: {
        !           165:     int         retval = 0;
        !           166: 
        !           167:     if( in_index >= in_buffer_total )
        !           168:     {
        !           169:         if( 0 != (retval = GetBuffer(wait)) )
        !           170:             return retval;
        !           171:     }
        !           172:     *ch = in_buffer[in_index++];
        !           173: 
        !           174:     return 0;
        !           175: }
        !           176: 
        !           177: //----------------------------------------------------------------------
        !           178: int SMTP::
        !           179: get_line( void )
        !           180: {
        !           181:        char    ch = '.';
        !           182:        char    in_data[MAXOUTLINE];
        !           183:        char    *index;
        !           184: 
        !           185:        index = in_data;
        !           186: 
        !           187:        while( ch != '\n' )
        !           188:        {
        !           189:                if( 0 != GetChar(0, &ch) )
        !           190:                {
        !           191:                        return -1;
        !           192:                }
        !           193:                else
        !           194:                {
        !           195:                        *index = ch;
        !           196:                        index++;
        !           197:                }
        !           198:        }
        !           199: 
        !           200:        if( in_data[3] == '-' ) 
        !           201:                return get_line();
        !           202:        else {
        !           203:                char *error_pos;
        !           204:                return strtol(in_data, &error_pos, 0);
        !           205:        }
        !           206: }
        !           207: 
        !           208: //---------------------------------------------------------------------------
        !           209: // returns 0 if all is OK
        !           210: void SMTP:: 
        !           211: SendLine(const char *data, unsigned long length)
        !           212: {
        !           213:     int         num_sent;
        !           214: 
        !           215:     FD_ZERO(&fds);
        !           216:     FD_SET(the_socket, &fds);
        !           217: 
        !           218:     timeout.tv_sec = 30;
        !           219: 
        !           220:     while( length > 0 ) 
        !           221:     {
        !           222:         if( SOCKET_ERROR == select(0, NULL, &fds, NULL, &timeout) ) 
        !           223:             THROW(0, 0,
        !           224:                                &origin_string,
        !           225:                        "connection::put_data() unexpected error from select: %d",
        !           226:                                        WSAGetLastError());
        !           227: 
        !           228:         num_sent = send(the_socket,
        !           229:                                data,
        !           230:                                length > 1024 ? 1024 : (int)length,
        !           231:                                0);
        !           232: 
        !           233:         if( SOCKET_ERROR == num_sent ) 
        !           234:         {
        !           235:             int  ws_error = WSAGetLastError();
        !           236:             
        !           237:             switch( ws_error ) 
        !           238:             {
        !           239:                    // this is the only error we really expect to see.
        !           240:                 case WSAENOTCONN:
        !           241:                        return;
        !           242: 
        !           243:                    // seems that we can still get a block
        !           244:                 case WSAEWOULDBLOCK:
        !           245:                        break;
        !           246: 
        !           247:                 default:
        !           248:                                        THROW(0, 0,
        !           249:                                                &origin_string,
        !           250:                                "connection::put_data() unexpected error from send(): %d",
        !           251:                                                        ws_error);
        !           252:             }
        !           253:         }
        !           254:         else 
        !           255:         {
        !           256:             length -= num_sent;
        !           257:             data += num_sent;
        !           258:         }
        !           259:     }
        !           260: }
        !           261: 
        !           262: //---------------------------------------------------------------------------
        !           263: // returns 0 if all is OK
        !           264: void SMTP:: 
        !           265: SendBuffer(const char *data, unsigned long length)
        !           266: {
        !           267:     DWORD             retval = 0;
        !           268:     unsigned int    sorta_sent = 0;
        !           269: 
        !           270:     while( length )
        !           271:     {
        !           272:         if( (out_index + length) < SOCKET_BUFFER_SIZE ) 
        !           273:         {
        !           274:             // we won't overflow, simply copy into the buffer
        !           275:             memcpy(out_buffer + out_index, data, length);
        !           276:             out_index += length;
        !           277:             length = 0;
        !           278:         }
        !           279:         else 
        !           280:         {
        !           281:             unsigned int orphaned_chunk = SOCKET_BUFFER_SIZE - out_index;
        !           282: 
        !           283:             // we will overflow, handle it
        !           284:             memcpy(out_buffer + out_index, data, orphaned_chunk);
        !           285: 
        !           286:             // send this buffer...
        !           287:             SendLine(out_buffer, SOCKET_BUFFER_SIZE); 
        !           288: 
        !           289:             length -= orphaned_chunk;
        !           290:             out_index = 0;
        !           291:             data += orphaned_chunk;
        !           292:         }
        !           293:     }
        !           294: }
        !           295: 
        !           296: //---------------------------------------------------------------------------
        !           297: // returns 0 if all is OK
        !           298: void SMTP:: 
        !           299: FlushBuffer()
        !           300: {
        !           301:     SendLine(out_buffer, out_index);
        !           302:        out_index = 0;
        !           303: }
        !           304: 
        !           305: //---------------------------------------------------------------------------
        !           306: BOOL SMTP:: 
        !           307: CloseConnect()
        !           308: {
        !           309:     if( closesocket(the_socket) == SOCKET_ERROR )
        !           310:         return FALSE;
        !           311: 
        !           312:     return TRUE;
        !           313: }
        !           314: 
        !           315: //----------------------------------------------------------------------
        !           316: void SMTP::
        !           317: SendSmtpError(const char * message)
        !           318: {
        !           319:        SendLine("QUIT\r\n", 6);
        !           320:        CloseConnect();
        !           321: 
        !           322:        THROW(0, 0,
        !           323:                &origin_string,
        !           324:                "failed: %s", message);
        !           325: }
        !           326: 
        !           327: //----------------------------------------------------------------------
        !           328: // returns 0 if all is OK
        !           329: // returns 20, 21, 22, 23, 24, 25 if SendBuffer() fails
        !           330: // returns 26 if FlushBuffer() fails
        !           331: void SMTP::
        !           332: transform_and_send_edit_data(const char * editptr )
        !           333: {
        !           334:        const char      *index;
        !           335:        const char      *header_end;
        !           336:        char            previous_char = 'x';
        !           337:        unsigned int    send_len;
        !           338:        BOOL            done = 0;
        !           339: 
        !           340:        send_len = lstrlen(editptr);
        !           341:        index = editptr;
        !           342: 
        !           343:        header_end = strstr(editptr, "\r\n\r\n");
        !           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' )
        !           354:                     {
        !           355:                                            /* send _two_ dots... */
        !           356:                                        SendBuffer(index, 1);
        !           357:                     }
        !           358:                                        SendBuffer(index, 1);
        !           359:                                        break;
        !           360: 
        !           361:                                case '\r':
        !           362:                                        // watch for soft-breaks in the header, and ignore them
        !           363:                                        if( index < header_end && (strncmp(index, "\r\r\n", 3) == 0) )
        !           364:                                                index += 2;
        !           365:                                        else
        !           366:                                                if( previous_char != '\r' )
        !           367:                                                        SendBuffer(index, 1);
        !           368:                                        // soft line-break (see EM_FMTLINES), skip extra CR */
        !           369:                                        break;
        !           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::
        !           398: send_data(const char * message)
        !           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::
        !           409: open_socket( char *server, char *service )
        !           410: {
        !           411:        ConnectToHost(server, service);
        !           412: 
        !           413:     if( gethostname(my_hostname, sizeof(my_hostname)) )
        !           414:                THROW(0, 0,
        !           415:                        &origin_string,
        !           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::
        !           424: prepare_message(char *from, char *to, char *server, char *service)
        !           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() )
        !           465:                        THROW(0, 0,
        !           466:                                &origin_string,
        !           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: static char *extractEmail(char *email) {
        !           503:        lsplit(email, '>'); lsplit(email, '\x0D');lsplit(email, '\x0A');
        !           504:        char *next=rsplit(email, '<');
        !           505:        if(next) email=next;
        !           506:        return email;
        !           507: }
        !           508: 
        !           509: //----------------------------------------------------------------------------
        !           510: //----------------------------------------------------------------------------
        !           511: // returns 0 if all is OK
        !           512: // returns 1 if MakeSmtpHeader() fails
        !           513: // returns 10, 11, 12, 13, 14, 15, 50, 51, 52 if prepare_message() fails
        !           514: void SMTP::
        !           515: Send(char *server, char *service, const char *msg, char *from, char *to)
        !           516: {
        !           517:     prepare_message( extractEmail(from), extractEmail(to), server, service);
        !           518: 
        !           519:     send_data(msg);
        !           520: 
        !           521:        SendLine("QUIT\r\n", 6 );
        !           522:        CloseConnect();
        !           523: }

E-mail: