| 1 |
/* |
| 2 |
* Copyright (C) 2009-2011 TSUBAKIMOTO Hiroya <z0rac@users.sourceforge.jp> |
| 3 |
* |
| 4 |
* This software comes with ABSOLUTELY NO WARRANTY; for details of |
| 5 |
* the license terms, see the LICENSE.txt file included with the program. |
| 6 |
*/ |
| 7 |
#include "mailbox.h" |
| 8 |
#include <algorithm> |
| 9 |
|
| 10 |
#if _DEBUG >= 2 |
| 11 |
#include <iostream> |
| 12 |
#define DBG(s) s |
| 13 |
#define LOG(s) (cout << s) |
| 14 |
#else |
| 15 |
#define DBG(s) |
| 16 |
#define LOG(s) |
| 17 |
#endif |
| 18 |
|
| 19 |
/** pop3 - pop3 protocol backend |
| 20 |
* This class is a mailbox::backend for POP3 protocol. |
| 21 |
*/ |
| 22 |
class pop3 : public mailbox::backend { |
| 23 |
bool _command(const string& cmd, bool ok = true); |
| 24 |
bool _ok(bool ok = true); |
| 25 |
typedef list< pair<string, string> > plist; |
| 26 |
plist _plist(bool upper = false); |
| 27 |
string _headers(); |
| 28 |
#ifdef _DEBUG |
| 29 |
using backend::read; |
| 30 |
string read() |
| 31 |
{ |
| 32 |
string line = backend::read(); |
| 33 |
LOG("R: " << line << endl); |
| 34 |
return line; |
| 35 |
} |
| 36 |
#endif |
| 37 |
public: |
| 38 |
void login(const uri& uri, const string& passwd); |
| 39 |
void logout(); |
| 40 |
size_t fetch(mailbox& mbox, const uri& uri); |
| 41 |
}; |
| 42 |
|
| 43 |
void |
| 44 |
pop3::login(const uri& uri, const string& passwd) |
| 45 |
{ |
| 46 |
_ok(); |
| 47 |
if (_command("CAPA", false)) { |
| 48 |
bool uidl = false, stls = false; |
| 49 |
plist cap(_plist(true)); |
| 50 |
plist::iterator p = cap.begin(); |
| 51 |
for (; p != cap.end(); ++p) { |
| 52 |
if (p->first == "UIDL") uidl = true; |
| 53 |
else if (p->first == "STLS") stls = true; |
| 54 |
} |
| 55 |
if (!uidl) throw mailbox::error("server not support UIDL command"); |
| 56 |
if (stls && !tls()) { |
| 57 |
_command("STLS"); |
| 58 |
starttls(uri[uri::host]); |
| 59 |
_command("CAPA"); |
| 60 |
cap = _plist(true); |
| 61 |
} |
| 62 |
for (p = cap.begin(); p != cap.end(); ++p) { |
| 63 |
if (p->first == "USER") break; |
| 64 |
} |
| 65 |
if (p == cap.end()) throw mailbox::error("login disabled"); |
| 66 |
} |
| 67 |
_command("USER " + uri[uri::user]); |
| 68 |
_command("PASS " + passwd); |
| 69 |
} |
| 70 |
|
| 71 |
void |
| 72 |
pop3::logout() |
| 73 |
{ |
| 74 |
_command("QUIT"); |
| 75 |
} |
| 76 |
|
| 77 |
size_t |
| 78 |
pop3::fetch(mailbox& mbox, const uri& uri) |
| 79 |
{ |
| 80 |
const list<string>& ignore = mbox.ignore(); |
| 81 |
list<string> ignored; |
| 82 |
list<mail> mails; |
| 83 |
list<mail> recents; |
| 84 |
bool recent = uri[uri::fragment] == "recent"; |
| 85 |
_command("UIDL"); |
| 86 |
plist uidl(_plist()); |
| 87 |
plist::iterator uidp = uidl.begin(); |
| 88 |
for (; uidp != uidl.end(); ++uidp) { |
| 89 |
string uid = uidp->second; |
| 90 |
if (find(ignore.begin(), ignore.end(), uid) != ignore.end()) { |
| 91 |
ignored.push_back(uid); |
| 92 |
continue; |
| 93 |
} |
| 94 |
LOG("Fetch mail: " << uid << endl); |
| 95 |
_command("TOP " + uidp->first + " 0"); |
| 96 |
mail m(uid); |
| 97 |
if (m.header(_headers())) { |
| 98 |
ignored.push_back(uid); |
| 99 |
continue; |
| 100 |
} |
| 101 |
if (recent) { |
| 102 |
ignored.push_back(uid); |
| 103 |
recents.push_back(m); |
| 104 |
} else { |
| 105 |
(mbox.find(uid) ? &mails : &recents)->push_back(m); |
| 106 |
} |
| 107 |
} |
| 108 |
size_t count = recents.size(); |
| 109 |
mails.splice(mails.end(), recents); |
| 110 |
mbox.mails(mails); |
| 111 |
mbox.ignore(ignored); |
| 112 |
return count; |
| 113 |
} |
| 114 |
|
| 115 |
bool |
| 116 |
pop3::_command(const string& cmd, bool ok) |
| 117 |
{ |
| 118 |
write(cmd); |
| 119 |
LOG("S: " << cmd << endl); |
| 120 |
return _ok(ok); |
| 121 |
} |
| 122 |
|
| 123 |
bool |
| 124 |
pop3::_ok(bool ok) |
| 125 |
{ |
| 126 |
string line = read(); |
| 127 |
bool resp = line.substr(0, line.find_first_of(' ')) == "+OK"; |
| 128 |
if (ok && !resp) throw mailbox::error(line); |
| 129 |
return resp; |
| 130 |
} |
| 131 |
|
| 132 |
pop3::plist |
| 133 |
pop3::_plist(bool upper) |
| 134 |
{ |
| 135 |
plist result; |
| 136 |
for (;;) { |
| 137 |
string line = read(); |
| 138 |
if (!line.empty() && line[0] == '.') { |
| 139 |
line.assign(line, 1, line.size() - 1); |
| 140 |
if (line.empty()) break; |
| 141 |
} |
| 142 |
if (upper) line = tokenizer::uppercase(line); |
| 143 |
pair<string, string> ps; |
| 144 |
string::size_type i = line.find_first_of(' '); |
| 145 |
ps.first.assign(line, 0, i); |
| 146 |
if (i != string::npos) { |
| 147 |
i = line.find_first_not_of(' ', i); |
| 148 |
if (i != string::npos) ps.second.assign(line, i, line.size() - i); |
| 149 |
} |
| 150 |
result.push_back(ps); |
| 151 |
} |
| 152 |
return result; |
| 153 |
} |
| 154 |
|
| 155 |
string |
| 156 |
pop3::_headers() |
| 157 |
{ |
| 158 |
string result; |
| 159 |
string line = read(); |
| 160 |
for (; !line.empty(); line = read()) { |
| 161 |
if (line[0] == '.') { |
| 162 |
if (line.size() == 1) break; |
| 163 |
line.erase(0, 1); |
| 164 |
} |
| 165 |
result += line + "\015\012"; |
| 166 |
} |
| 167 |
while (line.size() != 1 || line[0] != '.') line = read(); |
| 168 |
return result; |
| 169 |
} |
| 170 |
|
| 171 |
mailbox::backend* backendPOP3() { return new pop3; } |