mutt stable branch with some hacks
at jcs 1084 lines 25 kB view raw
1/* 2 * Copyright (C) 1996-1998,2010,2012-2013 Michael R. Elkins <me@mutt.org> 3 * Copyright (C) 1996-1999 Brandon Long <blong@fiction.net> 4 * Copyright (C) 1999-2009,2012 Brendan Cully <brendan@kublai.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 */ 20 21/* general IMAP utility functions */ 22 23#include "config.h" 24 25#include "mutt.h" 26#include "mx.h" /* for MUTT_IMAP */ 27#include "url.h" 28#include "imap_private.h" 29#ifdef USE_HCACHE 30#include "hcache.h" 31#endif 32 33#include <stdlib.h> 34#include <ctype.h> 35 36#include <sys/types.h> 37#include <sys/wait.h> 38#include <signal.h> 39#include <netdb.h> 40#include <netinet/in.h> 41 42#include <errno.h> 43 44/* -- public functions -- */ 45 46/* imap_expand_path: IMAP implementation of mutt_expand_path. Rewrite 47 * an IMAP path in canonical and absolute form. 48 * Inputs: a buffer containing an IMAP path. 49 * Outputs: The buffer is rewritten in place with the canonical IMAP path. 50 * Returns 0 on success, or -1 if imap_parse_path chokes or url_ciss_tobuffer 51 * fails, which it might if there isn't enough room in the buffer. */ 52int imap_expand_path (BUFFER* path) 53{ 54 IMAP_MBOX mx; 55 IMAP_DATA* idata; 56 ciss_url_t url; 57 char fixedpath[LONG_STRING]; 58 int rc; 59 60 if (imap_parse_path (mutt_b2s (path), &mx) < 0) 61 return -1; 62 63 idata = imap_conn_find (&mx.account, MUTT_IMAP_CONN_NONEW); 64 mutt_account_tourl (&mx.account, &url); 65 imap_fix_path (idata, mx.mbox, fixedpath, sizeof (fixedpath)); 66 url.path = fixedpath; 67 68 rc = url_ciss_tobuffer (&url, path, U_DECODE_PASSWD); 69 FREE (&mx.mbox); 70 71 return rc; 72} 73 74#ifdef USE_HCACHE 75 76/* Generates a seqseq of the UIDs in msn_index to persist in the header cache. 77 * 78 * Empty spots are stored as 0. 79 */ 80static void imap_msn_index_to_uid_seqset (BUFFER *b, IMAP_DATA *idata) 81{ 82 int first = 1, state = 0, match = 0; 83 HEADER *cur_header; 84 unsigned int msn, cur_uid = 0, last_uid = 0; 85 unsigned int range_begin = 0, range_end = 0; 86 87 for (msn = 1; msn <= idata->max_msn + 1; msn++) 88 { 89 match = 0; 90 if (msn <= idata->max_msn) 91 { 92 cur_header = idata->msn_index[msn - 1]; 93 cur_uid = cur_header ? HEADER_DATA(cur_header)->uid : 0; 94 if (!state || (cur_uid && (cur_uid - 1 == last_uid))) 95 match = 1; 96 last_uid = cur_uid; 97 } 98 99 if (match) 100 { 101 switch (state) 102 { 103 case 1: /* single: convert to a range */ 104 state = 2; 105 /* fall through */ 106 case 2: /* extend range ending */ 107 range_end = cur_uid; 108 break; 109 default: 110 state = 1; 111 range_begin = cur_uid; 112 break; 113 } 114 } 115 else if (state) 116 { 117 if (first) 118 first = 0; 119 else 120 mutt_buffer_addch (b, ','); 121 122 if (state == 1) 123 mutt_buffer_add_printf (b, "%u", range_begin); 124 else if (state == 2) 125 mutt_buffer_add_printf (b, "%u:%u", range_begin, range_end); 126 127 state = 1; 128 range_begin = cur_uid; 129 } 130 } 131} 132 133static void imap_hcache_namer (const char *path, BUFFER *dest) 134{ 135 mutt_buffer_printf (dest, "%s.hcache", path); 136} 137 138header_cache_t* imap_hcache_open (IMAP_DATA* idata, const char* path) 139{ 140 IMAP_MBOX mx; 141 ciss_url_t url; 142 BUFFER *cachepath = NULL; 143 BUFFER *mbox = NULL; 144 size_t len; 145 header_cache_t *rv = NULL; 146 147 mbox = mutt_buffer_pool_get (); 148 cachepath = mutt_buffer_pool_get (); 149 150 if (path) 151 imap_cachepath (idata, path, mbox); 152 else 153 { 154 if (!idata->ctx || imap_parse_path (idata->ctx->path, &mx) < 0) 155 goto cleanup; 156 157 imap_cachepath (idata, mx.mbox, mbox); 158 FREE (&mx.mbox); 159 } 160 161 if (strstr (mutt_b2s (mbox), "/../") || 162 (strcmp (mutt_b2s (mbox), "..") == 0) || 163 (strncmp(mutt_b2s (mbox), "../", 3) == 0)) 164 goto cleanup; 165 len = mutt_buffer_len (mbox); 166 if ((len > 3) && (strcmp(mutt_b2s (mbox) + len - 3, "/..") == 0)) 167 goto cleanup; 168 169 mutt_account_tourl (&idata->conn->account, &url); 170 url.path = mbox->data; 171 url_ciss_tobuffer (&url, cachepath, U_PATH); 172 173 rv = mutt_hcache_open (HeaderCache, mutt_b2s (cachepath), imap_hcache_namer); 174 175cleanup: 176 mutt_buffer_pool_release (&mbox); 177 mutt_buffer_pool_release (&cachepath); 178 return rv; 179} 180 181void imap_hcache_close (IMAP_DATA* idata) 182{ 183 if (!idata->hcache) 184 return; 185 186 mutt_hcache_close (idata->hcache); 187 idata->hcache = NULL; 188} 189 190HEADER* imap_hcache_get (IMAP_DATA* idata, unsigned int uid) 191{ 192 char key[16]; 193 void *data; 194 unsigned int uv; 195 HEADER* h = NULL; 196 197 if (!idata->hcache) 198 return NULL; 199 200 sprintf (key, "/%u", uid); 201 data = mutt_hcache_fetch (idata->hcache, key, 202 imap_hcache_keylen); 203 if (data) 204 { 205 memcpy (&uv, data, sizeof(unsigned int)); 206 if (uv == idata->uid_validity) 207 h = mutt_hcache_restore ((unsigned char *)data, NULL); 208 else 209 dprint (3, (debugfile, "hcache uidvalidity mismatch: %u", uv)); 210 mutt_hcache_free ((void **)&data); 211 } 212 213 return h; 214} 215 216int imap_hcache_put (IMAP_DATA* idata, HEADER* h) 217{ 218 char key[16]; 219 220 if (!idata->hcache) 221 return -1; 222 223 sprintf (key, "/%u", HEADER_DATA (h)->uid); 224 return mutt_hcache_store (idata->hcache, key, h, idata->uid_validity, 225 imap_hcache_keylen, 0); 226} 227 228int imap_hcache_del (IMAP_DATA* idata, unsigned int uid) 229{ 230 char key[16]; 231 232 if (!idata->hcache) 233 return -1; 234 235 sprintf (key, "/%u", uid); 236 return mutt_hcache_delete (idata->hcache, key, imap_hcache_keylen); 237} 238 239int imap_hcache_store_uid_seqset (IMAP_DATA *idata) 240{ 241 BUFFER *b; 242 int rc; 243 244 if (!idata->hcache) 245 return -1; 246 247 b = mutt_buffer_new (); 248 /* The seqset is likely large. Preallocate to reduce reallocs */ 249 mutt_buffer_increase_size (b, HUGE_STRING); 250 imap_msn_index_to_uid_seqset (b, idata); 251 252 rc = mutt_hcache_store_raw (idata->hcache, "/UIDSEQSET", 253 b->data, mutt_buffer_len (b) + 1, 254 imap_hcache_keylen); 255 dprint (5, (debugfile, "Stored /UIDSEQSET %s\n", b->data)); 256 mutt_buffer_free (&b); 257 return rc; 258} 259 260int imap_hcache_clear_uid_seqset (IMAP_DATA *idata) 261{ 262 if (!idata->hcache) 263 return -1; 264 265 return mutt_hcache_delete (idata->hcache, "/UIDSEQSET", imap_hcache_keylen); 266} 267 268char *imap_hcache_get_uid_seqset (IMAP_DATA *idata) 269{ 270 char *hc_seqset, *seqset; 271 272 if (!idata->hcache) 273 return NULL; 274 275 hc_seqset = mutt_hcache_fetch_raw (idata->hcache, "/UIDSEQSET", 276 imap_hcache_keylen); 277 seqset = safe_strdup (hc_seqset); 278 mutt_hcache_free ((void **)&hc_seqset); 279 dprint (5, (debugfile, "Retrieved /UIDSEQSET %s\n", NONULL (seqset))); 280 281 return seqset; 282} 283#endif 284 285/* imap_parse_path: given an IMAP mailbox name, return host, port 286 * and a path IMAP servers will recognize. 287 * mx.mbox is malloc'd, caller must free it */ 288int imap_parse_path (const char* path, IMAP_MBOX* mx) 289{ 290 static unsigned short ImapPort = 0; 291 static unsigned short ImapsPort = 0; 292 struct servent* service; 293 char tmp[128]; 294 ciss_url_t url; 295 char *c; 296 int n; 297 298 if (!ImapPort) 299 { 300 service = getservbyname ("imap", "tcp"); 301 if (service) 302 ImapPort = ntohs (service->s_port); 303 else 304 ImapPort = IMAP_PORT; 305 dprint (3, (debugfile, "Using default IMAP port %d\n", ImapPort)); 306 } 307 if (!ImapsPort) 308 { 309 service = getservbyname ("imaps", "tcp"); 310 if (service) 311 ImapsPort = ntohs (service->s_port); 312 else 313 ImapsPort = IMAP_SSL_PORT; 314 dprint (3, (debugfile, "Using default IMAPS port %d\n", ImapsPort)); 315 } 316 317 /* Defaults */ 318 memset(&mx->account, 0, sizeof(mx->account)); 319 mx->account.port = ImapPort; 320 mx->account.type = MUTT_ACCT_TYPE_IMAP; 321 322 c = safe_strdup (path); 323 url_parse_ciss (&url, c); 324 if (url.scheme == U_IMAP || url.scheme == U_IMAPS) 325 { 326 if (mutt_account_fromurl (&mx->account, &url) < 0 || !*mx->account.host) 327 { 328 FREE (&c); 329 return -1; 330 } 331 332 mx->mbox = safe_strdup (url.path); 333 334 if (url.scheme == U_IMAPS) 335 mx->account.flags |= MUTT_ACCT_SSL; 336 337 FREE (&c); 338 } 339 /* old PINE-compatibility code */ 340 else 341 { 342 FREE (&c); 343 if (sscanf (path, "{%127[^}]}", tmp) != 1) 344 return -1; 345 346 c = strchr (path, '}'); 347 if (!c) 348 return -1; 349 else 350 /* walk past closing '}' */ 351 mx->mbox = safe_strdup (c+1); 352 353 if ((c = strrchr (tmp, '@'))) 354 { 355 *c = '\0'; 356 strfcpy (mx->account.user, tmp, sizeof (mx->account.user)); 357 strfcpy (tmp, c+1, sizeof (tmp)); 358 mx->account.flags |= MUTT_ACCT_USER; 359 } 360 361 if ((n = sscanf (tmp, "%127[^:/]%127s", mx->account.host, tmp)) < 1) 362 { 363 dprint (1, (debugfile, "imap_parse_path: NULL host in %s\n", path)); 364 FREE (&mx->mbox); 365 return -1; 366 } 367 368 if (n > 1) 369 { 370 if (sscanf (tmp, ":%hu%127s", &(mx->account.port), tmp) >= 1) 371 mx->account.flags |= MUTT_ACCT_PORT; 372 if (sscanf (tmp, "/%s", tmp) == 1) 373 { 374 if (!ascii_strncmp (tmp, "ssl", 3)) 375 mx->account.flags |= MUTT_ACCT_SSL; 376 else 377 { 378 dprint (1, (debugfile, "imap_parse_path: Unknown connection type in %s\n", path)); 379 FREE (&mx->mbox); 380 return -1; 381 } 382 } 383 } 384 } 385 386 if ((mx->account.flags & MUTT_ACCT_SSL) && !(mx->account.flags & MUTT_ACCT_PORT)) 387 mx->account.port = ImapsPort; 388 389 return 0; 390} 391 392/* silly helper for mailbox name string comparisons, because of INBOX */ 393int imap_mxcmp (const char* mx1, const char* mx2) 394{ 395 char* b1; 396 char* b2; 397 int rc; 398 399 if (!mx1 || !*mx1) 400 mx1 = "INBOX"; 401 if (!mx2 || !*mx2) 402 mx2 = "INBOX"; 403 if (!ascii_strcasecmp (mx1, "INBOX") && !ascii_strcasecmp (mx2, "INBOX")) 404 return 0; 405 406 b1 = safe_malloc (strlen (mx1) + 1); 407 b2 = safe_malloc (strlen (mx2) + 1); 408 409 imap_fix_path (NULL, mx1, b1, strlen (mx1) + 1); 410 imap_fix_path (NULL, mx2, b2, strlen (mx2) + 1); 411 412 rc = mutt_strcmp (b1, b2); 413 FREE (&b1); 414 FREE (&b2); 415 416 return rc; 417} 418 419/* imap_pretty_mailbox: called by mutt_pretty_mailbox to make IMAP paths 420 * look nice. */ 421void imap_pretty_mailbox (char* path, size_t pathlen) 422{ 423 IMAP_MBOX home, target; 424 ciss_url_t url; 425 char* delim; 426 int tlen; 427 int hlen = 0; 428 char home_match = 0; 429 430 if (imap_parse_path (path, &target) < 0) 431 return; 432 433 tlen = mutt_strlen (target.mbox); 434 /* check whether we can do '=' substitution */ 435 if (mx_is_imap(Maildir) && !imap_parse_path (Maildir, &home)) 436 { 437 hlen = mutt_strlen (home.mbox); 438 if (tlen && mutt_account_match (&home.account, &target.account) && 439 !mutt_strncmp (home.mbox, target.mbox, hlen)) 440 { 441 if (! hlen) 442 home_match = 1; 443 else if (ImapDelimChars) 444 for (delim = ImapDelimChars; *delim != '\0'; delim++) 445 if (target.mbox[hlen] == *delim) 446 home_match = 1; 447 } 448 FREE (&home.mbox); 449 } 450 451 /* do the '=' substitution */ 452 if (home_match) 453 { 454 *path++ = '='; 455 /* copy remaining path, skipping delimiter */ 456 if (! hlen) 457 hlen = -1; 458 memcpy (path, target.mbox + hlen + 1, tlen - hlen - 1); 459 path[tlen - hlen - 1] = '\0'; 460 } 461 else 462 { 463 mutt_account_tourl (&target.account, &url); 464 url.path = target.mbox; 465 url_ciss_tostring (&url, path, pathlen, 0); 466 } 467 468 FREE (&target.mbox); 469} 470 471/* -- library functions -- */ 472 473/* imap_continue: display a message and ask the user if she wants to 474 * go on. */ 475int imap_continue (const char* msg, const char* resp) 476{ 477 imap_error (msg, resp); 478 return mutt_yesorno (_("Continue?"), 0); 479} 480 481/* imap_error: show an error and abort */ 482void imap_error (const char *where, const char *msg) 483{ 484 mutt_error ("%s [%s]\n", where, msg); 485 mutt_sleep (2); 486} 487 488/* imap_new_idata: Allocate and initialise a new IMAP_DATA structure. */ 489IMAP_DATA* imap_new_idata (void) 490{ 491 IMAP_DATA* idata = safe_calloc (1, sizeof (IMAP_DATA)); 492 493 idata->cmdbuf = mutt_buffer_new (); 494 idata->cmdslots = ImapPipelineDepth + 2; 495 idata->cmds = safe_calloc (idata->cmdslots, sizeof(*idata->cmds)); 496 497 return idata; 498} 499 500/* imap_free_idata: Release and clear storage in an IMAP_DATA structure. */ 501void imap_free_idata (IMAP_DATA** idata) 502{ 503 if (!idata) 504 return; 505 506 FREE (&(*idata)->capstr); 507 mutt_free_list (&(*idata)->flags); 508 imap_mboxcache_free (*idata); 509 mutt_buffer_free(&(*idata)->cmdbuf); 510 FREE (&(*idata)->buf); 511 mutt_bcache_close (&(*idata)->bcache); 512 FREE (&(*idata)->cmds); 513 FREE (idata); /* __FREE_CHECKED__ */ 514} 515 516/* 517 * Fix up the imap path. This is necessary because the rest of mutt 518 * assumes a hierarchy delimiter of '/', which is not necessarily true 519 * in IMAP. Additionally, the filesystem converts multiple hierarchy 520 * delimiters into a single one, ie "///" is equal to "/". IMAP servers 521 * are not required to do this. 522 * Moreover, IMAP servers may dislike the path ending with the delimiter. 523 */ 524char *imap_fix_path (IMAP_DATA *idata, const char *mailbox, char *path, 525 size_t plen) 526{ 527 int i = 0; 528 char delim = '\0'; 529 530 if (idata) 531 delim = idata->delim; 532 533 while (mailbox && *mailbox && i < plen - 1) 534 { 535 if ((ImapDelimChars && strchr(ImapDelimChars, *mailbox)) 536 || (delim && *mailbox == delim)) 537 { 538 /* use connection delimiter if known. Otherwise use user delimiter */ 539 if (!idata) 540 delim = *mailbox; 541 542 while (*mailbox 543 && ((ImapDelimChars && strchr(ImapDelimChars, *mailbox)) 544 || (delim && *mailbox == delim))) 545 mailbox++; 546 path[i] = delim; 547 } 548 else 549 { 550 path[i] = *mailbox; 551 mailbox++; 552 } 553 i++; 554 } 555 if (i && path[--i] != delim) 556 i++; 557 path[i] = '\0'; 558 559 return path; 560} 561 562void imap_cachepath (IMAP_DATA *idata, const char *mailbox, BUFFER *dest) 563{ 564 const char *p = mailbox; 565 566 mutt_buffer_clear (dest); 567 if (!p) 568 return; 569 570 while (*p) 571 { 572 if (*p == idata->delim) 573 { 574 mutt_buffer_addch (dest, '/'); 575 /* simple way to avoid collisions with UIDs */ 576 if (*(p + 1) >= '0' && *(p + 1) <= '9') 577 mutt_buffer_addch (dest, '_'); 578 } 579 else 580 mutt_buffer_addch (dest, *p); 581 p++; 582 } 583} 584 585/* imap_get_literal_count: write number of bytes in an IMAP literal into 586 * bytes, return 0 on success, -1 on failure. */ 587int imap_get_literal_count(const char *buf, unsigned int *bytes) 588{ 589 char *pc; 590 char *pn; 591 592 if (!buf || !(pc = strchr (buf, '{'))) 593 return -1; 594 595 pc++; 596 pn = pc; 597 while (isdigit ((unsigned char) *pc)) 598 pc++; 599 *pc = 0; 600 if (mutt_atoui (pn, bytes) < 0) 601 return -1; 602 603 return 0; 604} 605 606/* imap_get_qualifier: in a tagged response, skip tag and status for 607 * the qualifier message. Used by imap_copy_message for TRYCREATE */ 608char* imap_get_qualifier (char* buf) 609{ 610 char *s = buf; 611 612 /* skip tag */ 613 s = imap_next_word (s); 614 /* skip OK/NO/BAD response */ 615 s = imap_next_word (s); 616 617 return s; 618} 619 620/* imap_next_word: return index into string where next IMAP word begins */ 621char *imap_next_word (char *s) 622{ 623 int quoted = 0; 624 625 while (*s) 626 { 627 if (*s == '\\') 628 { 629 s++; 630 if (*s) 631 s++; 632 continue; 633 } 634 if (*s == '\"') 635 quoted = quoted ? 0 : 1; 636 if (!quoted && ISSPACE (*s)) 637 break; 638 s++; 639 } 640 641 SKIPWS (s); 642 return s; 643} 644 645/* imap_parse_date: date is of the form: DD-MMM-YYYY HH:MM:SS +ZZzz */ 646time_t imap_parse_date (char *s) 647{ 648 struct tm t; 649 time_t tz; 650 651 t.tm_mday = (s[0] == ' '? s[1] - '0' : (s[0] - '0') * 10 + (s[1] - '0')); 652 s += 2; 653 if (*s != '-') 654 return 0; 655 s++; 656 t.tm_mon = mutt_check_month (s); 657 s += 3; 658 if (*s != '-') 659 return 0; 660 s++; 661 t.tm_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0') - 1900; 662 s += 4; 663 if (*s != ' ') 664 return 0; 665 s++; 666 667 /* time */ 668 t.tm_hour = (s[0] - '0') * 10 + (s[1] - '0'); 669 s += 2; 670 if (*s != ':') 671 return 0; 672 s++; 673 t.tm_min = (s[0] - '0') * 10 + (s[1] - '0'); 674 s += 2; 675 if (*s != ':') 676 return 0; 677 s++; 678 t.tm_sec = (s[0] - '0') * 10 + (s[1] - '0'); 679 s += 2; 680 if (*s != ' ') 681 return 0; 682 s++; 683 684 /* timezone */ 685 tz = ((s[1] - '0') * 10 + (s[2] - '0')) * 3600 + 686 ((s[3] - '0') * 10 + (s[4] - '0')) * 60; 687 if (s[0] == '+') 688 tz = -tz; 689 690 return (mutt_mktime (&t, 0) + tz); 691} 692 693/* format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz. 694 * Caller should provide a buffer of IMAP_DATELEN bytes */ 695void imap_make_date (char *buf, time_t timestamp) 696{ 697 struct tm* tm = localtime (&timestamp); 698 time_t tz = mutt_local_tz (timestamp); 699 700 tz /= 60; 701 702 snprintf (buf, IMAP_DATELEN, "%02d-%s-%d %02d:%02d:%02d %+03d%02d", 703 tm->tm_mday, Months[tm->tm_mon], tm->tm_year + 1900, 704 tm->tm_hour, tm->tm_min, tm->tm_sec, 705 (int) tz / 60, (int) abs ((int) tz) % 60); 706} 707 708/* imap_qualify_path: make an absolute IMAP folder target, given IMAP_MBOX 709 * and relative path. */ 710void imap_qualify_path (char *dest, size_t len, IMAP_MBOX *mx, char* path) 711{ 712 ciss_url_t url; 713 714 mutt_account_tourl (&mx->account, &url); 715 url.path = path; 716 717 url_ciss_tostring (&url, dest, len, 0); 718} 719 720void imap_buffer_qualify_path (BUFFER *dest, IMAP_MBOX *mx, char* path) 721{ 722 ciss_url_t url; 723 724 mutt_account_tourl (&mx->account, &url); 725 url.path = path; 726 727 url_ciss_tobuffer (&url, dest, 0); 728} 729 730 731static void _imap_quote_string (char *dest, size_t dlen, const char *src, 732 const char *to_quote) 733{ 734 char *pt; 735 const char *s; 736 737 if (!(dest && dlen && src && to_quote)) 738 return; 739 740 if (dlen < 3) 741 { 742 *dest = 0; 743 return; 744 } 745 746 pt = dest; 747 s = src; 748 749 /* save room for pre/post quote-char and trailing null */ 750 dlen -= 3; 751 752 *pt++ = '"'; 753 for (; *s && dlen; s++) 754 { 755 if (strchr (to_quote, *s)) 756 { 757 if (dlen < 2) 758 break; 759 dlen -= 2; 760 *pt++ = '\\'; 761 *pt++ = *s; 762 } 763 else 764 { 765 *pt++ = *s; 766 dlen--; 767 } 768 } 769 *pt++ = '"'; 770 *pt = 0; 771} 772 773/* imap_quote_string: quote string according to IMAP rules: 774 * surround string with quotes, escape " and \ with \ */ 775void imap_quote_string (char *dest, size_t dlen, const char *src) 776{ 777 _imap_quote_string (dest, dlen, src, "\"\\"); 778} 779 780/* imap_quote_string_and_backquotes: quote string according to IMAP rules: 781 * surround string with quotes, escape " and \ with \. 782 * Additionally, escape backquotes with \ to protect against code injection 783 * when using the resulting string in mutt_parse_rc_line(). 784 */ 785void imap_quote_string_and_backquotes (char *dest, size_t dlen, const char *src) 786{ 787 _imap_quote_string (dest, dlen, src, "\"\\`"); 788} 789 790/* imap_unquote_string: equally stupid unquoting routine */ 791void imap_unquote_string (char *s) 792{ 793 char *d = s; 794 795 if (*s == '\"') 796 s++; 797 else 798 return; 799 800 while (*s) 801 { 802 if (*s == '\"') 803 { 804 *d = '\0'; 805 return; 806 } 807 if (*s == '\\') 808 { 809 s++; 810 } 811 if (*s) 812 { 813 *d = *s; 814 d++; 815 s++; 816 } 817 } 818 *d = '\0'; 819} 820 821 822/* 823 * Quoting and UTF-7 conversion 824 */ 825 826void imap_munge_mbox_name (IMAP_DATA *idata, char *dest, size_t dlen, const char *src) 827{ 828 char *buf; 829 830 buf = safe_strdup (src); 831 imap_utf_encode (idata, &buf); 832 833 imap_quote_string (dest, dlen, buf); 834 835 FREE (&buf); 836} 837 838void imap_unmunge_mbox_name (IMAP_DATA *idata, char *s) 839{ 840 char *buf; 841 842 imap_unquote_string(s); 843 844 buf = safe_strdup (s); 845 if (buf) 846 { 847 imap_utf_decode (idata, &buf); 848 strncpy (s, buf, strlen (s)); 849 } 850 851 FREE (&buf); 852} 853 854/* imap_wordcasecmp: find word a in word list b */ 855int imap_wordcasecmp(const char *a, const char *b) 856{ 857 char tmp[SHORT_STRING]; 858 char *s = (char *)b; 859 int i; 860 861 tmp[SHORT_STRING-1] = 0; 862 for (i=0;i < SHORT_STRING-2;i++,s++) 863 { 864 if (!*s || ISSPACE(*s)) 865 { 866 tmp[i] = 0; 867 break; 868 } 869 tmp[i] = *s; 870 } 871 tmp[i+1] = 0; 872 873 return ascii_strcasecmp(a, tmp); 874} 875 876/* 877 * Imap keepalive: poll the current folder to keep the 878 * connection alive. 879 * 880 */ 881 882static void alrm_handler (int sig) 883{ 884 /* empty */ 885} 886 887void imap_keepalive (void) 888{ 889 CONNECTION *conn; 890 CONTEXT *ctx = NULL; 891 IMAP_DATA *idata; 892 893 conn = mutt_socket_head (); 894 while (conn) 895 { 896 if (conn->account.type == MUTT_ACCT_TYPE_IMAP) 897 { 898 int need_free = 0; 899 900 idata = (IMAP_DATA*) conn->data; 901 902 if (idata->state >= IMAP_AUTHENTICATED 903 && time(NULL) >= idata->lastread + ImapKeepalive) 904 { 905 if (idata->ctx) 906 ctx = idata->ctx; 907 else 908 { 909 ctx = safe_calloc (1, sizeof (CONTEXT)); 910 ctx->data = idata; 911 /* imap_close_mailbox will set ctx->iadata->ctx to NULL, so we can't 912 * rely on the value of iadata->ctx to determine if this placeholder 913 * context needs to be freed. 914 */ 915 need_free = 1; 916 } 917 /* if the imap connection closes during this call, ctx may be invalid 918 * after this point, and thus should not be read. 919 */ 920 imap_check_mailbox (ctx, NULL, 1); 921 if (need_free) 922 FREE (&ctx); 923 } 924 } 925 926 conn = conn->next; 927 } 928} 929 930int imap_wait_keepalive (pid_t pid) 931{ 932 struct sigaction oldalrm; 933 struct sigaction act; 934 sigset_t oldmask; 935 int rc; 936 937 short imap_passive = option (OPTIMAPPASSIVE); 938 939 set_option (OPTIMAPPASSIVE); 940 set_option (OPTKEEPQUIET); 941 942 sigprocmask (SIG_SETMASK, NULL, &oldmask); 943 944 sigemptyset (&act.sa_mask); 945 act.sa_handler = alrm_handler; 946#ifdef SA_INTERRUPT 947 act.sa_flags = SA_INTERRUPT; 948#else 949 act.sa_flags = 0; 950#endif 951 952 sigaction (SIGALRM, &act, &oldalrm); 953 954 alarm (ImapKeepalive); 955 while (waitpid (pid, &rc, 0) < 0 && errno == EINTR) 956 { 957 alarm (0); /* cancel a possibly pending alarm */ 958 imap_keepalive (); 959 alarm (ImapKeepalive); 960 } 961 962 alarm (0); /* cancel a possibly pending alarm */ 963 964 sigaction (SIGALRM, &oldalrm, NULL); 965 sigprocmask (SIG_SETMASK, &oldmask, NULL); 966 967 unset_option (OPTKEEPQUIET); 968 if (!imap_passive) 969 unset_option (OPTIMAPPASSIVE); 970 971 return rc; 972} 973 974/* Allow/disallow re-opening a folder upon expunge. */ 975 976void imap_allow_reopen (CONTEXT *ctx) 977{ 978 if (ctx && ctx->magic == MUTT_IMAP && CTX_DATA->ctx == ctx) 979 CTX_DATA->reopen |= IMAP_REOPEN_ALLOW; 980} 981 982void imap_disallow_reopen (CONTEXT *ctx) 983{ 984 if (ctx && ctx->magic == MUTT_IMAP && CTX_DATA->ctx == ctx) 985 CTX_DATA->reopen &= ~IMAP_REOPEN_ALLOW; 986} 987 988int imap_account_match (const ACCOUNT* a1, const ACCOUNT* a2) 989{ 990 IMAP_DATA* a1_idata = imap_conn_find (a1, MUTT_IMAP_CONN_NONEW); 991 IMAP_DATA* a2_idata = imap_conn_find (a2, MUTT_IMAP_CONN_NONEW); 992 const ACCOUNT* a1_canon = a1_idata == NULL ? a1 : &a1_idata->conn->account; 993 const ACCOUNT* a2_canon = a2_idata == NULL ? a2 : &a2_idata->conn->account; 994 995 return mutt_account_match (a1_canon, a2_canon); 996} 997 998/* Sequence set iteration */ 999 1000SEQSET_ITERATOR *mutt_seqset_iterator_new (const char *seqset) 1001{ 1002 SEQSET_ITERATOR *iter; 1003 1004 if (!seqset || !*seqset) 1005 return NULL; 1006 1007 iter = safe_calloc (1, sizeof(SEQSET_ITERATOR)); 1008 iter->full_seqset = safe_strdup (seqset); 1009 iter->eostr = strchr (iter->full_seqset, '\0'); 1010 iter->substr_cur = iter->substr_end = iter->full_seqset; 1011 1012 return iter; 1013} 1014 1015/* Returns: 0 when the next sequence is generated 1016 * 1 when the iterator is finished 1017 * -1 on error 1018 */ 1019int mutt_seqset_iterator_next (SEQSET_ITERATOR *iter, unsigned int *next) 1020{ 1021 char *range_sep; 1022 1023 if (!iter || !next) 1024 return -1; 1025 1026 if (iter->in_range) 1027 { 1028 if ((iter->down && iter->range_cur == (iter->range_end - 1)) || 1029 (!iter->down && iter->range_cur == (iter->range_end + 1))) 1030 iter->in_range = 0; 1031 } 1032 1033 if (!iter->in_range) 1034 { 1035 iter->substr_cur = iter->substr_end; 1036 if (iter->substr_cur == iter->eostr) 1037 return 1; 1038 1039 while (!*(iter->substr_cur)) 1040 iter->substr_cur++; 1041 iter->substr_end = strchr (iter->substr_cur, ','); 1042 if (!iter->substr_end) 1043 iter->substr_end = iter->eostr; 1044 else 1045 *(iter->substr_end) = '\0'; 1046 1047 range_sep = strchr (iter->substr_cur, ':'); 1048 if (range_sep) 1049 *range_sep++ = '\0'; 1050 1051 if (mutt_atoui (iter->substr_cur, &iter->range_cur)) 1052 return -1; 1053 if (range_sep) 1054 { 1055 if (mutt_atoui (range_sep, &iter->range_end)) 1056 return -1; 1057 } 1058 else 1059 iter->range_end = iter->range_cur; 1060 1061 iter->down = (iter->range_end < iter->range_cur); 1062 iter->in_range = 1; 1063 } 1064 1065 *next = iter->range_cur; 1066 if (iter->down) 1067 iter->range_cur--; 1068 else 1069 iter->range_cur++; 1070 1071 return 0; 1072} 1073 1074void mutt_seqset_iterator_free (SEQSET_ITERATOR **p_iter) 1075{ 1076 SEQSET_ITERATOR *iter; 1077 1078 if (!p_iter || !*p_iter) 1079 return; 1080 1081 iter = *p_iter; 1082 FREE (&iter->full_seqset); 1083 FREE (p_iter); /* __FREE_CHECKED__ */ 1084}