| 1 |
/* |
| 2 |
* Copyright (C) 2009-2016 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 "winsock.h" |
| 9 |
#include <cassert> |
| 10 |
|
| 11 |
#ifdef _DEBUG |
| 12 |
#include <iostream> |
| 13 |
#define DBG(s) s |
| 14 |
#define LOG(s) (cout << s) |
| 15 |
#else |
| 16 |
#define DBG(s) |
| 17 |
#define LOG(s) |
| 18 |
#endif |
| 19 |
|
| 20 |
#define TCP_TIMEOUT 60 |
| 21 |
|
| 22 |
/** tcpstream - stream of TCP session. |
| 23 |
* This instance should be created by the function mailbox::backend::tcp. |
| 24 |
*/ |
| 25 |
namespace { |
| 26 |
class tcpstream : public mailbox::backend::stream { |
| 27 |
winsock::tcpclient _socket; |
| 28 |
int _verifylevel; |
| 29 |
public: |
| 30 |
tcpstream(int verifylevel) : _verifylevel(verifylevel) {} |
| 31 |
~tcpstream() { _socket.shutdown(); } |
| 32 |
void connect(const string& host, const string& port, int domain); |
| 33 |
size_t read(char* buf, size_t size); |
| 34 |
size_t write(const char* data, size_t size); |
| 35 |
int tls() const; |
| 36 |
mailbox::backend::stream* starttls(const string& host); |
| 37 |
}; |
| 38 |
} |
| 39 |
|
| 40 |
void |
| 41 |
tcpstream::connect(const string& host, const string& port, int domain) |
| 42 |
{ |
| 43 |
assert(_socket == INVALID_SOCKET); |
| 44 |
_socket.connect(host, port, domain).timeout(TCP_TIMEOUT); |
| 45 |
} |
| 46 |
|
| 47 |
size_t |
| 48 |
tcpstream::read(char* buf, size_t size) |
| 49 |
{ |
| 50 |
return _socket.recv(buf, size); |
| 51 |
} |
| 52 |
|
| 53 |
size_t |
| 54 |
tcpstream::write(const char* data, size_t size) |
| 55 |
{ |
| 56 |
return _socket.send(data, size); |
| 57 |
} |
| 58 |
|
| 59 |
/** sslstream - stream of SSL session. |
| 60 |
* This instance should be created by the function mailbox::backend::ssl(). |
| 61 |
*/ |
| 62 |
#if USE_OPENSSL |
| 63 |
#include "win32.h" |
| 64 |
namespace { |
| 65 |
extern "C" { |
| 66 |
typedef struct { int ssl; } SSL; |
| 67 |
typedef struct { int ctx; } SSL_CTX; |
| 68 |
typedef struct { int x509; } X509; |
| 69 |
typedef struct { int name; } X509NAME; |
| 70 |
|
| 71 |
typedef int (*SSL_library_init)(void); |
| 72 |
typedef SSL_CTX* (*SSL_CTX_new)(void*); |
| 73 |
typedef void (*SSL_CTX_free)(SSL_CTX*); |
| 74 |
typedef void* (*SSLv23_client_method)(void); |
| 75 |
typedef long (*SSL_CTX_set_options)(SSL_CTX*, long); |
| 76 |
typedef SSL* (*SSL_new)(SSL_CTX*); |
| 77 |
typedef void (*SSL_free)(SSL*); |
| 78 |
typedef int (*SSL_set_fd)(SSL*, int); |
| 79 |
typedef int (*SSL_connect)(SSL*); |
| 80 |
typedef int (*SSL_read)(SSL*, void*, int); |
| 81 |
typedef int (*SSL_write)(SSL*, const void*, int); |
| 82 |
typedef int (*SSL_shutdown)(SSL*); |
| 83 |
typedef int (*SSL_get_error)(const SSL*, int); |
| 84 |
typedef int (*SSL_CTX_load_verify_locations)(SSL_CTX*, const char*, const char*); |
| 85 |
typedef int (*SSL_get_verify_result)(const SSL*); |
| 86 |
typedef X509* (*SSL_get_peer_certificate)(const SSL*); |
| 87 |
|
| 88 |
typedef unsigned long (*ERR_get_error)(void); |
| 89 |
typedef char* (*ERR_error_string)(unsigned long, char*); |
| 90 |
typedef void (*X509_free)(X509*); |
| 91 |
typedef int (*X509_get_ext_by_NID)(X509*, int, int); |
| 92 |
typedef void* (*X509_get_ext)(X509*, int); |
| 93 |
typedef void* (*X509V3_EXT_d2i)(void*); |
| 94 |
typedef int (*sk_num)(const void*); |
| 95 |
typedef void* (*sk_value)(const void*, int); |
| 96 |
typedef void (*sk_free)(void*); |
| 97 |
typedef char* (*ASN1_STRING_data)(void*); |
| 98 |
typedef int (*ASN1_STRING_length)(void*); |
| 99 |
typedef X509NAME* (*X509_get_subject_name)(X509*); |
| 100 |
typedef int (*X509_NAME_get_text_by_NID)(X509NAME*, int, char*, int); |
| 101 |
} |
| 102 |
|
| 103 |
struct openssl { |
| 104 |
win32::dll ssleay; |
| 105 |
win32::dll libeay; |
| 106 |
openssl(); |
| 107 |
static void np() {} |
| 108 |
}; |
| 109 |
openssl _openssl; |
| 110 |
|
| 111 |
#define SSL(name) name(_openssl.ssleay(#name)) |
| 112 |
#define XSSL(name) name(_openssl.ssleay(#name, FARPROC(openssl::np))) |
| 113 |
#define LIB(name) name(_openssl.libeay(#name)) |
| 114 |
|
| 115 |
class sslstream : public mailbox::backend::stream { |
| 116 |
SSL_CTX* _ctx; |
| 117 |
SSL* _ssl; |
| 118 |
SSL_read _read; |
| 119 |
SSL_write _write; |
| 120 |
winsock::tcpclient _socket; |
| 121 |
int _verifylevel; |
| 122 |
int _want(int rc); |
| 123 |
void _connect(); |
| 124 |
void _shutdown(); |
| 125 |
bool _verify(const string& host); |
| 126 |
static bool _match(const char* host, const char* subj, int len); |
| 127 |
public: |
| 128 |
sslstream(int verifylevel); |
| 129 |
~sslstream(); |
| 130 |
void connect(SOCKET socket, const string& host); |
| 131 |
void connect(const string& host, const string& port, int domain); |
| 132 |
size_t read(char* buf, size_t size); |
| 133 |
size_t write(const char* data, size_t size); |
| 134 |
int tls() const { return 1; } |
| 135 |
mailbox::backend::stream* starttls(const string&) { return NULL; } |
| 136 |
static bool avail() { return _openssl.ssleay && _openssl.libeay; } |
| 137 |
public: |
| 138 |
struct error : public mailbox::error { |
| 139 |
error() : mailbox::error(emsg()) {} |
| 140 |
static string emsg(); |
| 141 |
}; |
| 142 |
friend struct error; |
| 143 |
}; |
| 144 |
} |
| 145 |
|
| 146 |
openssl::openssl() |
| 147 |
: ssleay("ssleay32.dll"), libeay("libeay32.dll") |
| 148 |
{ |
| 149 |
if (ssleay && libeay) XSSL(SSL_library_init)(); |
| 150 |
} |
| 151 |
|
| 152 |
string |
| 153 |
sslstream::error::emsg() |
| 154 |
{ |
| 155 |
char buf[256]; |
| 156 |
return LIB(ERR_error_string)(LIB(ERR_get_error)(), buf); |
| 157 |
} |
| 158 |
|
| 159 |
sslstream::sslstream(int verifylevel) |
| 160 |
: _ssl(NULL), _read(SSL(SSL_read)), _write(SSL(SSL_write)), |
| 161 |
_verifylevel(verifylevel) |
| 162 |
{ |
| 163 |
_ctx = SSL(SSL_CTX_new)(SSL(SSLv23_client_method)()); |
| 164 |
if (!_ctx) throw error(); |
| 165 |
#if !USE_SSL2 |
| 166 |
XSSL(SSL_CTX_set_options)(_ctx, 0x01000000L /* SSL_OP_NO_SSLv2 */); |
| 167 |
#endif |
| 168 |
if (_verifylevel > 1) { |
| 169 |
XSSL(SSL_CTX_load_verify_locations)(_ctx, "./ca-root.crt", NULL); |
| 170 |
} |
| 171 |
} |
| 172 |
|
| 173 |
sslstream::~sslstream() |
| 174 |
{ |
| 175 |
_shutdown(); |
| 176 |
XSSL(SSL_CTX_free)(_ctx); |
| 177 |
} |
| 178 |
|
| 179 |
int |
| 180 |
sslstream::_want(int rc) |
| 181 |
{ |
| 182 |
if (rc > 0) return rc; |
| 183 |
switch (SSL(SSL_get_error)(_ssl, rc)) { |
| 184 |
case 2: /* SSL_ERROR_WANT_READ */ return rc; |
| 185 |
case 3: /* SSL_ERROR_WANT_WRITE */ return rc; |
| 186 |
case 6: /* SSL_ERROR_ZERO_RETURN */ return 0; |
| 187 |
} |
| 188 |
throw error(); |
| 189 |
} |
| 190 |
|
| 191 |
void |
| 192 |
sslstream::_connect() |
| 193 |
{ |
| 194 |
assert(_socket != INVALID_SOCKET && !_ssl); |
| 195 |
try { |
| 196 |
_ssl = SSL(SSL_new)(_ctx); |
| 197 |
if (!_ssl || |
| 198 |
SSL(SSL_set_fd)(_ssl, _socket) != 1 || |
| 199 |
SSL(SSL_connect)(_ssl) != 1) throw error(); |
| 200 |
} catch (...) { |
| 201 |
if (_ssl) XSSL(SSL_free)(_ssl), _ssl = NULL; |
| 202 |
throw; |
| 203 |
} |
| 204 |
} |
| 205 |
|
| 206 |
void |
| 207 |
sslstream::_shutdown() |
| 208 |
{ |
| 209 |
if (_ssl) { |
| 210 |
XSSL(SSL_shutdown)(_ssl); |
| 211 |
XSSL(SSL_free)(_ssl); |
| 212 |
_ssl = NULL; |
| 213 |
} |
| 214 |
} |
| 215 |
|
| 216 |
bool |
| 217 |
sslstream::_verify(const string& host) |
| 218 |
{ |
| 219 |
switch (_verifylevel) { |
| 220 |
case 0: return true; |
| 221 |
case 1: break; |
| 222 |
default: |
| 223 |
switch(SSL(SSL_get_verify_result)(_ssl)) { |
| 224 |
case 0: break; /* X509_V_OK */ |
| 225 |
case 18: /* X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT */ |
| 226 |
if (_verifylevel < 3) break; |
| 227 |
default: return false; |
| 228 |
} |
| 229 |
} |
| 230 |
|
| 231 |
int hostn = host.size(); |
| 232 |
const char* hostp = host.c_str(); |
| 233 |
struct buf { |
| 234 |
char* data; |
| 235 |
buf(size_t n) : data(new char[n]) {} |
| 236 |
~buf() { delete [] data; } |
| 237 |
} buf(hostn + 1); |
| 238 |
|
| 239 |
X509* x509 = SSL(SSL_get_peer_certificate)(_ssl); |
| 240 |
if (!x509) return false; |
| 241 |
bool cn = true, ok = false; |
| 242 |
int id = LIB(X509_get_ext_by_NID)(x509, 85 /*NID_subject_alt_name*/, -1); |
| 243 |
if (id >= 0) { |
| 244 |
void* alt = LIB(X509V3_EXT_d2i)(LIB(X509_get_ext)(x509, id)); |
| 245 |
if (alt) { |
| 246 |
sk_value value = LIB(sk_value); |
| 247 |
ASN1_STRING_data data = LIB(ASN1_STRING_data); |
| 248 |
ASN1_STRING_length length = LIB(ASN1_STRING_length); |
| 249 |
int n = LIB(sk_num)(alt); |
| 250 |
for (int i = 0; i < n && !ok; ++i) { |
| 251 |
typedef struct { int type; union { void* ia5; } d; } GENERAL_NAME; |
| 252 |
GENERAL_NAME* p = (GENERAL_NAME*)value(alt, i); |
| 253 |
if (p->type == 2 /*GEN_DNS*/) { |
| 254 |
ok = _match(hostp, data(p->d.ia5), length(p->d.ia5)); |
| 255 |
cn = false; |
| 256 |
} |
| 257 |
} |
| 258 |
LIB(sk_free)(alt); |
| 259 |
} |
| 260 |
} |
| 261 |
if (cn) { |
| 262 |
X509NAME* name = LIB(X509_get_subject_name)(x509); |
| 263 |
if (name) { |
| 264 |
X509_NAME_get_text_by_NID get = LIB(X509_NAME_get_text_by_NID); |
| 265 |
int len = get(name, 13 /*NID_commonName*/, NULL, 0); |
| 266 |
if (len > 0 && len <= hostn && |
| 267 |
get(name, 13 /*NID_commonName*/, buf.data, len + 1) > 0) { |
| 268 |
ok = _match(hostp, buf.data, len); |
| 269 |
} |
| 270 |
} |
| 271 |
} |
| 272 |
LIB(X509_free)(x509); |
| 273 |
return ok; |
| 274 |
} |
| 275 |
|
| 276 |
bool |
| 277 |
sslstream::_match(const char* host, const char* subj, int len) |
| 278 |
{ |
| 279 |
LOG(host << " : " << string(subj, len) << endl); |
| 280 |
for (int i = 0; i < len; ++i) { |
| 281 |
if (subj[i] == '*' && i + 1 < len && subj[i + 1] == '.') { |
| 282 |
while (*host && *host != '.') ++host; |
| 283 |
++i; |
| 284 |
} else { |
| 285 |
if (tolower(*host) != tolower(subj[i])) return false; |
| 286 |
} |
| 287 |
if (!*host++) return false; |
| 288 |
} |
| 289 |
return !*host; |
| 290 |
} |
| 291 |
|
| 292 |
void |
| 293 |
sslstream::connect(SOCKET socket, const string& host) |
| 294 |
{ |
| 295 |
assert(_socket == INVALID_SOCKET); |
| 296 |
_socket(socket).timeout(TCP_TIMEOUT); |
| 297 |
_connect(); |
| 298 |
if (!_verify(host)) throw mailbox::error("invalid host"); |
| 299 |
} |
| 300 |
|
| 301 |
void |
| 302 |
sslstream::connect(const string& host, const string& port, int domain) |
| 303 |
{ |
| 304 |
assert(_socket == INVALID_SOCKET); |
| 305 |
_socket.connect(host, port, domain).timeout(TCP_TIMEOUT); |
| 306 |
_connect(); |
| 307 |
if (!_verify(host)) throw mailbox::error("invalid host"); |
| 308 |
} |
| 309 |
|
| 310 |
size_t |
| 311 |
sslstream::read(char* buf, size_t size) |
| 312 |
{ |
| 313 |
assert(_ssl); |
| 314 |
int n = -1; |
| 315 |
while (n < 0) n = _want(_read(_ssl, buf, size)); |
| 316 |
return size_t(n); |
| 317 |
} |
| 318 |
|
| 319 |
size_t |
| 320 |
sslstream::write(const char* data, size_t size) |
| 321 |
{ |
| 322 |
assert(_ssl); |
| 323 |
int n = -1; |
| 324 |
while (n < 0) n = _want(_write(_ssl, data, size)); |
| 325 |
return size_t(n); |
| 326 |
} |
| 327 |
|
| 328 |
int |
| 329 |
tcpstream::tls() const |
| 330 |
{ |
| 331 |
return sslstream::avail() ? 0 : -1; |
| 332 |
} |
| 333 |
|
| 334 |
#undef LIB |
| 335 |
#undef XSSL |
| 336 |
#undef SSL |
| 337 |
#else // !USE_OPENSSL |
| 338 |
namespace { |
| 339 |
class sslstream : public mailbox::backend::stream { |
| 340 |
struct tls : public winsock::tlsclient { |
| 341 |
winsock::tcpclient socket; |
| 342 |
#if USE_SSL2 |
| 343 |
tls() : winsock::tlsclient(SP_PROT_SSL2 | SP_PROT_SSL3) {} |
| 344 |
#endif |
| 345 |
~tls() { shutdown(); } |
| 346 |
size_t _recv(char* buf, size_t size); |
| 347 |
size_t _send(const char* data, size_t size); |
| 348 |
}; |
| 349 |
tls _tls; |
| 350 |
int _verifylevel; |
| 351 |
void _connect(const string& host); |
| 352 |
public: |
| 353 |
sslstream(int verifylevel) : _verifylevel(verifylevel) {} |
| 354 |
void connect(SOCKET socket, const string& host); |
| 355 |
void connect(const string& host, const string& port, int domain); |
| 356 |
size_t read(char* buf, size_t size); |
| 357 |
size_t write(const char* data, size_t size); |
| 358 |
int tls() const { return 1; } |
| 359 |
mailbox::backend::stream* starttls(const string&) { return NULL; } |
| 360 |
}; |
| 361 |
} |
| 362 |
|
| 363 |
size_t |
| 364 |
sslstream::tls::_recv(char* buf, size_t size) |
| 365 |
{ |
| 366 |
assert(socket != INVALID_SOCKET); |
| 367 |
return socket.recv(buf, size); |
| 368 |
} |
| 369 |
|
| 370 |
size_t |
| 371 |
sslstream::tls::_send(const char* data, size_t size) |
| 372 |
{ |
| 373 |
assert(socket != INVALID_SOCKET); |
| 374 |
return socket.send(data, size); |
| 375 |
} |
| 376 |
|
| 377 |
void |
| 378 |
sslstream::_connect(const string& host) |
| 379 |
{ |
| 380 |
_tls.connect(); |
| 381 |
if (_verifylevel) { |
| 382 |
DWORD ignore = 0; |
| 383 |
switch (_verifylevel) { |
| 384 |
case 1: ignore |= (SECURITY_FLAG_IGNORE_REVOCATION | |
| 385 |
SECURITY_FLAG_IGNORE_WRONG_USAGE | |
| 386 |
SECURITY_FLAG_IGNORE_CERT_DATE_INVALID); |
| 387 |
case 2: ignore |= SECURITY_FLAG_IGNORE_UNKNOWN_CA; |
| 388 |
} |
| 389 |
if (!_tls.verify(host, ignore)) throw mailbox::error("invalid host"); |
| 390 |
} |
| 391 |
} |
| 392 |
|
| 393 |
void |
| 394 |
sslstream::connect(SOCKET socket, const string& host) |
| 395 |
{ |
| 396 |
assert(_tls.socket == INVALID_SOCKET); |
| 397 |
_tls.socket(socket).timeout(TCP_TIMEOUT); |
| 398 |
_connect(host); |
| 399 |
} |
| 400 |
|
| 401 |
void |
| 402 |
sslstream::connect(const string& host, const string& port, int domain) |
| 403 |
{ |
| 404 |
assert(_tls.socket == INVALID_SOCKET); |
| 405 |
_tls.socket.connect(host, port, domain).timeout(TCP_TIMEOUT); |
| 406 |
_connect(host); |
| 407 |
} |
| 408 |
|
| 409 |
size_t |
| 410 |
sslstream::read(char* buf, size_t size) |
| 411 |
{ |
| 412 |
return _tls.recv(buf, size); |
| 413 |
} |
| 414 |
|
| 415 |
size_t |
| 416 |
sslstream::write(const char* data, size_t size) |
| 417 |
{ |
| 418 |
return _tls.send(data, size); |
| 419 |
} |
| 420 |
|
| 421 |
int |
| 422 |
tcpstream::tls() const |
| 423 |
{ |
| 424 |
return 0; |
| 425 |
} |
| 426 |
#endif // !USE_OPENSSL |
| 427 |
|
| 428 |
mailbox::backend::stream* |
| 429 |
tcpstream::starttls(const string& host) |
| 430 |
{ |
| 431 |
assert(_socket != INVALID_SOCKET); |
| 432 |
unique_ptr<sslstream> st(new sslstream(_verifylevel)); |
| 433 |
st->connect(_socket.release(), host); |
| 434 |
return st.release(); |
| 435 |
} |
| 436 |
|
| 437 |
/* |
| 438 |
* Functions of the class mailbox::backend |
| 439 |
*/ |
| 440 |
void |
| 441 |
mailbox::backend::tcp(const string& host, const string& port, int domain, int verify) |
| 442 |
{ |
| 443 |
unique_ptr<tcpstream> st(new tcpstream(verify)); |
| 444 |
st->connect(host, port, domain); |
| 445 |
_st.reset(st.release()); |
| 446 |
} |
| 447 |
|
| 448 |
void |
| 449 |
mailbox::backend::ssl(const string& host, const string& port, int domain, int verify) |
| 450 |
{ |
| 451 |
unique_ptr<sslstream> st(new sslstream(verify)); |
| 452 |
st->connect(host, port, domain); |
| 453 |
_st.reset(st.release()); |
| 454 |
} |
| 455 |
|
| 456 |
void |
| 457 |
mailbox::backend::starttls(const string& host) |
| 458 |
{ |
| 459 |
stream* st = _st->starttls(host); |
| 460 |
if (st) _st.reset(st); |
| 461 |
} |
| 462 |
|
| 463 |
string |
| 464 |
mailbox::backend::read(size_t size) |
| 465 |
{ |
| 466 |
string result; |
| 467 |
while (size) { |
| 468 |
char buf[1024]; |
| 469 |
size_t n = _st->read(buf, min(size, sizeof(buf))); |
| 470 |
if (!n) throw mailbox::error("connection reset"); |
| 471 |
result.append(buf, n); |
| 472 |
size -= n; |
| 473 |
} |
| 474 |
return result; |
| 475 |
} |
| 476 |
|
| 477 |
string |
| 478 |
mailbox::backend::read() |
| 479 |
{ |
| 480 |
char c; |
| 481 |
if (!_st->read(&c, 1)) throw mailbox::error("connection reset"); |
| 482 |
string result(1, c); |
| 483 |
do { |
| 484 |
do { |
| 485 |
if (!_st->read(&c, 1)) throw mailbox::error("connection reset"); |
| 486 |
result.push_back(c); |
| 487 |
} while (c != '\012'); |
| 488 |
} while (result[result.size() - 2] != '\015'); |
| 489 |
return result.erase(result.size() - 2); // remove CRLF |
| 490 |
} |
| 491 |
|
| 492 |
void |
| 493 |
mailbox::backend::write(const char* data, size_t size) |
| 494 |
{ |
| 495 |
while (size) { |
| 496 |
size_t n = _st->write(data, size); |
| 497 |
data += n, size -= n; |
| 498 |
} |
| 499 |
} |
| 500 |
|
| 501 |
void |
| 502 |
mailbox::backend::write(const string& data) |
| 503 |
{ |
| 504 |
string ln(data); |
| 505 |
ln.append("\015\012"); |
| 506 |
write(ln.data(), ln.size()); |
| 507 |
} |
| 508 |
|
| 509 |
/* |
| 510 |
* Functions of the class mailbox |
| 511 |
*/ |
| 512 |
mailbox& |
| 513 |
mailbox::uripasswd(const string& uri, const string& passwd) |
| 514 |
{ |
| 515 |
_uri = ::uri(uri); |
| 516 |
_passwd = passwd; |
| 517 |
return *this; |
| 518 |
} |
| 519 |
|
| 520 |
const mail* |
| 521 |
mailbox::find(const string& uid) const |
| 522 |
{ |
| 523 |
list<mail>::const_iterator p = _mails.begin(); |
| 524 |
for (; p != _mails.end(); ++p) { |
| 525 |
if (p->uid() == uid) return &*p; |
| 526 |
} |
| 527 |
return NULL; |
| 528 |
} |
| 529 |
|
| 530 |
void |
| 531 |
mailbox::fetchmail() |
| 532 |
{ |
| 533 |
extern backend* backendIMAP4(); |
| 534 |
extern backend* backendPOP3(); |
| 535 |
|
| 536 |
static const struct { |
| 537 |
const char* scheme; |
| 538 |
backend* (*make)(); |
| 539 |
void (backend::*stream)(const string&, const string&, int, int); |
| 540 |
const char* port; |
| 541 |
} backends[] = { |
| 542 |
{ "imap", backendIMAP4, &backend::tcp, "143" }, |
| 543 |
{ "imap+ssl", backendIMAP4, &backend::ssl, "993" }, |
| 544 |
{ "pop", backendPOP3, &backend::tcp, "110" }, |
| 545 |
{ "pop+ssl", backendPOP3, &backend::ssl, "995" }, |
| 546 |
}; |
| 547 |
|
| 548 |
_recent = -1; |
| 549 |
int i = sizeof(backends) / sizeof(*backends); |
| 550 |
while (i-- && _uri[uri::scheme] != backends[i].scheme) continue; |
| 551 |
if (i < 0) throw error("invalid scheme"); |
| 552 |
|
| 553 |
uri u(_uri); |
| 554 |
string pw(_passwd); |
| 555 |
if (u[uri::port].empty()) u[uri::port] = backends[i].port; |
| 556 |
if (u[uri::user].empty()) { |
| 557 |
u[uri::user] = "ANONYMOUS"; |
| 558 |
if (pw.empty()) pw = "befoo@"; |
| 559 |
} |
| 560 |
unique_ptr<backend> be(backends[i].make()); |
| 561 |
((*be).*backends[i].stream)(u[uri::host], u[uri::port], _domain, _verify); |
| 562 |
be->login(u, pw); |
| 563 |
_recent = static_cast<int>(be->fetch(*this, u)); |
| 564 |
be->logout(); |
| 565 |
} |