mutt stable branch with some hacks
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 (×tamp);
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}