mutt stable branch with some hacks
at master 596 lines 13 kB view raw
1/* 2 * Copyright (C) 2000-2003 Vsevolod Volkov <vvv@mutt.org.ua> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 */ 18 19#if HAVE_CONFIG_H 20# include "config.h" 21#endif 22 23#include "mutt.h" 24#include "mx.h" 25#include "url.h" 26#include "pop.h" 27#if defined(USE_SSL) 28# include "mutt_ssl.h" 29#endif 30 31#include <string.h> 32#include <unistd.h> 33#include <ctype.h> 34#include <netdb.h> 35#include <errno.h> 36#include <netinet/in.h> 37 38/* given an POP mailbox name, return host, port, username and password */ 39int pop_parse_path (const char* path, ACCOUNT* acct) 40{ 41 ciss_url_t url; 42 char *c; 43 struct servent *service; 44 45 /* Defaults */ 46 acct->flags = 0; 47 acct->type = MUTT_ACCT_TYPE_POP; 48 acct->port = 0; 49 50 c = safe_strdup (path); 51 url_parse_ciss (&url, c); 52 53 if ((url.scheme != U_POP && url.scheme != U_POPS) || 54 mutt_account_fromurl (acct, &url) < 0) 55 { 56 FREE(&c); 57 mutt_error(_("Invalid POP URL: %s\n"), path); 58 mutt_sleep(1); 59 return -1; 60 } 61 62 if (url.scheme == U_POPS) 63 acct->flags |= MUTT_ACCT_SSL; 64 65 service = getservbyname (url.scheme == U_POP ? "pop3" : "pop3s", "tcp"); 66 if (!acct->port) { 67 if (service) 68 acct->port = ntohs (service->s_port); 69 else 70 acct->port = url.scheme == U_POP ? POP_PORT : POP_SSL_PORT;; 71 } 72 73 FREE (&c); 74 return 0; 75} 76 77/* Copy error message to err_msg buffer */ 78void pop_error (POP_DATA *pop_data, char *msg) 79{ 80 char *t, *c, *c2; 81 82 t = strchr (pop_data->err_msg, '\0'); 83 c = msg; 84 85 if (!mutt_strncmp (msg, "-ERR ", 5)) 86 { 87 c2 = skip_email_wsp(msg + 5); 88 89 if (*c2) 90 c = c2; 91 } 92 93 strfcpy (t, c, sizeof (pop_data->err_msg) - strlen (pop_data->err_msg)); 94 mutt_remove_trailing_ws (pop_data->err_msg); 95} 96 97/* Parse CAPA output */ 98static int fetch_capa (char *line, void *data) 99{ 100 POP_DATA *pop_data = (POP_DATA *)data; 101 char *c; 102 103 if (!ascii_strncasecmp (line, "SASL", 4)) 104 { 105 FREE (&pop_data->auth_list); 106 c = skip_email_wsp(line + 4); 107 pop_data->auth_list = safe_strdup (c); 108 } 109 110 else if (!ascii_strncasecmp (line, "STLS", 4)) 111 pop_data->cmd_stls = 1; 112 113 else if (!ascii_strncasecmp (line, "USER", 4)) 114 pop_data->cmd_user = 1; 115 116 else if (!ascii_strncasecmp (line, "UIDL", 4)) 117 pop_data->cmd_uidl = 1; 118 119 else if (!ascii_strncasecmp (line, "TOP", 3)) 120 pop_data->cmd_top = 1; 121 122 return 0; 123} 124 125/* Fetch list of the authentication mechanisms */ 126static int fetch_auth (char *line, void *data) 127{ 128 POP_DATA *pop_data = (POP_DATA *)data; 129 130 if (!pop_data->auth_list) 131 { 132 pop_data->auth_list = safe_malloc (strlen (line) + 1); 133 *pop_data->auth_list = '\0'; 134 } 135 else 136 { 137 safe_realloc (&pop_data->auth_list, 138 strlen (pop_data->auth_list) + strlen (line) + 2); 139 strcat (pop_data->auth_list, " "); /* __STRCAT_CHECKED__ */ 140 } 141 strcat (pop_data->auth_list, line); /* __STRCAT_CHECKED__ */ 142 143 return 0; 144} 145 146/* 147 * Get capabilities 148 * 0 - successful, 149 * -1 - connection lost, 150 * -2 - execution error. 151*/ 152static int pop_capabilities (POP_DATA *pop_data, int mode) 153{ 154 char buf[LONG_STRING]; 155 156 /* don't check capabilities on reconnect */ 157 if (pop_data->capabilities) 158 return 0; 159 160 /* init capabilities */ 161 if (mode == 0) 162 { 163 pop_data->cmd_capa = 0; 164 pop_data->cmd_stls = 0; 165 pop_data->cmd_user = 0; 166 pop_data->cmd_uidl = 0; 167 pop_data->cmd_top = 0; 168 pop_data->resp_codes = 0; 169 pop_data->expire = 1; 170 pop_data->login_delay = 0; 171 FREE (&pop_data->auth_list); 172 } 173 174 /* Execute CAPA command */ 175 if (mode == 0 || pop_data->cmd_capa) 176 { 177 strfcpy (buf, "CAPA\r\n", sizeof (buf)); 178 switch (pop_fetch_data (pop_data, buf, NULL, fetch_capa, pop_data)) 179 { 180 case 0: 181 { 182 pop_data->cmd_capa = 1; 183 break; 184 } 185 case -1: 186 return -1; 187 } 188 } 189 190 /* CAPA not supported, use defaults */ 191 if (mode == 0 && !pop_data->cmd_capa) 192 { 193 pop_data->cmd_user = 2; 194 pop_data->cmd_uidl = 2; 195 pop_data->cmd_top = 2; 196 197 strfcpy (buf, "AUTH\r\n", sizeof (buf)); 198 if (pop_fetch_data (pop_data, buf, NULL, fetch_auth, pop_data) == -1) 199 return -1; 200 } 201 202 /* Check capabilities */ 203 if (mode == 2) 204 { 205 char *msg = NULL; 206 207 if (!pop_data->expire) 208 msg = _("Unable to leave messages on server."); 209 if (!pop_data->cmd_top) 210 msg = _("Command TOP is not supported by server."); 211 if (!pop_data->cmd_uidl) 212 msg = _("Command UIDL is not supported by server."); 213 if (msg && pop_data->cmd_capa) 214 { 215 mutt_error (msg); 216 return -2; 217 } 218 pop_data->capabilities = 1; 219 } 220 221 return 0; 222} 223 224/* 225 * Open connection 226 * 0 - successful, 227 * -1 - connection lost, 228 * -2 - invalid response. 229*/ 230int pop_connect (POP_DATA *pop_data) 231{ 232 char buf[LONG_STRING]; 233 234 pop_data->status = POP_NONE; 235 if (mutt_socket_open (pop_data->conn) < 0 || 236 mutt_socket_readln (buf, sizeof (buf), pop_data->conn) < 0) 237 { 238 mutt_error (_("Error connecting to server: %s"), pop_data->conn->account.host); 239 return -1; 240 } 241 242 pop_data->status = POP_CONNECTED; 243 244 if (mutt_strncmp (buf, "+OK", 3)) 245 { 246 *pop_data->err_msg = '\0'; 247 pop_error (pop_data, buf); 248 mutt_error ("%s", pop_data->err_msg); 249 return -2; 250 } 251 252 pop_apop_timestamp (pop_data, buf); 253 254 return 0; 255} 256 257/* 258 * Open connection and authenticate 259 * 0 - successful, 260 * -1 - connection lost, 261 * -2 - invalid command or execution error, 262 * -3 - authentication canceled. 263*/ 264int pop_open_connection (POP_DATA *pop_data) 265{ 266 int ret; 267 unsigned int n, size; 268 char buf[LONG_STRING]; 269 270 ret = pop_connect (pop_data); 271 if (ret < 0) 272 { 273 mutt_sleep (2); 274 return ret; 275 } 276 277 ret = pop_capabilities (pop_data, 0); 278 if (ret == -1) 279 goto err_conn; 280 if (ret == -2) 281 { 282 mutt_sleep (2); 283 return -2; 284 } 285 286#if defined(USE_SSL) 287 /* Attempt STLS if available and desired. */ 288 if (!pop_data->conn->ssf && (pop_data->cmd_stls || option(OPTSSLFORCETLS))) 289 { 290 if (option(OPTSSLFORCETLS)) 291 pop_data->use_stls = 2; 292 if (pop_data->use_stls == 0) 293 { 294 ret = query_quadoption (OPT_SSLSTARTTLS, 295 _("Secure connection with TLS?")); 296 if (ret == -1) 297 return -2; 298 pop_data->use_stls = 1; 299 if (ret == MUTT_YES) 300 pop_data->use_stls = 2; 301 } 302 if (pop_data->use_stls == 2) 303 { 304 strfcpy (buf, "STLS\r\n", sizeof (buf)); 305 ret = pop_query (pop_data, buf, sizeof (buf)); 306 if (ret == -1) 307 goto err_conn; 308 if (ret != 0) 309 { 310 mutt_error ("%s", pop_data->err_msg); 311 mutt_sleep (2); 312 } 313 else if (mutt_ssl_starttls (pop_data->conn)) 314 { 315 mutt_error (_("Could not negotiate TLS connection")); 316 mutt_sleep (2); 317 return -2; 318 } 319 else 320 { 321 /* recheck capabilities after STLS completes */ 322 ret = pop_capabilities (pop_data, 1); 323 if (ret == -1) 324 goto err_conn; 325 if (ret == -2) 326 { 327 mutt_sleep (2); 328 return -2; 329 } 330 } 331 } 332 } 333 334 if (option(OPTSSLFORCETLS) && !pop_data->conn->ssf) 335 { 336 mutt_error _("Encrypted connection unavailable"); 337 mutt_sleep (1); 338 return -2; 339 } 340#endif 341 342 ret = pop_authenticate (pop_data); 343 if (ret == -1) 344 goto err_conn; 345 if (ret == -3) 346 mutt_clear_error (); 347 if (ret != 0) 348 return ret; 349 350 /* recheck capabilities after authentication */ 351 ret = pop_capabilities (pop_data, 2); 352 if (ret == -1) 353 goto err_conn; 354 if (ret == -2) 355 { 356 mutt_sleep (2); 357 return -2; 358 } 359 360 /* get total size of mailbox */ 361 strfcpy (buf, "STAT\r\n", sizeof (buf)); 362 ret = pop_query (pop_data, buf, sizeof (buf)); 363 if (ret == -1) 364 goto err_conn; 365 if (ret == -2) 366 { 367 mutt_error ("%s", pop_data->err_msg); 368 mutt_sleep (2); 369 return ret; 370 } 371 372 sscanf (buf, "+OK %u %u", &n, &size); 373 pop_data->size = size; 374 return 0; 375 376err_conn: 377 pop_data->status = POP_DISCONNECTED; 378 mutt_error _("Server closed connection!"); 379 mutt_sleep (2); 380 return -1; 381} 382 383/* logout from POP server */ 384void pop_logout (CONTEXT *ctx) 385{ 386 int ret = 0; 387 char buf[LONG_STRING]; 388 POP_DATA *pop_data = (POP_DATA *)ctx->data; 389 390 if (pop_data->status == POP_CONNECTED) 391 { 392 mutt_message _("Closing connection to POP server..."); 393 394 if (ctx->readonly) 395 { 396 strfcpy (buf, "RSET\r\n", sizeof (buf)); 397 ret = pop_query (pop_data, buf, sizeof (buf)); 398 } 399 400 if (ret != -1) 401 { 402 strfcpy (buf, "QUIT\r\n", sizeof (buf)); 403 pop_query (pop_data, buf, sizeof (buf)); 404 } 405 406 mutt_clear_error (); 407 } 408 409 pop_data->status = POP_DISCONNECTED; 410 return; 411} 412 413/* 414 * Send data from buffer and receive answer to the same buffer 415 * 0 - successful, 416 * -1 - connection lost, 417 * -2 - invalid command or execution error. 418*/ 419int pop_query_d (POP_DATA *pop_data, char *buf, size_t buflen, char *msg) 420{ 421 int dbg = MUTT_SOCK_LOG_CMD; 422 char *c; 423 424 if (pop_data->status != POP_CONNECTED) 425 return -1; 426 427#ifdef DEBUG 428 /* print msg instead of real command */ 429 if (msg) 430 { 431 dbg = MUTT_SOCK_LOG_FULL; 432 dprint (MUTT_SOCK_LOG_CMD, (debugfile, "> %s", msg)); 433 } 434#endif 435 436 mutt_socket_write_d (pop_data->conn, buf, -1, dbg); 437 438 c = strpbrk (buf, " \r\n"); 439 *c = '\0'; 440 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s: ", buf); 441 442 if (mutt_socket_readln (buf, buflen, pop_data->conn) < 0) 443 { 444 pop_data->status = POP_DISCONNECTED; 445 return -1; 446 } 447 if (!mutt_strncmp (buf, "+OK", 3)) 448 return 0; 449 450 pop_error (pop_data, buf); 451 return -2; 452} 453 454/* 455 * This function calls funct(*line, *data) for each received line, 456 * funct(NULL, *data) if rewind(*data) needs, exits when fail or done. 457 * Returned codes: 458 * 0 - successful, 459 * -1 - connection lost, 460 * -2 - invalid command or execution error, 461 * -3 - error in funct(*line, *data) 462 */ 463int pop_fetch_data (POP_DATA *pop_data, char *query, progress_t *progressbar, 464 int (*funct) (char *, void *), void *data) 465{ 466 char buf[LONG_STRING]; 467 char *inbuf; 468 char *p; 469 int ret, chunk = 0; 470 long pos = 0; 471 size_t lenbuf = 0; 472 473 strfcpy (buf, query, sizeof (buf)); 474 ret = pop_query (pop_data, buf, sizeof (buf)); 475 if (ret < 0) 476 return ret; 477 478 inbuf = safe_malloc (sizeof (buf)); 479 480 FOREVER 481 { 482 chunk = mutt_socket_readln_d (buf, sizeof (buf), pop_data->conn, MUTT_SOCK_LOG_HDR); 483 if (chunk < 0) 484 { 485 pop_data->status = POP_DISCONNECTED; 486 ret = -1; 487 break; 488 } 489 490 p = buf; 491 if (!lenbuf && buf[0] == '.') 492 { 493 if (buf[1] != '.') 494 break; 495 p++; 496 } 497 498 strfcpy (inbuf + lenbuf, p, sizeof (buf)); 499 pos += chunk; 500 501 /* cast is safe since we break out of the loop when chunk<=0 */ 502 if ((size_t)chunk >= sizeof (buf)) 503 { 504 lenbuf += strlen (p); 505 } 506 else 507 { 508 if (progressbar) 509 mutt_progress_update (progressbar, pos, -1); 510 if (ret == 0 && funct (inbuf, data) < 0) 511 ret = -3; 512 lenbuf = 0; 513 } 514 515 safe_realloc (&inbuf, lenbuf + sizeof (buf)); 516 } 517 518 FREE (&inbuf); 519 return ret; 520} 521 522/* find message with this UIDL and set refno */ 523static int check_uidl (char *line, void *data) 524{ 525 int i; 526 unsigned int index; 527 CONTEXT *ctx = (CONTEXT *)data; 528 char *endp; 529 530 errno = 0; 531 index = strtoul(line, &endp, 10); 532 if (errno) 533 return -1; 534 while (*endp == ' ') 535 endp++; 536 memmove(line, endp, strlen(endp) + 1); 537 538 for (i = 0; i < ctx->msgcount; i++) 539 { 540 if (!mutt_strcmp (ctx->hdrs[i]->data, line)) 541 { 542 ctx->hdrs[i]->refno = index; 543 break; 544 } 545 } 546 547 return 0; 548} 549 550/* reconnect and verify idnexes if connection was lost */ 551int pop_reconnect (CONTEXT *ctx) 552{ 553 int ret; 554 POP_DATA *pop_data = (POP_DATA *)ctx->data; 555 progress_t progressbar; 556 557 if (pop_data->status == POP_CONNECTED) 558 return 0; 559 if (pop_data->status == POP_BYE) 560 return -1; 561 562 FOREVER 563 { 564 mutt_socket_close (pop_data->conn); 565 566 ret = pop_open_connection (pop_data); 567 if (ret == 0) 568 { 569 int i; 570 571 mutt_progress_init (&progressbar, _("Verifying message indexes..."), 572 MUTT_PROGRESS_SIZE, NetInc, 0); 573 574 for (i = 0; i < ctx->msgcount; i++) 575 ctx->hdrs[i]->refno = -1; 576 577 ret = pop_fetch_data (pop_data, "UIDL\r\n", &progressbar, check_uidl, ctx); 578 if (ret == -2) 579 { 580 mutt_error ("%s", pop_data->err_msg); 581 mutt_sleep (2); 582 } 583 } 584 if (ret == 0) 585 return 0; 586 587 pop_logout (ctx); 588 589 if (ret < -1) 590 return -1; 591 592 if (query_quadoption (OPT_POPRECONNECT, 593 _("Connection lost. Reconnect to POP server?")) != MUTT_YES) 594 return -1; 595 } 596}