Click here to Skip to main content
Click here to Skip to main content

CFastSmtp - Fast and easy SMTP class...

By , 4 Sep 2002
 

Introduction

zSmtp v1.1

Win32 SMTP Wrapper with handy local ip and hostname functions.

Notes

A real basic SMTP wrapper. I tried to find one, but I couldn't - so here is another one. It is fast and straight forward - just instantiate the class with a SMTP server and then set the sender email property and message body. The rest of the settings are pretty much optional. There are also a couple of nice utility functions for getting your local hostname and local ip address.

We hope this code helps you out!

To Do

  • UUEncode/Decode
  • Attachments

Usage

CFastSmtp mail;

if (mail.ConnectServer("SERVER")) {
    mail.SetSenderName("Sender's Name");
    mail.SetSenderEmail("senders@address.com");
    mail.SetSubject("CFastSmtp v1.1 Release");

    mail.AddRecipient("test@test.com");
    mail.AddCCRecipient("test@test.com");
    mail.AddBCCRecipient("test@test.com");

    mail.SetMessageBody("Here is another test of CFastSmtp SMTP class!");
    
    if (mail.GetConnectStatus()) {        
        printf(mail.Send() ? "Send was a success!" : "Send failed!");
        mail.Disconnect();                    
    }
}   

Source Code

Here is the source (FastSmtp.cpp):

//********************************************************************
/*
  Fast & Simple SMTP Class

  Author:
    christopher w. backen <immortal@cox.net>    
  Purpose:
    Simple smtp class with handy local ip and hostname functions
  ToDo:
    Attachments & UUEncode/Decode
*/
//********************************************************************
//*** Change History
//*** DEVELOPER             DATE        MODIFICATION
//*** 
//*** christopher w. backen 09.05.2002    Misc. updates and corrections
//*** christopher w. backen    08.28.2001    Bug fixes & code changes
//*** christopher w. backen    04.12.2001    Release
//***
//********************************************************************



#include "stdafx.h"
#include "FastSmtp.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CFastSmtp::CFastSmtp()
{
    // Initialize variables
    bCon            = false;

    m_pcFromEmail    = NULL;
    m_pcFromName    = NULL;
    m_pcSubject        = NULL;
    m_pcMsgBody        = NULL;
    m_pcXMailer        = NULL;
    m_pcReplyTo        = NULL;
    m_pcIPAddr        = NULL;

    // Initialize WinSock
    WORD wVer    = MAKEWORD(2,2);    
    if (WSAStartup(wVer,&wsaData) != NO_ERROR) {
        printf("WSAStartup %d\r\n", WSAGetLastError());        
        throw; 
    }
    if (LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 ) {
        printf("Can't find a useable WinSock DLL\r\n");
        WSACleanup();
        throw; 
    }    
}

CFastSmtp::~CFastSmtp()
{
    // Free recipient lists
    for (unsigned int n = 0; n < Recipients.size(); n++)
        delete Recipients[n];
    for (unsigned int n = 0; n < CCRecipients.size(); n++)
        delete CCRecipients[n];
    for (unsigned int n = 0; n < BCCRecipients.size(); n++)
        delete BCCRecipients[n];

    // Free variables
    if (m_pcFromEmail)
        delete m_pcFromEmail;
    if (m_pcFromName)
        delete m_pcFromName;
    if (m_pcSubject)
        delete m_pcSubject;
    if (m_pcMsgBody)
        delete m_pcMsgBody;
    if (m_pcXMailer)
        delete m_pcXMailer;
    if (m_pcReplyTo)
        delete m_pcReplyTo;

    // Close connection
    if (bCon)
        Disconnect();

    // Cleanup
    WSACleanup();
}

bool CFastSmtp::AddRecipient(const char email[], const char name[])
{
    assert(email);

    int s=strlen(email);
    if (s==0)
        return false;

    CRecipient *pRec = new CRecipient();

    char *pcBuf = new char[s+strlen(name)+4];
     sprintf(pcBuf,"%s<%s>",name,email);    
     pRec->SetEmail(pcBuf);
     Recipients.insert(Recipients.end(), pRec);
    delete pcBuf;

    return (true);    
}

bool CFastSmtp::AddCCRecipient(const char email[], const char name[])
{
    assert(email);

    int s=strlen(email);
    if (s==0)
        return false;

    CRecipient *pRec = new CRecipient();    

    char *pcBuf = new char[s+strlen(name)+4];
     sprintf(pcBuf,"%s<%s>",name,email);    
     pRec->SetEmail(pcBuf);
     CCRecipients.insert(CCRecipients.end(), pRec);
    delete pcBuf;

    return (true);
}

bool CFastSmtp::AddBCCRecipient(const char email[], const char name[])
{
    assert(email);

    int s=strlen(email);
    if (s==0)
        return false;

    CRecipient *pRec = new CRecipient();

    char *pcBuf = new char[s+strlen(name)+4];
     sprintf(pcBuf,"%s<%s>",name,email);    
     pRec->SetEmail(pcBuf);
     BCCRecipients.insert(BCCRecipients.end(), pRec);
    delete pcBuf;    
    
    return (true);
}

bool CFastSmtp::Disconnect()
{
    if (!bCon) {
        printf("Not connected to server!\r\n");
        return (false);
    }

    BYTE        sReceiveBuffer[4096];
    int            iLength = 0;
    int            iEnd = 0;

    if (send(hSocket, (LPSTR)"QUIT\r\n", strlen("QUIT\r\n"),
             NO_FLAGS) == SOCKET_ERROR) {
        printf("Socket send error: %d\r\n", WSAGetLastError());    
        return (false);
    }
    iLength = recv(hSocket, (LPSTR)sReceiveBuffer+iEnd,sizeof(sReceiveBuffer)-iEnd, 
                   NO_FLAGS);
    iEnd += iLength;
    sReceiveBuffer[iEnd] = '\0';

    bCon=false;    

    hSocket=NULL;

    return (true);
}

bool CFastSmtp::Send()
{
// verify sender email
    if (m_pcFromEmail == NULL) {
        printf("Please specifiy a sender email address\r\n");
        return (false);
    }

    BYTE        sReceiveBuffer[4096];
    int            iLength = 0;
    int            iEnd = 0;
    char        buf[4096];
// get proper header
    char* msgHeader = _formatHeader();

    if (msgHeader == NULL) {
        delete [] msgHeader;
        printf("Failed to format message header\r\n");
        return (false);
    }
// start
    strcpy(buf, "MAIL FROM: <");
    strcat(buf, m_pcFromEmail);
    strcat(buf, ">\r\n");
    if (send(hSocket, (LPSTR)buf, strlen(buf), NO_FLAGS) == SOCKET_ERROR) {
        printf("Socket send error: %d\r\n", WSAGetLastError());    
        return (false);
    }
    iLength = recv(hSocket, (LPSTR)sReceiveBuffer+iEnd,sizeof(sReceiveBuffer)-iEnd, 
                   NO_FLAGS);
    iEnd += iLength;
    sReceiveBuffer[iEnd] = '\0';
// create message receipts
    char *token;
    for(unsigned int i=0;i<Recipients.size();i++) {
        token = strtok(Recipients.at(i)->GetEmail(),"<");
        token = strtok(NULL,"<");    
        if (token == NULL) 
            token = strtok(Recipients.at(i)->GetEmail(),"<");
        strcpy(buf, "RCPT TO: <");
        strcat(buf, token);
        strcat(buf, "\r\n");
        if (send(hSocket, (LPSTR)buf, strlen(buf), NO_FLAGS) == SOCKET_ERROR) {
            printf("Socket send error: %d\r\n", WSAGetLastError());    
            return (false);
        }
        iLength = recv(hSocket, 
                      (LPSTR)sReceiveBuffer+iEnd,sizeof(sReceiveBuffer)-iEnd, 
                       NO_FLAGS);
        iEnd += iLength;
        sReceiveBuffer[iEnd] = '\0';
    }
    for(unsigned int i=0;i<CCRecipients.size();i++) {
        token = strtok(CCRecipients.at(i)->GetEmail(),"<");
        token = strtok(NULL,"<");
        if (token == NULL) 
            token = strtok(Recipients.at(i)->GetEmail(),"<");
        strcpy(buf, "RCPT TO: <");
        strcat(buf, token);
        strcat(buf, "\r\n");
        if (send(hSocket, (LPSTR)buf, strlen(buf), NO_FLAGS) == SOCKET_ERROR) {
            printf("Socket send error: %d\r\n", WSAGetLastError());    
            return (false);
        }
        iLength = recv(hSocket, 
                       (LPSTR)sReceiveBuffer+iEnd,sizeof(sReceiveBuffer)-iEnd,
                        NO_FLAGS);
        iEnd += iLength;
        sReceiveBuffer[iEnd] = '\0';
    }
    for(unsigned int i=0;i<BCCRecipients.size();i++) {
        token = strtok(BCCRecipients.at(i)->GetEmail(),"<");
        token = strtok(NULL,"<");
        if (token == NULL) 
            token = strtok(Recipients.at(i)->GetEmail(),"<");
        strcpy(buf, "RCPT TO: <");
        strcat(buf, token);
        strcat(buf, "\r\n");
        if (send(hSocket, (LPSTR)buf, strlen(buf), NO_FLAGS) == SOCKET_ERROR) {
            printf("Socket send error: %d\r\n", WSAGetLastError());    
            return (false);
        }
        iLength = recv(hSocket, (LPSTR)sReceiveBuffer+iEnd,
                       sizeof(sReceiveBuffer)-iEnd, 
                       NO_FLAGS);
        iEnd += iLength;
        sReceiveBuffer[iEnd] = '\0';
    }
// init data
    if (send(hSocket, (LPSTR)"DATA\r\n", strlen("DATA\r\n"), NO_FLAGS) == SOCKET_ERROR) {
        printf("Socket send error: %d\r\n", WSAGetLastError());    
        return (false);
    }
    iLength = recv(hSocket, (LPSTR)sReceiveBuffer+iEnd,sizeof(sReceiveBuffer)-iEnd,
                   NO_FLAGS);
    iEnd += iLength;
    sReceiveBuffer[iEnd] = '\0';
// send header    
    if (send(hSocket, 
             (LPSTR)msgHeader, strlen(msgHeader), NO_FLAGS) == SOCKET_ERROR) {
        printf("Socket send error: %d\r\n", WSAGetLastError());    
        delete [] msgHeader;
        return (false);
    }
// send body    
    if (send(hSocket, 
             (LPSTR)m_pcMsgBody, strlen(m_pcMsgBody), NO_FLAGS) == SOCKET_ERROR) {
        printf("Socket send error: %d\r\n", WSAGetLastError());    
        return (false);
    }
// signal end    
    if (send(hSocket,
            (LPSTR)"\r\n.\r\n", strlen("\r\n.\r\n"), NO_FLAGS) == SOCKET_ERROR) {
        printf("Socket send error: %d\r\n", WSAGetLastError());    
        return (false);
    }
    iLength = recv(hSocket, (LPSTR)sReceiveBuffer+iEnd,sizeof(sReceiveBuffer)-iEnd,
                   NO_FLAGS);
    iEnd += iLength;
    sReceiveBuffer[iEnd] = '\0';
    delete [] msgHeader;
    return (true);

}

bool CFastSmtp::ConnectServer(const char server[], const unsigned short port)
{
    assert(server);

    if (bCon)
        Disconnect();
    bCon=false;
    hSocket = INVALID_SOCKET;

    hSocket = _connectServerSocket(server, port);    
    if (hSocket != INVALID_SOCKET) {        
        BYTE        sReceiveBuffer[4096];
        int            iLength = 0;
        int            iEnd = 0;
        char        buf[4096];
      
        strcpy(buf, "HELO ");
        strcat(buf, server);
        strcat(buf, "\r\n");
        if (send(hSocket, (LPSTR)buf, strlen(buf), NO_FLAGS) == SOCKET_ERROR) {
            printf("Socket send error: %d\r\n", WSAGetLastError());    
            return (false);
        }
        iLength = recv(hSocket, 
                       (LPSTR)sReceiveBuffer+iEnd,sizeof(sReceiveBuffer)-iEnd, 
                        NO_FLAGS);
        iEnd += iLength;
        sReceiveBuffer[iEnd] = '\0';
    } else {
        printf("Invalid socket\r\n");
        return (false);
    }

    bCon=true;
    return (true);
}

SOCKET CFastSmtp::_connectServerSocket(const char server[],
                                       const unsigned short port)
{
    int                nConnect;
    short            nProtocolPort;
    LPHOSTENT        lpHostEnt;
    LPSERVENT        lpServEnt;
    SOCKADDR_IN        sockAddr;        

    SOCKET            hServerSocket = INVALID_SOCKET;
     
    lpHostEnt = gethostbyname(server);
    if (lpHostEnt) {        
        hServerSocket = socket(PF_INET, SOCK_STREAM,DEFAULT_PROTOCOL);
        if (hServerSocket != INVALID_SOCKET) {
            if (port != NULL) {
                nProtocolPort = port;
            }else{
                lpServEnt = getservbyname("mail", DEFAULT_PROTOCOL);
                if (lpServEnt == NULL) 
                    nProtocolPort = htons(IPPORT_SMTP); 
                else 
                    nProtocolPort = lpServEnt->s_port;
            }
            sockAddr.sin_family = AF_INET;
            sockAddr.sin_port = nProtocolPort;
            sockAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list);
            nConnect = connect(hServerSocket, (PSOCKADDR)&sockAddr, 
                               sizeof(sockAddr));
            if(nConnect) 
                hServerSocket = INVALID_SOCKET;
        } else {
            printf("Invalid socket\r\n");
            throw;
        }
    }
    return(hServerSocket);
}

char* CFastSmtp::_formatHeader()
{
// check for at least one recipient
    if(Recipients.size() <= 0 )    {
        printf("Please add a message recipient!\r\n");
        return NULL;
    }
    int s=0;
    char *msgHeader = new char[16385];
    //char to[1024];
    for (unsigned int i=s=0;i<Recipients.size();i++) {        
        s+=strlen(Recipients.at(i)->GetEmail())+1;
    } if (s==0) s=1; char *to = new char[s];
    //char cc[1024];
    for (i=s=0;i<CCRecipients.size();i++) {        
        s+=strlen(CCRecipients.at(i)->GetEmail())+1;
    } if (s==0) s=1; char *cc = new char[s];
    //char bcc[1024];
    for (i=s=0;i<BCCRecipients.size();i++) {        
        s+=strlen(BCCRecipients.at(i)->GetEmail())+1;
    } if (s==0) s=1; char *bcc = new char[s];

    TCHAR szDate[500];
    TCHAR sztTime[500];

// create the recipient string, cc string, and bcc string
    to[0] = '\0';        
    for (i=0;i<Recipients.size();i++) {        
        i > 0 ? strcat(to,","):strcat(to,"");
        strcat(to,Recipients.at(i)->GetEmail());
    }

    cc[0] = '\0';    
    for (i=0;i<CCRecipients.size();i++) {
        i > 0 ? strcat(cc,","):strcat(cc,"");
        strcat(cc,CCRecipients.at(i)->GetEmail());
    }

    bcc[0] = '\0';    
    for (i=0;i<BCCRecipients.size();i++) {
        i > 0 ? strcat(bcc,","):strcat(bcc,"");
        strcat(bcc,BCCRecipients.at(i)->GetEmail());
    }
// get the current date and time
    SYSTEMTIME st={0};
    ::GetSystemTime(&st);
    ::GetDateFormat(LOCALE_SYSTEM_DEFAULT,0,&st,"ddd',
                    ' dd MMM yyyy",szDate,sizeof(szDate));
    ::GetTimeFormat(LOCALE_SYSTEM_DEFAULT,TIME_FORCE24HOURFORMAT,&st,
                    "HH':'mm':'ss tt",sztTime,sizeof(sztTime));
// here it is...the main data of the message
    wsprintf(msgHeader,"DATE: %s %s\r\n", szDate, sztTime);    
    if (m_pcFromName != NULL) {
        strcat(msgHeader,"FROM: ");
        strcat(msgHeader, m_pcFromName);
        strcat(msgHeader, "\r\n");
    }
    strcat(msgHeader,"To: ");
    strcat(msgHeader, to);
    strcat(msgHeader, "\r\n");
    strcat(msgHeader,"Cc: ");
    strcat(msgHeader, cc);
    strcat(msgHeader, "\r\n");
    if (m_pcSubject != NULL) {
        strcat(msgHeader, "Subject: ");
        strcat(msgHeader, m_pcSubject);
        strcat(msgHeader, "\r\n");
    }
    if (m_pcXMailer != NULL) {
        strcat(msgHeader,"X-Mailer: ");
        strcat(msgHeader, m_pcXMailer);
        strcat(msgHeader, "\r\n");
    }
// start optional fields
    if (m_pcReplyTo != NULL) {
        strcat(msgHeader, "Reply-To: ");
        strcat(msgHeader, m_pcReplyTo);
        strcat(msgHeader, "\r\n");
    }
// start MIME versions
    strcat(msgHeader, 
           "MIME-Version: 1.0\r\nContent-type: text/plain; charset=US-ASCII\r\n");
// send header finish command
    strcat(msgHeader, "\r\n");    
// clean up
    delete to;
    delete cc;
    delete bcc;
// done    
    return msgHeader;    
}

const char* const CFastSmtp::GetLocalHostIp() 
{
    in_addr    *iaHost;
    
    if (gethostname(m_cHostName,255) != SOCKET_ERROR) {
        HOSTENT *pHe = NULL;        
        pHe = gethostbyname(m_cHostName);
        if (pHe != NULL) {
            for (int i=0;pHe->h_addr_list[i] != 0;i++) {
                iaHost = (LPIN_ADDR)pHe->h_addr_list[i];
                m_pcIPAddr = inet_ntoa(*iaHost);
            }
        }            
    } else {            
        DWORD dErr = WSAGetLastError();    
        printf("Failed to get the local ip address\r\n");
    }    

    return m_pcIPAddr;
}

const char* const CFastSmtp::GetLocalHostname() 
{    
    if (gethostname((char FAR*)m_cHostName,255) == SOCKET_ERROR)    
        printf("Failed to get the local hostname\r\n");
    return m_cHostName;
}


//**********************************************************************************
//*** Properties
//**********************************************************************************

bool CFastSmtp::GetConnectStatus()
{
    return (bCon);
}

unsigned const int CFastSmtp::GetBCCRecipientCount()
{
    return (BCCRecipients.size());
}

unsigned const int CFastSmtp::GetCCRecipientCount() 
{
    return (CCRecipients.size());
}

unsigned const int CFastSmtp::GetSocket()
{
    return (hSocket);
}

const char* const CFastSmtp::GetMessageBody() 
{
    return (m_pcMsgBody);
}

unsigned const int CFastSmtp::GetRecipientCount()
{
    return (Recipients.size());
}

const char* const CFastSmtp::GetReplyTo()  
{
    return (m_pcReplyTo);
}

const char* const CFastSmtp::GetSenderEmail() 
{
    return (m_pcFromEmail);
}

const char* const CFastSmtp::GetSenderName() 
{
    return (m_pcFromName);
}

const char* const CFastSmtp::GetSubject() 
{
    return (m_pcSubject);
}

const char* const CFastSmtp::GetXMailer() 
{
    return (m_pcXMailer);
}

void CFastSmtp::SetMessageBody(const char body[])
{
    assert(body);
    int s=strlen(body);
    if (m_pcMsgBody)
        delete [] m_pcMsgBody;
    m_pcMsgBody = new char[s+1];
    strcpy(m_pcMsgBody, body);    
}

void CFastSmtp::SetReplyTo(const char replyto[])
{
    assert(replyto);
    int s=strlen(replyto);
    if (m_pcReplyTo)
        delete [] m_pcReplyTo;
    m_pcReplyTo = new char[s+1];
    strcpy(m_pcReplyTo, replyto);
}

void CFastSmtp::SetSenderEmail(const char email[])
{
    assert(email);
    int s=strlen(email);
    if (m_pcFromEmail)
        delete [] m_pcFromEmail;
    m_pcFromEmail = new char[s+1];
    strcpy(m_pcFromEmail, email);        
}

void CFastSmtp::SetSenderName(const char name[])
{
    assert(name);
    int s=strlen(name);
    if (m_pcFromName)
        delete [] m_pcFromName;
    m_pcFromName = new char[s+1];
    strcpy(m_pcFromName, name);
}

void CFastSmtp::SetSubject(const char subject[])
{
    assert(subject);
    int s=strlen(subject);
    if (m_pcSubject)
        delete [] m_pcSubject;
    m_pcSubject = new char[s+1];
    strcpy(m_pcSubject, subject);
}

void CFastSmtp::SetXMailer(const char xmailer[])
{
    assert(xmailer);
    int s=strlen(xmailer);
    if (m_pcXMailer)
        delete [] m_pcXMailer;
    m_pcXMailer = new char[s+1];
    strcpy(m_pcXMailer, xmailer);
}

Here is the header (FastSmtp.h):
// CFastSmtp.h: interface for the Smtp class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_SMTP_H__C66CDD0A_4F6F_4465_BAD6_8FA531785B5D__INCLUDED_)
#define AFX_SMTP_H__C66CDD0A_4F6F_4465_BAD6_8FA531785B5D__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <winsock2.h>
#include <assert.h>
#include <vector>

const int DEFAULT_PROTOCOL = 0;
const int NO_FLAGS = 0;

class CFastSmtp  
{
public:        
    CFastSmtp();    
    virtual ~CFastSmtp();
    bool    AddRecipient(const char email[], const char name[]="");
    bool    AddBCCRecipient(const char email[], const char name[]="");
    bool    AddCCRecipient(const char email[], const char name[]="");    
    bool    ConnectServer(const char server[], const unsigned short port=NULL);
    bool    Disconnect();
    bool    GetConnectStatus();    
    const unsigned int    GetBCCRecipientCount();    
    const unsigned int    GetCCRecipientCount();
    const unsigned int    GetRecipientCount();    
    const unsigned int    GetSocket();
    const char*    const    GetLocalHostIp();
    const char*    const    GetLocalHostname();    
    const char*    const    GetMessageBody();    
    const char*    const    GetReplyTo();
    const char*    const    GetSenderEmail();
    const char*    const    GetSenderName();
    const char*    const    GetSubject();    
    const char*    const    GetXMailer();    
    bool    Send();
    void    SetMessageBody(const char body[]);    
    void    SetSubject(const char subject[]);    
    void    SetSenderName(const char name[]);    
    void    SetSenderEmail(const char email[]);    
    void    SetReplyTo(const char replyto[]);    
    void    SetXMailer(const char xmailer[]);

private:
    class CRecipient
    {
    public:
        CRecipient() 
        { 
            m_pcEmail = NULL;
        };
        CRecipient& operator=(const CRecipient& src)
        {
            if (&src != this)
            {
                if (m_pcEmail)
                    delete [] m_pcEmail;
                int s = strlen(src.m_pcEmail);
                m_pcEmail = new char[s+1];
                strcpy(m_pcEmail, src.m_pcEmail);
            }
            return (*this);
        };
        virtual ~CRecipient()
        {
            if (m_pcEmail)
                delete [] m_pcEmail;
        };
        char* GetEmail()
        {
            return m_pcEmail;
        };
        void SetEmail(const char email[])
        {
            assert(email);
            int s=strlen(email);
            if (s > 0)
            {
                m_pcEmail = new char[s+1];
                strcpy(m_pcEmail, email);
            }        
        };
    private:
        char *m_pcEmail;
    };
    bool bCon;
    char m_cHostName[MAX_PATH];
    char* m_pcFromEmail;
    char* m_pcFromName;
    char* m_pcSubject;
    char* m_pcMsgBody;
    char* m_pcXMailer;
    char* m_pcReplyTo;
    char* m_pcIPAddr;    

    WSADATA wsaData;
    SOCKET hSocket;

    std::vector<CRecipient*> Recipients;
    std::vector<CRecipient*> CCRecipients;
    std::vector<CRecipient*> BCCRecipients;    
    
    char*   _formatHeader();    
    bool    _formatMessage();
    SOCKET  _connectServerSocket(const char* server, const unsigned short port=NULL);    
};

#endif // !defined(AFX_SMTP_H__C66CDD0A_4F6F_4465_BAD6_8FA531785B5D__INCLUDED_)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Christopher W. Backen
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionProblem in connecting the socketmemberRocky_Bas8 May '13 - 20:34 
Hello
 
I am using server as "smtp.gmail.com" and the port number is 587 but still iam getting the error message "Invalid Socket".
Bugclosesocket never calledmemberMartin Chapman30 Nov '11 - 14:16 
I downloaded a version of this library a while ago and noticed that closesocket() is never called in the FastSmtp::Disconnect() function or anywhere else. Eventually this will lead to problems. If you use this code you should add the closesocket() call before setting the socket handle to INVALID_SOCKET. Infact, the correct way to shutdown the socket would be more like:
 
shutdown(hSocket, SD_SEND);
closesocket(hSocket);
hSocket = INVALID_SOCKET;
 
// NULL is not a valid value for a socket handle
 
Otherwise, this class is pretty helpful!
Questiondoes not work, why ?memberghostbuster1216 Aug '10 - 9:58 
I compiled the Code with an Eclipse project and got an exe-File.
This Program seems to run succesful and shows the message "Send was a success" at the end.
But the Test-emails does not reach my email-account at gmx (xxxxx@gmx.net)
 
As mail.ConnectServer() I tried "pentium3-1700" (the name of the used computer) and "pop.gmx.net"
 
Maybe the reason is that there is not possibility to define a user-name and a user-password as it is needed for normal email-accounts ?
GeneralMy vote of 5memberMember 432084420 Jul '10 - 22:46 
Nice Class
Generalsocket error 10051 - WSAENETUNREACHmemberkispo915 Apr '10 - 23:01 
Hi everybody!
 
I am new in network programming and i want a bit help....
I have stuck in function _connectServerSocket().
When i call the connect(), function returns SOCKET_ERROR,
and WSAGetLastError returns error code 10051.
I have tried this with various servers and various ports
(although the correct port for sending mail is 25),
and always i get the same error...
 
Does anybody knows how can i fix this?
General[My vote of 2] I am not able to get a valid connectionmemberAdriana Aragri28 Jun '09 - 21:50 
Sorry to bother you, but I am quite newly to connection protocol stuff...
I can't make work well the _connectServerSocket function.
If I set the port (=25) it says "Invalid Socket": the nConnect parameter is set to -1 by the function
 
nConnect = connect(hServerSocket,(PSOCKADDR)&sockAddr,sizeof(sockAddr));
 
while if I don't set the port it gets the port number from the line
 
nProtocolPort = lpServEnt->s_port;
 
and it always sets the port equal to 6400 (indipendently from the server I am using: smtp.gmail.com, out.alice.it, my company webmail, etc.) and so it doesn't fail by saying "Send was a success!", but it doesn't really work: I can't get any mail!!
What can I do? D'Oh! | :doh:
Thanks in advance for your help,
Adriana
GeneralRe: [My vote of 2] I am not able to get a valid connectionmemberknestel29 Jul '09 - 2:12 
I had the same problem as you. We also do not use port 25 in our company.
TongGao Tang gave me the hint to solve this problem in his answer:
Change the command
 
if (port != NULL)
   nProtocolPort = port;
int the function _connectServerSocket to
if (port != NULL)
   nProtocolPort = htons(port);
and you can use the regular ports when calling ConnectServer.
With this change it worked for me.
GeneralMy vote of 1memberAhmed Han8 Jun '09 - 20:44 
Full of errors, most of them are because of violating basic C language structure.
How come this man can call himself a programmer?
 
No download link and poor page design.
GeneralMy vote of 1memberMenuki28 Apr '09 - 1:22 
It's full of bug.
There is possibly some memory leak.
There is no explanation of how it works.
GeneralRe: My vote of 1memberAhmed Han8 Jun '09 - 20:46 
Full of errors, most of them are because of violating basic C++ language structure. I think he didn't compiled this code even once before publishing it.
How come this man can call himself a programmer?
 
No download link for source code, poor page design.
GeneralBug with CC and BCC recipients...memberMenuki28 Apr '09 - 1:03 
In the CFastSmtp::Send() method, when sending CC and BCC recipients, there is Copy/Paste error in lines 234 and 252.
 
The code is looping on CCRecipients and BCCRecipients lists and we found token = strtok(Recipients.at(i)->GetEmail(),"<");
 
I changed them in token = strtok(CCRecipients.at(i)->GetEmail(),"<"); and token = strtok(BCCRecipients.at(i)->GetEmail(),"<"); to get CC and BCC recipients working.
 
The use of strtok is also destructive. Another function may have been a better choice (like strchr or strrchr)...
Personaly, I replaced the code :
        token = strtok(Recipients.at(i)->GetEmail(),"<");
        token = strtok(NULL,"<");
        if (token == NULL) 
            token = strtok(Recipients.at(i)->GetEmail(),"<");
        strcpy(buf, "RCPT TO: <");
        strcat(buf, token);
        strcat(buf, "\r\n");
by this one :
        token = strrchr(Recipients.at(i)->GetEmail(),'<');
        strcpy(buf, "RCPT TO: ");
        strcat(buf, token);
        strcat(buf, "\r\n");
idem for CC and BCC parts.
 
It's horribly coded!
Questioncan support GMAIL?memberMotorcure12 Aug '08 - 17:26 
gmail use SSL or TSL, this class can support?
 
You Suffer,But Why?

AnswerRe: can support GMAIL?memberauralius13 Jan '09 - 7:25 
i guess no...
this doesn't even support plain auth (base64 encoding)...
 
From Indonesia with love..!!

GeneralThanks a lotmemberGeneral Diensten12 Jun '08 - 0:14 
Just wanted to say thanks for this useful class Laugh | :laugh:
 
See ya
Generalpossinle error by reason of incorrect locale settingsmemberintser12 Feb '08 - 6:08 
Locale settiongs for code:
::GetDateFormat(LOCALE_SYSTEM_DEFAULT,0,&st,"ddd',' dd MMM yyyy",szDate,sizeof(szDate));
::GetTimeFormat(LOCALE_SYSTEM_DEFAULT,TIME_FORCE24HOURFORMAT,&st, "HH':'mm':'ss tt",sztTime,sizeof(sztTime));
provoke error for russian locales - smtp server need English language identifier
use for it MAKELCID(0x409, SORT_DEFAULT)
GeneralComplies well but don't get workmemberAmitJPatil26 Sep '07 - 3:39 

 
CFastSmtp mail;
 
if (mail.ConnectServer("mail.yahoo.com"))
{
mail.SetSenderName("Amit");
mail.SetSenderEmail("sunnysearch@yahoo.com");
mail.SetSubject("CFastSmtp v1.1 Release");
 
mail.AddRecipient("sunnysearch@yahoo.com");
mail.AddCCRecipient("sunnysearch@yahoo.com");
mail.AddBCCRecipient("sunnysearch@yahoo.com");
 
mail.SetMessageBody("Here is another test of CFastSmtp SMTP class!");

if (mail.GetConnectStatus())
{ if( mail.Send() )
AfxMessageBox("Send was a success!" );
else
AfxMessageBox("Send failed!");
 
mail.Disconnect();
}
else
AfxMessageBox("not Connected!");
 
}
else
AfxMessageBox("Put Server Name");
 

Its always give me "Invalid socket" and "Put Server Name" message
 
I think port problem is der?
Can anyone plz put d sample calling sequence
GeneralRe: Complies well but don't get workmemberqqwwwee5 Dec '07 - 16:41 
The same as my error.
Questionavailable for VS.NET?memberwachaca8 Aug '07 - 3:58 
Hello,
Looking for this exact thing but in VS.Net.
 
Any one have an idea when I can find a version of this for .net?
 
Thanks

AnswerRe: available for VS.NET?memberVEMS30 Aug '07 - 2:15 
When you say VS.NET ... do u mean in C#? I compiled this with VS 2003 and 2005 .. works fine.
GeneralAMAZINGmemberMrGoodly26 Jul '07 - 2:52 
Your code works very very well....And i am sorry for the rest of the idiots in this forum who cant pay a compliment for a job well done. Instead, they spew their cheap thoughts on how you should use STL or this or that.....JUST ENJOY THE CODE, USE IT...It works. Dont threw in our faces the latest crap you read in a trade magazine.
Questionhi, My Code is not work... [modified]membernsh_enp24 May '07 - 21:52 
if (mail.ConnectServer("mail.hotmail.com")) {
mail.SetSenderName("test");
mail.SetSenderEmail("test@hotmail.com");
mail.SetSubject("test");
 
mail.AddRecipient("test@hotmail.com");
mail.AddCCRecipient("test@hotmail.com");
mail.AddBCCRecipient("test@hotmail.com");
 
mail.SetMessageBody("test mail");

if (mail.GetConnectStatus()) {
TRACE(mail.Send() ? "Send was a success!" : "Send failed!");
mail.Disconnect();
}
}
 

 
I Writed this code Sample...but Not Work...
 
Result is "Send was a success" ... but none recieved mail...
 
help me...
 
test@hotmail.com is Sample...Real mail is others...
 

 
-- modified at 3:59 Friday 25th May, 2007
QuestionSame question as Ahsan hasmemberTongGao Tang20 Feb '07 - 20:32 

This program is very small and nice.
 
I have the same question as Ahsan said before:
1) What name should we provide for SMTP server? (I want to send email from this address ahsan_1358@hotmail.com)
2) Which port should we use to we connect to SMTP server (port 25? / htons(25)?/ NULL).
3) How can we know about SMTP server for any emailing address(or if you can tell me about ahsan_1358@hotmail.com's SMTP server name).
 
Thanks,
TongGao Tang
 
TongGao Tang

AnswerRe: Same question as Ahsan hasmemberTongGao Tang4 Mar '07 - 9:49 

Try to get the exchage (mail server name) by
 
(Method 1) Can try the program in "DNS Query":
file:///H:/Project%20Sending%20Mail/Good%20DNS%20Query%20ns%20not%20MX/dnsquery.asp.htm
to get exchange "a.mx.mail.yahoo.com"(i.e.mail server name) from domain "yahoo.com"
 
(Method 2) Can try can use the program in "DNS Resolver RFC 1035":
file:///H:/Project%20Sending%20Mail/DNS%20Resolver%20RFC%201035%20RFC%201035/akashkavadnsresolver.asp.htm
to get exchange name "a.mx.mail.yahoo.com" (i.e.mail server name) "from yahoo.com"
 
In the Method 2. You shoul change the text "qinfo->qtype = htons(1);"(only for host address)
to be qinfo->qtype = htons(15);" (it is for mail information).
I feel the Method 2 more good than Method 1. The Method 2 can get
exchange "host1.boom.ru" from "soft.boom.ru"
 
Tong-Gao Tang
tonggaotang@hotmail.com
 
TongGao Tang

GeneralExcellent Work..memberflippydeflippydebop19 Feb '07 - 22:03 
Your a life saver.. This is exactly what i was lookign for. A Win32 implementation of a an SMTP server..
 
Youve got my five! Smile | :) Smile | :)
GeneralSmall bugmemberkaponka4 Sep '06 - 21:52 
It is more correct to use ATL "SetRfc822Time" (C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\atlmfc\include\atlmime.h) function for setting mime "Date: " field.
 
Best regards
Roman
GeneralAgain, the qualitymemberwaldermort29 Aug '06 - 19:52 
This code could have been just what I am looking for, but...
 
1. There is no demo app for us to play around with.
2. There is no downlaodable source code.
3. There is no description of how it works (smells like a copy)
4. The code has not been updated with bug fixes (see other comments below)
5. It is only ANSI compatible (no UNICODE or MBCS support)
6. There are more comments written on the bottom of my shoe!
 
These gets a 1 from me.
GeneralCode qualitymembercozy dude13 Apr '06 - 5:51 
It's useful, I agree, but the code is just ugly...
First, using OOP is a great thing!
Second, the code is very error-prone. Raw C string and arrays... Use STL instead!
GeneralWorks for me but small fixmemberchris17510 Apr '06 - 0:33 
I have the code working but while I was trying to build it I got an error...
 
// get the current date and time
    SYSTEMTIME st={0};
    ::GetSystemTime(&st);
    ::GetDateFormat(LOCALE_SYSTEM_DEFAULT,0,&st,"ddd',
                    ' dd MMM yyyy",szDate,sizeof(szDate));
    ::GetTimeFormat(LOCALE_SYSTEM_DEFAULT,TIME_FORCE24HOURFORMAT,&st,
                    "HH':'mm':'ss tt",sztTime,sizeof(sztTime));
 
Visual Studio 6.0 didnt like the code "ddd', 'dd MMM yyyy" so I had to change it to "dd MMM yyyy".
 
Otherwise works great for me.
 
Chris
Generalvery upset,I can't use the code correctly....memberdaveice4 Mar '06 - 23:12 
two mistakes:
 
1. before AUTH, app should send EHLO, right?
I add the EHLO command and passed authentication, but..
2.After server tells me "Authentication successful", server disconnects with me immediately, what's the matter?
 
Can you enlighten me in the second issue?
GeneralSome ISSUES/QUERIESmemberAhsan|nashA11 Jan '06 - 19:09 
Hello,
I am a newbie in mail programming. I have been using web-based mailing earlier(Yahoo, Hotmail). I want to send a mail using C++ program, I have working knowledge of socket programming(Winsock). I compiled the above program using VC++(6.0) Win32 console-based program, it runs but I am not yet able to connect to SMTP server(_connectServerSocket(char * ,char *) always returns INVALID_SOCKET).
 
There could be a number of reasons for it. I want your help to solve them. Especially from those who succeeded in sending mail using Fast SMTP.
 
1) What name should we provide for SMTP server? (I want to send email from this address ahsan_1358@hotmail.com)
2) Which port should we use to we connect to SMTP server (port 25 / htons(25) / NULL).
3) How can we know about SMTP server for any emailing address(or if you can tell me about ahsan_1358@hotmail.com's SMTP server name).
 
I am looking forward for answers from the coding gurus of this site.
 
P.S. If you know of any free e-book that is helpful regarding this topic. Plz share it with me.
 
Learn to Teach
GeneralRe: Some ISSUES/QUERIESmemberTongGao Tang4 Mar '07 - 9:45 

Try to get the exchage (mail server name) by
 
(Method 1) You try the program in "DNS Query":
file:///H:/Project%20Sending%20Mail/Good%20DNS%20Query%20ns%20not%20MX/dnsquery.asp.htm
to get exchange "a.mx.mail.yahoo.com"(i.e.mail server name) from domain "yahoo.com"
 
(Method 2) You try can use the program in "DNS Resolver RFC 1035":
file:///H:/Project%20Sending%20Mail/DNS%20Resolver%20RFC%201035%20RFC%201035/akashkavadnsresolver.asp.htm
to get exchange name "a.mx.mail.yahoo.com" (i.e.mail server name) "from yahoo.com"
 
In the Method 2. You shoul change the text "qinfo->qtype = htons(1);"(only for host address)
to be qinfo->qtype = htons(15);" (it is for mail information).
I feel the Method 2 more good than Method 1. The Method 2 can get
exchange "host1.boom.ru" from "soft.boom.ru"
 
Tong-Gao Tang
tonggaotang@hotmail.com
 
TongGao Tang

QuestionPossible bug?membernismael21 Sep '05 - 5:25 
I dunno what is the intention of the _connectServerSocket function when you give it a port number other than 0, but I think that if you get the port from a configuration file that can be edited by a user, the user will rather enter 25 than 6400, because he has no idea that he has to change the byte order of the SMTP port he wants to use. So I think that we should read
 
if (port != NULL)
{
nProtocolPort = htons(port);
}
 
instead of the simple assignation "nProtocolPort" = port;
 

GeneralThank you (Code with AUTH LOGIN)memberjrivero16 Aug '05 - 13:50 
I embedded your code in my Borland C++ Builder. Works fantastic. Thanks so much, you saved me lots of time.
 
I saw someone posting code having AUTH and base64 code. It is fine but I embedded the bas64 code into your class. If you need a copy let me know please. There is a tweak to use in Borland that I will gladly share (the winsock.h vs winsock2.h).
 
Again, thanks so much!
 
J. Rivero
GeneralSend a filememberrbpkirow9 Aug '05 - 0:25 
Could I send a file
GeneralAddRecipient, AddCCRecipient and AddBCCRecipient leaks detected!!memberEll14 Feb '05 - 21:22 
Hi! You write:
 
bool CFastSmtp::AddRecipient(const char email[], const char name[])
{
assert(email);
 
int s=strlen(email);
if (s==0)
return false;
 
CRecipient *pRec = new CRecipient();
 
char *pcBuf = new char[s+strlen(name)+4];
sprintf(pcBuf,"%s<%s>",name,email);
pRec->SetEmail(pcBuf);
Recipients.insert(Recipients.end(), pRec);
//!!!!!! delete pcBuf;
delete [] pcBuf;
return (true);
}

GeneralPlease helpme i am in confusemembermuneer basha3 Feb '05 - 2:29 
sir,
Actually Confused | :confused: what this smtp and pop3. is it means by using this we can send mails and receive mails. means what ever we are doing with InternetExplorer by opening with www.mail.yahoo.com and giving username and password and sending mails to friends. i.e now with out using this IE by using this program we can directly send mails to friends and received. is correct. please inform me.
 
Thanks in Advance.
Questionerror ??sussz_xf_w24 Jan '05 - 21:16 
FastSmtp.cpp(611) : fatal error C1010: unexpected end of file while looking for precompiled header directiveConfused | :confused:
AnswerRe: error ??sussz_xf_w24 Jan '05 - 21:59 
FastSmtp.cpp
\FastSmtp.cpp(60) : error C2374: 'n' : redefinition; multiple initialization
\FastSmtp.cpp(58) : see declaration of 'n'
\FastSmtp.cpp(62) : error C2374: 'n' : redefinition; multiple initialization
\FastSmtp.cpp(58) : see declaration of 'n'
\FastSmtp.cpp(224) : error C2374: 'i' : redefinition; multiple initialization
\FastSmtp.cpp(206) : see declaration of 'i'
\FastSmtp.cpp(225) : error C2227: left of '->GetEmail' must point to class/struct/union
\FastSmtp.cpp(228) : error C2227: left of '->GetEmail' must point to class/struct/union
\FastSmtp.cpp(242) : error C2374: 'i' : redefinition; multiple initialization
\FastSmtp.cpp(206) : see declaration of 'i'
\FastSmtp.cpp(243) : error C2227: left of '->GetEmail' must point to class/struct/union
\FastSmtp.cpp(246) : error C2227: left of '->GetEmail' must point to class/struct/union
\FastSmtp.cpp(418) : error C2001: newline in constant
\FastSmtp.cpp(419) : error C2001: newline in constant
\FastSmtp.cpp(419) : error C2015: too many characters in constant
\FastSmtp.cpp(419) : error C2143: syntax error : missing ')' before 'constant'
\FastSmtp.cpp(419) : error C2660: 'GetDateFormatA' : function does not take 4 parameters
Error executing cl.exe.
 
mysock.exe - 13 error(s), 0 warning(s)

GeneralRe: error ??memberDouglas R. Keesler9 Jul '05 - 16:23 

These are easy fixes: VC6 does not allow redefining variables in for loops within a single function. Just rename the variables in the second for loop to n2, the same thing with the 'i' redefs, use i1, i2, i3, etc... (be sure to change all references to the variable.

The newline errors are a result of copy/paste, just delete the newline so the code is on a single line (wordwrap is OK).

The rest of the errors should disappear once you fix these.. at least they did for me.

Also, you must add ws2_32.lib to the Object/Library Modules edit box in the Project/Settings/Link tab.
 


In business, if two people always agree, one of them is unnecessary.

 


GenerallicensingsussAnonymous21 Oct '04 - 5:13 
Hi!
 
Are there any license issues on CFastSmtp or are we allowed to use it in commercial applications?
 
Kind regards,
 
peter
GeneralError in Disconnect funct.memberBrunko Victor19 Jul '04 - 15:24 
Please, correct it. Add to the function Disconnect next strings:
 
shutdown(hSocket,0x02);
closesocket(hSocket);
 
Ok.
 
Neuro
GeneralRe: Error in Disconnect funct.memberDouglas R. Keesler9 Jul '05 - 16:07 
Why is shutdown(hSocket, 0x02) necessary. Seems to me that this might prevent reconnection to a socket. It seems to me that closesocket() should be adequate for memory management purposes. Am I missing something here?
 

In business, if two people always agree, one of them is unnecessary.

 


Generalsending concurrent emailsmemberDaberElay5 Jun '04 - 9:46 
Hi ,
 
I would like to know what makes this package a *fast* smtp class?
I haven't seen any synchronization code here and i'm wandering if this code is thread safe?
 
can you open multiple connections to a mail server on the same time without connection collisions ? if so do you have to specify diffrent port for each thread ?
 
please advise,
 
DaberElay.
GeneralSending HTML mailsmember12stephan3416 Apr '04 - 7:08 
Is it possible to send html mails using CFastSmtp?
 
regs,
 
Stephan
GeneralRe: Sending HTML mailsmemberHughJampton17 Nov '04 - 6:12 
Yes, in the _formatHeader(), replace:
 
strcat(msgHeader, "MIME-Version: 1.0\r\nContent-type: text/plain; charset=US-ASCII\r\n");
 
with:
 
strcat(msgHeader, "MIME-Version: 1.0\r\nContent-type: text/html\r\nContent-Transfer-Encoding: quoted-printable\r\n");
 
Hugh
GeneralRe: Sending HTML mailsmemberDouglas R. Keesler9 Jul '05 - 18:44 

Although it's better to use:

strcat(msgHeader, "MIME-Version: 1.0\r\nContent-type: multipart/alternative;\r\n\tboundary=\"----=_NEXTPART_000\"\r\n");

and then format your message as below:

This is a multi-part message in MIME format.
 
------=_NEXTPART_000
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
 
            Hi there,
                            
            This is the alternative (plaintext) version of this email.
                      
            Regards,
                                
            John A. Doe
                                 
------=_NEXTPART_000
Content-Type: text/html;
	charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
 
<HTML>
<BODY>
<p>Hi there,
<p>This is the primary (HTML) version of this email.
<p>Regards,
<p>John A. Doe
</BODY></HTML>
 
------=_NEXTPART_000--

This is the preferred method, since in the event a user receives your email on a client that doesn't accept HTML, they will have a plaintext alternative to read, otherwise they either receive a blank screen or they see the actual HTML code (depending on the client).

Just a couple things to note: 1) use a boundary identifier that is unique enough it won't chance being duplicated in any email text, and 2) note that each boundary line in the body is prefaced by two additional dashes '--' and the closing boundary is ended with two dashes.
 


In business, if two people always agree, one of them is unnecessary.

 


GeneralUnresolved Externalsmemberivax14 Apr '04 - 5:34 
I try to use this clas and when build the project the liker shows following:
 
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__WSACleanup@0
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__recv@16
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__WSAGetLastError@0
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__send@16
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__connect@12
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__htons@4
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__getservbyname@8
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__socket@12
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__gethostbyname@4
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__inet_ntoa@4
FastSmtp.obj : error LNK2001: unresolved external symbol __imp__gethostname@8
D
 
Anybody can help me?
 
Thacks in advance
GeneralRe: Unresolved ExternalssussLorenz Blum18 Apr '04 - 4:07 
Me too, but I don't know what to do too Sigh | :sigh:
GeneralRe: Unresolved Externalssussflypoint24 Aug '05 - 17:10 
Use Ws2_32.lib can solve this problem
GeneralRe: Unresolved Externals - I GOT ITsussLorenz Blum19 Apr '04 - 6:08 
Add "ws2_32.lib" to the linker arguments!Big Grin | :-D

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 5 Sep 2002
Article Copyright 2000 by Christopher W. Backen
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid