// ==========================================================================
// Author:  Yee Hsu
// Date:    6/11/2005
// File:    SMTP.cpp
//
// Desc:    This is the SMTP class implementation file. This file
//          implements all necessary variables and funtions to create
//          an open socket that connects to a remote SMTP Email Server,
//          and sends a contructed email to the recipient.
//
//          Currently, this implementation only supports TCP connection
//          and is only an MUA (Mail User Agent). What's cool about this
//          class and implementation is it is cross platform independent
//          and may be compiled on Windows or Unix machines.
//
// Legal:   Copyright (c) Yee Hsu 2005
// ==========================================================================

#include "SMTP.h"

// ==========================================================================
// Identifier:  SMTP()
//
// Description: SMTP Constructor
//              Initialize default Email fields
//              User should overside these variables before sending Email
// ==========================================================================

SMTP::SMTP()
{
    // initialize default SMTP Server
    this->SMTP_Server   = "localhost";

    // initialize default Email fields
    this->From          = "postmaster@localhost";
    this->Rcpt          = "postmaster@localhost";
    this->To            = "";
    this->Cc            = "";
    this->Subject       = "";
    this->Body          = "";

    // initialize Send Mail Vars
    this->Packet.erase();
}

// ==========================================================================
// Identifier:  ~SMTP()
//
// Description: SMTP Destructor
//              Deinitialize all Email fiends
// ==========================================================================

SMTP::~SMTP()
{
    // empty all Email fields
    this->From.erase();
    this->Rcpt.erase();
    this->To.erase();
    this->Cc.erase();
    this->Subject.erase();
    this->Body.erase();
    this->Packet.erase();
}

// ==========================================================================
// Identifier:  Send(void)
//
// Post:        Email is sent to the SMTP server which will deliver
//              the email to the recipent(s)
//
// Calls:       Many socket system calls to deliver the email message
//
// Returns:     True if email sent successfully, False otherwise
//
// Description: Send generates the Email packet and sends it to the
//              recipient. The packet is created by first opening a
//              socket and connecting to the destination SMTP server,
//              the email packet is created and finally sent off to
//              the recipient. This is an email sent via TCP stream
//              and currently is only an MUA.
// ==========================================================================

bool SMTP::Send(void)
{
    SOCKET sd = 0;
    struct sockaddr_in sock, sockName;
    struct hostent *pHost;

    // initialize Window Socket DLL
    this->WinInit();

    // translate domain name to ip address
    if ((pHost = gethostbyname(SMTP_Server.c_str())) == NULL)
    {
        fprintf(stderr, "Error: gethostbyname()\n");
        return false;
    }

    // setup server socket
    memset(&sock, 0, sizeof(sock));
    sock.sin_family = AF_INET;
    memcpy(&sock.sin_addr, pHost->h_addr_list[0], pHost->h_length);
    sock.sin_port = htons(SMTP_PORT);

    // setup local socket
    memset(&sockName,0, sizeof(sockName));
    sockName.sin_family = AF_INET;
    sockName.sin_addr.s_addr = INADDR_ANY;
    sockName.sin_port = htons(0);

    // Create a Socket
    if ((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        fprintf(stderr, "Error: socket()\n");
        return false;
    }

    // Bind to Socket
    if (bind(sd, (struct sockaddr*) &sockName, sizeof(sockName)) < 0)
    {
        fprintf(stderr, "Error: bind()\n");
        return false;
    }

    // Make TCP Connection to Socket
    if ((connect(sd, (struct sockaddr*) &sock, sizeof(sock))) < 0)
    {
        fprintf(stderr, "Error: connect()\n");
        return false;
    }

    // Contruct the Email Message according to SMTP Protocol
    this->Packet.erase();

    // Contruct Email Source and Recipient address
    this->Packet += "MAIL FROM:<"   + this->From    + ">\r\n";
    this->Packet += "RCPT TO:<"     + this->Rcpt    + ">\r\n";

    // Contruct Email Subject and Body
    this->Packet += "DATA\r\n";
    this->Packet += "From: "        + this->From    + "\r\n";
    this->Packet += "To: "          + this->To      + "\r\n";
    this->Packet += "Cc: "          + this->Cc      + "\r\n";
    this->Packet += "Subject: "     + this->Subject + "\r\n";
    this->Packet += ""              + this->Body    + "\r\n";

    // Tag Email termination seqeunce
    this->Packet += "\r\n.\r\n";
    this->Packet += "QUIT\r\n";

    // Finally, Send the Email
    if (send(sd, this->Packet.c_str(), this->Packet.length(), 0) < 0)
    {
        fprintf(stderr, "Error: send()\n");
        return false;
    }

    // Close all open sockets and release Window Socket DLL
    close(sd);
    this->WinTerm();

    return true;
}

// ==========================================================================
// Identifier:  WinInit(void)
//
// Description: A must to init for Window Socket.
//              This should be called at the beginning of every
//              program when using Window Sockets. This function opens
//              and utilizes an WinSock DLL.
// ==========================================================================

void SMTP::WinInit(void)
{
#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    int err = 0;

    wVersionRequested = MAKEWORD(2,0);
    err = WSAStartup(wVersionRequested, &wsaData);

    if (err != 0)
    {
        fprintf(stderr, "Error: unable to initialize WinSock DLL\n");
        return;
    }
#endif
}

// ==========================================================================
// Identifier:  WinTerm(void)
//
// Description: A must to term for Window Socket.
//              This should be called at the end of every
//              program when using Window Sockets.
// ==========================================================================

void SMTP::WinTerm(void)
{
#ifdef _WIN32
    WSACleanup();
#endif
}