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

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

E-mail: