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