]> git.llucax.com Git - software/mutt-debian.git/blob - imap/util.c
Move Mutt with NNTP support to mutt-nntp package
[software/mutt-debian.git] / imap / util.c
1 /*
2  * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org>
3  * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
4  * Copyright (C) 1999-2009 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 M_IMAP */
27 #include "url.h"
28 #include "imap_private.h"
29 #ifdef USE_HCACHE
30 #include "message.h"
31 #include "hcache.h"
32 #endif
33
34 #include <stdlib.h>
35 #include <ctype.h>
36
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <signal.h>
40 #include <netdb.h>
41 #include <netinet/in.h>
42
43 #include <errno.h>
44
45 /* -- public functions -- */
46
47 /* imap_expand_path: IMAP implementation of mutt_expand_path. Rewrite
48  *   an IMAP path in canonical and absolute form.
49  * Inputs: a buffer containing an IMAP path, and the number of bytes in
50  *   that buffer.
51  * Outputs: The buffer is rewritten in place with the canonical IMAP path.
52  * Returns 0 on success, or -1 if imap_parse_path chokes or url_ciss_tostring
53  *   fails, which it might if there isn't enough room in the buffer. */
54 int imap_expand_path (char* path, size_t len)
55 {
56   IMAP_MBOX mx;
57   IMAP_DATA* idata;
58   ciss_url_t url;
59   char fixedpath[LONG_STRING];
60   int rc;
61
62   if (imap_parse_path (path, &mx) < 0)
63     return -1;
64
65   idata = imap_conn_find (&mx.account, M_IMAP_CONN_NONEW);
66   mutt_account_tourl (&mx.account, &url);
67   imap_fix_path (idata, mx.mbox, fixedpath, sizeof (fixedpath));
68   url.path = fixedpath;
69
70   rc = url_ciss_tostring (&url, path, len, U_DECODE_PASSWD);
71   FREE (&mx.mbox);
72
73   return rc;
74 }
75
76 #ifdef USE_HCACHE
77 static int imap_hcache_namer (const char* path, char* dest, size_t dlen)
78 {
79   return snprintf (dest, dlen, "%s.hcache", path);
80 }
81
82 header_cache_t* imap_hcache_open (IMAP_DATA* idata, const char* path)
83 {
84   IMAP_MBOX mx;
85   ciss_url_t url;
86   char cachepath[LONG_STRING];
87   char mbox[LONG_STRING];
88
89   if (path)
90     imap_cachepath (idata, path, mbox, sizeof (mbox));
91   else
92   {
93     if (!idata->ctx || imap_parse_path (idata->ctx->path, &mx) < 0)
94       return NULL;
95
96     imap_cachepath (idata, mx.mbox, mbox, sizeof (mbox));
97     FREE (&mx.mbox);
98   }
99
100   mutt_account_tourl (&idata->conn->account, &url);
101   url.path = mbox;
102   url_ciss_tostring (&url, cachepath, sizeof (cachepath), U_PATH);
103
104   return mutt_hcache_open (HeaderCache, cachepath, imap_hcache_namer);
105 }
106
107 void imap_hcache_close (IMAP_DATA* idata)
108 {
109   if (!idata->hcache)
110     return;
111
112   mutt_hcache_close (idata->hcache);
113   idata->hcache = NULL;
114 }
115
116 HEADER* imap_hcache_get (IMAP_DATA* idata, unsigned int uid)
117 {
118   char key[16];
119   unsigned int* uv;
120   HEADER* h = NULL;
121
122   if (!idata->hcache)
123     return NULL;
124
125   sprintf (key, "/%u", uid);
126   uv = (unsigned int*)mutt_hcache_fetch (idata->hcache, key,
127                                          imap_hcache_keylen);
128   if (uv)
129   {
130     if (*uv == idata->uid_validity)
131       h = mutt_hcache_restore ((unsigned char*)uv, NULL);
132     else
133       dprint (3, (debugfile, "hcache uidvalidity mismatch: %u", *uv));
134     FREE (&uv);
135   }
136
137   return h;
138 }
139
140 int imap_hcache_put (IMAP_DATA* idata, HEADER* h)
141 {
142   char key[16];
143
144   if (!idata->hcache)
145     return -1;
146
147   sprintf (key, "/%u", HEADER_DATA (h)->uid);
148   return mutt_hcache_store (idata->hcache, key, h, idata->uid_validity,
149                             imap_hcache_keylen);
150 }
151
152 int imap_hcache_del (IMAP_DATA* idata, unsigned int uid)
153 {
154   char key[16];
155
156   if (!idata->hcache)
157     return -1;
158
159   sprintf (key, "/%u", uid);
160   return mutt_hcache_delete (idata->hcache, key, imap_hcache_keylen);
161 }
162 #endif
163
164 /* imap_parse_path: given an IMAP mailbox name, return host, port
165  *   and a path IMAP servers will recognise.
166  * mx.mbox is malloc'd, caller must free it */
167 int imap_parse_path (const char* path, IMAP_MBOX* mx)
168 {
169   static unsigned short ImapPort = 0;
170   static unsigned short ImapsPort = 0;
171   struct servent* service;
172   char tmp[128];
173   ciss_url_t url;
174   char *c;
175   int n;
176
177   if (!ImapPort)
178   {
179     service = getservbyname ("imap", "tcp");
180     if (service)
181       ImapPort = ntohs (service->s_port);
182     else
183       ImapPort = IMAP_PORT;
184     dprint (3, (debugfile, "Using default IMAP port %d\n", ImapPort));
185   }
186   if (!ImapsPort)
187   {
188     service = getservbyname ("imaps", "tcp");
189     if (service)
190       ImapsPort = ntohs (service->s_port);
191     else
192       ImapsPort = IMAP_SSL_PORT;
193     dprint (3, (debugfile, "Using default IMAPS port %d\n", ImapsPort));
194   }
195
196   /* Defaults */
197   memset(&mx->account, 0, sizeof(mx->account));
198   mx->account.port = ImapPort;
199   mx->account.type = M_ACCT_TYPE_IMAP;
200
201   c = safe_strdup (path);
202   url_parse_ciss (&url, c);
203   if (url.scheme == U_IMAP || url.scheme == U_IMAPS)
204   {
205     if (mutt_account_fromurl (&mx->account, &url) < 0 || !*mx->account.host)
206     {
207       FREE (&c);
208       return -1;
209     }
210
211     mx->mbox = safe_strdup (url.path);
212
213     if (url.scheme == U_IMAPS)
214       mx->account.flags |= M_ACCT_SSL;
215
216     FREE (&c);
217   }
218   /* old PINE-compatibility code */
219   else
220   {
221     FREE (&c);
222     if (sscanf (path, "{%127[^}]}", tmp) != 1)
223       return -1;
224
225     c = strchr (path, '}');
226     if (!c)
227       return -1;
228     else
229       /* walk past closing '}' */
230       mx->mbox = safe_strdup (c+1);
231
232     if ((c = strrchr (tmp, '@')))
233     {
234       *c = '\0';
235       strfcpy (mx->account.user, tmp, sizeof (mx->account.user));
236       strfcpy (tmp, c+1, sizeof (tmp));
237       mx->account.flags |= M_ACCT_USER;
238     }
239
240     if ((n = sscanf (tmp, "%127[^:/]%127s", mx->account.host, tmp)) < 1)
241     {
242       dprint (1, (debugfile, "imap_parse_path: NULL host in %s\n", path));
243       FREE (&mx->mbox);
244       return -1;
245     }
246
247     if (n > 1) {
248       if (sscanf (tmp, ":%hu%127s", &(mx->account.port), tmp) >= 1)
249         mx->account.flags |= M_ACCT_PORT;
250       if (sscanf (tmp, "/%s", tmp) == 1)
251       {
252         if (!ascii_strncmp (tmp, "ssl", 3))
253           mx->account.flags |= M_ACCT_SSL;
254         else
255         {
256           dprint (1, (debugfile, "imap_parse_path: Unknown connection type in %s\n", path));
257           FREE (&mx->mbox);
258           return -1;
259         }
260       }
261     }
262   }
263
264   if ((mx->account.flags & M_ACCT_SSL) && !(mx->account.flags & M_ACCT_PORT))
265     mx->account.port = ImapsPort;
266
267   return 0;
268 }
269
270 /* silly helper for mailbox name string comparisons, because of INBOX */
271 int imap_mxcmp (const char* mx1, const char* mx2)
272 {
273   char* b1;
274   char* b2;
275   int rc;
276
277   if (!mx1 || !*mx1)
278     mx1 = "INBOX";
279   if (!mx2 || !*mx2)
280     mx2 = "INBOX";
281   if (!ascii_strcasecmp (mx1, "INBOX") && !ascii_strcasecmp (mx2, "INBOX"))
282     return 0;
283
284   b1 = safe_malloc (strlen (mx1) + 1);
285   b2 = safe_malloc (strlen (mx2) + 1);
286
287   imap_fix_path (NULL, mx1, b1, strlen (mx1) + 1);
288   imap_fix_path (NULL, mx2, b2, strlen (mx2) + 1);
289
290   rc = mutt_strcmp (b1, b2);
291   FREE (&b1);
292   FREE (&b2);
293
294   return rc;
295 }
296
297 /* imap_pretty_mailbox: called by mutt_pretty_mailbox to make IMAP paths
298  *   look nice. */
299 void imap_pretty_mailbox (char* path)
300 {
301   IMAP_MBOX home, target;
302   ciss_url_t url;
303   char* delim;
304   int tlen;
305   int hlen = 0;
306   char home_match = 0;
307
308   if (imap_parse_path (path, &target) < 0)
309     return;
310
311   tlen = mutt_strlen (target.mbox);
312   /* check whether we can do '=' substitution */
313   if (mx_is_imap(Maildir) && !imap_parse_path (Maildir, &home))
314   {
315     hlen = mutt_strlen (home.mbox);
316     if (tlen && mutt_account_match (&home.account, &target.account) &&
317         !mutt_strncmp (home.mbox, target.mbox, hlen))
318     {
319       if (! hlen)
320         home_match = 1;
321       else if (ImapDelimChars)
322         for (delim = ImapDelimChars; *delim != '\0'; delim++)
323           if (target.mbox[hlen] == *delim)
324             home_match = 1;
325     }
326     FREE (&home.mbox);
327   }
328
329   /* do the '=' substitution */
330   if (home_match) {
331     *path++ = '=';
332     /* copy remaining path, skipping delimiter */
333     if (! hlen)
334       hlen = -1;
335     memcpy (path, target.mbox + hlen + 1, tlen - hlen - 1);
336     path[tlen - hlen - 1] = '\0';
337   }
338   else
339   {
340     mutt_account_tourl (&target.account, &url);
341     url.path = target.mbox;
342     /* FIXME: That hard-coded constant is bogus. But we need the actual
343      *   size of the buffer from mutt_pretty_mailbox. And these pretty
344      *   operations usually shrink the result. Still... */
345     url_ciss_tostring (&url, path, 1024, 0);
346   }
347
348   FREE (&target.mbox);
349 }
350
351 /* -- library functions -- */
352
353 /* imap_continue: display a message and ask the user if she wants to
354  *   go on. */
355 int imap_continue (const char* msg, const char* resp)
356 {
357   imap_error (msg, resp);
358   return mutt_yesorno (_("Continue?"), 0);
359 }
360
361 /* imap_error: show an error and abort */
362 void imap_error (const char *where, const char *msg)
363 {
364   mutt_error ("%s [%s]\n", where, msg);
365   mutt_sleep (2);
366 }
367
368 /* imap_new_idata: Allocate and initialise a new IMAP_DATA structure.
369  *   Returns NULL on failure (no mem) */
370 IMAP_DATA* imap_new_idata (void)
371 {
372   IMAP_DATA* idata = safe_calloc (1, sizeof (IMAP_DATA));
373
374   if (!idata)
375     return NULL;
376
377   if (!(idata->cmdbuf = mutt_buffer_init (NULL)))
378     FREE (&idata);
379
380   idata->cmdslots = ImapPipelineDepth + 2;
381   if (!(idata->cmds = safe_calloc(idata->cmdslots, sizeof(*idata->cmds))))
382   {
383     mutt_buffer_free(&idata->cmdbuf);
384     FREE (&idata);
385   }
386
387   return idata;
388 }
389
390 /* imap_free_idata: Release and clear storage in an IMAP_DATA structure. */
391 void imap_free_idata (IMAP_DATA** idata)
392 {
393   if (!idata)
394     return;
395
396   FREE (&(*idata)->capstr);
397   mutt_free_list (&(*idata)->flags);
398   imap_mboxcache_free (*idata);
399   mutt_buffer_free(&(*idata)->cmdbuf);
400   FREE (&(*idata)->buf);
401   mutt_bcache_close (&(*idata)->bcache);
402   FREE (&(*idata)->cmds);
403   FREE (idata);         /* __FREE_CHECKED__ */
404 }
405
406 /*
407  * Fix up the imap path.  This is necessary because the rest of mutt
408  * assumes a hierarchy delimiter of '/', which is not necessarily true
409  * in IMAP.  Additionally, the filesystem converts multiple hierarchy
410  * delimiters into a single one, ie "///" is equal to "/".  IMAP servers
411  * are not required to do this.
412  * Moreover, IMAP servers may dislike the path ending with the delimiter.
413  */
414 char *imap_fix_path (IMAP_DATA *idata, const char *mailbox, char *path,
415     size_t plen)
416 {
417   int i = 0;
418   char delim = '\0';
419
420   if (idata)
421     delim = idata->delim;
422
423   while (mailbox && *mailbox && i < plen - 1)
424   {
425     if ((ImapDelimChars && strchr(ImapDelimChars, *mailbox))
426         || (delim && *mailbox == delim))
427     {
428       /* use connection delimiter if known. Otherwise use user delimiter */
429       if (!idata)
430         delim = *mailbox;
431
432       while (*mailbox
433              && ((ImapDelimChars && strchr(ImapDelimChars, *mailbox))
434                  || (delim && *mailbox == delim)))
435         mailbox++;
436       path[i] = delim;
437     }
438     else
439     {
440       path[i] = *mailbox;
441       mailbox++;
442     }
443     i++;
444   }
445   if (i && path[--i] != delim)
446     i++;
447   path[i] = '\0';
448
449   return path;
450 }
451
452 void imap_cachepath(IMAP_DATA* idata, const char* mailbox, char* dest,
453                     size_t dlen)
454 {
455   char* s;
456   const char* p = mailbox;
457
458   for (s = dest; p && *p && dlen; dlen--)
459   {
460     if (*p == idata->delim)
461     {
462       *s = '/';
463       /* simple way to avoid collisions with UIDs */
464       if (*(p + 1) >= '0' && *(p + 1) <= '9')
465       {
466         if (--dlen)
467           *++s = '_';
468       }
469     }
470     else
471       *s = *p;
472     p++;
473     s++;
474   }
475   *s = '\0';
476 }
477
478 /* imap_get_literal_count: write number of bytes in an IMAP literal into
479  *   bytes, return 0 on success, -1 on failure. */
480 int imap_get_literal_count(const char *buf, long *bytes)
481 {
482   char *pc;
483   char *pn;
484
485   if (!buf || !(pc = strchr (buf, '{')))
486     return -1;
487
488   pc++;
489   pn = pc;
490   while (isdigit ((unsigned char) *pc))
491     pc++;
492   *pc = 0;
493   *bytes = atoi(pn);
494
495   return 0;
496 }
497
498 /* imap_get_qualifier: in a tagged response, skip tag and status for
499  *   the qualifier message. Used by imap_copy_message for TRYCREATE */
500 char* imap_get_qualifier (char* buf)
501 {
502   char *s = buf;
503
504   /* skip tag */
505   s = imap_next_word (s);
506   /* skip OK/NO/BAD response */
507   s = imap_next_word (s);
508
509   return s;
510 }
511
512 /* imap_next_word: return index into string where next IMAP word begins */
513 char *imap_next_word (char *s)
514 {
515   int quoted = 0;
516
517   while (*s) {
518     if (*s == '\\') {
519       s++;
520       if (*s)
521         s++;
522       continue;
523     }
524     if (*s == '\"')
525       quoted = quoted ? 0 : 1;
526     if (!quoted && ISSPACE (*s))
527       break;
528     s++;
529   }
530
531   SKIPWS (s);
532   return s;
533 }
534
535 /* imap_parse_date: date is of the form: DD-MMM-YYYY HH:MM:SS +ZZzz */
536 time_t imap_parse_date (char *s)
537 {
538   struct tm t;
539   time_t tz;
540
541   t.tm_mday = (s[0] == ' '? s[1] - '0' : (s[0] - '0') * 10 + (s[1] - '0'));
542   s += 2;
543   if (*s != '-')
544     return 0;
545   s++;
546   t.tm_mon = mutt_check_month (s);
547   s += 3;
548   if (*s != '-')
549     return 0;
550   s++;
551   t.tm_year = (s[0] - '0') * 1000 + (s[1] - '0') * 100 + (s[2] - '0') * 10 + (s[3] - '0') - 1900;
552   s += 4;
553   if (*s != ' ')
554     return 0;
555   s++;
556
557   /* time */
558   t.tm_hour = (s[0] - '0') * 10 + (s[1] - '0');
559   s += 2;
560   if (*s != ':')
561     return 0;
562   s++;
563   t.tm_min = (s[0] - '0') * 10 + (s[1] - '0');
564   s += 2;
565   if (*s != ':')
566     return 0;
567   s++;
568   t.tm_sec = (s[0] - '0') * 10 + (s[1] - '0');
569   s += 2;
570   if (*s != ' ')
571     return 0;
572   s++;
573
574   /* timezone */
575   tz = ((s[1] - '0') * 10 + (s[2] - '0')) * 3600 +
576     ((s[3] - '0') * 10 + (s[4] - '0')) * 60;
577   if (s[0] == '+')
578     tz = -tz;
579
580   return (mutt_mktime (&t, 0) + tz);
581 }
582
583 /* format date in IMAP style: DD-MMM-YYYY HH:MM:SS +ZZzz.
584  * Caller should provide a buffer of IMAP_DATELEN bytes */
585 void imap_make_date (char *buf, time_t timestamp)
586 {
587   struct tm* tm = localtime (&timestamp);
588   time_t tz = mutt_local_tz (timestamp);
589
590   tz /= 60;
591
592   snprintf (buf, IMAP_DATELEN, "%02d-%s-%d %02d:%02d:%02d %+03d%02d",
593       tm->tm_mday, Months[tm->tm_mon], tm->tm_year + 1900,
594       tm->tm_hour, tm->tm_min, tm->tm_sec,
595       (int) tz / 60, (int) abs (tz) % 60);
596 }
597
598 /* imap_qualify_path: make an absolute IMAP folder target, given IMAP_MBOX
599  *   and relative path. */
600 void imap_qualify_path (char *dest, size_t len, IMAP_MBOX *mx, char* path)
601 {
602   ciss_url_t url;
603
604   mutt_account_tourl (&mx->account, &url);
605   url.path = path;
606
607   url_ciss_tostring (&url, dest, len, 0);
608 }
609
610
611 /* imap_quote_string: quote string according to IMAP rules:
612  *   surround string with quotes, escape " and \ with \ */
613 void imap_quote_string (char *dest, size_t dlen, const char *src)
614 {
615   char quote[] = "\"\\", *pt;
616   const char *s;
617
618   pt = dest;
619   s  = src;
620
621   *pt++ = '"';
622   /* save room for trailing quote-char */
623   dlen -= 2;
624
625   for (; *s && dlen; s++)
626   {
627     if (strchr (quote, *s))
628     {
629       dlen -= 2;
630       if (!dlen)
631         break;
632       *pt++ = '\\';
633       *pt++ = *s;
634     }
635     else
636     {
637       *pt++ = *s;
638       dlen--;
639     }
640   }
641   *pt++ = '"';
642   *pt = 0;
643 }
644
645 /* imap_unquote_string: equally stupid unquoting routine */
646 void imap_unquote_string (char *s)
647 {
648   char *d = s;
649
650   if (*s == '\"')
651     s++;
652   else
653     return;
654
655   while (*s)
656   {
657     if (*s == '\"')
658     {
659       *d = '\0';
660       return;
661     }
662     if (*s == '\\')
663     {
664       s++;
665     }
666     if (*s)
667     {
668       *d = *s;
669       d++;
670       s++;
671     }
672   }
673   *d = '\0';
674 }
675
676 /*
677  * Quoting and UTF-7 conversion
678  */
679
680 void imap_munge_mbox_name (char *dest, size_t dlen, const char *src)
681 {
682   char *buf;
683
684   buf = safe_strdup (src);
685   imap_utf7_encode (&buf);
686
687   imap_quote_string (dest, dlen, buf);
688
689   FREE (&buf);
690 }
691
692 void imap_unmunge_mbox_name (char *s)
693 {
694   char *buf;
695
696   imap_unquote_string(s);
697
698   buf = safe_strdup (s);
699   if (buf)
700   {
701     imap_utf7_decode (&buf);
702     strncpy (s, buf, strlen (s));
703   }
704
705   FREE (&buf);
706 }
707
708 /* imap_wordcasecmp: find word a in word list b */
709 int imap_wordcasecmp(const char *a, const char *b)
710 {
711   char tmp[SHORT_STRING];
712   char *s = (char *)b;
713   int i;
714
715   tmp[SHORT_STRING-1] = 0;
716   for(i=0;i < SHORT_STRING-2;i++,s++)
717   {
718     if (!*s || ISSPACE(*s))
719     {
720       tmp[i] = 0;
721       break;
722     }
723     tmp[i] = *s;
724   }
725   tmp[i+1] = 0;
726
727   return ascii_strcasecmp(a, tmp);
728 }
729
730 /*
731  * Imap keepalive: poll the current folder to keep the
732  * connection alive.
733  *
734  */
735
736 static RETSIGTYPE alrm_handler (int sig)
737 {
738   /* empty */
739 }
740
741 void imap_keepalive (void)
742 {
743   CONNECTION *conn;
744   CONTEXT *ctx = NULL;
745   IMAP_DATA *idata;
746
747   conn = mutt_socket_head ();
748   while (conn)
749   {
750     if (conn->account.type == M_ACCT_TYPE_IMAP)
751     {
752       int need_free = 0;
753
754       idata = (IMAP_DATA*) conn->data;
755
756       if (idata->state >= IMAP_AUTHENTICATED
757           && time(NULL) >= idata->lastread + ImapKeepalive)
758       {
759         if (idata->ctx)
760           ctx = idata->ctx;
761         else
762         {
763           ctx = safe_calloc (1, sizeof (CONTEXT));
764           ctx->data = idata;
765           /* imap_close_mailbox will set ctx->iadata->ctx to NULL, so we can't
766            * rely on the value of iadata->ctx to determine if this placeholder
767            * context needs to be freed.
768            */
769           need_free = 1;
770         }
771         /* if the imap connection closes during this call, ctx may be invalid
772          * after this point, and thus should not be read.
773          */
774         imap_check_mailbox (ctx, NULL, 1);
775         if (need_free)
776           FREE (&ctx);
777       }
778     }
779
780     conn = conn->next;
781   }
782 }
783
784 int imap_wait_keepalive (pid_t pid)
785 {
786   struct sigaction oldalrm;
787   struct sigaction act;
788   sigset_t oldmask;
789   int rc;
790
791   short imap_passive = option (OPTIMAPPASSIVE);
792
793   set_option (OPTIMAPPASSIVE);
794   set_option (OPTKEEPQUIET);
795
796   sigprocmask (SIG_SETMASK, NULL, &oldmask);
797
798   sigemptyset (&act.sa_mask);
799   act.sa_handler = alrm_handler;
800 #ifdef SA_INTERRUPT
801   act.sa_flags = SA_INTERRUPT;
802 #else
803   act.sa_flags = 0;
804 #endif
805
806   sigaction (SIGALRM, &act, &oldalrm);
807
808   alarm (ImapKeepalive);
809   while (waitpid (pid, &rc, 0) < 0 && errno == EINTR)
810   {
811     alarm (0); /* cancel a possibly pending alarm */
812     imap_keepalive ();
813     alarm (ImapKeepalive);
814   }
815
816   alarm (0);    /* cancel a possibly pending alarm */
817
818   sigaction (SIGALRM, &oldalrm, NULL);
819   sigprocmask (SIG_SETMASK, &oldmask, NULL);
820
821   unset_option (OPTKEEPQUIET);
822   if (!imap_passive)
823     unset_option (OPTIMAPPASSIVE);
824
825   return rc;
826 }
827
828 /* Allow/disallow re-opening a folder upon expunge. */
829
830 void imap_allow_reopen (CONTEXT *ctx)
831 {
832   if (ctx && ctx->magic == M_IMAP && CTX_DATA->ctx == ctx)
833     CTX_DATA->reopen |= IMAP_REOPEN_ALLOW;
834 }
835
836 void imap_disallow_reopen (CONTEXT *ctx)
837 {
838   if (ctx && ctx->magic == M_IMAP && CTX_DATA->ctx == ctx)
839     CTX_DATA->reopen &= ~IMAP_REOPEN_ALLOW;
840 }
841
842 int imap_account_match (const ACCOUNT* a1, const ACCOUNT* a2)
843 {
844   IMAP_DATA* a1_idata = imap_conn_find (a1, M_IMAP_CONN_NONEW);
845   IMAP_DATA* a2_idata = imap_conn_find (a2, M_IMAP_CONN_NONEW);
846   const ACCOUNT* a1_canon = a1_idata == NULL ? a1 : &a1_idata->conn->account;
847   const ACCOUNT* a2_canon = a2_idata == NULL ? a2 : &a2_idata->conn->account;
848
849   return mutt_account_match (a1_canon, a2_canon);
850 }