Click here to Skip to main content
15,860,972 members
Articles / Desktop Programming / Win32

SMTP Client

Rate me:
Please Sign up or sign in to vote.
4.82/5 (62 votes)
16 Jul 2010CPOL4 min read 681.9K   22.4K   185   166
The CSmtp class allows to send emails with attachments. It only provides the AUTH LOGIN authentication.

Introduction

The CSmtp class allows to send emails from your program. The inspiration to write the CSmtp class was the article: CFastSmtp - A fast and easy SMTP class... I have used the code of CFastSmtp and introduced the following changes:

  • some bags have been removed (i.e., free memory)
  • logging in with authentication has been applied (AUTH LOGIN)
  • sending attachments has been added
  • error handling has been modified
  • new headlines from MIME specifications (i.e., X-Priority) have been added
  • non-blocking mode has been added
  • exceptions have been used
  • compatibility with Linux systems has been ensured

Typical scenarios while sending emails

After successful connection to an SMTP server, our client starts the conversation with the remote SMTP server. Each line sent by the client ought to be finished by "\r\n". If you want to know more details, check the References: [2], [3], [4], [5], [6], [7], [8], and [9]. In [2] is described the original SMTP protocol (1982), in [4] is discussed the SMTP extensions for authentication, and the MIME specification is improved in [5]-[9]. Below there are shown typical scenarios while sending e-mails. Example 3 fails because no TLS procedures were implemented in the CSmtp class. If you want to add TLS, see OpenSSL. I have introduced the following notation: S is a remote server, C is our client, and xxx means information censured.

Example 1 - Connecting to smtp.wp.pl and using an incorrect login or password:

S: 220 smtp.wp.pl ESMTP
C: EHLO: mydomain.com
S: 250-smtp.wp.pl
   250-PIPELINING
   250-AUTH=LOGIN PLAIN
   250-AUTH LOGIN PLAIN
   250-STARTTLS
   250-SIZE
   250-X-RCPTLIMIT 100
   250-8BITMIME
C: AUTH LOGIN
S: 334 VXNlcm5hbWU6
C: Kioq
S: 334 UGFzc3dvcmQ6
C: Kioq
S: 535 blad autoryzacji, niepoprawny login lub haslo / auth failure

Example 2 - Connecting to smtp.wp.pl and using the correct login and password:

S: 220 smtp.wp.pl ESMTP
C: EHLO: mydomain.com
S: 250-smtp.wp.pl
   250-PIPELINING
   250-AUTH=LOGIN PLAIN
   250-AUTH LOGIN PLAIN
   250-STARTTLS
   250-SIZE
   250-X-RCPTLIMIT 100
   250-8BITMIME
C: AUTH LOGIN
S: 334 VXNlcm5hbWU6
C: xxx
S: 334 UGFzc3dvcmQ6
C: xxx
S: 235 go ahead
C: MAIL FROM:<me@mydomain.com>
S: 250 ok
C: RCPR TO:<friend@domain.com>
S: 250 ok
C: DATA
S: 234 go ahead
C: Date: Sun, 24 Aug 2008 22:43:45
   From: JP<mail@domain.com>
   X-Mailer: The Bat! (v3.02) Professional
   Replay-to:mail@domain.com
   X-Priority: 3 (Normal)
   To:<friend@domain.com>
   Subject: The message
   MIME Version 1.0
   Content-Type: multipart/mixed; boundary="__MESSAGE__ID__54yg6f6h6y456345"

   
   --__MESSAGE__ID__54yg6f6h6y456345
   Content-type: text/plain; charset=US-ASCII
   Content-Transfer-Encoding: 7bit

   This is my message.

   --__MESSAGE__ID__54yg6f6h6y456345
   Content-Type: application/x-msdownload; name="test.exe"
   Content-Transfer-Encoding: base64
   Content-Disposition: attachment; filename="test.exe"
   
   TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
   (...)
   SU5HWFhQQURESU5HUEFERElOR1hYUEFERElOR1BBRERJTkdYWFBBRERJTkdQQURESU5HWA==
   --__MESSAGE__ID__54yg6f6h6y456345
   Content-Type: application/x-msdownload; name="test2.jpg"
   Content-Transfer-Encoding: base64
   Content-Disposition: attachment; filename="test2.jpg"
   
   /9j/4Sv+RXhpZgAASUkqAAgAAAAJAA8BAgAGAAAAegAAABABAgAWAAAAgAAAABIBAwABAAAA
   (...)
   A6YxR5YJJ5zUu6ZW4+NjC24E4q5Dcox5I+lRI0iWAAV9aay+lTctoYTjrml+9irRmz//2Q==
   
   --__MESSAGE__ID__54yg6f6h6y456345--
   
   .
   
S: 250 ok xxx qp xxx
C: QUIT
S: 221 smtp.wp.pl

Example 3 - Connecting to smtp.gmail.com:

S: 220 mx.google.com ESMTP
   w28sm1561195uge.4
C: EHLO: mydomain.com
S: 250-mx.google.com at your service [xxx.xxx.xxx.xxx],
   250-SIZE 28311552
   250-8BITMIME
   250-STARTTLS
   250 ENHANCEDSTATUSCODES
C: AUTH LOGIN
S: 530 5.7.0 Must issue a STARTTLS command first. w28sm1561195uge.4

Example 4 - Connecting to smtp.bizmail.yahoo.com and using an incorrect login or password:

S: 220 smtp103.biz.mail.re2.yahoo.com ESMTP
C: EHLO: mydomain.com
S: 250-smtp103.biz.mail.re2.yahoo.com
   250-AUTH LOGIN PLAIN XYMCOOKIE
   250-PIPELINING
   250-8BITMIME
C: AUTH LOGIN
S: 334 VXNlcm5hbWU6
C: dG9t
S: 334 UGFzc3dvcmQ6
C: bmVyb24xMg==
S: 535 authorization failed (#5.7.0)

Implementation of the CSmtp class

Implementation of the CSmtp class is very similar for Windows and Linux OSs. There is nothing surprising in this, because Windows uses the generally accepted Berkeley sockets application programming interface (API) [10]. The differences are shown in Table 1 (applies only to the CSmtp class implementation).

Table 1. Differences in implementation.
WindowsLinux
Winsock initialization neededNo Winsock initialization
Uses function closesocketUses function close
Uses function ioctlsocketUses function ioctl
Defined helpful type aliases; i.e., SOCKET, SOCKADDR_IN, LPHOSTENT, LPSERVENT, LPIN_ADDR, LPSOCKADDRAdditional types aliase should be defined

Below there are shown steps to be taken when connecting to a remote SMTP server.

  1. In Windows only, initialize Winsock2 (function: WSAStartup).
  2. Get socket descriptor on the local machine (function: socket).
  3. Convert port value (i.e., 25) to TCP/IP byte order (function: htons).
  4. Obtain Internet address of the remote machine (functions: inet_addr, gethostbyname).
  5. If non-blocking mode is used, set socket parameters (function: ioctl/ioctlsocket). Check necessarily what returns each function which will be called after ioctl/ioctlsocket (see next section - Using non-blocking mode).
  6. Connect to the remote server (function: connect).
  7. Introduce yourself - EHLO <SP> <domain> <CRLF>.
  8. Send AUTH LOGIN <CRLF> and another command described in the section "Typical scenarios while sending the email" (functions: send, recv).
  9. Finish the conversation with QUIT <CRLF>.
  10. Close connection with remote machine (function: close/closesocket).
  11. In Windows only, free Winsock2 resources (function: WSACleanup).

Using non-blocking mode

In the latest version of the program, I have used a non-blocking connection. There are many strategies to implement the non-blocking mode (i.e., Select model, WSAAsyncSelect model, WSAEventSelect model, or Completion port I/O model). In my code, I have decided to use the Select model. It is not so complicated as other methods, and works efficiently with a basic connection - one client to one server. The advantages of using non-blocking mode are: the program does not suspend if the remote server stops responding, data can be sent in uneven and unequal portions. Disadvantage of this approach is its complexity. After placing the socket in non-blocking mode, the next API calls are immediately closed. Typically, these calls fail with a an error WSAEWOULDBLOCK (Windows) or EINPROGRESS (Linux), which means that the requested operation is not completed so far. Therefore, in non-blocking mode, a lot of attention should be devoted to analyze errors returned by the API functions. In Select model, we are using the select function [11] after calling such API functions as: send, recv, connect, accept, and others. The parameter ndfs in select is ignored in Windows, but in Linux, it is the highest-numbered file descriptor in any of the three sets (fd_set *readfds, fd_set *writefds, fd_set *exceptfds) plus 1. To illustrate the difference between blocking and non-blocking modes, presented here are two ways of connecting to the remote server. For greater legibility, I have only presented versions for Windows (preprocessor directives were omitted).

C++
/*Connecting to the remote server in blocking mode*/
SOCKET CSmtp::ConnectRemoteServer(const char *szServer,const unsigned short nPort_)
{
    unsigned short nPort = 0;
    LPSERVENT lpServEnt;
    SOCKADDR_IN sockAddr;
    unsigned long ul = 1;
    int res = 0;

    SOCKET hSocket = INVALID_SOCKET;

    if((hSocket = socket(PF_INET, SOCK_STREAM,0)) == INVALID_SOCKET)
        throw ECSmtp(ECSmtp::WSA_INVALID_SOCKET);

    if(nPort_ != 0)
        nPort = htons(nPort_);
    else
    {
        lpServEnt = getservbyname("mail", 0);
        if (lpServEnt == NULL)
            nPort = htons(25);
        else 
            nPort = lpServEnt->s_port;
    }

    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = nPort;
    if((sockAddr.sin_addr.s_addr = inet_addr(szServer)) == INADDR_NONE)
    {
        LPHOSTENT host;

        host = gethostbyname(szServer);
        if (host)
            memcpy(&sockAddr.sin_addr,host->h_addr_list[0],host->h_length);
        else
        {
            closesocket(hSocket);
            throw ECSmtp(ECSmtp::WSA_GETHOSTBY_NAME_ADDR);
        }
    }

    if(connect(hSocket,(LPSOCKADDR)&sockAddr,sizeof(sockAddr)) == SOCKET_ERROR)
    {
        closesocket(hSocket);
        throw ECSmtp(ECSmtp::WSA_CONNECT);
    }

    return hSocket;
}

/*Connecting to the remote server in non-blocking mode*/
SOCKET CSmtp::ConnectRemoteServer(const char *szServer,const unsigned short nPort_)
{
    unsigned short nPort = 0;
    LPSERVENT lpServEnt;
    SOCKADDR_IN sockAddr;
    unsigned long ul = 1;
    fd_set fdwrite,fdexcept;
    timeval timeout;
    int res = 0;

    timeout.tv_sec = TIME_IN_SEC;
    timeout.tv_usec = 0;

    SOCKET hSocket = INVALID_SOCKET;

    if((hSocket = socket(PF_INET, SOCK_STREAM,0)) == INVALID_SOCKET)
        throw ECSmtp(ECSmtp::WSA_INVALID_SOCKET);

    if(nPort_ != 0)
        nPort = htons(nPort_);
    else
    {
        lpServEnt = getservbyname("mail", 0);
        if (lpServEnt == NULL)
            nPort = htons(25);
        else 
            nPort = lpServEnt->s_port;
    }

    sockAddr.sin_family = AF_INET;
    sockAddr.sin_port = nPort;
    if((sockAddr.sin_addr.s_addr = inet_addr(szServer)) == INADDR_NONE)
    {
        LPHOSTENT host;

        host = gethostbyname(szServer);
        if (host)
            memcpy(&sockAddr.sin_addr,host->h_addr_list[0],host->h_length);
        else
        {
            closesocket(hSocket);
            throw ECSmtp(ECSmtp::WSA_GETHOSTBY_NAME_ADDR);
        }
    }

    // start non-blocking mode for socket:
    if(ioctlsocket(hSocket,FIONBIO, (unsigned long*)&ul) == SOCKET_ERROR)
    {
        closesocket(hSocket);
        throw ECSmtp(ECSmtp::WSA_IOCTLSOCKET);
    }

    if(connect(hSocket,(LPSOCKADDR)&sockAddr,sizeof(sockAddr)) == SOCKET_ERROR)
    {
        if(WSAGetLastError() != WSAEWOULDBLOCK)
        {
            closesocket(hSocket);
            throw ECSmtp(ECSmtp::WSA_CONNECT);
        }
    }
    else
        return hSocket;

    while(true)
    {
        FD_ZERO(&fdwrite);
        FD_ZERO(&fdexcept);

        FD_SET(hSocket,&fdwrite);
        FD_SET(hSocket,&fdexcept);

        if((res = select(hSocket+1,NULL,&fdwrite,&fdexcept,&timeout)) == SOCKET_ERROR)
        {
            closesocket(hSocket);
            throw ECSmtp(ECSmtp::WSA_SELECT);
        }

        if(!res)
        {
            closesocket(hSocket);
            throw ECSmtp(ECSmtp::SELECT_TIMEOUT);
        }
        
        if(res && FD_ISSET(hSocket,&fdwrite))
            break;

        if(res && FD_ISSET(hSocket,&fdexcept))
        {
            closesocket(hSocket);
            throw ECSmtp(ECSmtp::WSA_SELECT);
        }
    } // while

    FD_CLR(hSocket,&fdwrite);
    FD_CLR(hSocket,&fdexcept);

    return hSocket;
}

Usage

C++
#include "CSmtp.h"
#include <iostream>

int main()
{
    bool bError = false;

    try
    {
        CSmtp mail;

        mail.SetSMTPServer("smtp.domain.com",25);
        mail.SetLogin("***");
        mail.SetPassword("***");
        mail.SetSenderName("User");
        mail.SetSenderMail("user@domain.com");
        mail.SetReplyTo("user@domain.com");
        mail.SetSubject("The message");
        mail.AddRecipient("friend@domain2.com");
        mail.SetXPriority(XPRIORITY_NORMAL);
        mail.SetXMailer("The Bat! (v3.02) Professional");
        mail.AddMsgLine("Hello,");
        mail.AddMsgLine("");
        mail.AddMsgLine("How are you today?");
        mail.AddMsgLine("");
        mail.AddMsgLine("Regards");
        mail.AddMsgLine("--");
        mail.AddMsgLine("User");
        mail.AddAttachment("c:\\test.exe");
        mail.AddAttachment("c:\\test2.jpg");
    
        mail.Send();
    }
    catch(ECSmtp e)
    {
        std::cout << "Error: " << e.GetErrorText().c_str() << ".\n";
        bError = true;
    }

    if(!bError)
    {
        std::cout << "Mail was send successfully.\n";
        return 0;
    }
    else
        return 1;
}

Author's notes

  1. If you have problems sending an email, use Visual Studio's debugger and analyze the conversation between your SMTP server and the client; perhaps, your server needs a different kind of authentication or doesn't need it at all.
  2. You are not allowed to use the CSmtp class for spamming.

Bibliography

  1. CFastSmtp - Fast and easy SMTP class...
  2. Simple Mail Transfer Protocol RFC 821
  3. Standard for the Format of ARPA Internet Text Messages RFC 822
  4. SMTP Service Extension for Authentication RFC 2554
  5. MIME: Format of Internet Message Bodies RFC 2045
  6. MIME: Media Types RFC 2046
  7. MIME: Message Header Extensions for Non-ASCII Text RFC 2047
  8. MIME: Registration Procedures RFC 2048
  9. MIME: Conformance Criteria and Examples RFC 2049
  10. The Berkeley Sockets Application Programming Interface (API)
  11. select function

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer Technical University of Lodz
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionportability bug w/ attachment name Pin
Spike!4-Sep-12 11:08
Spike!4-Sep-12 11:08 
AnswerRe: portability bug w/ attachment name Pin
David Johns3-Nov-12 16:32
David Johns3-Nov-12 16:32 
Questionportability bugs w/ std::string and exceptions Pin
Spike!4-Sep-12 8:01
Spike!4-Sep-12 8:01 
AnswerRe: portability bugs w/ std::string and exceptions Pin
David Johns3-Nov-12 16:26
David Johns3-Nov-12 16:26 
GeneralMy vote of 5 Pin
Evren Daglioglu28-Jun-12 0:45
Evren Daglioglu28-Jun-12 0:45 
GeneralMy vote of 4 Pin
xComaWhitex22-Apr-12 14:05
xComaWhitex22-Apr-12 14:05 
QuestionServer returned error after sending MAIL FROM Pin
namsaray26-Jan-12 8:41
namsaray26-Jan-12 8:41 
Questionbug fixes GetLocalHostName(), Send() Pin
jerko30-Nov-11 23:14
jerko30-Nov-11 23:14 
1st of all nice work...

your code could be a little prettier but work is done.

C#
const char* CSmtp::GetLocalHostName() const
{
    char* str = NULL;

    if((str = new char[255]) == NULL)
        throw ECSmtp(ECSmtp::LACK_OF_MEMORY);
    if(gethostname(str,255) == SOCKET_ERROR)
    {
        delete[] str;
        throw ECSmtp(ECSmtp::WSA_HOSTNAME);
    }

    // forgeting something ??? str -> m_sLocalHostName is missing

    delete[] str;
    return m_sLocalHostName.c_str();
}


instead i use this (so i can use it from domain):

C#
void CSmtp::SetLocalHostName(const char *sLocalHostName)
{
    m_sLocalHostName.erase();
    m_sLocalHostName.insert(0,sLocalHostName);
}


C#
const char* CSmtp::GetLocalHostName() const
{
    return m_sLocalHostName.c_str();
}



and from outside setting the comp name

C#
...
mail.SetLocalHostName( GetComputerName() ); // GetComputerName() method to return the computername
...



for server that does not need authentication u must skip the authentication part..
so I use bool param to skip it

like this:

C#
void CSmtp::Send(BOOL bAuthRequired)
{
    unsigned int i,rcpt_count,res,FileId;
    char *FileBuf = NULL, *FileName = NULL;
    FILE* hFile = NULL;
    unsigned long int FileSize,TotalSize,MsgPart;
    bool bAccepted;

    // ***** CONNECTING TO SMTP SERVER *****

    // connecting to remote host:
    if( (hSocket = ConnectRemoteServer(m_sSMTPSrvName.c_str(), m_iSMTPSrvPort)) == INVALID_SOCKET )
        throw ECSmtp(ECSmtp::WSA_INVALID_SOCKET);

    bAccepted = false;
    do
    {
        ReceiveData();
        switch(SmtpXYZdigits())
        {
            case 220:
                bAccepted = true;
                break;
            default:
                throw ECSmtp(ECSmtp::SERVER_NOT_READY);
        }
    }while(!bAccepted);

    // EHLO <SP> <domain> <CRLF>
    sprintf(SendBuf,"EHLO %s\r\n",GetLocalHostName()!=NULL ? m_sLocalHostName.c_str() : "domain");
    SendData();
    bAccepted = false;
    do
    {
        ReceiveData();
        switch(SmtpXYZdigits())
        {
            case 250:
                bAccepted = true;
                break;
            default:
                throw ECSmtp(ECSmtp::COMMAND_EHLO);
        }
    }while(!bAccepted);

    if ( bAuthRequired ) // this is skipped if auth is not required
    {
        // AUTH <SP> LOGIN <CRLF>
        strcpy(SendBuf,"AUTH LOGIN\r\n");
        SendData();
        bAccepted = false;
        do
        {
            ReceiveData();
            switch(SmtpXYZdigits())
            {
                case 250:
                    break;
                case 334:
                    bAccepted = true;
                    break;
                default:
                    throw ECSmtp(ECSmtp::COMMAND_AUTH_LOGIN);
            }
        }while(!bAccepted);

        // send login:
        if(!m_sLogin.size())
            throw ECSmtp(ECSmtp::UNDEF_LOGIN);
        std::string encoded_login = base64_encode(reinterpret_cast<const unsigned char*>(m_sLogin.c_str()),m_sLogin.size());
        sprintf(SendBuf,"%s\r\n",encoded_login.c_str());
        SendData();
        bAccepted = false;
        do
        {
            ReceiveData();
            switch(SmtpXYZdigits())
            {
                case 334:
                    bAccepted = true;
                    break;
                default:
                    throw ECSmtp(ECSmtp::UNDEF_XYZ_RESPONSE);
            }
        }while(!bAccepted);

        // send password:
        if(!m_sPassword.size())
            throw ECSmtp(ECSmtp::UNDEF_PASSWORD);
        std::string encoded_password = base64_encode(reinterpret_cast<const unsigned char*>(m_sPassword.c_str()),m_sPassword.size());
        sprintf(SendBuf,"%s\r\n",encoded_password.c_str());
        SendData();
        bAccepted = false;
        do
        {
            ReceiveData();
            switch(SmtpXYZdigits())
            {
                case 235:
                    bAccepted = true;
                    break;
                case 334:
                    break;
                case 535:
                    throw ECSmtp(ECSmtp::BAD_LOGIN_PASS);
                default:
                    throw ECSmtp(ECSmtp::UNDEF_XYZ_RESPONSE);
            }
        }while(!bAccepted);
    }

    // ***** SENDING E-MAIL *****

    // MAIL <SP> FROM:<reverse-path> <CRLF>
    if(!m_sMailFrom.size())
        throw ECSmtp(ECSmtp::UNDEF_MAIL_FROM);
    sprintf(SendBuf,"MAIL FROM:<%s>\r\n",m_sMailFrom.c_str());
    SendData();
    bAccepted = false;
    do
    {
        ReceiveData();
        switch(SmtpXYZdigits())
        {
            case 250:
                bAccepted = true;
                break;
            default:
                throw ECSmtp(ECSmtp::COMMAND_MAIL_FROM);
        }
    }while(!bAccepted);

    // RCPT <SP> TO:<forward-path> <CRLF>
    if(!(rcpt_count = Recipients.size()))
        throw ECSmtp(ECSmtp::UNDEF_RECIPIENTS);
    for(i=0;i<Recipients.size();i++)
    {
        sprintf(SendBuf,"RCPT TO:<%s>\r\n",(Recipients.at(i).Mail).c_str());
        SendData();
        bAccepted = false;
        do
        {
            ReceiveData();
            switch(SmtpXYZdigits())
            {
                case 250:
                    bAccepted = true;
                    break;
                default:
                    rcpt_count--;
            }
        }while(!bAccepted);
    }
    if(rcpt_count <= 0)
        throw ECSmtp(ECSmtp::COMMAND_RCPT_TO);

    for(i=0;i<CCRecipients.size();i++)
    {
        sprintf(SendBuf,"RCPT TO:<%s>\r\n",(CCRecipients.at(i).Mail).c_str());
        SendData();
        bAccepted = false;
        do
        {
            ReceiveData();
            switch(SmtpXYZdigits())
            {
                case 250:
                    bAccepted = true;
                    break;
                default:
                    ; // not necessary to throw
            }
        }while(!bAccepted);
    }

    for(i=0;i<BCCRecipients.size();i++)
    {
        sprintf(SendBuf,"RCPT TO:<%s>\r\n",(BCCRecipients.at(i).Mail).c_str());
        SendData();
        bAccepted = false;
        do
        {
            ReceiveData();
            switch(SmtpXYZdigits())
            {
                case 250:
                    bAccepted = true;
                    break;
                default:
                    ; // not necessary to throw
            }
        }while(!bAccepted);
    }

    // DATA <CRLF>
    strcpy(SendBuf,"DATA\r\n");
    SendData();
    bAccepted = false;
    do
    {
        ReceiveData();
        switch(SmtpXYZdigits())
        {
            case 354:
                bAccepted = true;
                break;
            case 250:
                break;
            default:
                throw ECSmtp(ECSmtp::COMMAND_DATA);
        }
    }while(!bAccepted);

    // send header(s)
    FormatHeader(SendBuf);
    SendData();

    // send text message
    if(GetMsgLines())
    {
        for(i=0;i<GetMsgLines();i++)
        {
            sprintf(SendBuf,"%s\r\n",GetMsgLineText(i));
            SendData();
        }
    }
    else
    {
        sprintf(SendBuf,"%s\r\n"," ");
        SendData();
    }

    // next goes attachments (if they are)
    if((FileBuf = new char[55]) == NULL)
        throw ECSmtp(ECSmtp::LACK_OF_MEMORY);

    if((FileName = new char[255]) == NULL)
        throw ECSmtp(ECSmtp::LACK_OF_MEMORY);

    TotalSize = 0;
    for(FileId=0;FileId<Attachments.size();FileId++)
    {
        strcpy(FileName,Attachments[FileId].c_str());

        sprintf(SendBuf,"--%s\r\n",BOUNDARY_TEXT);
        strcat(SendBuf,"Content-Type: application/x-msdownload; name=\"");
        strcat(SendBuf,&FileName[Attachments[FileId].find_last_of("\\") + 1]);
        strcat(SendBuf,"\"\r\n");
        strcat(SendBuf,"Content-Transfer-Encoding: base64\r\n");
        strcat(SendBuf,"Content-Disposition: attachment; filename=\"");
        strcat(SendBuf,&FileName[Attachments[FileId].find_last_of("\\") + 1]);
        strcat(SendBuf,"\"\r\n");
        strcat(SendBuf,"\r\n");

        SendData();

        // opening the file:
        hFile = fopen(FileName,"rb");
        if(hFile == NULL)
            throw ECSmtp(ECSmtp::FILE_NOT_EXIST);

        // checking file size:
        FileSize = 0;
        while(!feof(hFile))
            FileSize += fread(FileBuf,sizeof(char),54,hFile);
        TotalSize += FileSize;

        // sending the file:
        if(TotalSize/1024 > MSG_SIZE_IN_MB*1024)
            throw ECSmtp(ECSmtp::MSG_TOO_BIG);
        else
        {
            fseek (hFile,0,SEEK_SET);

            MsgPart = 0;
            for(i=0;i<FileSize/54+1;i++)
            {
                res = fread(FileBuf,sizeof(char),54,hFile);
                MsgPart ? strcat(SendBuf,base64_encode(reinterpret_cast<const unsigned char*>(FileBuf),res).c_str())
                          : strcpy(SendBuf,base64_encode(reinterpret_cast<const unsigned char*>(FileBuf),res).c_str());
                strcat(SendBuf,"\r\n");
                MsgPart += res + 2;
                if(MsgPart >= BUFFER_SIZE/2)
                { // sending part of the message
                    MsgPart = 0;
                    SendData(); // FileBuf, FileName, fclose(hFile);
                }
            }
            if(MsgPart)
            {
                SendData(); // FileBuf, FileName, fclose(hFile);
            }
        }
        fclose(hFile);
    }
    delete[] FileBuf;
    delete[] FileName;

    // sending last message block (if there is one or more attachments)
    if(Attachments.size())
    {
        sprintf(SendBuf,"\r\n--%s--\r\n",BOUNDARY_TEXT);
        SendData();
    }

    // <CRLF> . <CRLF>
    strcpy(SendBuf,"\r\n.\r\n");
    SendData();
    bAccepted = false;
    do
    {
        ReceiveData();
        switch(SmtpXYZdigits())
        {
            case 250:
                bAccepted = true;
                break;
            default:
                throw ECSmtp(ECSmtp::MSG_BODY_ERROR);
        }
    }while(!bAccepted);

    // ***** CLOSING CONNECTION *****

    // QUIT <CRLF>
    strcpy(SendBuf,"QUIT\r\n");
    SendData();
    bAccepted = false;
    do
    {
        ReceiveData();
        switch(SmtpXYZdigits())
        {
            case 221:
                bAccepted = true;
                break;
            default:
                throw ECSmtp(ECSmtp::COMMAND_QUIT);
        }
    }while(!bAccepted);

#ifdef LINUX
    close(hSocket);
#else
    closesocket(hSocket);
#endif
    hSocket = NULL;
}


bye
AnswerRe: bug fixes GetLocalHostName(), Send() Pin
Alan P Brown1-Dec-11 22:06
Alan P Brown1-Dec-11 22:06 
AnswerRe: bug fixes GetLocalHostName(), Send() Pin
Spike!4-Sep-12 8:12
Spike!4-Sep-12 8:12 
GeneralError Pin
min_2_max12-May-11 4:57
min_2_max12-May-11 4:57 
GeneralRe: Error Pin
min_2_max17-May-11 18:12
min_2_max17-May-11 18:12 
GeneralRe: Error Pin
highersky16-Aug-12 21:32
highersky16-Aug-12 21:32 
GeneralMy vote of 3 Pin
orighost16-Apr-11 15:43
orighost16-Apr-11 15:43 
GeneralRe: My vote of 3 Pin
Jakub Piwowarczyk10-Dec-11 6:22
Jakub Piwowarczyk10-Dec-11 6:22 
QuestionHTML body Pin
nj9626-Jan-11 10:53
nj9626-Jan-11 10:53 
AnswerRe: HTML body Pin
Jakub Piwowarczyk27-Jan-11 6:58
Jakub Piwowarczyk27-Jan-11 6:58 
GeneralRe: HTML body Pin
Robert Valentino26-Jan-12 14:12
Robert Valentino26-Jan-12 14:12 
GeneralError:Server returned error after sending EHLO Pin
caixin29-Nov-10 1:33
caixin29-Nov-10 1:33 
GeneralRe: Error:Server returned error after sending EHLO Pin
tmxq5610-Jan-11 12:14
tmxq5610-Jan-11 12:14 
AnswerRe: Error:Server returned error after sending EHLO Pin
Jakub Piwowarczyk11-Jan-11 8:25
Jakub Piwowarczyk11-Jan-11 8:25 
GeneralRe: Error:Server returned error after sending EHLO Pin
svyeo30-Jan-11 23:44
svyeo30-Jan-11 23:44 
GeneralRe: Error:Server returned error after sending EHLO Pin
min_2_max17-May-11 18:12
min_2_max17-May-11 18:12 
GeneralRe: Error:Server returned error after sending EHLO Pin
Ayyavu P1-Nov-11 5:15
Ayyavu P1-Nov-11 5:15 
GeneralRe: Error:Server returned error after sending EHLO Pin
jerko30-Nov-11 23:26
jerko30-Nov-11 23:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.