/******************************************************************************/
/*! @file IMAPMail.cc
    @brief IMAPMail class
    @author Masashi Astro Tachibana, Apolloron Project.
 ******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "apolloron.h"

using namespace apolloron;

namespace apolloron {

/*! Constructor of IMAPMail.
    @param void
    @return void
 */
IMAPMail::IMAPMail(IMAPStream *imap_stream, const String &mailbox, long uid) {
    (*this).clear();

    (*this).imapStream = imap_stream;
    (*this).mailbox = mailbox;
    (*this).uid = uid;
}


/*! Destructor of IMAPMail.
    @param void
    @return void
 */
IMAPMail::~IMAPMail() {
    (*this).clear();
}


/*! Delete instance of IMAPMail.
    @param void
    @retval true   success
    @retval false  failure
 */
bool IMAPMail::clear() {
    long i;

    (*this).imapStream = NULL;
    (*this).mailbox.clear();
    (*this).uid = 0L;

    if ((*this).mailData.internalDate) {
        delete [] (*this).mailData.internalDate;
        (*this).mailData.internalDate = (char *)NULL;
    }
    (*this).mailData.size = 0L;
    if ((*this).mailData.headerDate) {
        delete [] (*this).mailData.headerDate;
        (*this).mailData.headerDate = (char *)NULL;
    }
    if ((*this).mailData.messageID) {
        delete [] (*this).mailData.messageID;
        (*this).mailData.messageID = (char *)NULL;
    }
    if ((*this).mailData.subject) {
        delete [] (*this).mailData.subject;
        (*this).mailData.subject = (char *)NULL;
    }
    (*this).mailData.fromList.clear();
    (*this).mailData.senderList.clear();
    (*this).mailData.toList.clear();
    (*this).mailData.ccList.clear();
    (*this).mailData.bccList.clear();
    (*this).mailData.replyToList.clear();
    (*this).mailData.referenceList.clear();
    (*this).mailData.flagList.clear();
    if ((*this).mailData.parts != NULL) {
        for (i = 0; i < (*this).mailData.partsLength; i++) {
            if ((*this).mailData.parts[i].id) {
                delete [] (*this).mailData.parts[i].id;
            }
            if ((*this).mailData.parts[i].filename) {
                delete [] (*this).mailData.parts[i].filename;
            }
            if ((*this).mailData.parts[i].contentTransferEncoding) {
                delete [] (*this).mailData.parts[i].contentTransferEncoding;
            }
            if ((*this).mailData.parts[i].contentType) {
                delete [] (*this).mailData.parts[i].contentType;
            }
            if ((*this).mailData.parts[i].contentID) {
                delete [] (*this).mailData.parts[i].contentID;
            }
        }
        delete [] (*this).mailData.parts;
        (*this).mailData.parts = (TMailPart *)NULL;
    }
    (*this).mailData.partsLength = 0L;

    (*this).AttachFilePartIDList.clear();
    (*this).tmpString.clear();

    return true;
}


/*! set mail structure.
    @param void
    @retval true   success
    @retval false  failure
 */
bool IMAPMail::setMailStructure() {
    String receive_line;
    String send_line;
    String expect_line;
    String ext_header;
    String s;
    bool ret;
    long i, j, tag;

    (*this).clear();

    if ((*this).imapStream == NULL || (*this).imapStream->isLoggedIn() == false) {
        return false;
    }

    if ((*this).imapStream->getSelectedMailBox() != (*this).mailbox) {
        (*this).imapStream->examine((*this).mailbox);
    }

    // fetch body structure
    tag = (*this).imapStream->getNextTag();
    send_line.sprintf("%05ld UID FETCH %ld (INTERNALDATE RFC822.SIZE FLAGS "
                      "ENVELOPE BODYSTRUCTURE BODY.PEEK[HEADER.FIELDS (Cont"
                      "ent-Type X-Priority References)])\r\n", tag, (*this).uid);
    ret = (*this).imapStream->send(send_line);
    send_line.clear();
    if (ret == false) {
        return false;
    }

    ext_header = "";

    do {
        receive_line = (*this).imapStream->receiveLine();
        if (receive_line.left(2) == "* " && isdigit(receive_line[2])) {
            long col, length;

            length = receive_line.len();
            col = 2;
            while (col < length) {
                if (receive_line[col] == ' ') {
                    col++;
                    break;
                }
                col++;
            }
            if (strncasecmp(&(receive_line.c_str()[col]), "FETCH ", 6) != 0) {
                continue;
            }
            col += 6;

            s = receive_line.mid(col).trim();

            i = s.len();
            j = 0L;
            if (3 < i && s[i-1] == '}') {
                i = i - 2;
                while (0 < i && isdigit(s[i])) {
                    i--;
                }
                if (s[i] == '{') {
                    j = atol(s + i + 1);
                    ext_header = (*this).imapStream->receive(j);
                    receive_line = s.mid(0L, i);
                    receive_line += (*this).imapStream->receiveLine();
                    s = receive_line.trim();
                }
            } else {
                receive_line = s;
            }

            // parse response
            if (2 < receive_line.len()) {


                // ***** NOT IMPLEMENTED YET *****


            }
        }
    } while (receive_line.left(2) == "* ");

    expect_line.sprintf("%05ld OK", tag);
    if (receive_line.left(expect_line.len()) != expect_line) {
        expect_line.clear();
        receive_line.clear();
        return false;
    }

    s.clear();
    ext_header.clear();
    expect_line.clear();
    receive_line.clear();

    return false;
}


/*! get raw mail source.
    @param void
    @retuen .eml image
 */
String& IMAPMail::getEML() {
    String receive_line;
    String send_line;
    String expect_line;
    String ext_header;
    bool ret;
    long i, j, tag;

    if ((*this).imapStream == NULL || (*this).imapStream->isLoggedIn() == false) {
        (*this).tmpString = "";
        return (*this).tmpString;
    }

    if ((*this).imapStream->getSelectedMailBox() != (*this).mailbox) {
        (*this).imapStream->examine((*this).mailbox);
    }

    // get mail source (A002 UID FETCH 123 (RFC822))
    tag = (*this).imapStream->getNextTag();
    send_line.sprintf("%05ld UID FETCH %ld (RFC822)\r\n", tag, (*this).uid);
    ret = (*this).imapStream->send(send_line);
    send_line.clear();
    if (ret == false) {
        (*this).tmpString = "";
        return (*this).tmpString;
    }

    (*this).tmpString = "";

    do {
        receive_line = (*this).imapStream->receiveLine();
        if (receive_line.left(2) == "* " && isdigit(receive_line[2])) {
            long col, length;
            String s;

            length = receive_line.len();
            col = 2;
            while (col < length) {
                if (receive_line[col] == ' ') {
                    col++;
                    break;
                }
                col++;
            }
            if (strncasecmp(&(receive_line.c_str()[col]), "FETCH ", 6) != 0) {
                continue;
            }
            col += 6;

            s = receive_line.mid(col).trim();

            i = s.len();
            j = 0L;
            if (3 < i && s[i-1] == '}') {
                i = i - 2;
                while (0 < i && isdigit(s[i])) {
                    i--;
                }
                if (s[i] == '{') {
                    j = atol(s + i + 1);
                    (*this).tmpString = (*this).imapStream->receive(j);
                    receive_line = s.mid(0L, i);
                    receive_line += (*this).imapStream->receiveLine();
                    s = receive_line.trim();
                }
            } else {
                receive_line = s;
            }
            s.clear();
        }
    } while (receive_line.left(2) == "* ");

    expect_line.sprintf("%05ld OK", tag);
    if (receive_line.left(expect_line.len()) != expect_line) {
        expect_line.clear();
        receive_line.clear();
        (*this).tmpString = "";
        return (*this).tmpString;
    }

    expect_line.clear();
    receive_line.clear();

    return (*this).tmpString;
}


/*! get raw header text.
    @param void
    @retuen raw header text
 */
String& IMAPMail::getHeader() {
    String receive_line;
    String send_line;
    String expect_line;
    String ext_header;
    bool ret;
    long i, j, tag;

    if ((*this).imapStream == NULL || (*this).imapStream->isLoggedIn() == false) {
        (*this).tmpString = "";
        return (*this).tmpString;
    }

    if ((*this).imapStream->getSelectedMailBox() != (*this).mailbox) {
        (*this).imapStream->examine((*this).mailbox);
    }

    // get mail header (A002 UID FETCH 123 (RFC822.HEADER))
    tag = (*this).imapStream->getNextTag();
    send_line.sprintf("%05ld UID FETCH %ld (RFC822.HEADER)\r\n", tag, (*this).uid);
    ret = (*this).imapStream->send(send_line);
    send_line.clear();
    if (ret == false) {
        (*this).tmpString = "";
        return (*this).tmpString;
    }

    (*this).tmpString = "";

    do {
        receive_line = (*this).imapStream->receiveLine();
        if (receive_line.left(2) == "* " && isdigit(receive_line[2])) {
            long col, length;
            String s;

            length = receive_line.len();
            col = 2;
            while (col < length) {
                if (receive_line[col] == ' ') {
                    col++;
                    break;
                }
                col++;
            }
            if (strncasecmp(&(receive_line.c_str()[col]), "FETCH ", 6) != 0) {
                continue;
            }
            col += 6;

            s = receive_line.mid(col).trim();

            i = s.len();
            j = 0L;
            if (3 < i && s[i-1] == '}') {
                i = i - 2;
                while (0 < i && isdigit(s[i])) {
                    i--;
                }
                if (s[i] == '{') {
                    j = atol(s + i + 1);
                    (*this).tmpString = (*this).imapStream->receive(j);
                    receive_line = s.mid(0L, i);
                    receive_line += (*this).imapStream->receiveLine();
                    s = receive_line.trim();
                }
            } else {
                receive_line = s;
            }
            s.clear();
        }
    } while (receive_line.left(2) == "* ");

    expect_line.sprintf("%05ld OK", tag);
    if (receive_line.left(expect_line.len()) != expect_line) {
        expect_line.clear();
        receive_line.clear();
        (*this).tmpString = "";
        return (*this).tmpString;
    }

    expect_line.clear();
    receive_line.clear();

    return (*this).tmpString;
}


/*! get IMAP INTERNALDATE.
    @param void
    @retuen IMAP INTERNALDATE
 */
String& IMAPMail::getInteralDate() {

    if ((*this).mailData.internalDate == NULL) {
        // get IMAP INTERNALDATE
        (*this).setMailStructure();
    }

    (*this).tmpString = (*this).mailData.internalDate;

    return (*this).tmpString;
}


/*! get IMAP RFC822.SIZE
    @param void
    @retuen IMAP  RFC822.SIZE
 */
long IMAPMail::getSize() {

    if ((*this).mailData.internalDate == NULL) {
        // get IMAP RFC822.SIZE
        (*this).setMailStructure();
    }

    return (*this).mailData.size;
}


/*! get MIME-decoded date header (UTF-8)
    @param void
    @retuen MIME-decoded date header (UTF-8)
 */
String& IMAPMail::getHeaderDate() {

    if ((*this).mailData.internalDate == NULL) {
        // get date header
        (*this).setMailStructure();
    }

    (*this).tmpString = (*this).mailData.headerDate;

    return (*this).tmpString;
}


/*! get Message-ID
    @param void
    @retuen Message-ID
 */
String& IMAPMail::getMessageID() {

    if ((*this).mailData.internalDate == NULL) {
        // get Message-ID
        (*this).setMailStructure();
    }

    (*this).tmpString = (*this).mailData.messageID;

    return (*this).tmpString;
}


/*! get UTF-8 Subject
    @param void
    @retuen UTF-8 Subject
 */
String& IMAPMail::getSubject() {

    if ((*this).mailData.internalDate == NULL) {
        // get subject
        (*this).setMailStructure();
    }

    (*this).tmpString = (*this).mailData.subject;

    return (*this).tmpString;
}


/*! get UTF-8 From list
    @param void
    @retuen UTF-8 From list
 */
List& IMAPMail::getFromList() {

    if ((*this).mailData.internalDate == NULL) {
        // get From list
        (*this).setMailStructure();
    }

    return (*this).mailData.fromList;
}


/*! get UTF-8 Sender list
    @param void
    @retuen UTF-8 Sender list
 */
List& IMAPMail::getSenderList() {

    if ((*this).mailData.internalDate == NULL) {
        // get Sender list
        (*this).setMailStructure();
    }

    return (*this).mailData.senderList;
}


/*! get UTF-8 To list
    @param void
    @retuen UTF-8 To list
 */
List& IMAPMail::getToList() {

    if ((*this).mailData.internalDate == NULL) {
        // get To list
        (*this).setMailStructure();
    }

    return (*this).mailData.toList;
}


/*! get UTF-8 Cc list
    @param void
    @retuen UTF-8 Cc list
 */
List& IMAPMail::getCcList() {

    if ((*this).mailData.internalDate == NULL) {
        // get Cc list
        (*this).setMailStructure();
    }

    return (*this).mailData.ccList;
}


/*! get UTF-8 Bcc list
    @param void
    @retuen UTF-8 Bcc list
 */
List& IMAPMail::getBccList() {

    if ((*this).mailData.internalDate == NULL) {
        // get Bcc list
        (*this).setMailStructure();
    }

    return (*this).mailData.bccList;
}


/*! get UTF-8 Reply-To list
    @param void
    @retuen UTF-8 Reply-To list
 */
List& IMAPMail::getReplyToList() {

    if ((*this).mailData.internalDate == NULL) {
        // get Reply-To list
        (*this).setMailStructure();
    }

    return (*this).mailData.replyToList;
}


/*! get References
    @param void
    @retuen References
 */
List& IMAPMail::getReferenceList() {

    if ((*this).mailData.internalDate == NULL) {
        // get Reference list
        (*this).setMailStructure();
    }

    return (*this).mailData.referenceList;
}


/*! get Flags
    @param void
    @retuen Flags
 */
List& IMAPMail::getFlagList() {

    if ((*this).mailData.internalDate == NULL) {
        // get Flag list
        (*this).setMailStructure();
    }

    return (*this).mailData.flagList;
}


/*! get plain text body
    @param void
    @retuen plain text body
 */
String& IMAPMail::getPlainTextBody() {


    // get plain text body


    return (*this).tmpString;
}


/*! get plain HTML body
    @param void
    @retuen plain HTML body
 */
String& IMAPMail::getHTMLBody() {


    // get plain HTML body


    return (*this).tmpString;
}


/*! get Part ID list of attachment files
    @param void
    @retuen Part ID list of attachment files
 */
List& IMAPMail::getAttachFilePartIDList() {


    // get Part ID list of attachment files


    return (*this).AttachFilePartIDList;
}


/*! get UTF-8 file name by Part ID
    @param part_id Part ID
    @retuen get UTF-8 file name
 */
String& IMAPMail::getAttachFileNane(const String &part_id) {


    // get UTF-8 file name by Part ID


    return (*this).tmpString;
}


/*! get file content(binary)
    @param part_id Part ID
    @retuen file content(binary)
 */
String& IMAPMail::getAttachFile(const String &part_id) {


    // get file content by Part ID


    return (*this).tmpString;
}


/*! get mail part (TMailPart structure)
    @param part_id Part ID
    @retuen mail part (TMailPart structure)
 */
const TMailPart& IMAPMail::getMailPart(const String &part_id) {
    long i;

    if ((*this).mailData.parts == (TMailPart *)NULL) {
        (*this).mailData.parts = new TMailPart [1];
        memset(&((*this).mailData.parts[0]), 0, sizeof(TMailPart));
        return (*this).mailData.parts[0];
    }

    // get mail part by Part ID
    i = 0;


    return (*this).mailData.parts[i];
}


} // namespace apolloron
