sort out some prototypes, put them where they belong.
[apps/madmutt.git] / imap / imap.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-8 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1996-9 Brandon Long <blong@fiction.net>
5  * Copyright (C) 1999-2005 Brendan Cully <brendan@kublai.com>
6  *
7  * This file is part of mutt-ng, see http://www.muttng.org/.
8  * It's licensed under the GNU General Public License,
9  * please see the file GPL in the top level source directory.
10  */
11
12 /* Support for IMAP4rev1, with the occasional nod to IMAP 4. */
13
14 #if HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #include <unistd.h>
19 #include <ctype.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24
25 #include <lib-lib/lib-lib.h>
26
27 #include "mutt.h"
28 #include "mx.h"
29 #include "globals.h"
30 #include "sort.h"
31 #include "browser.h"
32 #include "message.h"
33 #include "imap_private.h"
34 #if defined(USE_SSL) || defined(USE_GNUTLS)
35 # include <lib-sys/mutt_ssl.h>
36 #endif
37 #include "buffy.h"
38
39 /* imap forward declarations */
40 static int imap_get_delim (IMAP_DATA * idata);
41 static char *imap_get_flags (string_list_t ** hflags, char *s);
42 static int imap_check_acl (IMAP_DATA * idata);
43 static int imap_check_capabilities (IMAP_DATA * idata);
44 static void imap_set_flag (IMAP_DATA * idata, int aclbit, int flag,
45                            const char *str, char *flags, size_t flsize);
46
47 /* imap_access: Check permissions on an IMAP mailbox.
48  * TODO: ACL checks. Right now we assume if it exists we can
49  * mess with it. */
50 int imap_access (const char *path, int flags __attribute__ ((unused)))
51 {
52   IMAP_DATA *idata;
53   IMAP_MBOX mx;
54   char buf[LONG_STRING];
55   char mailbox[LONG_STRING];
56   char mbox[LONG_STRING];
57
58   if (imap_parse_path (path, &mx))
59     return -1;
60
61   if (!(idata = imap_conn_find (&mx.account,
62                                 option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW :
63                                 0))) {
64     p_delete(&mx.mbox);
65     return -1;
66   }
67
68   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
69
70   /* we may already be in the folder we're checking */
71   if (!ascii_strcmp(idata->mailbox, mx.mbox)) {
72     p_delete(&mx.mbox);
73     return 0;
74   }
75
76   p_delete(&mx.mbox);
77   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
78
79   if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
80     snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
81   else if (mutt_bit_isset (idata->capabilities, STATUS))
82     snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
83   else {
84     return -1;
85   }
86
87   if (imap_exec (idata, buf, IMAP_CMD_FAIL_OK) < 0) {
88     return -1;
89   }
90
91   return 0;
92 }
93
94 int imap_create_mailbox (IMAP_DATA * idata, char *mailbox)
95 {
96   char buf[LONG_STRING], mbox[LONG_STRING];
97
98   imap_munge_mbox_name (mbox, sizeof (mbox), mailbox);
99   snprintf (buf, sizeof (buf), "CREATE %s", mbox);
100
101   if (imap_exec (idata, buf, 0) != 0)
102     return -1;
103
104   return 0;
105 }
106
107 int imap_rename_mailbox (IMAP_DATA * idata, IMAP_MBOX * mx,
108                          const char *newname)
109 {
110   char oldmbox[LONG_STRING];
111   char newmbox[LONG_STRING];
112   char buf[LONG_STRING];
113
114   imap_munge_mbox_name (oldmbox, sizeof (oldmbox), mx->mbox);
115   imap_munge_mbox_name (newmbox, sizeof (newmbox), newname);
116
117   snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox);
118
119   if (imap_exec (idata, buf, 0) != 0)
120     return -1;
121
122   return 0;
123 }
124
125 int imap_delete_mailbox (CONTEXT * ctx, IMAP_MBOX mx)
126 {
127   char buf[LONG_STRING], mbox[LONG_STRING];
128   IMAP_DATA *idata;
129
130   if (!ctx || !ctx->data) {
131     if (!(idata = imap_conn_find (&mx.account,
132                                   option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW
133                                   : 0))) {
134       p_delete(&mx.mbox);
135       return -1;
136     }
137   }
138   else {
139     idata = ctx->data;
140   }
141
142   imap_munge_mbox_name (mbox, sizeof (mbox), mx.mbox);
143   snprintf (buf, sizeof (buf), "DELETE %s", mbox);
144
145   if (imap_exec ((IMAP_DATA *) idata, buf, 0) != 0)
146     return -1;
147
148   return 0;
149 }
150
151 /* imap_logout_all: close all open connections. Quick and dirty until we can
152  *   make sure we've got all the context we need. */
153 void imap_logout_all (void)
154 {
155   CONNECTION *conn;
156   CONNECTION *tmp;
157
158   conn = mutt_socket_head ();
159
160   while (conn) {
161     tmp = conn->next;
162
163     if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0) {
164       mutt_message (_("Closing connection to %s..."), conn->account.host);
165       imap_logout ((IMAP_DATA *) conn->data);
166       mutt_clear_error ();
167       mutt_socket_close (conn);
168       mutt_socket_free (conn);
169     }
170
171     conn = tmp;
172   }
173 }
174
175 /* imap_read_literal: read bytes bytes from server into file. Not explicitly
176  *   buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
177  *   Apparently even literals use \r\n-terminated strings ?! */
178 int imap_read_literal (FILE * fp, IMAP_DATA * idata, long bytes, progress_t* bar)
179 {
180   long pos;
181   char c;
182
183   int r = 0;
184
185   for (pos = 0; pos < bytes; pos++) {
186     if (mutt_socket_readchar (idata->conn, &c) != 1) {
187       idata->status = IMAP_FATAL;
188
189       return -1;
190     }
191
192 #if 1
193     if (r == 1 && c != '\n')
194       fputc ('\r', fp);
195
196     if (c == '\r') {
197       r = 1;
198       continue;
199     }
200     else
201       r = 0;
202 #endif
203     fputc (c, fp);
204     if (bar && !(pos % 1024))
205       mutt_progress_bar (bar, pos);
206   }
207
208   return 0;
209 }
210
211 /* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the
212  *   context. Must not be done while something has a handle on any headers
213  *   (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */
214 void imap_expunge_mailbox (IMAP_DATA * idata)
215 {
216   HEADER *h;
217   int i, cacheno;
218
219   for (i = 0; i < idata->ctx->msgcount; i++) {
220     h = idata->ctx->hdrs[i];
221
222     if (h->index == -1) {
223       h->active = 0;
224
225       /* free cached body from disk, if neccessary */
226       cacheno = HEADER_DATA (h)->uid % IMAP_CACHE_LEN;
227       if (idata->cache[cacheno].uid == HEADER_DATA (h)->uid &&
228           idata->cache[cacheno].path) {
229         unlink (idata->cache[cacheno].path);
230         p_delete(&idata->cache[cacheno].path);
231       }
232
233       imap_free_header_data (&h->data);
234     }
235   }
236
237   /* We may be called on to expunge at any time. We can't rely on the caller
238    * to always know to rethread */
239   mx_update_tables (idata->ctx, 0);
240   mutt_sort_headers (idata->ctx, 1);
241 }
242
243 static int imap_get_delim (IMAP_DATA * idata)
244 {
245   char *s;
246   int rc;
247
248   /* assume that the delim is /.  If this fails, we're in bigger trouble
249    * than getting the delim wrong */
250   idata->delim = '/';
251
252   imap_cmd_start (idata, "string_list_t \"\" \"\"");
253
254   do {
255     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
256       break;
257
258     s = imap_next_word (idata->cmd.buf);
259     if (ascii_strncasecmp ("string_list_t", s, 4) == 0) {
260       s = imap_next_word (s);
261       s = imap_next_word (s);
262       if (s && s[0] == '\"' && s[1] && s[2] == '\"')
263         idata->delim = s[1];
264       else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2]
265                && s[3] == '\"')
266         idata->delim = s[2];
267     }
268   }
269   while (rc == IMAP_CMD_CONTINUE);
270
271   return -1;
272 }
273
274 /* get rights for folder, let imap_handle_untagged do the rest */
275 static int imap_check_acl (IMAP_DATA * idata)
276 {
277   char buf[LONG_STRING];
278   char mbox[LONG_STRING];
279
280   imap_munge_mbox_name (mbox, sizeof (mbox), idata->mailbox);
281   snprintf (buf, sizeof (buf), "MYRIGHTS %s", mbox);
282   if (imap_exec (idata, buf, 0) != 0) {
283     imap_error ("imap_check_acl", buf);
284     return -1;
285   }
286   return 0;
287 }
288
289 /* imap_check_capabilities: make sure we can log in to this server. */
290 static int imap_check_capabilities (IMAP_DATA * idata)
291 {
292   if (imap_exec (idata, "CAPABILITY", 0) != 0) {
293     imap_error ("imap_check_capabilities", idata->cmd.buf);
294     return -1;
295   }
296
297   if (!(mutt_bit_isset (idata->capabilities, IMAP4)
298         || mutt_bit_isset (idata->capabilities, IMAP4REV1))) {
299     mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
300
301     mutt_sleep (2);             /* pause a moment to let the user see the error */
302
303     return -1;
304   }
305
306   return 0;
307 }
308
309 /* imap_conn_find: Find an open IMAP connection matching account, or open
310  *   a new one if none can be found. */
311 IMAP_DATA *imap_conn_find (const ACCOUNT * account, int flags)
312 {
313   CONNECTION *conn;
314   IMAP_DATA *idata;
315   ACCOUNT *creds;
316   int new = 0;
317
318   if (!(conn = mutt_conn_find (NULL, account)))
319     return NULL;
320
321   /* if opening a new UNSELECTED connection, preserve existing creds */
322   creds = &(conn->account);
323
324   /* make sure this connection is not in SELECTED state, if neccessary */
325   if (flags & M_IMAP_CONN_NOSELECT)
326     while (conn->data && ((IMAP_DATA *) conn->data)->state == IMAP_SELECTED) {
327       if (!(conn = mutt_conn_find (conn, account)))
328         return NULL;
329       memcpy (&(conn->account), creds, sizeof (ACCOUNT));
330     }
331
332   idata = (IMAP_DATA *) conn->data;
333
334   /* don't open a new connection if one isn't wanted */
335   if (flags & M_IMAP_CONN_NONEW) {
336     if (!idata) {
337       mutt_socket_free (conn);
338       return NULL;
339     }
340     if (idata->state < IMAP_AUTHENTICATED)
341       return NULL;
342   }
343
344   if (!idata) {
345     /* The current connection is a new connection */
346     if (!(idata = imap_new_idata ())) {
347       mutt_socket_free (conn);
348       return NULL;
349     }
350
351     conn->data = idata;
352     idata->conn = conn;
353     new = 1;
354   }
355
356   if (idata->state == IMAP_DISCONNECTED)
357     imap_open_connection (idata);
358   if (idata->state == IMAP_CONNECTED) {
359     if (!imap_authenticate (idata)) {
360       idata->state = IMAP_AUTHENTICATED;
361     }
362     else
363       mutt_account_unsetpass (&idata->conn->account);
364
365     p_delete(&idata->capstr);
366   }
367   if (new && idata->state == IMAP_AUTHENTICATED) {
368     imap_get_delim (idata);
369     if (option (OPTIMAPCHECKSUBSCRIBED)) {
370       mutt_message _("Checking mailbox subscriptions");
371       imap_exec (idata, "LSUB \"\" \"*\"", 0);
372     }
373   }
374
375   return idata;
376 }
377
378 int imap_open_connection (IMAP_DATA * idata)
379 {
380   char buf[LONG_STRING];
381
382   if (mutt_socket_open (idata->conn) < 0)
383     return -1;
384
385   idata->state = IMAP_CONNECTED;
386
387   if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE) {
388     mutt_socket_close (idata->conn);
389     idata->state = IMAP_DISCONNECTED;
390     return -1;
391   }
392
393   if (ascii_strncasecmp ("* OK", idata->cmd.buf, 4) == 0) {
394     /* TODO: Parse new tagged CAPABILITY data (* OK [CAPABILITY...]) */
395     if (imap_check_capabilities (idata))
396       goto bail;
397 #if defined(USE_SSL) || defined(USE_GNUTLS)
398     /* Attempt STARTTLS if available and desired. */
399     if (!idata->conn->ssf && (option(OPTSSLFORCETLS) || 
400                               mutt_bit_isset (idata->capabilities, STARTTLS))) {
401       int rc;
402
403       if (option (OPTSSLFORCETLS))
404         rc = M_YES;
405       else if ((rc = query_quadoption (OPT_SSLSTARTTLS,
406                                        _("Secure connection with TLS?"))) == -1)
407         goto err_close_conn;
408       if (rc == M_YES) {
409         if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
410           goto bail;
411         if (rc != -2) {
412 #if defined (USE_SSL) || defined (USE_GNUTLS)
413           if (mutt_ssl_starttls (idata->conn))
414 #endif
415           {
416             mutt_error (_("Could not negotiate TLS connection"));
417             mutt_sleep (1);
418             goto err_close_conn;
419           }
420           else {
421             /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
422             if (imap_exec (idata, "CAPABILITY", 0))
423               goto bail;
424           }
425         }
426       }
427     }
428
429     if (option(OPTSSLFORCETLS) && ! idata->conn->ssf) {
430       mutt_error _("Encrypted connection unavailable");
431       mutt_sleep (1);
432       goto err_close_conn;
433     }
434 #endif
435   }
436   else if (ascii_strncasecmp ("* PREAUTH", idata->cmd.buf, 9) == 0) {
437     idata->state = IMAP_AUTHENTICATED;
438     if (imap_check_capabilities (idata) != 0)
439       goto bail;
440     p_delete(&idata->capstr);
441   }
442   else {
443     imap_error ("imap_open_connection()", buf);
444     goto bail;
445   }
446
447   return 0;
448
449 err_close_conn:
450   mutt_socket_close (idata->conn);
451   idata->state = IMAP_DISCONNECTED;
452 bail:
453   p_delete(&idata->capstr);
454   return -1;
455 }
456
457 /* imap_get_flags: Make a simple list out of a FLAGS response.
458  *   return stream following FLAGS response */
459 static char *imap_get_flags (string_list_t ** hflags, char *s)
460 {
461   string_list_t *flags;
462   char *flag_word;
463   char ctmp;
464
465   /* sanity-check string */
466   if (ascii_strncasecmp ("FLAGS", s, 5) != 0) {
467     return NULL;
468   }
469   s = vskipspaces(s + 5);
470   if (*s != '(') {
471     return NULL;
472   }
473
474   /* create list, update caller's flags handle */
475   flags = string_item_new();
476   *hflags = flags;
477
478   while (*s && *s != ')') {
479     s = vskipspaces(s + 1);
480     flag_word = s;
481     while (*s && (*s != ')') && !ISSPACE (*s))
482       s++;
483     ctmp = *s;
484     *s = '\0';
485     if (*flag_word)
486       mutt_add_list (flags, flag_word);
487     *s = ctmp;
488   }
489
490   /* note bad flags response */
491   if (*s != ')') {
492     string_list_wipe(hflags);
493
494     return NULL;
495   }
496
497   s++;
498
499   return s;
500 }
501
502 int imap_open_mailbox (CONTEXT * ctx)
503 {
504   CONNECTION *conn;
505   IMAP_DATA *idata;
506   char buf[LONG_STRING];
507   char bufout[LONG_STRING];
508   int count = 0;
509   IMAP_MBOX mx;
510   int rc;
511
512   if (imap_parse_path (ctx->path, &mx)) {
513     mutt_error (_("%s is an invalid IMAP path"), ctx->path);
514     return -1;
515   }
516
517   /* we require a connection which isn't currently in IMAP_SELECTED state */
518   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NOSELECT)))
519     goto fail_noidata;
520   if (idata->state < IMAP_AUTHENTICATED)
521     goto fail;
522
523   conn = idata->conn;
524
525   /* once again the context is new */
526   ctx->data = idata;
527
528   /* Clean up path and replace the one in the ctx */
529   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
530   p_delete(&(idata->mailbox));
531   idata->mailbox = m_strdup(buf);
532   imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
533
534   p_delete(&(ctx->path));
535   ctx->path = m_strdup(buf);
536
537   idata->ctx = ctx;
538
539   /* clear mailbox status */
540   idata->status = 0;
541   memset (idata->rights, 0, (RIGHTSMAX + 7) / 8);
542   idata->newMailCount = 0;
543
544   mutt_message (_("Selecting %s..."), idata->mailbox);
545   imap_munge_mbox_name (buf, sizeof (buf), idata->mailbox);
546   snprintf (bufout, sizeof (bufout), "%s %s",
547             ctx->readonly ? "EXAMINE" : "SELECT", buf);
548
549   idata->state = IMAP_SELECTED;
550
551   imap_cmd_start (idata, bufout);
552
553   do {
554     char *pc;
555
556     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
557       break;
558
559     pc = idata->cmd.buf + 2;
560
561     /* Obtain list of available flags here, may be overridden by a
562      * PERMANENTFLAGS tag in the OK response */
563     if (ascii_strncasecmp ("FLAGS", pc, 5) == 0) {
564       /* don't override PERMANENTFLAGS */
565       if (!idata->flags) {
566         if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
567           goto fail;
568       }
569     }
570     /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
571     else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0) {
572       /* safe to call on NULL */
573       string_list_wipe(&(idata->flags));
574       /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
575       pc += 13;
576       if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
577         goto fail;
578     }
579 #ifdef USE_HCACHE
580     /* save UIDVALIDITY for the header cache */
581     else if (ascii_strncasecmp ("OK [UIDVALIDITY", pc, 14) == 0) {
582       pc += 3;
583       pc = imap_next_word (pc);
584
585       sscanf (pc, "%lu", &(idata->uid_validity));
586     }
587 #endif
588     else {
589       pc = imap_next_word (pc);
590       if (!ascii_strncasecmp ("EXISTS", pc, 6)) {
591         count = idata->newMailCount;
592         idata->newMailCount = 0;
593       }
594     }
595   }
596   while (rc == IMAP_CMD_CONTINUE);
597
598   if (rc == IMAP_CMD_NO) {
599     char *s;
600
601     s = imap_next_word (idata->cmd.buf);        /* skip seq */
602     s = imap_next_word (s);     /* Skip response */
603     mutt_error ("%s", s);
604     mutt_sleep (2);
605     goto fail;
606   }
607
608   if (rc != IMAP_CMD_OK)
609     goto fail;
610
611   /* check for READ-ONLY notification */
612   if (!ascii_strncasecmp
613       (imap_get_qualifier (idata->cmd.buf), "[READ-ONLY]", 11)
614       && !mutt_bit_isset (idata->capabilities, ACL)) {
615     ctx->readonly = 1;
616   }
617
618   if (mutt_bit_isset (idata->capabilities, ACL)) {
619     if (imap_check_acl (idata))
620       goto fail;
621     if (!(mutt_bit_isset (idata->rights, ACL_DELETE) ||
622           mutt_bit_isset (idata->rights, ACL_SEEN) ||
623           mutt_bit_isset (idata->rights, ACL_WRITE) ||
624           mutt_bit_isset (idata->rights, ACL_INSERT)))
625       ctx->readonly = 1;
626   }
627   /* assume we have all rights if ACL is unavailable */
628   else {
629     mutt_bit_set (idata->rights, ACL_LOOKUP);
630     mutt_bit_set (idata->rights, ACL_READ);
631     mutt_bit_set (idata->rights, ACL_SEEN);
632     mutt_bit_set (idata->rights, ACL_WRITE);
633     mutt_bit_set (idata->rights, ACL_INSERT);
634     mutt_bit_set (idata->rights, ACL_POST);
635     mutt_bit_set (idata->rights, ACL_CREATE);
636     mutt_bit_set (idata->rights, ACL_DELETE);
637   }
638
639   ctx->hdrmax = count;
640   ctx->hdrs = p_new(HEADER *, count);
641   ctx->v2r = p_new(int, count);
642   ctx->msgcount = 0;
643   if (count && (imap_read_headers (idata, 0, count - 1) < 0)) {
644     mutt_error _("Error opening mailbox");
645
646     mutt_sleep (1);
647     goto fail;
648   }
649
650   p_delete(&mx.mbox);
651   return 0;
652
653 fail:
654   if (idata->state == IMAP_SELECTED)
655     idata->state = IMAP_AUTHENTICATED;
656 fail_noidata:
657   p_delete(&mx.mbox);
658   return -1;
659 }
660
661 int imap_open_mailbox_append (CONTEXT * ctx)
662 {
663   CONNECTION *conn;
664   IMAP_DATA *idata;
665   char buf[LONG_STRING];
666   char mailbox[LONG_STRING];
667   IMAP_MBOX mx;
668
669   if (imap_parse_path (ctx->path, &mx))
670     return -1;
671
672   /* in APPEND mode, we appear to hijack an existing IMAP connection -
673    * ctx is brand new and mostly empty */
674
675   if (!(idata = imap_conn_find (&(mx.account), 0))) {
676     p_delete(&mx.mbox);
677     return (-1);
678   }
679   conn = idata->conn;
680
681   ctx->magic = M_IMAP;
682   ctx->data = idata;
683
684   imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
685
686   p_delete(&mx.mbox);
687
688   /* really we should also check for W_OK */
689   if (!imap_access (ctx->path, F_OK))
690     return 0;
691
692   snprintf (buf, sizeof (buf), _("Create %s?"), mailbox);
693   if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1)
694     return -1;
695
696   if (imap_create_mailbox (idata, mailbox) < 0)
697     return -1;
698
699   return (0);
700 }
701
702 /* imap_logout: Gracefully log out of server. */
703 void imap_logout (IMAP_DATA * idata)
704 {
705   /* we set status here to let imap_handle_untagged know we _expect_ to
706    * receive a bye response (so it doesn't freak out and close the conn) */
707   idata->status = IMAP_BYE;
708   imap_cmd_start (idata, "LOGOUT");
709   while (imap_cmd_step (idata) == IMAP_CMD_CONTINUE);
710   p_delete(&idata->cmd.buf);
711   p_delete(&idata);
712 }
713
714 /*
715 int imap_close_connection (CONTEXT *ctx)
716 {
717   if (CTX_DATA->status != IMAP_BYE)
718   {
719     mutt_message _("Closing connection to IMAP server...");
720     imap_logout (CTX_DATA);
721     mutt_clear_error ();
722   }
723   mutt_socket_close (CTX_DATA->conn);
724   CTX_DATA->state = IMAP_DISCONNECTED;
725   CTX_DATA->conn->data = NULL;
726   return 0;
727 }
728 */
729
730 /* imap_set_flag: append str to flags if we currently have permission
731  *   according to aclbit */
732 static void imap_set_flag(IMAP_DATA *idata, int aclbit, int flag,
733                           const char *str, char *flags, size_t flsize)
734 {
735     if (mutt_bit_isset(idata->rights, aclbit)) {
736         if (flag)
737             m_strcat(flags, flsize, str);
738     }
739 }
740
741 /* imap_make_msg_set: make an IMAP4rev1 UID message set out of a set of
742  *   headers, given a flag enum to filter on.
743  * Params: idata: IMAP_DATA containing context containing header set
744  *         buf: to write message set into
745  *         buflen: length of buffer
746  *         flag: enum of flag type on which to filter
747  *         changed: include only changed messages in message set
748  * Returns: number of messages in message set (0 if no matches) */
749 int imap_make_msg_set (IMAP_DATA * idata, BUFFER * buf, int flag, int changed)
750 {
751   HEADER **hdrs;                /* sorted local copy */
752   int count = 0;                /* number of messages in message set */
753   int match = 0;                /* whether current message matches flag condition */
754   int setstart = 0;             /* start of current message range */
755   int n;
756   short oldsort;                /* we clobber reverse, must restore it */
757
758   /* assuming 32-bit UIDs */
759   char uid[12];
760   int started = 0;
761
762   /* make copy of header pointers to sort in natural order */
763   hdrs = p_dup(idata->ctx->hdrs, idata->ctx->msgcount);
764
765   if (Sort != SORT_ORDER) {
766     oldsort = Sort;
767     Sort = SORT_ORDER;
768     qsort ((void *) hdrs, idata->ctx->msgcount, sizeof (HEADER *),
769            mutt_get_sort_func (SORT_ORDER));
770     Sort = oldsort;
771   }
772
773   for (n = 0; n < idata->ctx->msgcount; n++) {
774     match = 0;
775     /* don't include pending expunged messages */
776     if (hdrs[n]->active)
777       switch (flag) {
778       case M_DELETE:
779         if (hdrs[n]->deleted)
780           match = 1;
781         break;
782       case M_TAG:
783         if (hdrs[n]->tagged)
784           match = 1;
785         break;
786       }
787
788     if (match && (!changed || hdrs[n]->changed)) {
789       count++;
790       if (setstart == 0) {
791         setstart = HEADER_DATA (hdrs[n])->uid;
792         if (started == 0) {
793           snprintf (uid, sizeof (uid), "%u", HEADER_DATA (hdrs[n])->uid);
794           mutt_buffer_addstr (buf, uid);
795           started = 1;
796         }
797         else {
798           snprintf (uid, sizeof (uid), ",%u", HEADER_DATA (hdrs[n])->uid);
799           mutt_buffer_addstr (buf, uid);
800         }
801       }
802       /* tie up if the last message also matches */
803       else if (n == idata->ctx->msgcount - 1) {
804         snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n])->uid);
805         mutt_buffer_addstr (buf, uid);
806       }
807     }
808     /* this message is not expunged and doesn't match. End current set. */
809     else if (setstart && hdrs[n]->active) {
810       if (HEADER_DATA (hdrs[n - 1])->uid > setstart) {
811         snprintf (uid, sizeof (uid), ":%u", HEADER_DATA (hdrs[n - 1])->uid);
812         mutt_buffer_addstr (buf, uid);
813       }
814       setstart = 0;
815     }
816   }
817
818   p_delete(&hdrs);
819
820   return count;
821 }
822
823 /* Update the IMAP server to reflect the flags a single message.  */
824
825 int imap_sync_message (IMAP_DATA *idata, HEADER *hdr, BUFFER *cmd,
826                        int *err_continue)
827 {
828   char flags[LONG_STRING];
829   char uid[11];
830
831   hdr->changed = 0;
832
833   snprintf (uid, sizeof (uid), "%u", HEADER_DATA(hdr)->uid);
834   cmd->dptr = cmd->data;
835   mutt_buffer_addstr (cmd, "UID STORE ");
836   mutt_buffer_addstr (cmd, uid);
837
838   flags[0] = '\0';
839       
840   imap_set_flag (idata, ACL_SEEN, hdr->read, "\\Seen ",
841                  flags, sizeof (flags));
842   imap_set_flag (idata, ACL_WRITE, hdr->flagged,
843                  "\\Flagged ", flags, sizeof (flags));
844   imap_set_flag (idata, ACL_WRITE, hdr->replied,
845                  "\\Answered ", flags, sizeof (flags));
846   imap_set_flag (idata, ACL_DELETE, hdr->deleted,
847                  "\\Deleted ", flags, sizeof (flags));
848
849   /* now make sure we don't lose custom tags */
850   if (mutt_bit_isset (idata->rights, ACL_WRITE))
851     imap_add_keywords (flags, hdr, idata->flags, sizeof (flags));
852
853   m_strrtrim(flags);
854
855   /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
856    * explicitly revoke all system flags (if we have permission) */
857   if (!*flags)
858   {
859     imap_set_flag (idata, ACL_SEEN, 1, "\\Seen ", flags, sizeof (flags));
860     imap_set_flag (idata, ACL_WRITE, 1, "\\Flagged ", flags, sizeof (flags));
861     imap_set_flag (idata, ACL_WRITE, 1, "\\Answered ", flags, sizeof (flags));
862     imap_set_flag (idata, ACL_DELETE, 1, "\\Deleted ", flags, sizeof (flags));
863
864     m_strrtrim(flags);
865
866     mutt_buffer_addstr (cmd, " -FLAGS.SILENT (");
867   } else
868     mutt_buffer_addstr (cmd, " FLAGS.SILENT (");
869
870   mutt_buffer_addstr (cmd, flags);
871   mutt_buffer_addstr (cmd, ")");
872
873   /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */
874   hdr->active = 0;
875
876   /* after all this it's still possible to have no flags, if you
877    * have no ACL rights */
878   if (*flags && (imap_exec (idata, cmd->data, 0) != 0) &&
879       err_continue && (*err_continue != M_YES))
880   {
881     *err_continue = imap_continue ("imap_sync_message: STORE failed",
882                                    idata->cmd.buf);
883     if (*err_continue != M_YES)
884       return -1;
885   }
886
887   hdr->active = 1;
888   idata->ctx->changed--;
889
890   return 0;
891 }
892
893 /* update the IMAP server to reflect message changes done within mutt.
894  * Arguments
895  *   ctx: the current context
896  *   expunge: 0 or 1 - do expunge? 
897  */
898
899 int imap_sync_mailbox (CONTEXT * ctx, int expunge, int *index_hint)
900 {
901   IMAP_DATA *idata;
902   CONTEXT *appendctx = NULL;
903   BUFFER cmd;
904   int deleted;
905   int n;
906   int err_continue = M_NO;      /* continue on error? */
907   int rc;
908
909   idata = (IMAP_DATA *) ctx->data;
910
911   if (idata->state != IMAP_SELECTED) {
912     return -1;
913   }
914
915   /* This function is only called when the calling code expects the context
916    * to be changed. */
917   imap_allow_reopen (ctx);
918
919   if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0)
920     return rc;
921
922   p_clear(&cmd, 1);
923
924   /* if we are expunging anyway, we can do deleted messages very quickly... */
925   if (expunge && mutt_bit_isset (idata->rights, ACL_DELETE)) {
926     mutt_buffer_addstr (&cmd, "UID STORE ");
927     deleted = imap_make_msg_set (idata, &cmd, M_DELETE, 1);
928
929     /* if we have a message set, then let's delete */
930     if (deleted) {
931       mutt_message (_("Marking %d messages deleted..."), deleted);
932       mutt_buffer_addstr (&cmd, " +FLAGS.SILENT (\\Deleted)");
933       /* mark these messages as unchanged so second pass ignores them. Done
934        * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
935       for (n = 0; n < ctx->msgcount; n++)
936         if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
937           ctx->hdrs[n]->active = 0;
938       if (imap_exec (idata, cmd.data, 0) != 0) {
939         mutt_error (_("Expunge failed"));
940         mutt_sleep (1);
941         rc = -1;
942         goto out;
943       }
944     }
945   }
946
947   /* save status changes */
948   for (n = 0; n < ctx->msgcount; n++) {
949     if (ctx->hdrs[n]->active && ctx->hdrs[n]->changed) {
950
951       mutt_message (_("Saving message status flags... [%d/%d]"), n + 1,
952                     ctx->msgcount);
953
954       /* if the message has been rethreaded or attachments have been deleted
955        * we delete the message and reupload it.
956        * This works better if we're expunging, of course. */
957       if ((ctx->hdrs[n]->env && (ctx->hdrs[n]->env->refs_changed || ctx->hdrs[n]->env->irt_changed)) ||
958           ctx->hdrs[n]->attach_del) {
959         if (!appendctx)
960           appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL);
961         if (appendctx) {
962           _mutt_save_message (ctx->hdrs[n], appendctx, 1, 0, 0);
963         }
964       }
965
966       if (imap_sync_message (idata, ctx->hdrs[n], &cmd, &err_continue) < 0) {
967         rc = -1;
968         goto out;
969       }
970     }
971   }
972   ctx->changed = 0;
973
974   /* We must send an EXPUNGE command if we're not closing. */
975   if (expunge && !(ctx->closing) &&
976       mutt_bit_isset (idata->rights, ACL_DELETE)) {
977     mutt_message _("Expunging messages from server...");
978
979     /* Set expunge bit so we don't get spurious reopened messages */
980     idata->reopen |= IMAP_EXPUNGE_EXPECTED;
981     if (imap_exec (idata, "EXPUNGE", 0) != 0) {
982       imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->cmd.buf);
983       rc = imap_reconnect (ctx);
984       goto out;
985     }
986   }
987
988   if (expunge && ctx->closing) {
989     if (imap_exec (idata, "CLOSE", 0))
990       mutt_error (_("CLOSE failed"));
991     idata->state = IMAP_AUTHENTICATED;
992   }
993
994   rc = 0;
995 out:
996   if (cmd.data)
997     p_delete(&cmd.data);
998   if (appendctx) {
999     mx_fastclose_mailbox (appendctx);
1000     p_delete(&appendctx);
1001   }
1002   return rc;
1003 }
1004
1005 /* imap_close_mailbox: clean up IMAP data in CONTEXT */
1006 void imap_close_mailbox (CONTEXT * ctx)
1007 {
1008   IMAP_DATA *idata;
1009   int i;
1010
1011   idata = (IMAP_DATA *) ctx->data;
1012   /* Check to see if the mailbox is actually open */
1013   if (!idata)
1014     return;
1015
1016   if (ctx == idata->ctx) {
1017     if (idata->state != IMAP_FATAL && idata->state == IMAP_SELECTED) {
1018       /* mx_close_mailbox won't sync if there are no deleted messages
1019       * and the mailbox is unchanged, so we may have to close here */
1020       if (!ctx->deleted && imap_exec (idata, "CLOSE", 0))
1021         mutt_error (_("CLOSE failed"));
1022       idata->state = IMAP_AUTHENTICATED;
1023     }
1024
1025     idata->reopen &= IMAP_REOPEN_ALLOW;
1026     p_delete(&(idata->mailbox));
1027     string_list_wipe(&idata->flags);
1028     idata->ctx = NULL;
1029   }
1030
1031   /* free IMAP part of headers */
1032   for (i = 0; i < ctx->msgcount; i++)
1033     imap_free_header_data (&(ctx->hdrs[i]->data));
1034
1035   for (i = 0; i < IMAP_CACHE_LEN; i++) {
1036     if (idata->cache[i].path) {
1037       unlink (idata->cache[i].path);
1038       p_delete(&idata->cache[i].path);
1039     }
1040   }
1041 }
1042
1043 /* use the NOOP command to poll for new mail
1044  *
1045  * return values:
1046  *      M_REOPENED      mailbox has been externally modified
1047  *      M_NEW_MAIL      new mail has arrived!
1048  *      0               no change
1049  *      -1              error
1050  */
1051 int imap_check_mailbox (CONTEXT * ctx, int *index_hint __attribute__ ((unused)), int force)
1052 {
1053   /* overload keyboard timeout to avoid many mailbox checks in a row.
1054    * Most users don't like having to wait exactly when they press a key. */
1055   IMAP_DATA *idata;
1056   int result = 0;
1057
1058   idata = (IMAP_DATA *) ctx->data;
1059
1060   if ((force || time (NULL) >= idata->lastread + Timeout)
1061       && imap_exec (idata, "NOOP", 0) != 0)
1062     return -1;
1063
1064   /* We call this even when we haven't run NOOP in case we have pending
1065    * changes to process, since we can reopen here. */
1066   imap_cmd_finish (idata);
1067
1068   if (idata->check_status & IMAP_EXPUNGE_PENDING)
1069     result = M_REOPENED;
1070   else if (idata->check_status & IMAP_NEWMAIL_PENDING)
1071     result = M_NEW_MAIL;
1072   else if (idata->check_status & IMAP_FLAGS_PENDING)
1073     result = M_FLAGS;
1074
1075   idata->check_status = 0;
1076
1077   return result;
1078 }
1079
1080 /*
1081  * count messages:
1082  *      new == 1:  recent
1083  *      new == 2:  unseen
1084  *      otherwise: total
1085  * return:
1086  *   0+   number of messages in mailbox
1087  *  -1    error while polling mailboxes
1088  */
1089 int imap_mailbox_check (char *path, int new)
1090 {
1091   CONNECTION *conn;
1092   IMAP_DATA *idata;
1093   char buf[LONG_STRING];
1094   char mbox[LONG_STRING];
1095   char mbox_unquoted[LONG_STRING];
1096   char *s;
1097   int msgcount = 0;
1098   int connflags = 0;
1099   IMAP_MBOX mx;
1100   int rc;
1101
1102   if (imap_parse_path (path, &mx))
1103     return -1;
1104
1105   /* If imap_passive is set, don't open a connection to check for new mail */
1106   if (option (OPTIMAPPASSIVE))
1107     connflags = M_IMAP_CONN_NONEW;
1108
1109   if (!(idata = imap_conn_find (&(mx.account), connflags))) {
1110     p_delete(&mx.mbox);
1111     return -1;
1112   }
1113   conn = idata->conn;
1114
1115   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1116   p_delete(&mx.mbox);
1117
1118   imap_munge_mbox_name (mbox, sizeof (mbox), buf);
1119   m_strcpy(mbox_unquoted, sizeof(mbox_unquoted), buf);
1120
1121   /* The draft IMAP implementor's guide warns againts using the STATUS
1122    * command on a mailbox that you have selected 
1123    */
1124
1125   if (m_strcmp(mbox_unquoted, idata->mailbox) == 0
1126       || (ascii_strcasecmp (mbox_unquoted, "INBOX") == 0
1127           && m_strcasecmp(mbox_unquoted, idata->mailbox) == 0)) {
1128     m_strcpy(buf, sizeof(buf), "NOOP");
1129   }
1130   else if (mutt_bit_isset (idata->capabilities, IMAP4REV1) ||
1131            mutt_bit_isset (idata->capabilities, STATUS)) {
1132     snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox,
1133               new == 1 ? "RECENT" : (new == 2 ? "UNSEEN" : "MESSAGES"));
1134   }
1135   else
1136     /* Server does not support STATUS, and this is not the current mailbox.
1137      * There is no lightweight way to check recent arrivals */
1138     return -1;
1139
1140   imap_cmd_start (idata, buf);
1141
1142   do {
1143     if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
1144       break;
1145
1146     s = imap_next_word (idata->cmd.buf);
1147     if (ascii_strncasecmp ("STATUS", s, 6) == 0) {
1148       s = imap_next_word (s);
1149       /* The mailbox name may or may not be quoted here. We could try to 
1150        * munge the server response and compare with quoted (or vise versa)
1151        * but it is probably more efficient to just strncmp against both. */
1152       if (m_strncmp(mbox_unquoted, s, m_strlen(mbox_unquoted)) == 0
1153           || m_strncmp(mbox, s, m_strlen(mbox)) == 0) {
1154         s = imap_next_word (s);
1155         s = imap_next_word (s);
1156         if (isdigit ((unsigned char) *s)) {
1157           if (*s != '0') {
1158             msgcount = atoi (s);
1159           }
1160         }
1161       }
1162     }
1163   }
1164   while (rc == IMAP_CMD_CONTINUE);
1165
1166   return msgcount;
1167 }
1168
1169 /* returns number of patterns in the search that should be done server-side
1170  * (eg are full-text) */
1171 static int do_search (const pattern_t* search, int allpats)
1172 {
1173   int rc = 0;
1174   const pattern_t* pat;
1175
1176   for (pat = search; pat; pat = pat->next) {
1177     switch (pat->op) {
1178       case M_BODY:
1179       case M_HEADER:
1180       case M_WHOLE_MSG:
1181         if (pat->stringmatch)
1182           rc++;
1183         break;
1184       default:
1185       if (pat->child && do_search (pat->child, 1))
1186         rc++;
1187     }
1188
1189     if (!allpats)
1190       break;
1191   }
1192
1193   return rc;
1194 }
1195
1196 /* convert mutt pattern_t to IMAP SEARCH command containing only elements
1197 * that require full-text search (mutt already has what it needs for most
1198 * match types, and does a better job (eg server doesn't support regexps). */
1199 static int imap_compile_search (const pattern_t* pat, BUFFER* buf)
1200 {
1201   if (! do_search (pat, 0))
1202     return 0;
1203
1204   if (pat->not)
1205     mutt_buffer_addstr (buf, "NOT ");
1206
1207   if (pat->child) {
1208     int clauses;
1209
1210     if ((clauses = do_search (pat->child, 1)) > 0) {
1211       const pattern_t* clause = pat->child;
1212
1213       mutt_buffer_addch (buf, '(');
1214
1215       while (clauses) {
1216         if (do_search (clause, 0)) {
1217           if (pat->op == M_OR && clauses > 1)
1218             mutt_buffer_addstr (buf, "OR ");
1219           clauses--;
1220           if (imap_compile_search (clause, buf) < 0)
1221             return -1;
1222
1223           if (clauses)
1224             mutt_buffer_addch (buf, ' ');
1225           
1226           clause = clause->next;
1227         }
1228       }
1229
1230       mutt_buffer_addch (buf, ')');
1231     }
1232   } else {
1233     char term[STRING];
1234     char *delim;
1235
1236     switch (pat->op) {
1237       case M_HEADER:
1238         mutt_buffer_addstr (buf, "HEADER ");
1239
1240         /* extract header name */
1241         if (! (delim = strchr (pat->str, ':'))) {
1242           mutt_error (_("Header search without header name: %s"), pat->str);
1243           return -1;
1244         }
1245         *delim = '\0';
1246         imap_quote_string (term, sizeof (term), pat->str);
1247         mutt_buffer_addstr (buf, term);
1248         mutt_buffer_addch (buf, ' ');
1249
1250         /* and field */
1251         *delim++ = ':';
1252         delim = vskipspaces(delim);
1253         imap_quote_string (term, sizeof (term), delim);
1254         mutt_buffer_addstr (buf, term);
1255         break;
1256
1257       case M_BODY:
1258         mutt_buffer_addstr (buf, "BODY ");
1259         imap_quote_string (term, sizeof (term), pat->str);
1260         mutt_buffer_addstr (buf, term);
1261         break;
1262
1263       case M_WHOLE_MSG:
1264         mutt_buffer_addstr (buf, "TEXT ");
1265         imap_quote_string (term, sizeof (term), pat->str);
1266         mutt_buffer_addstr (buf, term);
1267       break;
1268     }
1269   }
1270
1271   return 0;
1272 }
1273
1274 int imap_search (CONTEXT* ctx, const pattern_t* pat) {
1275   BUFFER buf;
1276   IMAP_DATA* idata = (IMAP_DATA*)ctx->data;
1277   int i;
1278
1279   for (i = 0; i < ctx->msgcount; i++)
1280     ctx->hdrs[i]->matched = 0;
1281
1282   if (!do_search (pat, 1))
1283     return 0;
1284
1285   p_clear(&buf, 1);
1286   mutt_buffer_addstr (&buf, "UID SEARCH ");
1287   if (imap_compile_search (pat, &buf) < 0) {
1288     p_delete(&buf.data);
1289     return -1;
1290   }
1291   if (imap_exec (idata, buf.data, 0) < 0) {
1292     p_delete(&buf.data);
1293     return -1;
1294   }
1295
1296   p_delete(&buf.data);
1297   return 0;
1298 }
1299
1300 /* all this listing/browsing is a mess. I don't like that name is a pointer
1301  *   into idata->buf (used to be a pointer into the passed in buffer, just
1302  *   as bad), nor do I like the fact that the fetch is done here. This
1303  *   code can't possibly handle non-string_list_t untagged responses properly.
1304  *   FIXME. ?! */
1305 int imap_parse_list_response (IMAP_DATA * idata, char **name, int *noselect,
1306                               int *noinferiors, char *delim)
1307 {
1308   char *s;
1309   long bytes;
1310   int rc;
1311
1312   *name = NULL;
1313
1314   rc = imap_cmd_step (idata);
1315   if (rc == IMAP_CMD_OK)
1316     return 0;
1317   if (rc != IMAP_CMD_CONTINUE)
1318     return -1;
1319
1320   s = imap_next_word (idata->cmd.buf);
1321   if ((ascii_strncasecmp ("string_list_t", s, 4) == 0) ||
1322       (ascii_strncasecmp ("LSUB", s, 4) == 0)) {
1323     *noselect = 0;
1324     *noinferiors = 0;
1325
1326     s = imap_next_word (s);     /* flags */
1327     if (*s == '(') {
1328       char *ep;
1329
1330       s++;
1331       ep = s;
1332       while (*ep && *ep != ')')
1333         ep++;
1334       do {
1335         if (!ascii_strncasecmp (s, "\\NoSelect", 9))
1336           *noselect = 1;
1337         if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
1338           *noinferiors = 1;
1339         /* See draft-gahrns-imap-child-mailbox-?? */
1340         if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
1341           *noinferiors = 1;
1342         if (*s != ')')
1343           s++;
1344         while (*s && *s != '\\' && *s != ')')
1345           s++;
1346       } while (s != ep);
1347     }
1348     else
1349       return 0;
1350     s = imap_next_word (s);     /* delim */
1351     /* Reset the delimiter, this can change */
1352     if (ascii_strncasecmp (s, "NIL", 3)) {
1353       if (s && s[0] == '\"' && s[1] && s[2] == '\"')
1354         *delim = s[1];
1355       else if (s && s[0] == '\"' && s[1] && s[1] == '\\' && s[2]
1356                && s[3] == '\"')
1357         *delim = s[2];
1358     }
1359     s = imap_next_word (s);     /* name */
1360     if (s && *s == '{') {       /* Literal */
1361       if (imap_get_literal_count (idata->cmd.buf, &bytes) < 0)
1362         return -1;
1363       if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1364         return -1;
1365       *name = idata->cmd.buf;
1366     }
1367     else
1368       *name = s;
1369   }
1370
1371   return 0;
1372 }
1373
1374 int imap_subscribe (char *path, int subscribe)
1375 {
1376   CONNECTION *conn;
1377   IMAP_DATA *idata;
1378   char buf[LONG_STRING];
1379   char mbox[LONG_STRING];
1380   char errstr[STRING];
1381   BUFFER err, token;
1382   IMAP_MBOX mx;
1383
1384   if (mx_get_magic (path) != M_IMAP || imap_parse_path (path, &mx) < 0) {
1385     mutt_error (_("Bad mailbox name"));
1386     return -1;
1387   }
1388
1389   if (!(idata = imap_conn_find (&(mx.account), 0)))
1390     goto fail;
1391
1392   conn = idata->conn;
1393
1394   imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
1395
1396   if (option (OPTIMAPCHECKSUBSCRIBED)) {
1397     p_clear(&token, 1);
1398     err.data = errstr;
1399     err.dsize = sizeof (errstr);
1400     snprintf (mbox, sizeof (mbox), "%smailboxes \"%s\"",
1401               subscribe ? "" : "un", path);
1402     mutt_parse_rc_line (mbox, &token, &err);
1403     p_delete(&token.data);
1404   }
1405
1406   if (subscribe)
1407     mutt_message (_("Subscribing to %s..."), buf);
1408   else
1409     mutt_message (_("Unsubscribing to %s..."), buf);
1410   imap_munge_mbox_name (mbox, sizeof (mbox), buf);
1411
1412   snprintf (buf, sizeof (buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mbox);
1413
1414   if (imap_exec (idata, buf, 0) < 0)
1415     goto fail;
1416
1417   p_delete(&mx.mbox);
1418   return 0;
1419
1420 fail:
1421   p_delete(&mx.mbox);
1422   return -1;
1423 }
1424
1425 /* trim dest to the length of the longest prefix it shares with src,
1426  * returning the length of the trimmed string */
1427 static int longest_common_prefix (char *dest, const char* src,
1428                                   int start, ssize_t dlen) {
1429   int pos = start;
1430
1431   while (pos < dlen && dest[pos] && dest[pos] == src[pos])
1432     pos++;
1433   dest[pos] = '\0';
1434
1435   return pos;
1436 }
1437
1438 /* look for IMAP URLs to complete from defined mailboxes. Could be extended
1439  * to complete over open connections and account/folder hooks too. */
1440 static int imap_complete_hosts (char *dest, ssize_t len) {
1441   BUFFY* mailbox;
1442   CONNECTION* conn;
1443   int rc = -1;
1444   int matchlen;
1445   int i = 0;
1446
1447   matchlen = m_strlen(dest);
1448   if (list_empty (Incoming))
1449     return (-1);
1450   for (i = 0; i < Incoming->length; i++) {
1451     mailbox = (BUFFY*) Incoming->data[i];
1452     if (!m_strncmp(dest, mailbox->path, matchlen)) {
1453       if (rc) {
1454         m_strcpy(dest, len, mailbox->path);
1455         rc = 0;
1456       } else
1457         longest_common_prefix (dest, mailbox->path, matchlen, len);
1458     }
1459   }
1460
1461   for (conn = mutt_socket_head (); conn && conn->next; conn = conn->next) {
1462     ciss_url_t url;
1463     char urlstr[LONG_STRING];
1464
1465     if (conn->account.type != M_ACCT_TYPE_IMAP)
1466       continue;
1467
1468     mutt_account_tourl (&conn->account, &url);
1469     /* FIXME: how to handle multiple users on the same host? */
1470     url.user = NULL;
1471     url.path = NULL;
1472     url_ciss_tostring (&url, urlstr, sizeof (urlstr), 0);
1473     if (!m_strncmp(dest, urlstr, matchlen)) {
1474       if (rc) {
1475         m_strcpy(dest, len, urlstr);
1476         rc = 0;
1477       } else
1478         longest_common_prefix (dest, urlstr, matchlen, len);
1479     }
1480   }
1481
1482   return rc;
1483 }
1484
1485 /* imap_complete: given a partial IMAP folder path, return a string which
1486  *   adds as much to the path as is unique */
1487 int imap_complete (char *dest, size_t dlen, char *path) {
1488   CONNECTION *conn;
1489   IMAP_DATA *idata;
1490   char list[LONG_STRING];
1491   char buf[LONG_STRING];
1492   char *list_word = NULL;
1493   int noselect, noinferiors;
1494   char delim;
1495   char completion[LONG_STRING];
1496   int clen, matchlen = 0;
1497   int completions = 0;
1498   IMAP_MBOX mx;
1499
1500   if (imap_parse_path (path, &mx) || !mx.mbox) {
1501     m_strcpy(dest, dlen, path);
1502     return imap_complete_hosts (dest, dlen);
1503   }
1504
1505   /* don't open a new socket just for completion. Instead complete over
1506    * known mailboxes/hooks/etc */
1507   if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW))) {
1508     p_delete(&mx.mbox);
1509     m_strcpy(dest, dlen, path);
1510     return imap_complete_hosts (dest, dlen);
1511   }
1512   conn = idata->conn;
1513
1514   /* reformat path for IMAP list, and append wildcard */
1515   /* don't use INBOX in place of "" */
1516   if (mx.mbox && mx.mbox[0])
1517     imap_fix_path (idata, mx.mbox, list, sizeof (list));
1518   else
1519     list[0] = '\0';
1520
1521   /* fire off command */
1522   snprintf (buf, sizeof (buf), "%s \"\" \"%s%%\"",
1523             option (OPTIMAPLSUB) ? "LSUB" : "string_list_t", list);
1524
1525   imap_cmd_start (idata, buf);
1526
1527   /* and see what the results are */
1528   m_strcpy(completion, sizeof(completion), NONULL(mx.mbox));
1529   do {
1530     if (imap_parse_list_response (idata, &list_word, &noselect, &noinferiors,
1531                                   &delim))
1532       break;
1533
1534     if (list_word) {
1535       /* store unquoted */
1536       imap_unmunge_mbox_name (list_word);
1537
1538       /* if the folder isn't selectable, append delimiter to force browse
1539        * to enter it on second tab. */
1540       if (noselect) {
1541         clen = m_strlen(list_word);
1542         list_word[clen++] = delim;
1543         list_word[clen] = '\0';
1544       }
1545       /* copy in first word */
1546       if (!completions) {
1547         m_strcpy(completion, sizeof(completion), list_word);
1548         matchlen = m_strlen(completion);
1549         completions++;
1550         continue;
1551       }
1552
1553       matchlen = longest_common_prefix (completion, list_word, 0, matchlen);
1554       completions++;
1555     }
1556   }
1557   while (ascii_strncmp (idata->cmd.seq, idata->cmd.buf, SEQLEN));
1558
1559   if (completions) {
1560     /* reformat output */
1561     imap_qualify_path (dest, dlen, &mx, completion);
1562     mutt_pretty_mailbox (dest);
1563
1564     p_delete(&mx.mbox);
1565     return 0;
1566   }
1567
1568   return -1;
1569 }
1570
1571 /* reconnect if connection was lost */
1572 int imap_reconnect (CONTEXT * ctx)
1573 {
1574   IMAP_DATA *imap_data;
1575
1576   if (!ctx)
1577     return (-1);
1578
1579   imap_data = (IMAP_DATA *) ctx->data;
1580
1581   if (imap_data) {
1582     if (imap_data->status == IMAP_CONNECTED)
1583       return -1;
1584   }
1585
1586   if (query_quadoption
1587       (OPT_IMAPRECONNECT,
1588        _("Connection lost. Reconnect to IMAP server?")) != M_YES)
1589     return -1;
1590
1591   mx_open_mailbox (ctx->path, 0, ctx);
1592   return 0;
1593 }