Small library for reading mail messages via POP3. Currently doesn\'t return all header fields, and does only plain text authorization.

Dependents:   mmain pop3demo

pop3.cpp

Committer:
hlipka
Date:
2011-01-10
Revision:
0:ec8a3cef200d
Child:
1:f2b05d1c91be

File content as of revision 0:ec8a3cef200d:

p#include "pop3.h"
#include "dnsresolve.h"

class Pop3CommandResponse
{
    public:
        Pop3CommandResponse(string line);
        bool addLine(string line);

        bool success;
        string responseLine;
        list<string> responseLines;
};

Pop3::Pop3(const char* servername, const char* username, const char* password) {
    _username=username;
    _password=password;
    _servername=servername;

    _readpos=0;
    _readlen=0;

    _closed=false;

    _socket.setOnEvent(this,&Pop3::onTCPSocketEvent);
}

Pop3::~Pop3() {
    close();
}

void Pop3::onTCPSocketEvent(TCPSocketEvent e) {
//    printf("New TCPSocketEvent: %d\n",e);
    switch (e) {
        case TCPSOCKET_CONNECTED:
            _connected = true;
            _connecting=false;
            break;

        case TCPSOCKET_READABLE:
            break;
        case TCPSOCKET_WRITEABLE:
            break;

        case TCPSOCKET_CONTIMEOUT:
        case TCPSOCKET_CONRST:
        case TCPSOCKET_CONABRT:
        case TCPSOCKET_ERROR:
        case TCPSOCKET_DISCONNECTED: {
            _socket.close();
            _connected = false;
            _connecting=false;
        }
        break;
    }
}

list<string> *Pop3::getMessages() {
    Pop3CommandResponse* pcr=sendCommandMultiResponse("LIST");
    if (!pcr->success) {
        delete pcr;
        return NULL;
    }
    list<string> *ids=new list<string>();
    list<string> lines=pcr->responseLines;
    list<string>::iterator it;
    for ( it=lines.begin() ; it != lines.end(); it++ ) {
        string line=*it;
        string id=line.substr(0,line.find(' '));
        ids->push_back(id);
    }
    delete pcr;
    return ids;
}

Pop3Message* Pop3::getMessage(string id, bool getSig, bool deleteOnReceive) {
    Pop3CommandResponse* pcr=sendCommandMultiResponse(string("RETR ").append(id));
    if (!pcr->success) {
        delete pcr;
        return NULL;
    }
    Pop3Message *msg=new Pop3Message();
    list<string> lines=pcr->responseLines;
    list<string>::iterator it;
    for ( it=lines.begin() ; it != lines.end(); it++ ) {
        string line=*it;
        if (0==line.find("From: ")) {
            msg->from=line.substr(6);
        } else if (0==line.find("Subject: ")) {
            msg->subject=line.substr(9);
        } else if (0==line.length()) {
            break;
        }
    }
    for (/* use iterator from above - it start right at the content */ ; it != lines.end(); it++ ) {
        string line=*it;
        if (!getSig && 0==line.compare("-- "))
            break;
        msg->content.push_back(line);
    }
    delete pcr;
    if (deleteOnReceive)
    {
        pcr=deleteMessage(id);
        delete pcr;
    }
    return msg;
}

bool Pop3::deleteMessage(string id) {
    Pop3CommandResponse* pcr=sendCommandMultiResponse(string("DELE ").append(id));
    if (!pcr->success) {
        delete pcr;
        return false;
    }
    delete pcr;
    return true;
}


Pop3CommandResponse* Pop3::sendCommand(string cmd) {
//    printf("send [%s]\n",cmd.c_str());
    if (!_connected) {
        printf("not connected\n");
        return NULL;
    }
    int err=_socket.send(cmd.append("\r\n").c_str(),cmd.length()+2);
    Net::poll();
    string r=readResponseLine();
    return new Pop3CommandResponse(r);
}

Pop3CommandResponse* Pop3::sendCommandMultiResponse(string cmd) {
    // first, send command
//    printf("send [%s]\n",cmd.c_str());
    if (!_connected) {
        printf("not connected\n");
        return NULL;
    }
    int err=_socket.send(cmd.append("\r\n").c_str(),cmd.length()+2);
    Net::poll();
    // read first response line -> contains status
    string r=readResponseLine();
//    printf("response=[%s]\n",r.c_str());
    // create response object to collect remaining lines
    Pop3CommandResponse *pcr=new Pop3CommandResponse(r);
    if (!pcr->success)
        return pcr;
    // read other lines, stop when marker reached
    while (true) {
        r=readResponseLine();
        if (pcr->addLine(r))
            break;
    }
    return pcr;
}

bool Pop3::init() {
    // retrieve IP address for hostname
    DNSResolver *dnsr=new DNSResolver();
    IpAddr addr=dnsr->resolveName(_servername);
    delete dnsr;

    if (addr.isNull()) {
        printf("cannot resolve server name %s\n",_servername);
        return false;
    }

//    printf("server ip=%i.%i.%i.%i\n",addr[0],addr[1],addr[2],addr[3]);

    _connecting=true;

    // create tcp socket to server
    Host host(addr, 110);

    TCPSocketErr bindErr = _socket.connect(host);

    if (bindErr != 0) {
        printf("connection error %i\n", bindErr);
        return false;
    }

    // wait for connection established
    Timer tmr;
    tmr.start();

    while (_connecting) {
        Net::poll();
        wait(0.1);
        if (tmr.read()>10)
            break;
    }

    if (!_connected) {
        printf("error - could not connect (timeout)\n");
        return false;
    }

    // read server banner
    string banner=readResponseLine();
//    printf("banner=[%s]\n",banner.c_str());

    Pop3CommandResponse pcr(banner);
    if (!pcr.success)
        return false;

    // send username
    string cmd=string("user ").append(_username);
    Pop3CommandResponse *response=sendCommand(cmd);

    if (!response->success) {
        delete response;
        return false;
    }

    delete response;

    // send password
    cmd=string("pass ").append(_password);
    response=sendCommand(cmd);

    if (!response->success) {
        delete response;
        return false;
    }

    delete response;

    return true;
}

void Pop3::close() {
    if (_closed)
        return;

    Pop3CommandResponse *pcr=sendCommand("CLOSE");
    delete pcr;

    while (_connected) {
        Net::poll();
        // read remaining data
        int err=_socket.recv(_readbuf,BUFSIZE-1);
        if (err<=0)
            break;
    }

    _socket.resetOnEvent();
    _socket.close();

    _closed=true;
}

string Pop3::readResponseLine() {
    string r;
    bool got_r=false;

    if (!_connected)
        return r;

    while (true) {
        Net::poll();
        if (_readlen>_readpos) {
            while (_readbuf[_readpos]!=0 && _readpos<_readlen) {
                char c=_readbuf[_readpos++];
                if (!got_r) {
                    if (c=='\r') {
                        got_r=true;
                    } else {
                        r.push_back(c);
                    }
                } else {
                    if (c=='\n') {
                        return r;
                    } else {
                        r.push_back('\r'); // push missed \r also, so push it to string
                        r.push_back(c);
                        got_r=false;
                    }
                }
            }
        } else {
            int err=_socket.recv(_readbuf,BUFSIZE-1);
            if (err < 0) {
                printf("error while receiving data: %i!\n",err);
            } else if (err>0) {
                _readbuf[err]=0;
                _readlen=err;
                _readpos=0;
            }
        }
        wait(0.1);
        if (_connected==false) {
            return r;
        }
    }
}

Pop3CommandResponse::Pop3CommandResponse(string line) {
    if (0==line.find("+OK")) {
        success=true;
        responseLine=line.substr(4);
    } else if (0==line.find("-ERR")) {
        success=false;
        responseLine=line.substr(5);
    } else {
        success=false;
    }

}

bool Pop3CommandResponse::addLine(string line) {
    // last line is single dot only
    if (line.length()==1 && line.at(0)=='.')
        return true;
    // remove leading dot if any
    if (line.length()>0 && line.at(0)=='.')
        line=line.substr(1);
    responseLines.push_back(line);
    return false;
}