// pop_ex.cpp
//
// The example program in this file shows how to retrieve messages using
// the POP3 protocol
//

#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <mimepp/mimepp.h>

using namespace std;

//
// Parses the response line and returns the number of messages
//
static int NumMessages(const char* str)
{
    int numMessages = 0;
    int n = strcspn(str, "0123456789");
    numMessages = atoi(&str[n]);
    return numMessages;
}


//
// Retrieves one message from the POP3 server and writes it to a file.
// POP is the POP3 client, which should be in the transaction state.
// WHICH is the number of the message to retrieve.  DELETEMESSAGE is a
// boolean which indicates whether the message should be deleted after
// it is successfully retrieved (true value), or whether it should be
// left on the server (false value).
//
static void RetrieveOneMessage(DwPopClient& pop, int which, int deleteMessage)
{
    cout << "Retrieving message " << which << "..." << flush;

    // Send RETR request to get the message

    int responseCode = pop.Retr(which);

    // Check the response code:
    //
    //   '+' == success
    //   '-' == failure
    //   0   == failure (no response from server)

    if (responseCode == '+') {

        // If the request is successful, get the message

        DwString msg = pop.MultiLineResponse();

        // Convert to LF ('\n') end-of-line character.  CR LF is
        // standard for text in virtually all Internet protocols;
        // however, LF is standard for text in the C/C++ programming
        // environment.  In the terminology of the RFCs, LF is the
        // "local newline convention".

        DwToLfEol(msg, msg);

        // Write the message to a file

        char filename[80];
        sprintf(filename, "%d.txt", which);
        ofstream out(filename);
        out << msg;
        out.close();
        cout << "ok" << endl;

        // Delete message from server, if requested

        if (deleteMessage) {
            pop.Dele(which);
        }
    }
    else if (responseCode == '-') {
        // The server indicated an error
        cout << "err" << endl;
    }
    else /* if (responseCode != '+' && responseCode != '-') */ {
        // No response from the server
        cout << "err" << endl;
    }
}


//
// Retrieves all messages from the POP3 server and writes them to
// files.  POP is the POP3 client, which should be in the transaction
// state.  DELETEMESSAGES is a boolean which indicates whether the
// messages should be deleted after they are successfully retrieved
// (true value), or whether they should be left on the server (false
// value).
//
static void RetrieveAllMessages(DwPopClient& pop, int deleteMessages)
{
    // Send the STAT request to get the number of messages; that is,
    // the "status"

    int responseCode = pop.Stat();

    // Check the response code:
    //
    //   '+' == success
    //   '-' == failure
    //   0   == failure (no response from server)

    if (responseCode == '+') {

        // Get the full server response.  The format for the response is
        // 
        //      +OK nn mm
        //
        // where nn is the number of messages and mm is the size of the
        // mailbox in bytes.

        DwString response = pop.SingleLineResponse();
        int numMessages = NumMessages(response.c_str());
        cout << "You have " << numMessages << " messages" << endl;

        // Retrieve messages one by one, and save each one to a file

        int i;
        for (i=1; i <= numMessages; ++i) {
            RetrieveOneMessage(pop, i, deleteMessages);
        }
    }
    else if (responseCode == '-') {
        cout << "There was a problem retrieving mailbox status" << endl;
        cout << "The server's response was \""
            << pop.SingleLineResponse().substr(5) << "\"" << endl;
    }
    else /* if (responseCode != '+' && responseCode != '-') */ {
        if (pop.LastFailure() == DwProtocolClient::kFailNoFailure) {
            cout << "There was a problem retrieving mailbox status: "
                << "reason unknown" << endl;
        }
        else {
            cout << "There was a problem retrieving mailbox status: "
                << pop.LastFailureStr() << endl;
        }
    }
}


//
// Opens a connection to a POP3 server, retrieves all the messages and
// writes them to files, and optionally deletes the messages after they
// are retrieved.
//
int main(int argc, char* argv[])
{
    // Process commandline arguments

    if (argc < 4) {
        cout << "Usage: " << argv[0] << " [-d] server username password"
           << endl;
        cout << "Options:" << endl;
        cout << "  -d  delete after retrieving" << endl;
        exit(1);
    }
    int deleteMessages = 0;
    int argsOffset = 0;
    if (argv[1][0] == '-') {
        if (argv[1][1] == 'd') {
            deleteMessages = 1;
            argsOffset = 1;
            cout << "Delete requested" << endl;
        }
    }
    const char* server = argv[argsOffset+1];
    const char* user = argv[argsOffset+2];
    const char* password = argv[argsOffset+3];

    // Open connection to POP3 server

    cout << "Opening connection to " << server << endl;
    DwPopClient pop;
    int errorInCtor = pop.LastError();
    if (errorInCtor == 0) {
        int openResponseCode = pop.Open(server);

        // Check the response code
        //
        //   '+' == success
        //   '-' == failure
        //   0   == failure (no response from server)

        if (openResponseCode == '+') {

            // Send USER request with user name as parameter

            cout << "Sending user name" << endl;
            int userResponseCode = pop.User(user);

            // Check the response code

            if (userResponseCode == '+') {

                // Send the PASS request with password as parameter

                cout << "Sending password" << endl;
                int passResponseCode = pop.Pass(password);

                // Check the response code

                if (passResponseCode == '+') {

                    // Get all the messages

                    RetrieveAllMessages(pop, deleteMessages);

                    // Send QUIT command to complete the transaction.
                    // If we don't send QUIT, the server will not
                    // change the state of the mailbox -- in
                    // particular, no messages will be deleted

                    pop.Quit();
                }
                else if (passResponseCode == '-') {
                    cout << "Password not accepted by server" << endl;
                    cout << "The server's response was \""
                        << pop.SingleLineResponse().substr(5) << "\"" << endl;
                }
                else if (passResponseCode == 0) {
                    if (pop.LastFailure()==DwProtocolClient::kFailNoFailure) {
                        cout << "Password not accepted by server: "
                            << "reason unknown" << endl;
                    }
                    else {
                        cout << "Password not accepted by server: "
                            << pop.LastFailureStr() << endl;
                    }
                }
            }
            else if (userResponseCode == '-') {
                cout << "User name not accepted by server" << endl;
                cout << "The server's response was \""
                    << pop.SingleLineResponse().substr(5) << "\"" << endl;
            }
            else if (userResponseCode == 0) {
                if (pop.LastFailure() == DwProtocolClient::kFailNoFailure) {
                    cout << "User name not accepted by server: "
                        << "reason unknown" << endl;
                }
                else {
                    cout << "User name not accepted by server: "
                        << pop.LastFailureStr() << endl;
                }
            }
        }
        else if (openResponseCode == '-') {
            cout << "Connection refused by server" << endl;
            cout << "The server's response was \""
                << pop.SingleLineResponse().substr(5) << "\"" << endl;
        }
        else if (openResponseCode == 0) {
            if (pop.LastFailure() == DwProtocolClient::kFailNoFailure) {
                cout << "Can't connect to server: " << "reason unknown"
                    << endl;
            }
            else {
                cout << "Can't connect to server: " << pop.LastFailureStr()
                    << endl;
            }
        }
    }
    else if (errorInCtor != 0) {
        if (pop.LastFailure() == DwProtocolClient::kFailNoFailure) {
            // It doesn't make sense to report an error that is not
            // a failure, since it would confuse a user.  Errors that
            // are not failures are supposed to be the result of a
            // programmer's error.  Logging errors is probably helpful
            // to identify problems.
            cout << "Can't connect to server: " << "reason unknown"
                << endl;
         }
        else {
            cout << "Can't connect to server: " << pop.LastFailureStr()
                << endl;
        }
    }

    // Close the connection, if necessary

    if (pop.IsOpen()) {
        pop.Close();
    }
    return 0;
}
