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