· 9 years ago · Apr 05, 2016, 04:03 AM
1diff -rbuN mutt-1.6.0-orig/buffy.c mutt-1.6.0/buffy.c
2--- mutt-1.6.0-orig/buffy.c 2016-04-02 14:12:22.000000000 -0400
3+++ mutt-1.6.0/buffy.c 2016-04-04 23:42:10.890620195 -0400
4@@ -161,6 +161,49 @@
5 }
6 }
7
8+static int buffy_compare_name(const void *a, const void *b) {
9+ const BUFFY *b1 = * (BUFFY * const *) a;
10+ const BUFFY *b2 = * (BUFFY * const *) b;
11+
12+ return mutt_strcoll(b1->path, b2->path);
13+}
14+
15+static BUFFY *buffy_sort(BUFFY *b)
16+{
17+ BUFFY *tmp = b;
18+ int buffycount = 0;
19+ BUFFY **ary;
20+ int i;
21+
22+ if (!option(OPTSIDEBARSORT))
23+ return b;
24+
25+ for (; tmp != NULL; tmp = tmp->next)
26+ buffycount++;
27+
28+ ary = (BUFFY **) safe_calloc(buffycount, sizeof (*ary));
29+
30+ tmp = b;
31+ for (i = 0; tmp != NULL; tmp = tmp->next, i++) {
32+ ary[i] = tmp;
33+ }
34+
35+ qsort(ary, buffycount, sizeof(*ary), buffy_compare_name);
36+
37+ for (i = 0; i < buffycount - 1; i++) {
38+ ary[i]->next = ary[i+1];
39+ }
40+ ary[buffycount - 1]->next = NULL;
41+ for (i = 1; i < buffycount; i++) {
42+ ary[i]->prev = ary[i-1];
43+ }
44+ ary[0]->prev = NULL;
45+
46+ tmp = ary[0];
47+ free(ary);
48+ return tmp;
49+}
50+
51 BUFFY *mutt_find_mailbox (const char *path)
52 {
53 BUFFY *tmp = NULL;
54@@ -196,9 +239,13 @@
55 static BUFFY *buffy_new (const char *path)
56 {
57 BUFFY* buffy;
58+ char rp[PATH_MAX];
59+ char *r;
60
61 buffy = (BUFFY *) safe_calloc (1, sizeof (BUFFY));
62 strfcpy (buffy->path, path, sizeof (buffy->path));
63+ r = realpath(path, rp);
64+ strfcpy (buffy->realpath, r ? rp : path, sizeof (buffy->realpath));
65 buffy->next = NULL;
66 buffy->magic = 0;
67
68@@ -243,8 +290,8 @@
69 p = realpath (buf, f1);
70 for (tmp = &Incoming; *tmp; tmp = &((*tmp)->next))
71 {
72- q = realpath ((*tmp)->path, f2);
73- if (mutt_strcmp (p ? p : buf, q ? q : (*tmp)->path) == 0)
74+ q = (*tmp)->realpath;
75+ if (mutt_strcmp (p ? p : buf, q) == 0)
76 {
77 dprint(3,(debugfile,"mailbox '%s' already registered as '%s'\n", buf, (*tmp)->path));
78 break;
79@@ -282,6 +329,7 @@
80 else
81 (*tmp)->size = 0;
82 }
83+ Incoming = buffy_sort(Incoming);
84 return 0;
85 }
86
87@@ -306,6 +354,11 @@
88 return 0;
89 }
90
91+ if (option(OPTSIDEBAR) && mailbox->msg_unread > 0) {
92+ mailbox->new = 1;
93+ return 1;
94+ }
95+
96 if ((dirp = opendir (path)) == NULL)
97 {
98 mailbox->magic = 0;
99@@ -357,6 +410,73 @@
100
101 return 0;
102 }
103+
104+ /* update message counts for the sidebar */
105+void buffy_maildir_update (BUFFY* mailbox)
106+{
107+ char path[_POSIX_PATH_MAX];
108+ DIR *dirp;
109+ struct dirent *de;
110+ char *p;
111+
112+ if(!option(OPTSIDEBAR))
113+ return;
114+
115+ mailbox->msgcount = 0;
116+ mailbox->msg_unread = 0;
117+ mailbox->msg_flagged = 0;
118+
119+ snprintf (path, sizeof (path), "%s/new", mailbox->path);
120+
121+ if ((dirp = opendir (path)) == NULL)
122+ {
123+ mailbox->magic = 0;
124+ return;
125+ }
126+
127+ while ((de = readdir (dirp)) != NULL)
128+ {
129+ if (*de->d_name == '.')
130+ continue;
131+
132+ if (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T')) {
133+ mailbox->new = 1;
134+ mailbox->msgcount++;
135+ mailbox->msg_unread++;
136+ }
137+ }
138+
139+ closedir (dirp);
140+ snprintf (path, sizeof (path), "%s/cur", mailbox->path);
141+
142+ if ((dirp = opendir (path)) == NULL)
143+ {
144+ mailbox->magic = 0;
145+ return;
146+ }
147+
148+ while ((de = readdir (dirp)) != NULL)
149+ {
150+ if (*de->d_name == '.')
151+ continue;
152+
153+ if (!(p = strstr (de->d_name, ":2,")) || !strchr (p + 3, 'T')) {
154+ mailbox->msgcount++;
155+ if ((p = strstr (de->d_name, ":2,"))) {
156+ if (!strchr (p + 3, 'T')) {
157+ if (!strchr (p + 3, 'S'))
158+ mailbox->msg_unread++;
159+ if (strchr(p + 3, 'F'))
160+ mailbox->msg_flagged++;
161+ }
162+ }
163+ }
164+ }
165+
166+ mailbox->sb_last_checked = time(NULL);
167+ closedir (dirp);
168+}
169+
170 /* returns 1 if mailbox has new mail */
171 static int buffy_mbox_hasnew (BUFFY* mailbox, struct stat *sb)
172 {
173@@ -368,7 +488,7 @@
174 else
175 statcheck = sb->st_mtime > sb->st_atime
176 || (mailbox->newly_created && sb->st_ctime == sb->st_mtime && sb->st_ctime == sb->st_atime);
177- if (statcheck)
178+ if ((!option(OPTSIDEBAR) && statcheck) || (option(OPTSIDEBAR) && mailbox->msg_unread > 0))
179 {
180 if (!option(OPTMAILCHECKRECENT) || sb->st_mtime > mailbox->last_visited)
181 {
182@@ -388,6 +508,27 @@
183 return rc;
184 }
185
186+/* update message counts for the sidebar */
187+void buffy_mbox_update (BUFFY* mailbox, struct stat *sb)
188+{
189+ CONTEXT *ctx = NULL;
190+
191+ if(!option(OPTSIDEBAR))
192+ return;
193+ if(mailbox->sb_last_checked > sb->st_mtime && mailbox->msgcount != 0)
194+ return; /* no check necessary */
195+
196+ ctx = mx_open_mailbox(mailbox->path, M_READONLY | M_QUIET | M_NOSORT | M_PEEK, NULL);
197+ if(ctx)
198+ {
199+ mailbox->msgcount = ctx->msgcount;
200+ mailbox->msg_unread = ctx->unread;
201+ mailbox->msg_flagged = ctx->flagged;
202+ mailbox->sb_last_checked = time(NULL);
203+ mx_close_mailbox(ctx, 0);
204+ }
205+}
206+
207 int mutt_buffy_check (int force)
208 {
209 BUFFY *tmp;
210@@ -461,16 +602,19 @@
211 {
212 case M_MBOX:
213 case M_MMDF:
214+ buffy_mbox_update (tmp, &sb);
215 if (buffy_mbox_hasnew (tmp, &sb) > 0)
216 BuffyCount++;
217 break;
218
219 case M_MAILDIR:
220+ buffy_maildir_update (tmp);
221 if (buffy_maildir_hasnew (tmp) > 0)
222 BuffyCount++;
223 break;
224
225 case M_MH:
226+ mh_buffy_update (tmp->path, &tmp->msgcount, &tmp->msg_unread, &tmp->msg_flagged, &tmp->sb_last_checked);
227 mh_buffy(tmp);
228 if (tmp->new)
229 BuffyCount++;
230diff -rbuN mutt-1.6.0-orig/buffy.h mutt-1.6.0/buffy.h
231--- mutt-1.6.0-orig/buffy.h 2016-04-02 14:12:22.000000000 -0400
232+++ mutt-1.6.0/buffy.h 2016-04-04 23:42:10.890620195 -0400
233@@ -23,13 +23,19 @@
234 typedef struct buffy_t
235 {
236 char path[_POSIX_PATH_MAX];
237+ char realpath[_POSIX_PATH_MAX];
238 off_t size;
239 struct buffy_t *next;
240+ struct buffy_t *prev;
241 short new; /* mailbox has new mail */
242+ int msgcount; /* total number of messages */
243+ int msg_unread; /* number of unread messages */
244+ int msg_flagged; /* number of flagged messages */
245 short notified; /* user has been notified */
246 short magic; /* mailbox type */
247 short newly_created; /* mbox or mmdf just popped into existence */
248 time_t last_visited; /* time of last exit from this mailbox */
249+ time_t sb_last_checked; /* time of last buffy check from sidebar */
250 }
251 BUFFY;
252
253diff -rbuN mutt-1.6.0-orig/color.c mutt-1.6.0/color.c
254--- mutt-1.6.0-orig/color.c 2016-04-02 14:12:22.000000000 -0400
255+++ mutt-1.6.0/color.c 2016-04-04 23:42:10.890620195 -0400
256@@ -94,6 +94,8 @@
257 { "underline", MT_COLOR_UNDERLINE },
258 { "index", MT_COLOR_INDEX },
259 { "prompt", MT_COLOR_PROMPT },
260+ { "sidebar_new", MT_COLOR_NEW },
261+ { "sidebar_flagged", MT_COLOR_FLAGGED },
262 { NULL, 0 }
263 };
264
265diff -rbuN mutt-1.6.0-orig/compose.c mutt-1.6.0/compose.c
266--- mutt-1.6.0-orig/compose.c 2016-04-02 14:12:22.000000000 -0400
267+++ mutt-1.6.0/compose.c 2016-04-04 23:42:10.890620195 -0400
268@@ -72,7 +72,7 @@
269
270 #define HDR_XOFFSET 10
271 #define TITLE_FMT "%10s" /* Used for Prompts, which are ASCII */
272-#define W (COLS - HDR_XOFFSET)
273+#define W (COLS - HDR_XOFFSET - SidebarWidth)
274
275 static const char * const Prompts[] =
276 {
277@@ -110,7 +110,7 @@
278
279 static void redraw_crypt_lines (HEADER *msg)
280 {
281- mvaddstr (HDR_CRYPT, 0, "Security: ");
282+ mvaddstr (HDR_CRYPT, SidebarWidth, "Security: ");
283
284 if ((WithCrypto & (APPLICATION_PGP | APPLICATION_SMIME)) == 0)
285 {
286@@ -145,7 +145,7 @@
287 addstr (_(" (OppEnc mode)"));
288
289 clrtoeol ();
290- move (HDR_CRYPTINFO, 0);
291+ move (HDR_CRYPTINFO, SidebarWidth);
292 clrtoeol ();
293
294 if ((WithCrypto & APPLICATION_PGP)
295@@ -162,7 +162,7 @@
296 && (msg->security & ENCRYPT)
297 && SmimeCryptAlg
298 && *SmimeCryptAlg) {
299- mvprintw (HDR_CRYPTINFO, 40, "%s%s", _("Encrypt with: "),
300+ mvprintw (HDR_CRYPTINFO, SidebarWidth + 40, "%s%s", _("Encrypt with: "),
301 NONULL(SmimeCryptAlg));
302 }
303 }
304@@ -175,7 +175,7 @@
305 int c;
306 char *t;
307
308- mvaddstr (HDR_MIX, 0, " Mix: ");
309+ mvaddstr (HDR_MIX, SidebarWidth, " Mix: ");
310
311 if (!chain)
312 {
313@@ -190,7 +190,7 @@
314 if (t && t[0] == '0' && t[1] == '\0')
315 t = "<random>";
316
317- if (c + mutt_strlen (t) + 2 >= COLS)
318+ if (c + mutt_strlen (t) + 2 >= COLS - SidebarWidth)
319 break;
320
321 addstr (NONULL(t));
322@@ -242,7 +242,7 @@
323
324 buf[0] = 0;
325 rfc822_write_address (buf, sizeof (buf), addr, 1);
326- mvprintw (line, 0, TITLE_FMT, Prompts[line - 1]);
327+ mvprintw (line, SidebarWidth, TITLE_FMT, Prompts[line - 1]);
328 mutt_paddstr (W, buf);
329 }
330
331@@ -252,10 +252,10 @@
332 draw_envelope_addr (HDR_TO, msg->env->to);
333 draw_envelope_addr (HDR_CC, msg->env->cc);
334 draw_envelope_addr (HDR_BCC, msg->env->bcc);
335- mvprintw (HDR_SUBJECT, 0, TITLE_FMT, Prompts[HDR_SUBJECT - 1]);
336+ mvprintw (HDR_SUBJECT, SidebarWidth, TITLE_FMT, Prompts[HDR_SUBJECT - 1]);
337 mutt_paddstr (W, NONULL (msg->env->subject));
338 draw_envelope_addr (HDR_REPLYTO, msg->env->reply_to);
339- mvprintw (HDR_FCC, 0, TITLE_FMT, Prompts[HDR_FCC - 1]);
340+ mvprintw (HDR_FCC, SidebarWidth, TITLE_FMT, Prompts[HDR_FCC - 1]);
341 mutt_paddstr (W, fcc);
342
343 if (WithCrypto)
344@@ -266,7 +266,7 @@
345 #endif
346
347 SETCOLOR (MT_COLOR_STATUS);
348- mvaddstr (HDR_ATTACH - 1, 0, _("-- Attachments"));
349+ mvaddstr (HDR_ATTACH - 1, SidebarWidth, _("-- Attachments"));
350 clrtoeol ();
351
352 NORMAL_COLOR;
353@@ -302,7 +302,7 @@
354 /* redraw the expanded list so the user can see the result */
355 buf[0] = 0;
356 rfc822_write_address (buf, sizeof (buf), *addr, 1);
357- move (line, HDR_XOFFSET);
358+ move (line, HDR_XOFFSET+SidebarWidth);
359 mutt_paddstr (W, buf);
360
361 return 0;
362@@ -564,7 +564,7 @@
363 if (mutt_get_field ("Subject: ", buf, sizeof (buf), 0) == 0)
364 {
365 mutt_str_replace (&msg->env->subject, buf);
366- move (HDR_SUBJECT, HDR_XOFFSET);
367+ move (HDR_SUBJECT, HDR_XOFFSET + SidebarWidth);
368 if (msg->env->subject)
369 mutt_paddstr (W, msg->env->subject);
370 else
371@@ -582,7 +582,7 @@
372 {
373 strfcpy (fcc, buf, fcclen);
374 mutt_pretty_mailbox (fcc, fcclen);
375- move (HDR_FCC, HDR_XOFFSET);
376+ move (HDR_FCC, HDR_XOFFSET + SidebarWidth);
377 mutt_paddstr (W, fcc);
378 fccSet = 1;
379 }
380diff -rbuN mutt-1.6.0-orig/configure.ac mutt-1.6.0/configure.ac
381--- mutt-1.6.0-orig/configure.ac 2016-04-02 14:12:22.000000000 -0400
382+++ mutt-1.6.0/configure.ac 2016-04-04 23:42:10.893953425 -0400
383@@ -1301,6 +1301,8 @@
384 AC_DEFINE(HAVE_LANGINFO_YESEXPR,1,[ Define if you have <langinfo.h> and nl_langinfo(YESEXPR). ])
385 fi
386
387+AC_CHECK_FUNCS(fmemopen open_memstream)
388+
389 dnl Documentation tools
390 have_openjade="no"
391 AC_PATH_PROG([OSPCAT], [ospcat], [none])
392diff -rbuN mutt-1.6.0-orig/curs_main.c mutt-1.6.0/curs_main.c
393--- mutt-1.6.0-orig/curs_main.c 2016-04-02 14:12:22.000000000 -0400
394+++ mutt-1.6.0/curs_main.c 2016-04-04 23:42:10.893953425 -0400
395@@ -26,7 +26,9 @@
396 #include "mailbox.h"
397 #include "mapping.h"
398 #include "sort.h"
399+#include "buffy.h"
400 #include "mx.h"
401+#include "sidebar.h"
402
403 #ifdef USE_POP
404 #include "pop.h"
405@@ -595,20 +597,31 @@
406 menu->redraw |= REDRAW_STATUS;
407 if (do_buffy_notify)
408 {
409- if (mutt_buffy_notify () && option (OPTBEEPNEW))
410+ if (mutt_buffy_notify ())
411+ {
412+ menu->redraw |= REDRAW_STATUS;
413+ if (option (OPTBEEPNEW))
414 beep ();
415 }
416+ }
417 else
418 do_buffy_notify = 1;
419 }
420
421+ if(option(OPTSIDEBAR))
422+ menu->redraw |= REDRAW_SIDEBAR;
423+
424 if (op != -1)
425 mutt_curs_set (0);
426
427 if (menu->redraw & REDRAW_FULL)
428 {
429 menu_redraw_full (menu);
430+ draw_sidebar(menu->menu);
431 mutt_show_error ();
432+ } else if(menu->redraw & REDRAW_SIDEBAR) {
433+ draw_sidebar(menu->menu);
434+ menu->redraw &= ~REDRAW_SIDEBAR;
435 }
436
437 if (menu->menu == MENU_MAIN)
438@@ -630,9 +643,12 @@
439
440 if (menu->redraw & REDRAW_STATUS)
441 {
442+ DrawFullLine = 1;
443 menu_status_line (buf, sizeof (buf), menu, NONULL (Status));
444+ DrawFullLine = 0;
445 move (option (OPTSTATUSONTOP) ? 0 : LINES-2, 0);
446 SETCOLOR (MT_COLOR_STATUS);
447+ set_buffystats(Context);
448 mutt_paddstr (COLS, buf);
449 NORMAL_COLOR;
450 menu->redraw &= ~REDRAW_STATUS;
451@@ -652,7 +668,7 @@
452 menu->oldcurrent = -1;
453
454 if (option (OPTARROWCURSOR))
455- move (menu->current - menu->top + menu->offset, 2);
456+ move (menu->current - menu->top + menu->offset, SidebarWidth + 2);
457 else if (option (OPTBRAILLEFRIENDLY))
458 move (menu->current - menu->top + menu->offset, 0);
459 else
460@@ -1091,6 +1107,7 @@
461 break;
462
463 CHECK_MSGCOUNT;
464+ CHECK_VISIBLE;
465 CHECK_READONLY;
466 {
467 int oldvcount = Context->vcount;
468@@ -1150,6 +1167,7 @@
469 menu->redraw = REDRAW_FULL;
470 break;
471
472+ case OP_SIDEBAR_OPEN:
473 case OP_MAIN_CHANGE_FOLDER:
474 case OP_MAIN_NEXT_UNREAD_MAILBOX:
475
476@@ -1181,7 +1199,11 @@
477 {
478 mutt_buffy (buf, sizeof (buf));
479
480- if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1)
481+ if ( op == OP_SIDEBAR_OPEN ) {
482+ if(!CurBuffy)
483+ break;
484+ strncpy( buf, CurBuffy->path, sizeof(buf) );
485+ } else if (mutt_enter_fname (cp, buf, sizeof (buf), &menu->redraw, 1) == -1)
486 {
487 if (menu->menu == MENU_PAGER)
488 {
489@@ -1199,6 +1221,7 @@
490 }
491
492 mutt_expand_path (buf, sizeof (buf));
493+ set_curbuffy(buf);
494 if (mx_get_magic (buf) <= 0)
495 {
496 mutt_error (_("%s is not a mailbox."), buf);
497@@ -2310,6 +2333,12 @@
498 mutt_what_key();
499 break;
500
501+ case OP_SIDEBAR_SCROLL_UP:
502+ case OP_SIDEBAR_SCROLL_DOWN:
503+ case OP_SIDEBAR_NEXT:
504+ case OP_SIDEBAR_PREV:
505+ scroll_sidebar(op, menu->menu);
506+ break;
507 default:
508 if (menu->menu == MENU_MAIN)
509 km_error_key (MENU_MAIN);
510diff -rbuN mutt-1.6.0-orig/doc/Muttrc mutt-1.6.0/doc/Muttrc
511--- mutt-1.6.0-orig/doc/Muttrc 2016-04-02 14:15:01.000000000 -0400
512+++ mutt-1.6.0/doc/Muttrc 2016-04-04 23:42:10.913952805 -0400
513@@ -657,6 +657,26 @@
514 # $crypt_autosign, $crypt_replysign and $smime_is_default.
515 #
516 #
517+# set sidebar_visible=no
518+#
519+# Name: sidebar_visible
520+# Type: boolean
521+# Default: no
522+#
523+#
524+# This specifies whether or not to show sidebar (left-side list of folders).
525+#
526+#
527+# set sidebar_width=0
528+#
529+# Name: sidebar_width
530+# Type: number
531+# Default: 0
532+#
533+#
534+# The width of the sidebar.
535+#
536+#
537 # set crypt_autosign=no
538 #
539 # Name: crypt_autosign
540diff -rbuN mutt-1.6.0-orig/flags.c mutt-1.6.0/flags.c
541--- mutt-1.6.0-orig/flags.c 2016-04-02 14:12:22.000000000 -0400
542+++ mutt-1.6.0/flags.c 2016-04-04 23:42:10.893953425 -0400
543@@ -22,8 +22,10 @@
544
545 #include "mutt.h"
546 #include "mutt_curses.h"
547+#include "mutt_menu.h"
548 #include "sort.h"
549 #include "mx.h"
550+#include "sidebar.h"
551
552 void _mutt_set_flag (CONTEXT *ctx, HEADER *h, int flag, int bf, int upd_ctx)
553 {
554@@ -263,6 +265,7 @@
555 */
556 if (h->searched && (changed != h->changed || deleted != ctx->deleted || tagged != ctx->tagged || flagged != ctx->flagged))
557 h->searched = 0;
558+ draw_sidebar(0);
559 }
560
561 void mutt_tag_set_flag (int flag, int bf)
562diff -rbuN mutt-1.6.0-orig/functions.h mutt-1.6.0/functions.h
563--- mutt-1.6.0-orig/functions.h 2016-04-02 14:12:22.000000000 -0400
564+++ mutt-1.6.0/functions.h 2016-04-04 23:42:10.897286656 -0400
565@@ -169,6 +169,11 @@
566 { "decrypt-save", OP_DECRYPT_SAVE, NULL },
567
568
569+ { "sidebar-scroll-up", OP_SIDEBAR_SCROLL_UP, NULL },
570+ { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL },
571+ { "sidebar-next", OP_SIDEBAR_NEXT, NULL },
572+ { "sidebar-prev", OP_SIDEBAR_PREV, NULL },
573+ { "sidebar-open", OP_SIDEBAR_OPEN, NULL },
574 { NULL, 0, NULL }
575 };
576
577@@ -272,6 +277,11 @@
578
579 { "what-key", OP_WHAT_KEY, NULL },
580
581+ { "sidebar-scroll-up", OP_SIDEBAR_SCROLL_UP, NULL },
582+ { "sidebar-scroll-down", OP_SIDEBAR_SCROLL_DOWN, NULL },
583+ { "sidebar-next", OP_SIDEBAR_NEXT, NULL },
584+ { "sidebar-prev", OP_SIDEBAR_PREV, NULL },
585+ { "sidebar-open", OP_SIDEBAR_OPEN, NULL },
586 { NULL, 0, NULL }
587 };
588
589diff -rbuN mutt-1.6.0-orig/globals.h mutt-1.6.0/globals.h
590--- mutt-1.6.0-orig/globals.h 2016-04-02 14:12:22.000000000 -0400
591+++ mutt-1.6.0/globals.h 2016-04-04 23:42:10.897286656 -0400
592@@ -118,6 +118,9 @@
593 WHERE char *SendCharset;
594 WHERE char *Sendmail;
595 WHERE char *Shell;
596+WHERE char *SidebarDelim;
597+WHERE char *SidebarFormat;
598+WHERE char *SidebarIndentStr;
599 WHERE char *Signature;
600 WHERE char *SimpleSearch;
601 #if USE_SMTP
602@@ -214,6 +217,9 @@
603 WHERE short ScoreThresholdRead;
604 WHERE short ScoreThresholdFlag;
605
606+WHERE struct buffy_t *CurBuffy INITVAL(0);
607+WHERE short DrawFullLine INITVAL(0);
608+WHERE short SidebarWidth;
609 #ifdef USE_IMAP
610 WHERE short ImapKeepalive;
611 WHERE short ImapPipelineDepth;
612diff -rbuN mutt-1.6.0-orig/handler.c mutt-1.6.0/handler.c
613--- mutt-1.6.0-orig/handler.c 2016-04-02 14:12:22.000000000 -0400
614+++ mutt-1.6.0/handler.c 2016-04-04 23:42:10.897286656 -0400
615@@ -1603,6 +1603,11 @@
616
617 fseeko (s->fpin, b->offset, 0);
618
619+#ifdef HAVE_FMEMOPEN
620+ char *temp;
621+ size_t tempsize;
622+#endif
623+
624 /* see if we need to decode this part before processing it */
625 if (b->encoding == ENCBASE64 || b->encoding == ENCQUOTEDPRINTABLE ||
626 b->encoding == ENCUUENCODED || plaintext ||
627@@ -1618,6 +1623,14 @@
628 {
629 /* decode to a tempfile, saving the original destination */
630 fp = s->fpout;
631+#ifdef HAVE_FMEMOPEN
632+ if ((s->fpout = open_memstream(&temp, &tempsize)) == NULL)
633+ {
634+ mutt_error _("Unable to open memory stream!");
635+ dprint (1, (debugfile, "Can't open memory stream.\n"));
636+ return -1;
637+ }
638+#else
639 mutt_mktemp (tempfile, sizeof (tempfile));
640 if ((s->fpout = safe_fopen (tempfile, "w")) == NULL)
641 {
642@@ -1625,6 +1638,7 @@
643 dprint (1, (debugfile, "Can't open %s.\n", tempfile));
644 return -1;
645 }
646+#endif
647 /* decoding the attachment changes the size and offset, so save a copy
648 * of the "real" values now, and restore them after processing
649 */
650@@ -1653,9 +1667,19 @@
651 /* restore final destination and substitute the tempfile for input */
652 s->fpout = fp;
653 fp = s->fpin;
654+#ifdef HAVE_FMEMOPEN
655+ if(tempsize)
656+ s->fpin = fmemopen(temp, tempsize, "r");
657+ else /* fmemopen cannot handle zero-length buffers */
658+ s->fpin = safe_fopen ("/dev/null", "r");
659+ if(s->fpin == NULL) {
660+ mutt_perror("failed to re-open memstream!");
661+ return (-1);
662+ }
663+#else
664 s->fpin = fopen (tempfile, "r");
665 unlink (tempfile);
666-
667+#endif
668 /* restore the prefix */
669 s->prefix = savePrefix;
670 }
671@@ -1680,6 +1704,10 @@
672
673 /* restore the original source stream */
674 safe_fclose (&s->fpin);
675+#ifdef HAVE_FMEMOPEN
676+ if(tempsize)
677+ FREE(&temp);
678+#endif
679 s->fpin = fp;
680 }
681 }
682diff -rbuN mutt-1.6.0-orig/imap/command.c mutt-1.6.0/imap/command.c
683--- mutt-1.6.0-orig/imap/command.c 2016-04-02 14:12:22.000000000 -0400
684+++ mutt-1.6.0/imap/command.c 2016-04-04 23:42:10.917286035 -0400
685@@ -1016,6 +1016,13 @@
686 opened */
687 status->uidnext = oldun;
688
689+ /* Added to make the sidebar show the correct numbers */
690+ if (status->messages)
691+ {
692+ inc->msgcount = status->messages;
693+ inc->msg_unread = status->unseen;
694+ }
695+
696 FREE (&value);
697 return;
698 }
699diff -rbuN mutt-1.6.0-orig/imap/command.c.orig mutt-1.6.0/imap/command.c.orig
700--- mutt-1.6.0-orig/imap/command.c.orig 1969-12-31 19:00:00.000000000 -0500
701+++ mutt-1.6.0/imap/command.c.orig 2016-04-02 14:12:22.000000000 -0400
702@@ -0,0 +1,1041 @@
703+/*
704+ * Copyright (C) 1996-1998,2010,2012 Michael R. Elkins <me@mutt.org>
705+ * Copyright (C) 1996-1999 Brandon Long <blong@fiction.net>
706+ * Copyright (C) 1999-2009,2011 Brendan Cully <brendan@kublai.com>
707+ *
708+ * This program is free software; you can redistribute it and/or modify
709+ * it under the terms of the GNU General Public License as published by
710+ * the Free Software Foundation; either version 2 of the License, or
711+ * (at your option) any later version.
712+ *
713+ * This program is distributed in the hope that it will be useful,
714+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
715+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
716+ * GNU General Public License for more details.
717+ *
718+ * You should have received a copy of the GNU General Public License
719+ * along with this program; if not, write to the Free Software
720+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
721+ */
722+
723+/* command.c: routines for sending commands to an IMAP server and parsing
724+ * responses */
725+
726+#if HAVE_CONFIG_H
727+# include "config.h"
728+#endif
729+
730+#include "mutt.h"
731+#include "imap_private.h"
732+#include "mx.h"
733+#include "buffy.h"
734+
735+#include <ctype.h>
736+#include <stdlib.h>
737+
738+#define IMAP_CMD_BUFSIZE 512
739+
740+/* forward declarations */
741+static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags);
742+static int cmd_queue_full (IMAP_DATA* idata);
743+static int cmd_queue (IMAP_DATA* idata, const char* cmdstr);
744+static IMAP_COMMAND* cmd_new (IMAP_DATA* idata);
745+static int cmd_status (const char *s);
746+static void cmd_handle_fatal (IMAP_DATA* idata);
747+static int cmd_handle_untagged (IMAP_DATA* idata);
748+static void cmd_parse_capability (IMAP_DATA* idata, char* s);
749+static void cmd_parse_expunge (IMAP_DATA* idata, const char* s);
750+static void cmd_parse_list (IMAP_DATA* idata, char* s);
751+static void cmd_parse_lsub (IMAP_DATA* idata, char* s);
752+static void cmd_parse_fetch (IMAP_DATA* idata, char* s);
753+static void cmd_parse_myrights (IMAP_DATA* idata, const char* s);
754+static void cmd_parse_search (IMAP_DATA* idata, const char* s);
755+static void cmd_parse_status (IMAP_DATA* idata, char* s);
756+static void cmd_parse_enabled (IMAP_DATA* idata, const char* s);
757+
758+static const char * const Capabilities[] = {
759+ "IMAP4",
760+ "IMAP4rev1",
761+ "STATUS",
762+ "ACL",
763+ "NAMESPACE",
764+ "AUTH=CRAM-MD5",
765+ "AUTH=GSSAPI",
766+ "AUTH=ANONYMOUS",
767+ "STARTTLS",
768+ "LOGINDISABLED",
769+ "IDLE",
770+ "SASL-IR",
771+ "ENABLE",
772+
773+ NULL
774+};
775+
776+/* imap_cmd_start: Given an IMAP command, send it to the server.
777+ * If cmdstr is NULL, sends queued commands. */
778+int imap_cmd_start (IMAP_DATA* idata, const char* cmdstr)
779+{
780+ return cmd_start (idata, cmdstr, 0);
781+}
782+
783+/* imap_cmd_step: Reads server responses from an IMAP command, detects
784+ * tagged completion response, handles untagged messages, can read
785+ * arbitrarily large strings (using malloc, so don't make it _too_
786+ * large!). */
787+int imap_cmd_step (IMAP_DATA* idata)
788+{
789+ size_t len = 0;
790+ int c;
791+ int rc;
792+ int stillrunning = 0;
793+ IMAP_COMMAND* cmd;
794+
795+ if (idata->status == IMAP_FATAL)
796+ {
797+ cmd_handle_fatal (idata);
798+ return IMAP_CMD_BAD;
799+ }
800+
801+ /* read into buffer, expanding buffer as necessary until we have a full
802+ * line */
803+ do
804+ {
805+ if (len == idata->blen)
806+ {
807+ safe_realloc (&idata->buf, idata->blen + IMAP_CMD_BUFSIZE);
808+ idata->blen = idata->blen + IMAP_CMD_BUFSIZE;
809+ dprint (3, (debugfile, "imap_cmd_step: grew buffer to %u bytes\n",
810+ idata->blen));
811+ }
812+
813+ /* back up over '\0' */
814+ if (len)
815+ len--;
816+ c = mutt_socket_readln (idata->buf + len, idata->blen - len, idata->conn);
817+ if (c <= 0)
818+ {
819+ dprint (1, (debugfile, "imap_cmd_step: Error reading server response.\n"));
820+ cmd_handle_fatal (idata);
821+ return IMAP_CMD_BAD;
822+ }
823+
824+ len += c;
825+ }
826+ /* if we've read all the way to the end of the buffer, we haven't read a
827+ * full line (mutt_socket_readln strips the \r, so we always have at least
828+ * one character free when we've read a full line) */
829+ while (len == idata->blen);
830+
831+ /* don't let one large string make cmd->buf hog memory forever */
832+ if ((idata->blen > IMAP_CMD_BUFSIZE) && (len <= IMAP_CMD_BUFSIZE))
833+ {
834+ safe_realloc (&idata->buf, IMAP_CMD_BUFSIZE);
835+ idata->blen = IMAP_CMD_BUFSIZE;
836+ dprint (3, (debugfile, "imap_cmd_step: shrank buffer to %u bytes\n", idata->blen));
837+ }
838+
839+ idata->lastread = time (NULL);
840+
841+ /* handle untagged messages. The caller still gets its shot afterwards. */
842+ if ((!ascii_strncmp (idata->buf, "* ", 2)
843+ || !ascii_strncmp (imap_next_word (idata->buf), "OK [", 4))
844+ && cmd_handle_untagged (idata))
845+ return IMAP_CMD_BAD;
846+
847+ /* server demands a continuation response from us */
848+ if (idata->buf[0] == '+')
849+ return IMAP_CMD_RESPOND;
850+
851+ /* look for tagged command completions */
852+ rc = IMAP_CMD_CONTINUE;
853+ c = idata->lastcmd;
854+ do
855+ {
856+ cmd = &idata->cmds[c];
857+ if (cmd->state == IMAP_CMD_NEW)
858+ {
859+ if (!ascii_strncmp (idata->buf, cmd->seq, SEQLEN)) {
860+ if (!stillrunning)
861+ {
862+ /* first command in queue has finished - move queue pointer up */
863+ idata->lastcmd = (idata->lastcmd + 1) % idata->cmdslots;
864+ }
865+ cmd->state = cmd_status (idata->buf);
866+ /* bogus - we don't know which command result to return here. Caller
867+ * should provide a tag. */
868+ rc = cmd->state;
869+ }
870+ else
871+ stillrunning++;
872+ }
873+
874+ c = (c + 1) % idata->cmdslots;
875+ }
876+ while (c != idata->nextcmd);
877+
878+ if (stillrunning)
879+ rc = IMAP_CMD_CONTINUE;
880+ else
881+ {
882+ dprint (3, (debugfile, "IMAP queue drained\n"));
883+ imap_cmd_finish (idata);
884+ }
885+
886+
887+ return rc;
888+}
889+
890+/* imap_code: returns 1 if the command result was OK, or 0 if NO or BAD */
891+int imap_code (const char *s)
892+{
893+ return cmd_status (s) == IMAP_CMD_OK;
894+}
895+
896+/* imap_cmd_trailer: extra information after tagged command response if any */
897+const char* imap_cmd_trailer (IMAP_DATA* idata)
898+{
899+ static const char* notrailer = "";
900+ const char* s = idata->buf;
901+
902+ if (!s)
903+ {
904+ dprint (2, (debugfile, "imap_cmd_trailer: not a tagged response"));
905+ return notrailer;
906+ }
907+
908+ s = imap_next_word ((char *)s);
909+ if (!s || (ascii_strncasecmp (s, "OK", 2) &&
910+ ascii_strncasecmp (s, "NO", 2) &&
911+ ascii_strncasecmp (s, "BAD", 3)))
912+ {
913+ dprint (2, (debugfile, "imap_cmd_trailer: not a command completion: %s",
914+ idata->buf));
915+ return notrailer;
916+ }
917+
918+ s = imap_next_word ((char *)s);
919+ if (!s)
920+ return notrailer;
921+
922+ return s;
923+}
924+
925+/* imap_exec: execute a command, and wait for the response from the server.
926+ * Also, handle untagged responses.
927+ * Flags:
928+ * IMAP_CMD_FAIL_OK: the calling procedure can handle failure. This is used
929+ * for checking for a mailbox on append and login
930+ * IMAP_CMD_PASS: command contains a password. Suppress logging.
931+ * IMAP_CMD_QUEUE: only queue command, do not execute.
932+ * Return 0 on success, -1 on Failure, -2 on OK Failure
933+ */
934+int imap_exec (IMAP_DATA* idata, const char* cmdstr, int flags)
935+{
936+ int rc;
937+
938+ if ((rc = cmd_start (idata, cmdstr, flags)) < 0)
939+ {
940+ cmd_handle_fatal (idata);
941+ return -1;
942+ }
943+
944+ if (flags & IMAP_CMD_QUEUE)
945+ return 0;
946+
947+ do
948+ rc = imap_cmd_step (idata);
949+ while (rc == IMAP_CMD_CONTINUE);
950+
951+ if (rc == IMAP_CMD_NO && (flags & IMAP_CMD_FAIL_OK))
952+ return -2;
953+
954+ if (rc != IMAP_CMD_OK)
955+ {
956+ if ((flags & IMAP_CMD_FAIL_OK) && idata->status != IMAP_FATAL)
957+ return -2;
958+
959+ dprint (1, (debugfile, "imap_exec: command failed: %s\n", idata->buf));
960+ return -1;
961+ }
962+
963+ return 0;
964+}
965+
966+/* imap_cmd_finish: Attempts to perform cleanup (eg fetch new mail if
967+ * detected, do expunge). Called automatically by imap_cmd_step, but
968+ * may be called at any time. Called by imap_check_mailbox just before
969+ * the index is refreshed, for instance. */
970+void imap_cmd_finish (IMAP_DATA* idata)
971+{
972+ if (idata->status == IMAP_FATAL)
973+ {
974+ cmd_handle_fatal (idata);
975+ return;
976+ }
977+
978+ if (!(idata->state >= IMAP_SELECTED) || idata->ctx->closing)
979+ return;
980+
981+ if (idata->reopen & IMAP_REOPEN_ALLOW)
982+ {
983+ int count = idata->newMailCount;
984+
985+ if (!(idata->reopen & IMAP_EXPUNGE_PENDING) &&
986+ (idata->reopen & IMAP_NEWMAIL_PENDING)
987+ && count > idata->ctx->msgcount)
988+ {
989+ /* read new mail messages */
990+ dprint (2, (debugfile, "imap_cmd_finish: Fetching new mail\n"));
991+ /* check_status: curs_main uses imap_check_mailbox to detect
992+ * whether the index needs updating */
993+ idata->check_status = IMAP_NEWMAIL_PENDING;
994+ imap_read_headers (idata, idata->ctx->msgcount, count-1);
995+ }
996+ else if (idata->reopen & IMAP_EXPUNGE_PENDING)
997+ {
998+ dprint (2, (debugfile, "imap_cmd_finish: Expunging mailbox\n"));
999+ imap_expunge_mailbox (idata);
1000+ /* Detect whether we've gotten unexpected EXPUNGE messages */
1001+ if ((idata->reopen & IMAP_EXPUNGE_PENDING) &&
1002+ !(idata->reopen & IMAP_EXPUNGE_EXPECTED))
1003+ idata->check_status = IMAP_EXPUNGE_PENDING;
1004+ idata->reopen &= ~(IMAP_EXPUNGE_PENDING | IMAP_NEWMAIL_PENDING |
1005+ IMAP_EXPUNGE_EXPECTED);
1006+ }
1007+ }
1008+
1009+ idata->status = 0;
1010+}
1011+
1012+/* imap_cmd_idle: Enter the IDLE state. */
1013+int imap_cmd_idle (IMAP_DATA* idata)
1014+{
1015+ int rc;
1016+
1017+ imap_cmd_start (idata, "IDLE");
1018+ do
1019+ rc = imap_cmd_step (idata);
1020+ while (rc == IMAP_CMD_CONTINUE);
1021+
1022+ if (rc == IMAP_CMD_RESPOND)
1023+ {
1024+ /* successfully entered IDLE state */
1025+ idata->state = IMAP_IDLE;
1026+ /* queue automatic exit when next command is issued */
1027+ mutt_buffer_printf (idata->cmdbuf, "DONE\r\n");
1028+ rc = IMAP_CMD_OK;
1029+ }
1030+ if (rc != IMAP_CMD_OK)
1031+ {
1032+ dprint (1, (debugfile, "imap_cmd_idle: error starting IDLE\n"));
1033+ return -1;
1034+ }
1035+
1036+ return 0;
1037+}
1038+
1039+static int cmd_queue_full (IMAP_DATA* idata)
1040+{
1041+ if ((idata->nextcmd + 1) % idata->cmdslots == idata->lastcmd)
1042+ return 1;
1043+
1044+ return 0;
1045+}
1046+
1047+/* sets up a new command control block and adds it to the queue.
1048+ * Returns NULL if the pipeline is full. */
1049+static IMAP_COMMAND* cmd_new (IMAP_DATA* idata)
1050+{
1051+ IMAP_COMMAND* cmd;
1052+
1053+ if (cmd_queue_full (idata))
1054+ {
1055+ dprint (3, (debugfile, "cmd_new: IMAP command queue full\n"));
1056+ return NULL;
1057+ }
1058+
1059+ cmd = idata->cmds + idata->nextcmd;
1060+ idata->nextcmd = (idata->nextcmd + 1) % idata->cmdslots;
1061+
1062+ snprintf (cmd->seq, sizeof (cmd->seq), "a%04u", idata->seqno++);
1063+ if (idata->seqno > 9999)
1064+ idata->seqno = 0;
1065+
1066+ cmd->state = IMAP_CMD_NEW;
1067+
1068+ return cmd;
1069+}
1070+
1071+/* queues command. If the queue is full, attempts to drain it. */
1072+static int cmd_queue (IMAP_DATA* idata, const char* cmdstr)
1073+{
1074+ IMAP_COMMAND* cmd;
1075+ int rc;
1076+
1077+ if (cmd_queue_full (idata))
1078+ {
1079+ dprint (3, (debugfile, "Draining IMAP command pipeline\n"));
1080+
1081+ rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
1082+
1083+ if (rc < 0 && rc != -2)
1084+ return rc;
1085+ }
1086+
1087+ if (!(cmd = cmd_new (idata)))
1088+ return IMAP_CMD_BAD;
1089+
1090+ if (mutt_buffer_printf (idata->cmdbuf, "%s %s\r\n", cmd->seq, cmdstr) < 0)
1091+ return IMAP_CMD_BAD;
1092+
1093+ return 0;
1094+}
1095+
1096+static int cmd_start (IMAP_DATA* idata, const char* cmdstr, int flags)
1097+{
1098+ int rc;
1099+
1100+ if (idata->status == IMAP_FATAL)
1101+ {
1102+ cmd_handle_fatal (idata);
1103+ return -1;
1104+ }
1105+
1106+ if (cmdstr && ((rc = cmd_queue (idata, cmdstr)) < 0))
1107+ return rc;
1108+
1109+ if (flags & IMAP_CMD_QUEUE)
1110+ return 0;
1111+
1112+ if (idata->cmdbuf->dptr == idata->cmdbuf->data)
1113+ return IMAP_CMD_BAD;
1114+
1115+ rc = mutt_socket_write_d (idata->conn, idata->cmdbuf->data, -1,
1116+ flags & IMAP_CMD_PASS ? IMAP_LOG_PASS : IMAP_LOG_CMD);
1117+ idata->cmdbuf->dptr = idata->cmdbuf->data;
1118+
1119+ /* unidle when command queue is flushed */
1120+ if (idata->state == IMAP_IDLE)
1121+ idata->state = IMAP_SELECTED;
1122+
1123+ return (rc < 0) ? IMAP_CMD_BAD : 0;
1124+}
1125+
1126+/* parse response line for tagged OK/NO/BAD */
1127+static int cmd_status (const char *s)
1128+{
1129+ s = imap_next_word((char*)s);
1130+
1131+ if (!ascii_strncasecmp("OK", s, 2))
1132+ return IMAP_CMD_OK;
1133+ if (!ascii_strncasecmp("NO", s, 2))
1134+ return IMAP_CMD_NO;
1135+
1136+ return IMAP_CMD_BAD;
1137+}
1138+
1139+/* cmd_handle_fatal: when IMAP_DATA is in fatal state, do what we can */
1140+static void cmd_handle_fatal (IMAP_DATA* idata)
1141+{
1142+ idata->status = IMAP_FATAL;
1143+
1144+ if ((idata->state >= IMAP_SELECTED) &&
1145+ (idata->reopen & IMAP_REOPEN_ALLOW))
1146+ {
1147+ mx_fastclose_mailbox (idata->ctx);
1148+ mutt_error (_("Mailbox closed"));
1149+ mutt_sleep (1);
1150+ idata->state = IMAP_DISCONNECTED;
1151+ }
1152+
1153+ if (idata->state < IMAP_SELECTED)
1154+ imap_close_connection (idata);
1155+}
1156+
1157+/* cmd_handle_untagged: fallback parser for otherwise unhandled messages. */
1158+static int cmd_handle_untagged (IMAP_DATA* idata)
1159+{
1160+ char* s;
1161+ char* pn;
1162+ int count;
1163+
1164+ s = imap_next_word (idata->buf);
1165+ pn = imap_next_word (s);
1166+
1167+ if ((idata->state >= IMAP_SELECTED) && isdigit ((unsigned char) *s))
1168+ {
1169+ pn = s;
1170+ s = imap_next_word (s);
1171+
1172+ /* EXISTS and EXPUNGE are always related to the SELECTED mailbox for the
1173+ * connection, so update that one.
1174+ */
1175+ if (ascii_strncasecmp ("EXISTS", s, 6) == 0)
1176+ {
1177+ dprint (2, (debugfile, "Handling EXISTS\n"));
1178+
1179+ /* new mail arrived */
1180+ count = atoi (pn);
1181+
1182+ if ( !(idata->reopen & IMAP_EXPUNGE_PENDING) &&
1183+ count < idata->ctx->msgcount)
1184+ {
1185+ /* Notes 6.0.3 has a tendency to report fewer messages exist than
1186+ * it should. */
1187+ dprint (1, (debugfile, "Message count is out of sync"));
1188+ return 0;
1189+ }
1190+ /* at least the InterChange server sends EXISTS messages freely,
1191+ * even when there is no new mail */
1192+ else if (count == idata->ctx->msgcount)
1193+ dprint (3, (debugfile,
1194+ "cmd_handle_untagged: superfluous EXISTS message.\n"));
1195+ else
1196+ {
1197+ if (!(idata->reopen & IMAP_EXPUNGE_PENDING))
1198+ {
1199+ dprint (2, (debugfile,
1200+ "cmd_handle_untagged: New mail in %s - %d messages total.\n",
1201+ idata->mailbox, count));
1202+ idata->reopen |= IMAP_NEWMAIL_PENDING;
1203+ }
1204+ idata->newMailCount = count;
1205+ }
1206+ }
1207+ /* pn vs. s: need initial seqno */
1208+ else if (ascii_strncasecmp ("EXPUNGE", s, 7) == 0)
1209+ cmd_parse_expunge (idata, pn);
1210+ else if (ascii_strncasecmp ("FETCH", s, 5) == 0)
1211+ cmd_parse_fetch (idata, pn);
1212+ }
1213+ else if (ascii_strncasecmp ("CAPABILITY", s, 10) == 0)
1214+ cmd_parse_capability (idata, s);
1215+ else if (!ascii_strncasecmp ("OK [CAPABILITY", s, 14))
1216+ cmd_parse_capability (idata, pn);
1217+ else if (!ascii_strncasecmp ("OK [CAPABILITY", pn, 14))
1218+ cmd_parse_capability (idata, imap_next_word (pn));
1219+ else if (ascii_strncasecmp ("LIST", s, 4) == 0)
1220+ cmd_parse_list (idata, s);
1221+ else if (ascii_strncasecmp ("LSUB", s, 4) == 0)
1222+ cmd_parse_lsub (idata, s);
1223+ else if (ascii_strncasecmp ("MYRIGHTS", s, 8) == 0)
1224+ cmd_parse_myrights (idata, s);
1225+ else if (ascii_strncasecmp ("SEARCH", s, 6) == 0)
1226+ cmd_parse_search (idata, s);
1227+ else if (ascii_strncasecmp ("STATUS", s, 6) == 0)
1228+ cmd_parse_status (idata, s);
1229+ else if (ascii_strncasecmp ("ENABLED", s, 7) == 0)
1230+ cmd_parse_enabled (idata, s);
1231+ else if (ascii_strncasecmp ("BYE", s, 3) == 0)
1232+ {
1233+ dprint (2, (debugfile, "Handling BYE\n"));
1234+
1235+ /* check if we're logging out */
1236+ if (idata->status == IMAP_BYE)
1237+ return 0;
1238+
1239+ /* server shut down our connection */
1240+ s += 3;
1241+ SKIPWS (s);
1242+ mutt_error ("%s", s);
1243+ mutt_sleep (2);
1244+ cmd_handle_fatal (idata);
1245+
1246+ return -1;
1247+ }
1248+ else if (option (OPTIMAPSERVERNOISE) && (ascii_strncasecmp ("NO", s, 2) == 0))
1249+ {
1250+ dprint (2, (debugfile, "Handling untagged NO\n"));
1251+
1252+ /* Display the warning message from the server */
1253+ mutt_error ("%s", s+3);
1254+ mutt_sleep (2);
1255+ }
1256+
1257+ return 0;
1258+}
1259+
1260+/* cmd_parse_capabilities: set capability bits according to CAPABILITY
1261+ * response */
1262+static void cmd_parse_capability (IMAP_DATA* idata, char* s)
1263+{
1264+ int x;
1265+ char* bracket;
1266+
1267+ dprint (3, (debugfile, "Handling CAPABILITY\n"));
1268+
1269+ s = imap_next_word (s);
1270+ if ((bracket = strchr (s, ']')))
1271+ *bracket = '\0';
1272+ FREE(&idata->capstr);
1273+ idata->capstr = safe_strdup (s);
1274+
1275+ memset (idata->capabilities, 0, sizeof (idata->capabilities));
1276+
1277+ while (*s)
1278+ {
1279+ for (x = 0; x < CAPMAX; x++)
1280+ if (imap_wordcasecmp(Capabilities[x], s) == 0)
1281+ {
1282+ mutt_bit_set (idata->capabilities, x);
1283+ break;
1284+ }
1285+ s = imap_next_word (s);
1286+ }
1287+}
1288+
1289+/* cmd_parse_expunge: mark headers with new sequence ID and mark idata to
1290+ * be reopened at our earliest convenience */
1291+static void cmd_parse_expunge (IMAP_DATA* idata, const char* s)
1292+{
1293+ int expno, cur;
1294+ HEADER* h;
1295+
1296+ dprint (2, (debugfile, "Handling EXPUNGE\n"));
1297+
1298+ expno = atoi (s);
1299+
1300+ /* walk headers, zero seqno of expunged message, decrement seqno of those
1301+ * above. Possibly we could avoid walking the whole list by resorting
1302+ * and guessing a good starting point, but I'm guessing the resort would
1303+ * nullify the gains */
1304+ for (cur = 0; cur < idata->ctx->msgcount; cur++)
1305+ {
1306+ h = idata->ctx->hdrs[cur];
1307+
1308+ if (h->index+1 == expno)
1309+ h->index = -1;
1310+ else if (h->index+1 > expno)
1311+ h->index--;
1312+ }
1313+
1314+ idata->reopen |= IMAP_EXPUNGE_PENDING;
1315+}
1316+
1317+/* cmd_parse_fetch: Load fetch response into IMAP_DATA. Currently only
1318+ * handles unanticipated FETCH responses, and only FLAGS data. We get
1319+ * these if another client has changed flags for a mailbox we've selected.
1320+ * Of course, a lot of code here duplicates code in message.c. */
1321+static void cmd_parse_fetch (IMAP_DATA* idata, char* s)
1322+{
1323+ int msgno, cur;
1324+ HEADER* h = NULL;
1325+
1326+ dprint (3, (debugfile, "Handling FETCH\n"));
1327+
1328+ msgno = atoi (s);
1329+
1330+ if (msgno <= idata->ctx->msgcount)
1331+ /* see cmd_parse_expunge */
1332+ for (cur = 0; cur < idata->ctx->msgcount; cur++)
1333+ {
1334+ h = idata->ctx->hdrs[cur];
1335+
1336+ if (h && h->active && h->index+1 == msgno)
1337+ {
1338+ dprint (2, (debugfile, "Message UID %d updated\n", HEADER_DATA(h)->uid));
1339+ break;
1340+ }
1341+
1342+ h = NULL;
1343+ }
1344+
1345+ if (!h)
1346+ {
1347+ dprint (3, (debugfile, "FETCH response ignored for this message\n"));
1348+ return;
1349+ }
1350+
1351+ /* skip FETCH */
1352+ s = imap_next_word (s);
1353+ s = imap_next_word (s);
1354+
1355+ if (*s != '(')
1356+ {
1357+ dprint (1, (debugfile, "Malformed FETCH response"));
1358+ return;
1359+ }
1360+ s++;
1361+
1362+ if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
1363+ {
1364+ dprint (2, (debugfile, "Only handle FLAGS updates\n"));
1365+ return;
1366+ }
1367+
1368+ /* If server flags could conflict with mutt's flags, reopen the mailbox. */
1369+ if (h->changed)
1370+ idata->reopen |= IMAP_EXPUNGE_PENDING;
1371+ else {
1372+ imap_set_flags (idata, h, s);
1373+ idata->check_status = IMAP_FLAGS_PENDING;
1374+ }
1375+}
1376+
1377+static void cmd_parse_list (IMAP_DATA* idata, char* s)
1378+{
1379+ IMAP_LIST* list;
1380+ IMAP_LIST lb;
1381+ char delimbuf[5]; /* worst case: "\\"\0 */
1382+ long litlen;
1383+
1384+ if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
1385+ list = (IMAP_LIST*)idata->cmddata;
1386+ else
1387+ list = &lb;
1388+
1389+ memset (list, 0, sizeof (IMAP_LIST));
1390+
1391+ /* flags */
1392+ s = imap_next_word (s);
1393+ if (*s != '(')
1394+ {
1395+ dprint (1, (debugfile, "Bad LIST response\n"));
1396+ return;
1397+ }
1398+ s++;
1399+ while (*s)
1400+ {
1401+ if (!ascii_strncasecmp (s, "\\NoSelect", 9))
1402+ list->noselect = 1;
1403+ else if (!ascii_strncasecmp (s, "\\NoInferiors", 12))
1404+ list->noinferiors = 1;
1405+ /* See draft-gahrns-imap-child-mailbox-?? */
1406+ else if (!ascii_strncasecmp (s, "\\HasNoChildren", 14))
1407+ list->noinferiors = 1;
1408+
1409+ s = imap_next_word (s);
1410+ if (*(s - 2) == ')')
1411+ break;
1412+ }
1413+
1414+ /* Delimiter */
1415+ if (ascii_strncasecmp (s, "NIL", 3))
1416+ {
1417+ delimbuf[0] = '\0';
1418+ safe_strcat (delimbuf, 5, s);
1419+ imap_unquote_string (delimbuf);
1420+ list->delim = delimbuf[0];
1421+ }
1422+
1423+ /* Name */
1424+ s = imap_next_word (s);
1425+ /* Notes often responds with literals here. We need a real tokenizer. */
1426+ if (!imap_get_literal_count (s, &litlen))
1427+ {
1428+ if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1429+ {
1430+ idata->status = IMAP_FATAL;
1431+ return;
1432+ }
1433+ list->name = idata->buf;
1434+ }
1435+ else
1436+ {
1437+ imap_unmunge_mbox_name (idata, s);
1438+ list->name = s;
1439+ }
1440+
1441+ if (list->name[0] == '\0')
1442+ {
1443+ idata->delim = list->delim;
1444+ dprint (3, (debugfile, "Root delimiter: %c\n", idata->delim));
1445+ }
1446+}
1447+
1448+static void cmd_parse_lsub (IMAP_DATA* idata, char* s)
1449+{
1450+ char buf[STRING];
1451+ char errstr[STRING];
1452+ BUFFER err, token;
1453+ ciss_url_t url;
1454+ IMAP_LIST list;
1455+
1456+ if (idata->cmddata && idata->cmdtype == IMAP_CT_LIST)
1457+ {
1458+ /* caller will handle response itself */
1459+ cmd_parse_list (idata, s);
1460+ return;
1461+ }
1462+
1463+ if (!option (OPTIMAPCHECKSUBSCRIBED))
1464+ return;
1465+
1466+ idata->cmdtype = IMAP_CT_LIST;
1467+ idata->cmddata = &list;
1468+ cmd_parse_list (idata, s);
1469+ idata->cmddata = NULL;
1470+ /* noselect is for a gmail quirk (#3445) */
1471+ if (!list.name || list.noselect)
1472+ return;
1473+
1474+ dprint (3, (debugfile, "Subscribing to %s\n", list.name));
1475+
1476+ strfcpy (buf, "mailboxes \"", sizeof (buf));
1477+ mutt_account_tourl (&idata->conn->account, &url);
1478+ /* escape \ and " */
1479+ imap_quote_string(errstr, sizeof (errstr), list.name);
1480+ url.path = errstr + 1;
1481+ url.path[strlen(url.path) - 1] = '\0';
1482+ if (!mutt_strcmp (url.user, ImapUser))
1483+ url.user = NULL;
1484+ url_ciss_tostring (&url, buf + 11, sizeof (buf) - 10, 0);
1485+ safe_strcat (buf, sizeof (buf), "\"");
1486+ mutt_buffer_init (&token);
1487+ mutt_buffer_init (&err);
1488+ err.data = errstr;
1489+ err.dsize = sizeof (errstr);
1490+ if (mutt_parse_rc_line (buf, &token, &err))
1491+ dprint (1, (debugfile, "Error adding subscribed mailbox: %s\n", errstr));
1492+ FREE (&token.data);
1493+}
1494+
1495+/* cmd_parse_myrights: set rights bits according to MYRIGHTS response */
1496+static void cmd_parse_myrights (IMAP_DATA* idata, const char* s)
1497+{
1498+ dprint (2, (debugfile, "Handling MYRIGHTS\n"));
1499+
1500+ s = imap_next_word ((char*)s);
1501+ s = imap_next_word ((char*)s);
1502+
1503+ /* zero out current rights set */
1504+ memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
1505+
1506+ while (*s && !isspace((unsigned char) *s))
1507+ {
1508+ switch (*s)
1509+ {
1510+ case 'l':
1511+ mutt_bit_set (idata->ctx->rights, M_ACL_LOOKUP);
1512+ break;
1513+ case 'r':
1514+ mutt_bit_set (idata->ctx->rights, M_ACL_READ);
1515+ break;
1516+ case 's':
1517+ mutt_bit_set (idata->ctx->rights, M_ACL_SEEN);
1518+ break;
1519+ case 'w':
1520+ mutt_bit_set (idata->ctx->rights, M_ACL_WRITE);
1521+ break;
1522+ case 'i':
1523+ mutt_bit_set (idata->ctx->rights, M_ACL_INSERT);
1524+ break;
1525+ case 'p':
1526+ mutt_bit_set (idata->ctx->rights, M_ACL_POST);
1527+ break;
1528+ case 'a':
1529+ mutt_bit_set (idata->ctx->rights, M_ACL_ADMIN);
1530+ break;
1531+ case 'k':
1532+ mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
1533+ break;
1534+ case 'x':
1535+ mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
1536+ break;
1537+ case 't':
1538+ mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
1539+ break;
1540+ case 'e':
1541+ mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
1542+ break;
1543+
1544+ /* obsolete rights */
1545+ case 'c':
1546+ mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
1547+ mutt_bit_set (idata->ctx->rights, M_ACL_DELMX);
1548+ break;
1549+ case 'd':
1550+ mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
1551+ mutt_bit_set (idata->ctx->rights, M_ACL_EXPUNGE);
1552+ break;
1553+ default:
1554+ dprint(1, (debugfile, "Unknown right: %c\n", *s));
1555+ }
1556+ s++;
1557+ }
1558+}
1559+
1560+/* This should be optimised (eg with a tree or hash) */
1561+static int uid2msgno (IMAP_DATA* idata, unsigned int uid)
1562+{
1563+ int i;
1564+
1565+ for (i = 0; i < idata->ctx->msgcount; i++)
1566+ {
1567+ HEADER* h = idata->ctx->hdrs[i];
1568+ if (HEADER_DATA(h)->uid == uid)
1569+ return i;
1570+ }
1571+
1572+ return -1;
1573+}
1574+
1575+/* cmd_parse_search: store SEARCH response for later use */
1576+static void cmd_parse_search (IMAP_DATA* idata, const char* s)
1577+{
1578+ unsigned int uid;
1579+ int msgno;
1580+
1581+ dprint (2, (debugfile, "Handling SEARCH\n"));
1582+
1583+ while ((s = imap_next_word ((char*)s)) && *s != '\0')
1584+ {
1585+ uid = atoi (s);
1586+ msgno = uid2msgno (idata, uid);
1587+
1588+ if (msgno >= 0)
1589+ idata->ctx->hdrs[uid2msgno (idata, uid)]->matched = 1;
1590+ }
1591+}
1592+
1593+/* first cut: just do buffy update. Later we may wish to cache all
1594+ * mailbox information, even that not desired by buffy */
1595+static void cmd_parse_status (IMAP_DATA* idata, char* s)
1596+{
1597+ char* mailbox;
1598+ char* value;
1599+ BUFFY* inc;
1600+ IMAP_MBOX mx;
1601+ int count;
1602+ IMAP_STATUS *status;
1603+ unsigned int olduv, oldun;
1604+ long litlen;
1605+
1606+ mailbox = imap_next_word (s);
1607+
1608+ /* We need a real tokenizer. */
1609+ if (!imap_get_literal_count (mailbox, &litlen))
1610+ {
1611+ if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
1612+ {
1613+ idata->status = IMAP_FATAL;
1614+ return;
1615+ }
1616+ mailbox = idata->buf;
1617+ s = mailbox + litlen;
1618+ *s = '\0';
1619+ s++;
1620+ SKIPWS(s);
1621+ }
1622+ else
1623+ {
1624+ s = imap_next_word (mailbox);
1625+ *(s - 1) = '\0';
1626+ imap_unmunge_mbox_name (idata, mailbox);
1627+ }
1628+
1629+ status = imap_mboxcache_get (idata, mailbox, 1);
1630+ olduv = status->uidvalidity;
1631+ oldun = status->uidnext;
1632+
1633+ if (*s++ != '(')
1634+ {
1635+ dprint (1, (debugfile, "Error parsing STATUS\n"));
1636+ return;
1637+ }
1638+ while (*s && *s != ')')
1639+ {
1640+ value = imap_next_word (s);
1641+ count = strtol (value, &value, 10);
1642+
1643+ if (!ascii_strncmp ("MESSAGES", s, 8))
1644+ status->messages = count;
1645+ else if (!ascii_strncmp ("RECENT", s, 6))
1646+ status->recent = count;
1647+ else if (!ascii_strncmp ("UIDNEXT", s, 7))
1648+ status->uidnext = count;
1649+ else if (!ascii_strncmp ("UIDVALIDITY", s, 11))
1650+ status->uidvalidity = count;
1651+ else if (!ascii_strncmp ("UNSEEN", s, 6))
1652+ status->unseen = count;
1653+
1654+ s = value;
1655+ if (*s && *s != ')')
1656+ s = imap_next_word (s);
1657+ }
1658+ dprint (3, (debugfile, "%s (UIDVALIDITY: %d, UIDNEXT: %d) %d messages, %d recent, %d unseen\n",
1659+ status->name, status->uidvalidity, status->uidnext,
1660+ status->messages, status->recent, status->unseen));
1661+
1662+ /* caller is prepared to handle the result herself */
1663+ if (idata->cmddata && idata->cmdtype == IMAP_CT_STATUS)
1664+ {
1665+ memcpy (idata->cmddata, status, sizeof (IMAP_STATUS));
1666+ return;
1667+ }
1668+
1669+ dprint (3, (debugfile, "Running default STATUS handler\n"));
1670+
1671+ /* should perhaps move this code back to imap_buffy_check */
1672+ for (inc = Incoming; inc; inc = inc->next)
1673+ {
1674+ if (inc->magic != M_IMAP)
1675+ continue;
1676+
1677+ if (imap_parse_path (inc->path, &mx) < 0)
1678+ {
1679+ dprint (1, (debugfile, "Error parsing mailbox %s, skipping\n", inc->path));
1680+ continue;
1681+ }
1682+ /* dprint (2, (debugfile, "Buffy entry: [%s] mbox: [%s]\n", inc->path, NONULL(mx.mbox))); */
1683+
1684+ if (imap_account_match (&idata->conn->account, &mx.account))
1685+ {
1686+ if (mx.mbox)
1687+ {
1688+ value = safe_strdup (mx.mbox);
1689+ imap_fix_path (idata, mx.mbox, value, mutt_strlen (value) + 1);
1690+ FREE (&mx.mbox);
1691+ }
1692+ else
1693+ value = safe_strdup ("INBOX");
1694+
1695+ if (value && !imap_mxcmp (mailbox, value))
1696+ {
1697+ dprint (3, (debugfile, "Found %s in buffy list (OV: %d ON: %d U: %d)\n",
1698+ mailbox, olduv, oldun, status->unseen));
1699+
1700+ if (option(OPTMAILCHECKRECENT))
1701+ {
1702+ if (olduv && olduv == status->uidvalidity)
1703+ {
1704+ if (oldun < status->uidnext)
1705+ inc->new = status->unseen;
1706+ }
1707+ else if (!olduv && !oldun)
1708+ /* first check per session, use recent. might need a flag for this. */
1709+ inc->new = status->recent;
1710+ else
1711+ inc->new = status->unseen;
1712+ }
1713+ else
1714+ inc->new = status->unseen;
1715+
1716+ if (inc->new)
1717+ /* force back to keep detecting new mail until the mailbox is
1718+ opened */
1719+ status->uidnext = oldun;
1720+
1721+ FREE (&value);
1722+ return;
1723+ }
1724+
1725+ FREE (&value);
1726+ }
1727+
1728+ FREE (&mx.mbox);
1729+ }
1730+}
1731+
1732+/* cmd_parse_enabled: record what the server has enabled */
1733+static void cmd_parse_enabled (IMAP_DATA* idata, const char* s)
1734+{
1735+ dprint (2, (debugfile, "Handling ENABLED\n"));
1736+
1737+ while ((s = imap_next_word ((char*)s)) && *s != '\0')
1738+ {
1739+ if (ascii_strncasecmp(s, "UTF8=ACCEPT", 11) == 0 ||
1740+ ascii_strncasecmp(s, "UTF8=ONLY", 9) == 0)
1741+ idata->unicode = 1;
1742+ }
1743+}
1744diff -rbuN mutt-1.6.0-orig/imap/imap.c mutt-1.6.0/imap/imap.c
1745--- mutt-1.6.0-orig/imap/imap.c 2016-04-02 14:12:22.000000000 -0400
1746+++ mutt-1.6.0/imap/imap.c 2016-04-04 23:42:10.917286035 -0400
1747@@ -1535,7 +1535,7 @@
1748
1749 imap_munge_mbox_name (idata, munged, sizeof (munged), name);
1750 snprintf (command, sizeof (command),
1751- "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged);
1752+ "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT MESSAGES)", munged);
1753
1754 if (imap_exec (idata, command, IMAP_CMD_QUEUE) < 0)
1755 {
1756diff -rbuN mutt-1.6.0-orig/imap/imap.c.orig mutt-1.6.0/imap/imap.c.orig
1757--- mutt-1.6.0-orig/imap/imap.c.orig 1969-12-31 19:00:00.000000000 -0500
1758+++ mutt-1.6.0/imap/imap.c.orig 2016-04-02 14:12:22.000000000 -0400
1759@@ -0,0 +1,2040 @@
1760+/*
1761+ * Copyright (C) 1996-1998,2012 Michael R. Elkins <me@mutt.org>
1762+ * Copyright (C) 1996-1999 Brandon Long <blong@fiction.net>
1763+ * Copyright (C) 1999-2009,2012 Brendan Cully <brendan@kublai.com>
1764+ *
1765+ * This program is free software; you can redistribute it and/or modify
1766+ * it under the terms of the GNU General Public License as published by
1767+ * the Free Software Foundation; either version 2 of the License, or
1768+ * (at your option) any later version.
1769+ *
1770+ * This program is distributed in the hope that it will be useful,
1771+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1772+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1773+ * GNU General Public License for more details.
1774+ *
1775+ * You should have received a copy of the GNU General Public License
1776+ * along with this program; if not, write to the Free Software
1777+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1778+ */
1779+
1780+/* Support for IMAP4rev1, with the occasional nod to IMAP 4. */
1781+
1782+#if HAVE_CONFIG_H
1783+# include "config.h"
1784+#endif
1785+
1786+#include "mutt.h"
1787+#include "mx.h"
1788+#include "mailbox.h"
1789+#include "globals.h"
1790+#include "sort.h"
1791+#include "browser.h"
1792+#include "imap_private.h"
1793+#if defined(USE_SSL)
1794+# include "mutt_ssl.h"
1795+#endif
1796+#include "buffy.h"
1797+#if USE_HCACHE
1798+#include "hcache.h"
1799+#endif
1800+
1801+#include <unistd.h>
1802+#include <ctype.h>
1803+#include <string.h>
1804+#include <stdlib.h>
1805+#include <sys/types.h>
1806+#include <sys/stat.h>
1807+
1808+/* imap forward declarations */
1809+static char* imap_get_flags (LIST** hflags, char* s);
1810+static int imap_check_capabilities (IMAP_DATA* idata);
1811+static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
1812+ const char* str, char* flags, size_t flsize);
1813+
1814+/* imap_access: Check permissions on an IMAP mailbox.
1815+ * TODO: ACL checks. Right now we assume if it exists we can
1816+ * mess with it. */
1817+int imap_access (const char* path, int flags)
1818+{
1819+ IMAP_DATA* idata;
1820+ IMAP_MBOX mx;
1821+ char buf[LONG_STRING];
1822+ char mailbox[LONG_STRING];
1823+ char mbox[LONG_STRING];
1824+ int rc;
1825+
1826+ if (imap_parse_path (path, &mx))
1827+ return -1;
1828+
1829+ if (!(idata = imap_conn_find (&mx.account,
1830+ option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0)))
1831+ {
1832+ FREE (&mx.mbox);
1833+ return -1;
1834+ }
1835+
1836+ imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
1837+ if (!*mailbox)
1838+ strfcpy (mailbox, "INBOX", sizeof (mailbox));
1839+
1840+ /* we may already be in the folder we're checking */
1841+ if (!ascii_strcmp(idata->mailbox, mx.mbox))
1842+ {
1843+ FREE (&mx.mbox);
1844+ return 0;
1845+ }
1846+ FREE (&mx.mbox);
1847+
1848+ if (imap_mboxcache_get (idata, mailbox, 0))
1849+ {
1850+ dprint (3, (debugfile, "imap_access: found %s in cache\n", mailbox));
1851+ return 0;
1852+ }
1853+
1854+ imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox);
1855+
1856+ if (mutt_bit_isset (idata->capabilities, IMAP4REV1))
1857+ snprintf (buf, sizeof (buf), "STATUS %s (UIDVALIDITY)", mbox);
1858+ else if (mutt_bit_isset (idata->capabilities, STATUS))
1859+ snprintf (buf, sizeof (buf), "STATUS %s (UID-VALIDITY)", mbox);
1860+ else
1861+ {
1862+ dprint (2, (debugfile, "imap_access: STATUS not supported?\n"));
1863+ return -1;
1864+ }
1865+
1866+ if ((rc = imap_exec (idata, buf, IMAP_CMD_FAIL_OK)) < 0)
1867+ {
1868+ dprint (1, (debugfile, "imap_access: Can't check STATUS of %s\n", mbox));
1869+ return rc;
1870+ }
1871+
1872+ return 0;
1873+}
1874+
1875+int imap_create_mailbox (IMAP_DATA* idata, char* mailbox)
1876+{
1877+ char buf[LONG_STRING], mbox[LONG_STRING];
1878+
1879+ imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox);
1880+ snprintf (buf, sizeof (buf), "CREATE %s", mbox);
1881+
1882+ if (imap_exec (idata, buf, 0) != 0)
1883+ {
1884+ mutt_error (_("CREATE failed: %s"), imap_cmd_trailer (idata));
1885+ return -1;
1886+ }
1887+
1888+ return 0;
1889+}
1890+
1891+int imap_rename_mailbox (IMAP_DATA* idata, IMAP_MBOX* mx, const char* newname)
1892+{
1893+ char oldmbox[LONG_STRING];
1894+ char newmbox[LONG_STRING];
1895+ char buf[LONG_STRING];
1896+
1897+ imap_munge_mbox_name (idata, oldmbox, sizeof (oldmbox), mx->mbox);
1898+ imap_munge_mbox_name (idata, newmbox, sizeof (newmbox), newname);
1899+
1900+ snprintf (buf, sizeof (buf), "RENAME %s %s", oldmbox, newmbox);
1901+
1902+ if (imap_exec (idata, buf, 0) != 0)
1903+ return -1;
1904+
1905+ return 0;
1906+}
1907+
1908+int imap_delete_mailbox (CONTEXT* ctx, IMAP_MBOX mx)
1909+{
1910+ char buf[LONG_STRING], mbox[LONG_STRING];
1911+ IMAP_DATA *idata;
1912+
1913+ if (!ctx || !ctx->data) {
1914+ if (!(idata = imap_conn_find (&mx.account,
1915+ option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0)))
1916+ {
1917+ FREE (&mx.mbox);
1918+ return -1;
1919+ }
1920+ } else {
1921+ idata = ctx->data;
1922+ }
1923+
1924+ imap_munge_mbox_name (idata, mbox, sizeof (mbox), mx.mbox);
1925+ snprintf (buf, sizeof (buf), "DELETE %s", mbox);
1926+
1927+ if (imap_exec ((IMAP_DATA*) idata, buf, 0) != 0)
1928+ return -1;
1929+
1930+ return 0;
1931+}
1932+
1933+/* imap_logout_all: close all open connections. Quick and dirty until we can
1934+ * make sure we've got all the context we need. */
1935+void imap_logout_all (void)
1936+{
1937+ CONNECTION* conn;
1938+ CONNECTION* tmp;
1939+
1940+ conn = mutt_socket_head ();
1941+
1942+ while (conn)
1943+ {
1944+ tmp = conn->next;
1945+
1946+ if (conn->account.type == M_ACCT_TYPE_IMAP && conn->fd >= 0)
1947+ {
1948+ mutt_message (_("Closing connection to %s..."), conn->account.host);
1949+ imap_logout ((IMAP_DATA**) (void*) &conn->data);
1950+ mutt_clear_error ();
1951+ mutt_socket_free (conn);
1952+ }
1953+
1954+ conn = tmp;
1955+ }
1956+}
1957+
1958+/* imap_read_literal: read bytes bytes from server into file. Not explicitly
1959+ * buffered, relies on FILE buffering. NOTE: strips \r from \r\n.
1960+ * Apparently even literals use \r\n-terminated strings ?! */
1961+int imap_read_literal (FILE* fp, IMAP_DATA* idata, long bytes, progress_t* pbar)
1962+{
1963+ long pos;
1964+ char c;
1965+
1966+ int r = 0;
1967+
1968+ dprint (2, (debugfile, "imap_read_literal: reading %ld bytes\n", bytes));
1969+
1970+ for (pos = 0; pos < bytes; pos++)
1971+ {
1972+ if (mutt_socket_readchar (idata->conn, &c) != 1)
1973+ {
1974+ dprint (1, (debugfile, "imap_read_literal: error during read, %ld bytes read\n", pos));
1975+ idata->status = IMAP_FATAL;
1976+
1977+ return -1;
1978+ }
1979+
1980+#if 1
1981+ if (r == 1 && c != '\n')
1982+ fputc ('\r', fp);
1983+
1984+ if (c == '\r')
1985+ {
1986+ r = 1;
1987+ continue;
1988+ }
1989+ else
1990+ r = 0;
1991+#endif
1992+ fputc (c, fp);
1993+
1994+ if (pbar && !(pos % 1024))
1995+ mutt_progress_update (pbar, pos, -1);
1996+#ifdef DEBUG
1997+ if (debuglevel >= IMAP_LOG_LTRL)
1998+ fputc (c, debugfile);
1999+#endif
2000+ }
2001+
2002+ return 0;
2003+}
2004+
2005+/* imap_expunge_mailbox: Purge IMAP portion of expunged messages from the
2006+ * context. Must not be done while something has a handle on any headers
2007+ * (eg inside pager or editor). That is, check IMAP_REOPEN_ALLOW. */
2008+void imap_expunge_mailbox (IMAP_DATA* idata)
2009+{
2010+ HEADER* h;
2011+ int i, cacheno;
2012+
2013+#ifdef USE_HCACHE
2014+ idata->hcache = imap_hcache_open (idata, NULL);
2015+#endif
2016+
2017+ for (i = 0; i < idata->ctx->msgcount; i++)
2018+ {
2019+ h = idata->ctx->hdrs[i];
2020+
2021+ if (h->index == -1)
2022+ {
2023+ dprint (2, (debugfile, "Expunging message UID %d.\n", HEADER_DATA (h)->uid));
2024+
2025+ h->active = 0;
2026+ idata->ctx->size -= h->content->length;
2027+
2028+ imap_cache_del (idata, h);
2029+#if USE_HCACHE
2030+ imap_hcache_del (idata, HEADER_DATA(h)->uid);
2031+#endif
2032+
2033+ /* free cached body from disk, if necessary */
2034+ cacheno = HEADER_DATA(h)->uid % IMAP_CACHE_LEN;
2035+ if (idata->cache[cacheno].uid == HEADER_DATA(h)->uid &&
2036+ idata->cache[cacheno].path)
2037+ {
2038+ unlink (idata->cache[cacheno].path);
2039+ FREE (&idata->cache[cacheno].path);
2040+ }
2041+
2042+ imap_free_header_data ((IMAP_HEADER_DATA**)&h->data);
2043+ }
2044+ }
2045+
2046+#if USE_HCACHE
2047+ imap_hcache_close (idata);
2048+#endif
2049+
2050+ /* We may be called on to expunge at any time. We can't rely on the caller
2051+ * to always know to rethread */
2052+ mx_update_tables (idata->ctx, 0);
2053+ mutt_sort_headers (idata->ctx, 1);
2054+}
2055+
2056+/* imap_check_capabilities: make sure we can log in to this server. */
2057+static int imap_check_capabilities (IMAP_DATA* idata)
2058+{
2059+ if (imap_exec (idata, "CAPABILITY", 0) != 0)
2060+ {
2061+ imap_error ("imap_check_capabilities", idata->buf);
2062+ return -1;
2063+ }
2064+
2065+ if (!(mutt_bit_isset(idata->capabilities,IMAP4)
2066+ ||mutt_bit_isset(idata->capabilities,IMAP4REV1)))
2067+ {
2068+ mutt_error _("This IMAP server is ancient. Mutt does not work with it.");
2069+ mutt_sleep (2); /* pause a moment to let the user see the error */
2070+
2071+ return -1;
2072+ }
2073+
2074+ return 0;
2075+}
2076+
2077+/* imap_conn_find: Find an open IMAP connection matching account, or open
2078+ * a new one if none can be found. */
2079+IMAP_DATA* imap_conn_find (const ACCOUNT* account, int flags)
2080+{
2081+ CONNECTION* conn = NULL;
2082+ ACCOUNT* creds = NULL;
2083+ IMAP_DATA* idata = NULL;
2084+ int new = 0;
2085+
2086+ while ((conn = mutt_conn_find (conn, account)))
2087+ {
2088+ if (!creds)
2089+ creds = &conn->account;
2090+ else
2091+ memcpy (&conn->account, creds, sizeof (ACCOUNT));
2092+
2093+ idata = (IMAP_DATA*)conn->data;
2094+ if (flags & M_IMAP_CONN_NONEW)
2095+ {
2096+ if (!idata)
2097+ {
2098+ /* This should only happen if we've come to the end of the list */
2099+ mutt_socket_free (conn);
2100+ return NULL;
2101+ }
2102+ else if (idata->state < IMAP_AUTHENTICATED)
2103+ continue;
2104+ }
2105+ if (flags & M_IMAP_CONN_NOSELECT && idata && idata->state >= IMAP_SELECTED)
2106+ continue;
2107+ if (idata && idata->status == IMAP_FATAL)
2108+ continue;
2109+ break;
2110+ }
2111+ if (!conn)
2112+ return NULL; /* this happens when the initial connection fails */
2113+
2114+ if (!idata)
2115+ {
2116+ /* The current connection is a new connection */
2117+ if (! (idata = imap_new_idata ()))
2118+ {
2119+ mutt_socket_free (conn);
2120+ return NULL;
2121+ }
2122+
2123+ conn->data = idata;
2124+ idata->conn = conn;
2125+ new = 1;
2126+ }
2127+
2128+ if (idata->state == IMAP_DISCONNECTED)
2129+ imap_open_connection (idata);
2130+ if (idata->state == IMAP_CONNECTED)
2131+ {
2132+ if (!imap_authenticate (idata))
2133+ {
2134+ idata->state = IMAP_AUTHENTICATED;
2135+ FREE (&idata->capstr);
2136+ new = 1;
2137+ if (idata->conn->ssf)
2138+ dprint (2, (debugfile, "Communication encrypted at %d bits\n",
2139+ idata->conn->ssf));
2140+ }
2141+ else
2142+ mutt_account_unsetpass (&idata->conn->account);
2143+ }
2144+ if (new && idata->state == IMAP_AUTHENTICATED)
2145+ {
2146+ /* capabilities may have changed */
2147+ imap_exec (idata, "CAPABILITY", IMAP_CMD_QUEUE);
2148+ /* enable RFC6855, if the server supports that */
2149+ if (mutt_bit_isset (idata->capabilities, ENABLE))
2150+ imap_exec (idata, "ENABLE UTF8=ACCEPT", IMAP_CMD_QUEUE);
2151+ /* get root delimiter, '/' as default */
2152+ idata->delim = '/';
2153+ imap_exec (idata, "LIST \"\" \"\"", IMAP_CMD_QUEUE);
2154+ if (option (OPTIMAPCHECKSUBSCRIBED))
2155+ imap_exec (idata, "LSUB \"\" \"*\"", IMAP_CMD_QUEUE);
2156+ /* we may need the root delimiter before we open a mailbox */
2157+ imap_exec (idata, NULL, IMAP_CMD_FAIL_OK);
2158+ }
2159+
2160+ return idata;
2161+}
2162+
2163+int imap_open_connection (IMAP_DATA* idata)
2164+{
2165+ char buf[LONG_STRING];
2166+
2167+ if (mutt_socket_open (idata->conn) < 0)
2168+ return -1;
2169+
2170+ idata->state = IMAP_CONNECTED;
2171+
2172+ if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
2173+ {
2174+ imap_close_connection (idata);
2175+ return -1;
2176+ }
2177+
2178+ if (ascii_strncasecmp ("* OK", idata->buf, 4) == 0)
2179+ {
2180+ if (ascii_strncasecmp ("* OK [CAPABILITY", idata->buf, 16)
2181+ && imap_check_capabilities (idata))
2182+ goto bail;
2183+#if defined(USE_SSL)
2184+ /* Attempt STARTTLS if available and desired. */
2185+ if (!idata->conn->ssf && (option(OPTSSLFORCETLS) ||
2186+ mutt_bit_isset (idata->capabilities, STARTTLS)))
2187+ {
2188+ int rc;
2189+
2190+ if (option(OPTSSLFORCETLS))
2191+ rc = M_YES;
2192+ else if ((rc = query_quadoption (OPT_SSLSTARTTLS,
2193+ _("Secure connection with TLS?"))) == -1)
2194+ goto err_close_conn;
2195+ if (rc == M_YES) {
2196+ if ((rc = imap_exec (idata, "STARTTLS", IMAP_CMD_FAIL_OK)) == -1)
2197+ goto bail;
2198+ if (rc != -2)
2199+ {
2200+ if (mutt_ssl_starttls (idata->conn))
2201+ {
2202+ mutt_error (_("Could not negotiate TLS connection"));
2203+ mutt_sleep (1);
2204+ goto err_close_conn;
2205+ }
2206+ else
2207+ {
2208+ /* RFC 2595 demands we recheck CAPABILITY after TLS completes. */
2209+ if (imap_exec (idata, "CAPABILITY", 0))
2210+ goto bail;
2211+ }
2212+ }
2213+ }
2214+ }
2215+
2216+ if (option(OPTSSLFORCETLS) && ! idata->conn->ssf)
2217+ {
2218+ mutt_error _("Encrypted connection unavailable");
2219+ mutt_sleep (1);
2220+ goto err_close_conn;
2221+ }
2222+#endif
2223+ }
2224+ else if (ascii_strncasecmp ("* PREAUTH", idata->buf, 9) == 0)
2225+ {
2226+ idata->state = IMAP_AUTHENTICATED;
2227+ if (imap_check_capabilities (idata) != 0)
2228+ goto bail;
2229+ FREE (&idata->capstr);
2230+ }
2231+ else
2232+ {
2233+ imap_error ("imap_open_connection()", buf);
2234+ goto bail;
2235+ }
2236+
2237+ return 0;
2238+
2239+#if defined(USE_SSL)
2240+ err_close_conn:
2241+ imap_close_connection (idata);
2242+#endif
2243+ bail:
2244+ FREE (&idata->capstr);
2245+ return -1;
2246+}
2247+
2248+void imap_close_connection(IMAP_DATA* idata)
2249+{
2250+ if (idata->state != IMAP_DISCONNECTED)
2251+ {
2252+ mutt_socket_close (idata->conn);
2253+ idata->state = IMAP_DISCONNECTED;
2254+ }
2255+ idata->seqno = idata->nextcmd = idata->lastcmd = idata->status = 0;
2256+ memset (idata->cmds, 0, sizeof (IMAP_COMMAND) * idata->cmdslots);
2257+}
2258+
2259+/* imap_get_flags: Make a simple list out of a FLAGS response.
2260+ * return stream following FLAGS response */
2261+static char* imap_get_flags (LIST** hflags, char* s)
2262+{
2263+ LIST* flags;
2264+ char* flag_word;
2265+ char ctmp;
2266+
2267+ /* sanity-check string */
2268+ if (ascii_strncasecmp ("FLAGS", s, 5) != 0)
2269+ {
2270+ dprint (1, (debugfile, "imap_get_flags: not a FLAGS response: %s\n",
2271+ s));
2272+ return NULL;
2273+ }
2274+ s += 5;
2275+ SKIPWS(s);
2276+ if (*s != '(')
2277+ {
2278+ dprint (1, (debugfile, "imap_get_flags: bogus FLAGS response: %s\n",
2279+ s));
2280+ return NULL;
2281+ }
2282+
2283+ /* create list, update caller's flags handle */
2284+ flags = mutt_new_list();
2285+ *hflags = flags;
2286+
2287+ while (*s && *s != ')')
2288+ {
2289+ s++;
2290+ SKIPWS(s);
2291+ flag_word = s;
2292+ while (*s && (*s != ')') && !ISSPACE (*s))
2293+ s++;
2294+ ctmp = *s;
2295+ *s = '\0';
2296+ if (*flag_word)
2297+ mutt_add_list (flags, flag_word);
2298+ *s = ctmp;
2299+ }
2300+
2301+ /* note bad flags response */
2302+ if (*s != ')')
2303+ {
2304+ dprint (1, (debugfile,
2305+ "imap_get_flags: Unterminated FLAGS response: %s\n", s));
2306+ mutt_free_list (hflags);
2307+
2308+ return NULL;
2309+ }
2310+
2311+ s++;
2312+
2313+ return s;
2314+}
2315+
2316+int imap_open_mailbox (CONTEXT* ctx)
2317+{
2318+ IMAP_DATA *idata;
2319+ IMAP_STATUS* status;
2320+ char buf[LONG_STRING];
2321+ char bufout[LONG_STRING];
2322+ int count = 0;
2323+ IMAP_MBOX mx, pmx;
2324+ int rc;
2325+
2326+ if (imap_parse_path (ctx->path, &mx))
2327+ {
2328+ mutt_error (_("%s is an invalid IMAP path"), ctx->path);
2329+ return -1;
2330+ }
2331+
2332+ /* we require a connection which isn't currently in IMAP_SELECTED state */
2333+ if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NOSELECT)))
2334+ goto fail_noidata;
2335+ if (idata->state < IMAP_AUTHENTICATED)
2336+ goto fail;
2337+
2338+ /* once again the context is new */
2339+ ctx->data = idata;
2340+ ctx->mx_close = imap_close_mailbox;
2341+
2342+ /* Clean up path and replace the one in the ctx */
2343+ imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
2344+ if (!*buf)
2345+ strfcpy (buf, "INBOX", sizeof (buf));
2346+ FREE(&(idata->mailbox));
2347+ idata->mailbox = safe_strdup (buf);
2348+ imap_qualify_path (buf, sizeof (buf), &mx, idata->mailbox);
2349+
2350+ FREE (&(ctx->path));
2351+ ctx->path = safe_strdup (buf);
2352+
2353+ idata->ctx = ctx;
2354+
2355+ /* clear mailbox status */
2356+ idata->status = 0;
2357+ memset (idata->ctx->rights, 0, sizeof (idata->ctx->rights));
2358+ idata->newMailCount = 0;
2359+
2360+ mutt_message (_("Selecting %s..."), idata->mailbox);
2361+ imap_munge_mbox_name (idata, buf, sizeof(buf), idata->mailbox);
2362+
2363+ /* pipeline ACL test */
2364+ if (mutt_bit_isset (idata->capabilities, ACL))
2365+ {
2366+ snprintf (bufout, sizeof (bufout), "MYRIGHTS %s", buf);
2367+ imap_exec (idata, bufout, IMAP_CMD_QUEUE);
2368+ }
2369+ /* assume we have all rights if ACL is unavailable */
2370+ else
2371+ {
2372+ mutt_bit_set (idata->ctx->rights, M_ACL_LOOKUP);
2373+ mutt_bit_set (idata->ctx->rights, M_ACL_READ);
2374+ mutt_bit_set (idata->ctx->rights, M_ACL_SEEN);
2375+ mutt_bit_set (idata->ctx->rights, M_ACL_WRITE);
2376+ mutt_bit_set (idata->ctx->rights, M_ACL_INSERT);
2377+ mutt_bit_set (idata->ctx->rights, M_ACL_POST);
2378+ mutt_bit_set (idata->ctx->rights, M_ACL_CREATE);
2379+ mutt_bit_set (idata->ctx->rights, M_ACL_DELETE);
2380+ }
2381+ /* pipeline the postponed count if possible */
2382+ pmx.mbox = NULL;
2383+ if (mx_is_imap (Postponed) && !imap_parse_path (Postponed, &pmx)
2384+ && mutt_account_match (&pmx.account, &mx.account))
2385+ imap_status (Postponed, 1);
2386+ FREE (&pmx.mbox);
2387+
2388+ snprintf (bufout, sizeof (bufout), "%s %s",
2389+ ctx->readonly ? "EXAMINE" : "SELECT", buf);
2390+
2391+ idata->state = IMAP_SELECTED;
2392+
2393+ imap_cmd_start (idata, bufout);
2394+
2395+ status = imap_mboxcache_get (idata, idata->mailbox, 1);
2396+
2397+ do
2398+ {
2399+ char *pc;
2400+
2401+ if ((rc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
2402+ break;
2403+
2404+ pc = idata->buf + 2;
2405+
2406+ /* Obtain list of available flags here, may be overridden by a
2407+ * PERMANENTFLAGS tag in the OK response */
2408+ if (ascii_strncasecmp ("FLAGS", pc, 5) == 0)
2409+ {
2410+ /* don't override PERMANENTFLAGS */
2411+ if (!idata->flags)
2412+ {
2413+ dprint (3, (debugfile, "Getting mailbox FLAGS\n"));
2414+ if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
2415+ goto fail;
2416+ }
2417+ }
2418+ /* PERMANENTFLAGS are massaged to look like FLAGS, then override FLAGS */
2419+ else if (ascii_strncasecmp ("OK [PERMANENTFLAGS", pc, 18) == 0)
2420+ {
2421+ dprint (3, (debugfile, "Getting mailbox PERMANENTFLAGS\n"));
2422+ /* safe to call on NULL */
2423+ mutt_free_list (&(idata->flags));
2424+ /* skip "OK [PERMANENT" so syntax is the same as FLAGS */
2425+ pc += 13;
2426+ if ((pc = imap_get_flags (&(idata->flags), pc)) == NULL)
2427+ goto fail;
2428+ }
2429+ /* save UIDVALIDITY for the header cache */
2430+ else if (ascii_strncasecmp ("OK [UIDVALIDITY", pc, 14) == 0)
2431+ {
2432+ dprint (3, (debugfile, "Getting mailbox UIDVALIDITY\n"));
2433+ pc += 3;
2434+ pc = imap_next_word (pc);
2435+ idata->uid_validity = strtol (pc, NULL, 10);
2436+ status->uidvalidity = idata->uid_validity;
2437+ }
2438+ else if (ascii_strncasecmp ("OK [UIDNEXT", pc, 11) == 0)
2439+ {
2440+ dprint (3, (debugfile, "Getting mailbox UIDNEXT\n"));
2441+ pc += 3;
2442+ pc = imap_next_word (pc);
2443+ idata->uidnext = strtol (pc, NULL, 10);
2444+ status->uidnext = idata->uidnext;
2445+ }
2446+ else
2447+ {
2448+ pc = imap_next_word (pc);
2449+ if (!ascii_strncasecmp ("EXISTS", pc, 6))
2450+ {
2451+ count = idata->newMailCount;
2452+ idata->newMailCount = 0;
2453+ }
2454+ }
2455+ }
2456+ while (rc == IMAP_CMD_CONTINUE);
2457+
2458+ if (rc == IMAP_CMD_NO)
2459+ {
2460+ char *s;
2461+ s = imap_next_word (idata->buf); /* skip seq */
2462+ s = imap_next_word (s); /* Skip response */
2463+ mutt_error ("%s", s);
2464+ mutt_sleep (2);
2465+ goto fail;
2466+ }
2467+
2468+ if (rc != IMAP_CMD_OK)
2469+ goto fail;
2470+
2471+ /* check for READ-ONLY notification */
2472+ if (!ascii_strncasecmp (imap_get_qualifier (idata->buf), "[READ-ONLY]", 11) \
2473+ && !mutt_bit_isset (idata->capabilities, ACL))
2474+ {
2475+ dprint (2, (debugfile, "Mailbox is read-only.\n"));
2476+ ctx->readonly = 1;
2477+ }
2478+
2479+#ifdef DEBUG
2480+ /* dump the mailbox flags we've found */
2481+ if (debuglevel > 2)
2482+ {
2483+ if (!idata->flags)
2484+ dprint (3, (debugfile, "No folder flags found\n"));
2485+ else
2486+ {
2487+ LIST* t = idata->flags;
2488+
2489+ dprint (3, (debugfile, "Mailbox flags: "));
2490+
2491+ t = t->next;
2492+ while (t)
2493+ {
2494+ dprint (3, (debugfile, "[%s] ", t->data));
2495+ t = t->next;
2496+ }
2497+ dprint (3, (debugfile, "\n"));
2498+ }
2499+ }
2500+#endif
2501+
2502+ if (!(mutt_bit_isset(idata->ctx->rights, M_ACL_DELETE) ||
2503+ mutt_bit_isset(idata->ctx->rights, M_ACL_SEEN) ||
2504+ mutt_bit_isset(idata->ctx->rights, M_ACL_WRITE) ||
2505+ mutt_bit_isset(idata->ctx->rights, M_ACL_INSERT)))
2506+ ctx->readonly = 1;
2507+
2508+ ctx->hdrmax = count;
2509+ ctx->hdrs = safe_calloc (count, sizeof (HEADER *));
2510+ ctx->v2r = safe_calloc (count, sizeof (int));
2511+ ctx->msgcount = 0;
2512+
2513+ if (count && (imap_read_headers (idata, 0, count-1) < 0))
2514+ {
2515+ mutt_error _("Error opening mailbox");
2516+ mutt_sleep (1);
2517+ goto fail;
2518+ }
2519+
2520+ dprint (2, (debugfile, "imap_open_mailbox: msgcount is %d\n", ctx->msgcount));
2521+ FREE (&mx.mbox);
2522+ return 0;
2523+
2524+ fail:
2525+ if (idata->state == IMAP_SELECTED)
2526+ idata->state = IMAP_AUTHENTICATED;
2527+ fail_noidata:
2528+ FREE (&mx.mbox);
2529+ return -1;
2530+}
2531+
2532+int imap_open_mailbox_append (CONTEXT *ctx)
2533+{
2534+ IMAP_DATA *idata;
2535+ char buf[LONG_STRING];
2536+ char mailbox[LONG_STRING];
2537+ IMAP_MBOX mx;
2538+ int rc;
2539+
2540+ if (imap_parse_path (ctx->path, &mx))
2541+ return -1;
2542+
2543+ /* in APPEND mode, we appear to hijack an existing IMAP connection -
2544+ * ctx is brand new and mostly empty */
2545+
2546+ if (!(idata = imap_conn_find (&(mx.account), 0)))
2547+ {
2548+ FREE (&mx.mbox);
2549+ return -1;
2550+ }
2551+
2552+ ctx->magic = M_IMAP;
2553+ ctx->data = idata;
2554+
2555+ imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
2556+ if (!*mailbox)
2557+ strfcpy (mailbox, "INBOX", sizeof (mailbox));
2558+ FREE (&mx.mbox);
2559+
2560+ /* really we should also check for W_OK */
2561+ if ((rc = imap_access (ctx->path, F_OK)) == 0)
2562+ return 0;
2563+
2564+ if (rc == -1)
2565+ return -1;
2566+
2567+ snprintf (buf, sizeof (buf), _("Create %s?"), mailbox);
2568+ if (option (OPTCONFIRMCREATE) && mutt_yesorno (buf, 1) < 1)
2569+ return -1;
2570+
2571+ if (imap_create_mailbox (idata, mailbox) < 0)
2572+ return -1;
2573+
2574+ return 0;
2575+}
2576+
2577+/* imap_logout: Gracefully log out of server. */
2578+void imap_logout (IMAP_DATA** idata)
2579+{
2580+ /* we set status here to let imap_handle_untagged know we _expect_ to
2581+ * receive a bye response (so it doesn't freak out and close the conn) */
2582+ (*idata)->status = IMAP_BYE;
2583+ imap_cmd_start (*idata, "LOGOUT");
2584+ while (imap_cmd_step (*idata) == IMAP_CMD_CONTINUE)
2585+ ;
2586+
2587+ mutt_socket_close ((*idata)->conn);
2588+ imap_free_idata (idata);
2589+}
2590+
2591+/* imap_set_flag: append str to flags if we currently have permission
2592+ * according to aclbit */
2593+static void imap_set_flag (IMAP_DATA* idata, int aclbit, int flag,
2594+ const char *str, char *flags, size_t flsize)
2595+{
2596+ if (mutt_bit_isset (idata->ctx->rights, aclbit))
2597+ if (flag && imap_has_flag (idata->flags, str))
2598+ safe_strcat (flags, flsize, str);
2599+}
2600+
2601+/* imap_has_flag: do a caseless comparison of the flag against a flag list,
2602+* return 1 if found or flag list has '\*', 0 otherwise */
2603+int imap_has_flag (LIST* flag_list, const char* flag)
2604+{
2605+ if (!flag_list)
2606+ return 0;
2607+
2608+ flag_list = flag_list->next;
2609+ while (flag_list)
2610+ {
2611+ if (!ascii_strncasecmp (flag_list->data, flag, strlen (flag_list->data)))
2612+ return 1;
2613+
2614+ if (!ascii_strncmp (flag_list->data, "\\*", strlen (flag_list->data)))
2615+ return 1;
2616+
2617+ flag_list = flag_list->next;
2618+ }
2619+
2620+ return 0;
2621+}
2622+
2623+/* Note: headers must be in SORT_ORDER. See imap_exec_msgset for args.
2624+ * Pos is an opaque pointer a la strtok. It should be 0 at first call. */
2625+static int imap_make_msg_set (IMAP_DATA* idata, BUFFER* buf, int flag,
2626+ int changed, int invert, int* pos)
2627+{
2628+ HEADER** hdrs = idata->ctx->hdrs;
2629+ int count = 0; /* number of messages in message set */
2630+ int match = 0; /* whether current message matches flag condition */
2631+ unsigned int setstart = 0; /* start of current message range */
2632+ int n;
2633+ int started = 0;
2634+
2635+ hdrs = idata->ctx->hdrs;
2636+
2637+ for (n = *pos;
2638+ n < idata->ctx->msgcount && buf->dptr - buf->data < IMAP_MAX_CMDLEN;
2639+ n++)
2640+ {
2641+ match = 0;
2642+ /* don't include pending expunged messages */
2643+ if (hdrs[n]->active)
2644+ switch (flag)
2645+ {
2646+ case M_DELETED:
2647+ if (hdrs[n]->deleted != HEADER_DATA(hdrs[n])->deleted)
2648+ match = invert ^ hdrs[n]->deleted;
2649+ break;
2650+ case M_FLAG:
2651+ if (hdrs[n]->flagged != HEADER_DATA(hdrs[n])->flagged)
2652+ match = invert ^ hdrs[n]->flagged;
2653+ break;
2654+ case M_OLD:
2655+ if (hdrs[n]->old != HEADER_DATA(hdrs[n])->old)
2656+ match = invert ^ hdrs[n]->old;
2657+ break;
2658+ case M_READ:
2659+ if (hdrs[n]->read != HEADER_DATA(hdrs[n])->read)
2660+ match = invert ^ hdrs[n]->read;
2661+ break;
2662+ case M_REPLIED:
2663+ if (hdrs[n]->replied != HEADER_DATA(hdrs[n])->replied)
2664+ match = invert ^ hdrs[n]->replied;
2665+ break;
2666+
2667+ case M_TAG:
2668+ if (hdrs[n]->tagged)
2669+ match = 1;
2670+ break;
2671+ }
2672+
2673+ if (match && (!changed || hdrs[n]->changed))
2674+ {
2675+ count++;
2676+ if (setstart == 0)
2677+ {
2678+ setstart = HEADER_DATA (hdrs[n])->uid;
2679+ if (started == 0)
2680+ {
2681+ mutt_buffer_printf (buf, "%u", HEADER_DATA (hdrs[n])->uid);
2682+ started = 1;
2683+ }
2684+ else
2685+ mutt_buffer_printf (buf, ",%u", HEADER_DATA (hdrs[n])->uid);
2686+ }
2687+ /* tie up if the last message also matches */
2688+ else if (n == idata->ctx->msgcount-1)
2689+ mutt_buffer_printf (buf, ":%u", HEADER_DATA (hdrs[n])->uid);
2690+ }
2691+ /* End current set if message doesn't match or we've reached the end
2692+ * of the mailbox via inactive messages following the last match. */
2693+ else if (setstart && (hdrs[n]->active || n == idata->ctx->msgcount-1))
2694+ {
2695+ if (HEADER_DATA (hdrs[n-1])->uid > setstart)
2696+ mutt_buffer_printf (buf, ":%u", HEADER_DATA (hdrs[n-1])->uid);
2697+ setstart = 0;
2698+ }
2699+ }
2700+
2701+ *pos = n;
2702+
2703+ return count;
2704+}
2705+
2706+/* Prepares commands for all messages matching conditions (must be flushed
2707+ * with imap_exec)
2708+ * Params:
2709+ * idata: IMAP_DATA containing context containing header set
2710+ * pre, post: commands are of the form "%s %s %s %s", tag,
2711+ * pre, message set, post
2712+ * flag: enum of flag type on which to filter
2713+ * changed: include only changed messages in message set
2714+ * invert: invert sense of flag, eg M_READ matches unread messages
2715+ * Returns: number of matched messages, or -1 on failure */
2716+int imap_exec_msgset (IMAP_DATA* idata, const char* pre, const char* post,
2717+ int flag, int changed, int invert)
2718+{
2719+ HEADER** hdrs = NULL;
2720+ short oldsort;
2721+ BUFFER* cmd;
2722+ int pos;
2723+ int rc;
2724+ int count = 0;
2725+
2726+ if (! (cmd = mutt_buffer_new ()))
2727+ {
2728+ dprint (1, (debugfile, "imap_exec_msgset: unable to allocate buffer\n"));
2729+ return -1;
2730+ }
2731+
2732+ /* We make a copy of the headers just in case resorting doesn't give
2733+ exactly the original order (duplicate messages?), because other parts of
2734+ the ctx are tied to the header order. This may be overkill. */
2735+ oldsort = Sort;
2736+ if (Sort != SORT_ORDER)
2737+ {
2738+ hdrs = idata->ctx->hdrs;
2739+ idata->ctx->hdrs = safe_malloc (idata->ctx->msgcount * sizeof (HEADER*));
2740+ memcpy (idata->ctx->hdrs, hdrs, idata->ctx->msgcount * sizeof (HEADER*));
2741+
2742+ Sort = SORT_ORDER;
2743+ qsort (idata->ctx->hdrs, idata->ctx->msgcount, sizeof (HEADER*),
2744+ mutt_get_sort_func (SORT_ORDER));
2745+ }
2746+
2747+ pos = 0;
2748+
2749+ do
2750+ {
2751+ cmd->dptr = cmd->data;
2752+ mutt_buffer_printf (cmd, "%s ", pre);
2753+ rc = imap_make_msg_set (idata, cmd, flag, changed, invert, &pos);
2754+ if (rc > 0)
2755+ {
2756+ mutt_buffer_printf (cmd, " %s", post);
2757+ if (imap_exec (idata, cmd->data, IMAP_CMD_QUEUE))
2758+ {
2759+ rc = -1;
2760+ goto out;
2761+ }
2762+ count += rc;
2763+ }
2764+ }
2765+ while (rc > 0);
2766+
2767+ rc = count;
2768+
2769+out:
2770+ mutt_buffer_free (&cmd);
2771+ if (oldsort != Sort)
2772+ {
2773+ Sort = oldsort;
2774+ FREE (&idata->ctx->hdrs);
2775+ idata->ctx->hdrs = hdrs;
2776+ }
2777+
2778+ return rc;
2779+}
2780+
2781+/* returns 0 if mutt's flags match cached server flags */
2782+static int compare_flags (HEADER* h)
2783+{
2784+ IMAP_HEADER_DATA* hd = (IMAP_HEADER_DATA*)h->data;
2785+
2786+ if (h->read != hd->read)
2787+ return 1;
2788+ if (h->old != hd->old)
2789+ return 1;
2790+ if (h->flagged != hd->flagged)
2791+ return 1;
2792+ if (h->replied != hd->replied)
2793+ return 1;
2794+ if (h->deleted != hd->deleted)
2795+ return 1;
2796+
2797+ return 0;
2798+}
2799+
2800+/* Update the IMAP server to reflect the flags a single message. */
2801+int imap_sync_message (IMAP_DATA *idata, HEADER *hdr, BUFFER *cmd,
2802+ int *err_continue)
2803+{
2804+ char flags[LONG_STRING];
2805+ char uid[11];
2806+
2807+ hdr->changed = 0;
2808+
2809+ if (!compare_flags (hdr))
2810+ {
2811+ idata->ctx->changed--;
2812+ return 0;
2813+ }
2814+
2815+ snprintf (uid, sizeof (uid), "%u", HEADER_DATA(hdr)->uid);
2816+ cmd->dptr = cmd->data;
2817+ mutt_buffer_addstr (cmd, "UID STORE ");
2818+ mutt_buffer_addstr (cmd, uid);
2819+
2820+ flags[0] = '\0';
2821+
2822+ imap_set_flag (idata, M_ACL_SEEN, hdr->read, "\\Seen ",
2823+ flags, sizeof (flags));
2824+ imap_set_flag (idata, M_ACL_WRITE, hdr->old,
2825+ "Old ", flags, sizeof (flags));
2826+ imap_set_flag (idata, M_ACL_WRITE, hdr->flagged,
2827+ "\\Flagged ", flags, sizeof (flags));
2828+ imap_set_flag (idata, M_ACL_WRITE, hdr->replied,
2829+ "\\Answered ", flags, sizeof (flags));
2830+ imap_set_flag (idata, M_ACL_DELETE, hdr->deleted,
2831+ "\\Deleted ", flags, sizeof (flags));
2832+
2833+ /* now make sure we don't lose custom tags */
2834+ if (mutt_bit_isset (idata->ctx->rights, M_ACL_WRITE))
2835+ imap_add_keywords (flags, hdr, idata->flags, sizeof (flags));
2836+
2837+ mutt_remove_trailing_ws (flags);
2838+
2839+ /* UW-IMAP is OK with null flags, Cyrus isn't. The only solution is to
2840+ * explicitly revoke all system flags (if we have permission) */
2841+ if (!*flags)
2842+ {
2843+ imap_set_flag (idata, M_ACL_SEEN, 1, "\\Seen ", flags, sizeof (flags));
2844+ imap_set_flag (idata, M_ACL_WRITE, 1, "Old ", flags, sizeof (flags));
2845+ imap_set_flag (idata, M_ACL_WRITE, 1, "\\Flagged ", flags, sizeof (flags));
2846+ imap_set_flag (idata, M_ACL_WRITE, 1, "\\Answered ", flags, sizeof (flags));
2847+ imap_set_flag (idata, M_ACL_DELETE, 1, "\\Deleted ", flags, sizeof (flags));
2848+
2849+ mutt_remove_trailing_ws (flags);
2850+
2851+ mutt_buffer_addstr (cmd, " -FLAGS.SILENT (");
2852+ } else
2853+ mutt_buffer_addstr (cmd, " FLAGS.SILENT (");
2854+
2855+ mutt_buffer_addstr (cmd, flags);
2856+ mutt_buffer_addstr (cmd, ")");
2857+
2858+ /* dumb hack for bad UW-IMAP 4.7 servers spurious FLAGS updates */
2859+ hdr->active = 0;
2860+
2861+ /* after all this it's still possible to have no flags, if you
2862+ * have no ACL rights */
2863+ if (*flags && (imap_exec (idata, cmd->data, 0) != 0) &&
2864+ err_continue && (*err_continue != M_YES))
2865+ {
2866+ *err_continue = imap_continue ("imap_sync_message: STORE failed",
2867+ idata->buf);
2868+ if (*err_continue != M_YES)
2869+ return -1;
2870+ }
2871+
2872+ hdr->active = 1;
2873+ idata->ctx->changed--;
2874+
2875+ return 0;
2876+}
2877+
2878+static int sync_helper (IMAP_DATA* idata, int right, int flag, const char* name)
2879+{
2880+ int count = 0;
2881+ int rc;
2882+ char buf[LONG_STRING];
2883+
2884+ if (!idata->ctx)
2885+ return -1;
2886+
2887+ if (!mutt_bit_isset (idata->ctx->rights, right))
2888+ return 0;
2889+
2890+ if (right == M_ACL_WRITE && !imap_has_flag (idata->flags, name))
2891+ return 0;
2892+
2893+ snprintf (buf, sizeof(buf), "+FLAGS.SILENT (%s)", name);
2894+ if ((rc = imap_exec_msgset (idata, "UID STORE", buf, flag, 1, 0)) < 0)
2895+ return rc;
2896+ count += rc;
2897+
2898+ buf[0] = '-';
2899+ if ((rc = imap_exec_msgset (idata, "UID STORE", buf, flag, 1, 1)) < 0)
2900+ return rc;
2901+ count += rc;
2902+
2903+ return count;
2904+}
2905+
2906+/* update the IMAP server to reflect message changes done within mutt.
2907+ * Arguments
2908+ * ctx: the current context
2909+ * expunge: 0 or 1 - do expunge?
2910+ */
2911+int imap_sync_mailbox (CONTEXT* ctx, int expunge, int* index_hint)
2912+{
2913+ IMAP_DATA* idata;
2914+ CONTEXT* appendctx = NULL;
2915+ HEADER* h;
2916+ HEADER** hdrs = NULL;
2917+ int oldsort;
2918+ int n;
2919+ int rc;
2920+
2921+ idata = (IMAP_DATA*) ctx->data;
2922+
2923+ if (idata->state < IMAP_SELECTED)
2924+ {
2925+ dprint (2, (debugfile, "imap_sync_mailbox: no mailbox selected\n"));
2926+ return -1;
2927+ }
2928+
2929+ /* This function is only called when the calling code expects the context
2930+ * to be changed. */
2931+ imap_allow_reopen (ctx);
2932+
2933+ if ((rc = imap_check_mailbox (ctx, index_hint, 0)) != 0)
2934+ return rc;
2935+
2936+ /* if we are expunging anyway, we can do deleted messages very quickly... */
2937+ if (expunge && mutt_bit_isset (ctx->rights, M_ACL_DELETE))
2938+ {
2939+ if ((rc = imap_exec_msgset (idata, "UID STORE", "+FLAGS.SILENT (\\Deleted)",
2940+ M_DELETED, 1, 0)) < 0)
2941+ {
2942+ mutt_error (_("Expunge failed"));
2943+ mutt_sleep (1);
2944+ goto out;
2945+ }
2946+
2947+ if (rc > 0)
2948+ {
2949+ /* mark these messages as unchanged so second pass ignores them. Done
2950+ * here so BOGUS UW-IMAP 4.7 SILENT FLAGS updates are ignored. */
2951+ for (n = 0; n < ctx->msgcount; n++)
2952+ if (ctx->hdrs[n]->deleted && ctx->hdrs[n]->changed)
2953+ ctx->hdrs[n]->active = 0;
2954+ mutt_message (_("Marking %d messages deleted..."), rc);
2955+ }
2956+ }
2957+
2958+#if USE_HCACHE
2959+ idata->hcache = imap_hcache_open (idata, NULL);
2960+#endif
2961+
2962+ /* save messages with real (non-flag) changes */
2963+ for (n = 0; n < ctx->msgcount; n++)
2964+ {
2965+ h = ctx->hdrs[n];
2966+
2967+ if (h->deleted)
2968+ {
2969+ imap_cache_del (idata, h);
2970+#if USE_HCACHE
2971+ imap_hcache_del (idata, HEADER_DATA(h)->uid);
2972+#endif
2973+ }
2974+
2975+ if (h->active && h->changed)
2976+ {
2977+#if USE_HCACHE
2978+ imap_hcache_put (idata, h);
2979+#endif
2980+ /* if the message has been rethreaded or attachments have been deleted
2981+ * we delete the message and reupload it.
2982+ * This works better if we're expunging, of course. */
2983+ if ((h->env && (h->env->refs_changed || h->env->irt_changed)) ||
2984+ h->attach_del)
2985+ {
2986+ mutt_message (_("Saving changed messages... [%d/%d]"), n+1,
2987+ ctx->msgcount);
2988+ if (!appendctx)
2989+ appendctx = mx_open_mailbox (ctx->path, M_APPEND | M_QUIET, NULL);
2990+ if (!appendctx)
2991+ dprint (1, (debugfile, "imap_sync_mailbox: Error opening mailbox in append mode\n"));
2992+ else
2993+ _mutt_save_message (h, appendctx, 1, 0, 0);
2994+ }
2995+ }
2996+ }
2997+
2998+#if USE_HCACHE
2999+ imap_hcache_close (idata);
3000+#endif
3001+
3002+ /* presort here to avoid doing 10 resorts in imap_exec_msgset */
3003+ oldsort = Sort;
3004+ if (Sort != SORT_ORDER)
3005+ {
3006+ hdrs = ctx->hdrs;
3007+ ctx->hdrs = safe_malloc (ctx->msgcount * sizeof (HEADER*));
3008+ memcpy (ctx->hdrs, hdrs, ctx->msgcount * sizeof (HEADER*));
3009+
3010+ Sort = SORT_ORDER;
3011+ qsort (ctx->hdrs, ctx->msgcount, sizeof (HEADER*),
3012+ mutt_get_sort_func (SORT_ORDER));
3013+ }
3014+
3015+ rc = sync_helper (idata, M_ACL_DELETE, M_DELETED, "\\Deleted");
3016+ if (rc >= 0)
3017+ rc |= sync_helper (idata, M_ACL_WRITE, M_FLAG, "\\Flagged");
3018+ if (rc >= 0)
3019+ rc |= sync_helper (idata, M_ACL_WRITE, M_OLD, "Old");
3020+ if (rc >= 0)
3021+ rc |= sync_helper (idata, M_ACL_SEEN, M_READ, "\\Seen");
3022+ if (rc >= 0)
3023+ rc |= sync_helper (idata, M_ACL_WRITE, M_REPLIED, "\\Answered");
3024+
3025+ if (oldsort != Sort)
3026+ {
3027+ Sort = oldsort;
3028+ FREE (&ctx->hdrs);
3029+ ctx->hdrs = hdrs;
3030+ }
3031+
3032+ /* Flush the queued flags if any were changed in sync_helper. */
3033+ if (rc > 0)
3034+ if (imap_exec (idata, NULL, 0) != IMAP_CMD_OK)
3035+ rc = -1;
3036+
3037+ if (rc < 0)
3038+ {
3039+ if (ctx->closing)
3040+ {
3041+ if (mutt_yesorno (_("Error saving flags. Close anyway?"), 0) == M_YES)
3042+ {
3043+ rc = 0;
3044+ idata->state = IMAP_AUTHENTICATED;
3045+ goto out;
3046+ }
3047+ }
3048+ else
3049+ mutt_error _("Error saving flags");
3050+ rc = -1;
3051+ goto out;
3052+ }
3053+
3054+ /* Update local record of server state to reflect the synchronization just
3055+ * completed. imap_read_headers always overwrites hcache-origin flags, so
3056+ * there is no need to mutate the hcache after flag-only changes. */
3057+ for (n = 0; n < ctx->msgcount; n++)
3058+ {
3059+ HEADER_DATA(ctx->hdrs[n])->deleted = ctx->hdrs[n]->deleted;
3060+ HEADER_DATA(ctx->hdrs[n])->flagged = ctx->hdrs[n]->flagged;
3061+ HEADER_DATA(ctx->hdrs[n])->old = ctx->hdrs[n]->old;
3062+ HEADER_DATA(ctx->hdrs[n])->read = ctx->hdrs[n]->read;
3063+ HEADER_DATA(ctx->hdrs[n])->replied = ctx->hdrs[n]->replied;
3064+ ctx->hdrs[n]->changed = 0;
3065+ }
3066+ ctx->changed = 0;
3067+
3068+ /* We must send an EXPUNGE command if we're not closing. */
3069+ if (expunge && !(ctx->closing) &&
3070+ mutt_bit_isset(ctx->rights, M_ACL_DELETE))
3071+ {
3072+ mutt_message _("Expunging messages from server...");
3073+ /* Set expunge bit so we don't get spurious reopened messages */
3074+ idata->reopen |= IMAP_EXPUNGE_EXPECTED;
3075+ if (imap_exec (idata, "EXPUNGE", 0) != 0)
3076+ {
3077+ imap_error (_("imap_sync_mailbox: EXPUNGE failed"), idata->buf);
3078+ rc = -1;
3079+ goto out;
3080+ }
3081+ }
3082+
3083+ if (expunge && ctx->closing)
3084+ {
3085+ imap_exec (idata, "CLOSE", IMAP_CMD_QUEUE);
3086+ idata->state = IMAP_AUTHENTICATED;
3087+ }
3088+
3089+ if (option (OPTMESSAGECACHECLEAN))
3090+ imap_cache_clean (idata);
3091+
3092+ rc = 0;
3093+
3094+ out:
3095+ if (appendctx)
3096+ {
3097+ mx_fastclose_mailbox (appendctx);
3098+ FREE (&appendctx);
3099+ }
3100+ return rc;
3101+}
3102+
3103+/* imap_close_mailbox: clean up IMAP data in CONTEXT */
3104+int imap_close_mailbox (CONTEXT* ctx)
3105+{
3106+ IMAP_DATA* idata;
3107+ int i;
3108+
3109+ idata = (IMAP_DATA*) ctx->data;
3110+ /* Check to see if the mailbox is actually open */
3111+ if (!idata)
3112+ return 0;
3113+
3114+ if (ctx == idata->ctx)
3115+ {
3116+ if (idata->status != IMAP_FATAL && idata->state >= IMAP_SELECTED)
3117+ {
3118+ /* mx_close_mailbox won't sync if there are no deleted messages
3119+ * and the mailbox is unchanged, so we may have to close here */
3120+ if (!ctx->deleted)
3121+ imap_exec (idata, "CLOSE", IMAP_CMD_QUEUE);
3122+ idata->state = IMAP_AUTHENTICATED;
3123+ }
3124+
3125+ idata->reopen &= IMAP_REOPEN_ALLOW;
3126+ FREE (&(idata->mailbox));
3127+ mutt_free_list (&idata->flags);
3128+ idata->ctx = NULL;
3129+ }
3130+
3131+ /* free IMAP part of headers */
3132+ for (i = 0; i < ctx->msgcount; i++)
3133+ /* mailbox may not have fully loaded */
3134+ if (ctx->hdrs[i] && ctx->hdrs[i]->data)
3135+ imap_free_header_data ((IMAP_HEADER_DATA**)&(ctx->hdrs[i]->data));
3136+
3137+ for (i = 0; i < IMAP_CACHE_LEN; i++)
3138+ {
3139+ if (idata->cache[i].path)
3140+ {
3141+ unlink (idata->cache[i].path);
3142+ FREE (&idata->cache[i].path);
3143+ }
3144+ }
3145+
3146+ mutt_bcache_close (&idata->bcache);
3147+
3148+ return 0;
3149+}
3150+
3151+/* use the NOOP or IDLE command to poll for new mail
3152+ *
3153+ * return values:
3154+ * M_REOPENED mailbox has been externally modified
3155+ * M_NEW_MAIL new mail has arrived!
3156+ * 0 no change
3157+ * -1 error
3158+ */
3159+int imap_check_mailbox (CONTEXT *ctx, int *index_hint, int force)
3160+{
3161+ /* overload keyboard timeout to avoid many mailbox checks in a row.
3162+ * Most users don't like having to wait exactly when they press a key. */
3163+ IMAP_DATA* idata;
3164+ int result = 0;
3165+
3166+ idata = (IMAP_DATA*) ctx->data;
3167+
3168+ /* try IDLE first, unless force is set */
3169+ if (!force && option (OPTIMAPIDLE) && mutt_bit_isset (idata->capabilities, IDLE)
3170+ && (idata->state != IMAP_IDLE || time(NULL) >= idata->lastread + ImapKeepalive))
3171+ {
3172+ if (imap_cmd_idle (idata) < 0)
3173+ return -1;
3174+ }
3175+ if (idata->state == IMAP_IDLE)
3176+ {
3177+ while ((result = mutt_socket_poll (idata->conn)) > 0)
3178+ {
3179+ if (imap_cmd_step (idata) != IMAP_CMD_CONTINUE)
3180+ {
3181+ dprint (1, (debugfile, "Error reading IDLE response\n"));
3182+ return -1;
3183+ }
3184+ }
3185+ if (result < 0)
3186+ {
3187+ dprint (1, (debugfile, "Poll failed, disabling IDLE\n"));
3188+ mutt_bit_unset (idata->capabilities, IDLE);
3189+ }
3190+ }
3191+
3192+ if ((force ||
3193+ (idata->state != IMAP_IDLE && time(NULL) >= idata->lastread + Timeout))
3194+ && imap_exec (idata, "NOOP", 0) != 0)
3195+ return -1;
3196+
3197+ /* We call this even when we haven't run NOOP in case we have pending
3198+ * changes to process, since we can reopen here. */
3199+ imap_cmd_finish (idata);
3200+
3201+ if (idata->check_status & IMAP_EXPUNGE_PENDING)
3202+ result = M_REOPENED;
3203+ else if (idata->check_status & IMAP_NEWMAIL_PENDING)
3204+ result = M_NEW_MAIL;
3205+ else if (idata->check_status & IMAP_FLAGS_PENDING)
3206+ result = M_FLAGS;
3207+
3208+ idata->check_status = 0;
3209+
3210+ return result;
3211+}
3212+
3213+/* split path into (idata,mailbox name) */
3214+static int imap_get_mailbox (const char* path, IMAP_DATA** hidata, char* buf, size_t blen)
3215+{
3216+ IMAP_MBOX mx;
3217+
3218+ if (imap_parse_path (path, &mx))
3219+ {
3220+ dprint (1, (debugfile, "imap_get_mailbox: Error parsing %s\n", path));
3221+ return -1;
3222+ }
3223+ if (!(*hidata = imap_conn_find (&(mx.account), option (OPTIMAPPASSIVE) ? M_IMAP_CONN_NONEW : 0))
3224+ || (*hidata)->state < IMAP_AUTHENTICATED)
3225+ {
3226+ FREE (&mx.mbox);
3227+ return -1;
3228+ }
3229+
3230+ imap_fix_path (*hidata, mx.mbox, buf, blen);
3231+ if (!*buf)
3232+ strfcpy (buf, "INBOX", blen);
3233+ FREE (&mx.mbox);
3234+
3235+ return 0;
3236+}
3237+
3238+/* check for new mail in any subscribed mailboxes. Given a list of mailboxes
3239+ * rather than called once for each so that it can batch the commands and
3240+ * save on round trips. Returns number of mailboxes with new mail. */
3241+int imap_buffy_check (int force)
3242+{
3243+ IMAP_DATA* idata;
3244+ IMAP_DATA* lastdata = NULL;
3245+ BUFFY* mailbox;
3246+ char name[LONG_STRING];
3247+ char command[LONG_STRING];
3248+ char munged[LONG_STRING];
3249+ int buffies = 0;
3250+
3251+ for (mailbox = Incoming; mailbox; mailbox = mailbox->next)
3252+ {
3253+ /* Init newly-added mailboxes */
3254+ if (! mailbox->magic)
3255+ {
3256+ if (mx_is_imap (mailbox->path))
3257+ mailbox->magic = M_IMAP;
3258+ }
3259+
3260+ if (mailbox->magic != M_IMAP)
3261+ continue;
3262+
3263+ mailbox->new = 0;
3264+
3265+ if (imap_get_mailbox (mailbox->path, &idata, name, sizeof (name)) < 0)
3266+ continue;
3267+
3268+ /* Don't issue STATUS on the selected mailbox, it will be NOOPed or
3269+ * IDLEd elsewhere.
3270+ * idata->mailbox may be NULL for connections other than the current
3271+ * mailbox's, and shouldn't expand to INBOX in that case. #3216. */
3272+ if (idata->mailbox && !imap_mxcmp (name, idata->mailbox))
3273+ continue;
3274+
3275+ if (!mutt_bit_isset (idata->capabilities, IMAP4REV1) &&
3276+ !mutt_bit_isset (idata->capabilities, STATUS))
3277+ {
3278+ dprint (2, (debugfile, "Server doesn't support STATUS\n"));
3279+ continue;
3280+ }
3281+
3282+ if (lastdata && idata != lastdata)
3283+ {
3284+ /* Send commands to previous server. Sorting the buffy list
3285+ * may prevent some infelicitous interleavings */
3286+ if (imap_exec (lastdata, NULL, IMAP_CMD_FAIL_OK) == -1)
3287+ dprint (1, (debugfile, "Error polling mailboxes\n"));
3288+
3289+ lastdata = NULL;
3290+ }
3291+
3292+ if (!lastdata)
3293+ lastdata = idata;
3294+
3295+ imap_munge_mbox_name (idata, munged, sizeof (munged), name);
3296+ snprintf (command, sizeof (command),
3297+ "STATUS %s (UIDNEXT UIDVALIDITY UNSEEN RECENT)", munged);
3298+
3299+ if (imap_exec (idata, command, IMAP_CMD_QUEUE) < 0)
3300+ {
3301+ dprint (1, (debugfile, "Error queueing command\n"));
3302+ return 0;
3303+ }
3304+ }
3305+
3306+ if (lastdata && (imap_exec (lastdata, NULL, IMAP_CMD_FAIL_OK) == -1))
3307+ {
3308+ dprint (1, (debugfile, "Error polling mailboxes\n"));
3309+ return 0;
3310+ }
3311+
3312+ /* collect results */
3313+ for (mailbox = Incoming; mailbox; mailbox = mailbox->next)
3314+ {
3315+ if (mailbox->magic == M_IMAP && mailbox->new)
3316+ buffies++;
3317+ }
3318+
3319+ return buffies;
3320+}
3321+
3322+/* imap_status: returns count of messages in mailbox, or -1 on error.
3323+ * if queue != 0, queue the command and expect it to have been run
3324+ * on the next call (for pipelining the postponed count) */
3325+int imap_status (char* path, int queue)
3326+{
3327+ static int queued = 0;
3328+
3329+ IMAP_DATA *idata;
3330+ char buf[LONG_STRING];
3331+ char mbox[LONG_STRING];
3332+ IMAP_STATUS* status;
3333+
3334+ if (imap_get_mailbox (path, &idata, buf, sizeof (buf)) < 0)
3335+ return -1;
3336+
3337+ if (!imap_mxcmp (buf, idata->mailbox))
3338+ /* We are in the folder we're polling - just return the mailbox count */
3339+ return idata->ctx->msgcount;
3340+ else if (mutt_bit_isset(idata->capabilities,IMAP4REV1) ||
3341+ mutt_bit_isset(idata->capabilities,STATUS))
3342+ {
3343+ imap_munge_mbox_name (idata, mbox, sizeof(mbox), buf);
3344+ snprintf (buf, sizeof (buf), "STATUS %s (%s)", mbox, "MESSAGES");
3345+ imap_unmunge_mbox_name (idata, mbox);
3346+ }
3347+ else
3348+ /* Server does not support STATUS, and this is not the current mailbox.
3349+ * There is no lightweight way to check recent arrivals */
3350+ return -1;
3351+
3352+ if (queue)
3353+ {
3354+ imap_exec (idata, buf, IMAP_CMD_QUEUE);
3355+ queued = 1;
3356+ return 0;
3357+ }
3358+ else if (!queued)
3359+ imap_exec (idata, buf, 0);
3360+
3361+ queued = 0;
3362+ if ((status = imap_mboxcache_get (idata, mbox, 0)))
3363+ return status->messages;
3364+
3365+ return 0;
3366+}
3367+
3368+/* return cached mailbox stats or NULL if create is 0 */
3369+IMAP_STATUS* imap_mboxcache_get (IMAP_DATA* idata, const char* mbox, int create)
3370+{
3371+ LIST* cur;
3372+ IMAP_STATUS* status;
3373+ IMAP_STATUS scache;
3374+#ifdef USE_HCACHE
3375+ header_cache_t *hc = NULL;
3376+ unsigned int *uidvalidity = NULL;
3377+ unsigned int *uidnext = NULL;
3378+#endif
3379+
3380+ for (cur = idata->mboxcache; cur; cur = cur->next)
3381+ {
3382+ status = (IMAP_STATUS*)cur->data;
3383+
3384+ if (!imap_mxcmp (mbox, status->name))
3385+ return status;
3386+ }
3387+ status = NULL;
3388+
3389+ /* lame */
3390+ if (create)
3391+ {
3392+ memset (&scache, 0, sizeof (scache));
3393+ scache.name = (char*)mbox;
3394+ idata->mboxcache = mutt_add_list_n (idata->mboxcache, &scache,
3395+ sizeof (scache));
3396+ status = imap_mboxcache_get (idata, mbox, 0);
3397+ status->name = safe_strdup (mbox);
3398+ }
3399+
3400+#ifdef USE_HCACHE
3401+ hc = imap_hcache_open (idata, mbox);
3402+ if (hc)
3403+ {
3404+ uidvalidity = mutt_hcache_fetch_raw (hc, "/UIDVALIDITY", imap_hcache_keylen);
3405+ uidnext = mutt_hcache_fetch_raw (hc, "/UIDNEXT", imap_hcache_keylen);
3406+ mutt_hcache_close (hc);
3407+ if (uidvalidity)
3408+ {
3409+ if (!status)
3410+ {
3411+ FREE (&uidvalidity);
3412+ FREE (&uidnext);
3413+ return imap_mboxcache_get (idata, mbox, 1);
3414+ }
3415+ status->uidvalidity = *uidvalidity;
3416+ status->uidnext = uidnext ? *uidnext: 0;
3417+ dprint (3, (debugfile, "mboxcache: hcache uidvalidity %d, uidnext %d\n",
3418+ status->uidvalidity, status->uidnext));
3419+ }
3420+ FREE (&uidvalidity);
3421+ FREE (&uidnext);
3422+ }
3423+#endif
3424+
3425+ return status;
3426+}
3427+
3428+void imap_mboxcache_free (IMAP_DATA* idata)
3429+{
3430+ LIST* cur;
3431+ IMAP_STATUS* status;
3432+
3433+ for (cur = idata->mboxcache; cur; cur = cur->next)
3434+ {
3435+ status = (IMAP_STATUS*)cur->data;
3436+
3437+ FREE (&status->name);
3438+ }
3439+
3440+ mutt_free_list (&idata->mboxcache);
3441+}
3442+
3443+/* returns number of patterns in the search that should be done server-side
3444+ * (eg are full-text) */
3445+static int do_search (const pattern_t* search, int allpats)
3446+{
3447+ int rc = 0;
3448+ const pattern_t* pat;
3449+
3450+ for (pat = search; pat; pat = pat->next)
3451+ {
3452+ switch (pat->op)
3453+ {
3454+ case M_BODY:
3455+ case M_HEADER:
3456+ case M_WHOLE_MSG:
3457+ if (pat->stringmatch)
3458+ rc++;
3459+ break;
3460+ default:
3461+ if (pat->child && do_search (pat->child, 1))
3462+ rc++;
3463+ }
3464+
3465+ if (!allpats)
3466+ break;
3467+ }
3468+
3469+ return rc;
3470+}
3471+
3472+/* convert mutt pattern_t to IMAP SEARCH command containing only elements
3473+ * that require full-text search (mutt already has what it needs for most
3474+ * match types, and does a better job (eg server doesn't support regexps). */
3475+static int imap_compile_search (const pattern_t* pat, BUFFER* buf)
3476+{
3477+ if (! do_search (pat, 0))
3478+ return 0;
3479+
3480+ if (pat->not)
3481+ mutt_buffer_addstr (buf, "NOT ");
3482+
3483+ if (pat->child)
3484+ {
3485+ int clauses;
3486+
3487+ if ((clauses = do_search (pat->child, 1)) > 0)
3488+ {
3489+ const pattern_t* clause = pat->child;
3490+
3491+ mutt_buffer_addch (buf, '(');
3492+
3493+ while (clauses)
3494+ {
3495+ if (do_search (clause, 0))
3496+ {
3497+ if (pat->op == M_OR && clauses > 1)
3498+ mutt_buffer_addstr (buf, "OR ");
3499+ clauses--;
3500+
3501+ if (imap_compile_search (clause, buf) < 0)
3502+ return -1;
3503+
3504+ if (clauses)
3505+ mutt_buffer_addch (buf, ' ');
3506+
3507+ }
3508+ clause = clause->next;
3509+ }
3510+
3511+ mutt_buffer_addch (buf, ')');
3512+ }
3513+ }
3514+ else
3515+ {
3516+ char term[STRING];
3517+ char *delim;
3518+
3519+ switch (pat->op)
3520+ {
3521+ case M_HEADER:
3522+ mutt_buffer_addstr (buf, "HEADER ");
3523+
3524+ /* extract header name */
3525+ if (! (delim = strchr (pat->p.str, ':')))
3526+ {
3527+ mutt_error (_("Header search without header name: %s"), pat->p.str);
3528+ return -1;
3529+ }
3530+ *delim = '\0';
3531+ imap_quote_string (term, sizeof (term), pat->p.str);
3532+ mutt_buffer_addstr (buf, term);
3533+ mutt_buffer_addch (buf, ' ');
3534+
3535+ /* and field */
3536+ *delim = ':';
3537+ delim++;
3538+ SKIPWS(delim);
3539+ imap_quote_string (term, sizeof (term), delim);
3540+ mutt_buffer_addstr (buf, term);
3541+ break;
3542+ case M_BODY:
3543+ mutt_buffer_addstr (buf, "BODY ");
3544+ imap_quote_string (term, sizeof (term), pat->p.str);
3545+ mutt_buffer_addstr (buf, term);
3546+ break;
3547+ case M_WHOLE_MSG:
3548+ mutt_buffer_addstr (buf, "TEXT ");
3549+ imap_quote_string (term, sizeof (term), pat->p.str);
3550+ mutt_buffer_addstr (buf, term);
3551+ break;
3552+ }
3553+ }
3554+
3555+ return 0;
3556+}
3557+
3558+int imap_search (CONTEXT* ctx, const pattern_t* pat)
3559+{
3560+ BUFFER buf;
3561+ IMAP_DATA* idata = (IMAP_DATA*)ctx->data;
3562+ int i;
3563+
3564+ for (i = 0; i < ctx->msgcount; i++)
3565+ ctx->hdrs[i]->matched = 0;
3566+
3567+ if (!do_search (pat, 1))
3568+ return 0;
3569+
3570+ mutt_buffer_init (&buf);
3571+ mutt_buffer_addstr (&buf, "UID SEARCH ");
3572+ if (imap_compile_search (pat, &buf) < 0)
3573+ {
3574+ FREE (&buf.data);
3575+ return -1;
3576+ }
3577+ if (imap_exec (idata, buf.data, 0) < 0)
3578+ {
3579+ FREE (&buf.data);
3580+ return -1;
3581+ }
3582+
3583+ FREE (&buf.data);
3584+ return 0;
3585+}
3586+
3587+int imap_subscribe (char *path, int subscribe)
3588+{
3589+ IMAP_DATA *idata;
3590+ char buf[LONG_STRING];
3591+ char mbox[LONG_STRING];
3592+ char errstr[STRING];
3593+ BUFFER err, token;
3594+ IMAP_MBOX mx;
3595+
3596+ if (!mx_is_imap (path) || imap_parse_path (path, &mx) || !mx.mbox)
3597+ {
3598+ mutt_error (_("Bad mailbox name"));
3599+ return -1;
3600+ }
3601+ if (!(idata = imap_conn_find (&(mx.account), 0)))
3602+ goto fail;
3603+
3604+ imap_fix_path (idata, mx.mbox, buf, sizeof (buf));
3605+ if (!*buf)
3606+ strfcpy (buf, "INBOX", sizeof (buf));
3607+
3608+ if (option (OPTIMAPCHECKSUBSCRIBED))
3609+ {
3610+ mutt_buffer_init (&token);
3611+ mutt_buffer_init (&err);
3612+ err.data = errstr;
3613+ err.dsize = sizeof (errstr);
3614+ snprintf (mbox, sizeof (mbox), "%smailboxes \"%s\"",
3615+ subscribe ? "" : "un", path);
3616+ if (mutt_parse_rc_line (mbox, &token, &err))
3617+ dprint (1, (debugfile, "Error adding subscribed mailbox: %s\n", errstr));
3618+ FREE (&token.data);
3619+ }
3620+
3621+ if (subscribe)
3622+ mutt_message (_("Subscribing to %s..."), buf);
3623+ else
3624+ mutt_message (_("Unsubscribing from %s..."), buf);
3625+ imap_munge_mbox_name (idata, mbox, sizeof(mbox), buf);
3626+
3627+ snprintf (buf, sizeof (buf), "%sSUBSCRIBE %s", subscribe ? "" : "UN", mbox);
3628+
3629+ if (imap_exec (idata, buf, 0) < 0)
3630+ goto fail;
3631+
3632+ imap_unmunge_mbox_name(idata, mx.mbox);
3633+ if (subscribe)
3634+ mutt_message (_("Subscribed to %s"), mx.mbox);
3635+ else
3636+ mutt_message (_("Unsubscribed from %s"), mx.mbox);
3637+ FREE (&mx.mbox);
3638+ return 0;
3639+
3640+ fail:
3641+ FREE (&mx.mbox);
3642+ return -1;
3643+}
3644+
3645+/* trim dest to the length of the longest prefix it shares with src,
3646+ * returning the length of the trimmed string */
3647+static int
3648+longest_common_prefix (char *dest, const char* src, int start, size_t dlen)
3649+{
3650+ int pos = start;
3651+
3652+ while (pos < dlen && dest[pos] && dest[pos] == src[pos])
3653+ pos++;
3654+ dest[pos] = '\0';
3655+
3656+ return pos;
3657+}
3658+
3659+/* look for IMAP URLs to complete from defined mailboxes. Could be extended
3660+ * to complete over open connections and account/folder hooks too. */
3661+static int
3662+imap_complete_hosts (char *dest, size_t len)
3663+{
3664+ BUFFY* mailbox;
3665+ CONNECTION* conn;
3666+ int rc = -1;
3667+ int matchlen;
3668+
3669+ matchlen = mutt_strlen (dest);
3670+ for (mailbox = Incoming; mailbox; mailbox = mailbox->next)
3671+ {
3672+ if (!mutt_strncmp (dest, mailbox->path, matchlen))
3673+ {
3674+ if (rc)
3675+ {
3676+ strfcpy (dest, mailbox->path, len);
3677+ rc = 0;
3678+ }
3679+ else
3680+ longest_common_prefix (dest, mailbox->path, matchlen, len);
3681+ }
3682+ }
3683+
3684+ for (conn = mutt_socket_head (); conn; conn = conn->next)
3685+ {
3686+ ciss_url_t url;
3687+ char urlstr[LONG_STRING];
3688+
3689+ if (conn->account.type != M_ACCT_TYPE_IMAP)
3690+ continue;
3691+
3692+ mutt_account_tourl (&conn->account, &url);
3693+ /* FIXME: how to handle multiple users on the same host? */
3694+ url.user = NULL;
3695+ url.path = NULL;
3696+ url_ciss_tostring (&url, urlstr, sizeof (urlstr), 0);
3697+ if (!mutt_strncmp (dest, urlstr, matchlen))
3698+ {
3699+ if (rc)
3700+ {
3701+ strfcpy (dest, urlstr, len);
3702+ rc = 0;
3703+ }
3704+ else
3705+ longest_common_prefix (dest, urlstr, matchlen, len);
3706+ }
3707+ }
3708+
3709+ return rc;
3710+}
3711+
3712+/* imap_complete: given a partial IMAP folder path, return a string which
3713+ * adds as much to the path as is unique */
3714+int imap_complete(char* dest, size_t dlen, char* path) {
3715+ IMAP_DATA* idata;
3716+ char list[LONG_STRING];
3717+ char buf[LONG_STRING];
3718+ IMAP_LIST listresp;
3719+ char completion[LONG_STRING];
3720+ int clen, matchlen = 0;
3721+ int completions = 0;
3722+ IMAP_MBOX mx;
3723+ int rc;
3724+
3725+ if (imap_parse_path (path, &mx))
3726+ {
3727+ strfcpy (dest, path, dlen);
3728+ return imap_complete_hosts (dest, dlen);
3729+ }
3730+
3731+ /* don't open a new socket just for completion. Instead complete over
3732+ * known mailboxes/hooks/etc */
3733+ if (!(idata = imap_conn_find (&(mx.account), M_IMAP_CONN_NONEW)))
3734+ {
3735+ FREE (&mx.mbox);
3736+ strfcpy (dest, path, dlen);
3737+ return imap_complete_hosts (dest, dlen);
3738+ }
3739+
3740+ /* reformat path for IMAP list, and append wildcard */
3741+ /* don't use INBOX in place of "" */
3742+ if (mx.mbox && mx.mbox[0])
3743+ imap_fix_path (idata, mx.mbox, list, sizeof(list));
3744+ else
3745+ list[0] = '\0';
3746+
3747+ /* fire off command */
3748+ snprintf (buf, sizeof(buf), "%s \"\" \"%s%%\"",
3749+ option (OPTIMAPLSUB) ? "LSUB" : "LIST", list);
3750+
3751+ imap_cmd_start (idata, buf);
3752+
3753+ /* and see what the results are */
3754+ strfcpy (completion, NONULL(mx.mbox), sizeof(completion));
3755+ idata->cmdtype = IMAP_CT_LIST;
3756+ idata->cmddata = &listresp;
3757+ do
3758+ {
3759+ listresp.name = NULL;
3760+ rc = imap_cmd_step (idata);
3761+
3762+ if (rc == IMAP_CMD_CONTINUE && listresp.name)
3763+ {
3764+ /* if the folder isn't selectable, append delimiter to force browse
3765+ * to enter it on second tab. */
3766+ if (listresp.noselect)
3767+ {
3768+ clen = strlen(listresp.name);
3769+ listresp.name[clen++] = listresp.delim;
3770+ listresp.name[clen] = '\0';
3771+ }
3772+ /* copy in first word */
3773+ if (!completions)
3774+ {
3775+ strfcpy (completion, listresp.name, sizeof(completion));
3776+ matchlen = strlen (completion);
3777+ completions++;
3778+ continue;
3779+ }
3780+
3781+ matchlen = longest_common_prefix (completion, listresp.name, 0, matchlen);
3782+ completions++;
3783+ }
3784+ }
3785+ while (rc == IMAP_CMD_CONTINUE);
3786+ idata->cmddata = NULL;
3787+
3788+ if (completions)
3789+ {
3790+ /* reformat output */
3791+ imap_qualify_path (dest, dlen, &mx, completion);
3792+ mutt_pretty_mailbox (dest, dlen);
3793+
3794+ FREE (&mx.mbox);
3795+ return 0;
3796+ }
3797+
3798+ return -1;
3799+}
3800diff -rbuN mutt-1.6.0-orig/init.h mutt-1.6.0/init.h
3801--- mutt-1.6.0-orig/init.h 2016-04-02 14:12:22.000000000 -0400
3802+++ mutt-1.6.0/init.h 2016-04-04 23:42:10.897286656 -0400
3803@@ -2049,6 +2049,54 @@
3804 ** not used.
3805 ** (PGP only)
3806 */
3807+ {"sidebar_delim", DT_STR, R_BOTH, UL &SidebarDelim, UL "|"},
3808+ /*
3809+ ** .pp
3810+ ** This specifies the delimiter between the sidebar (if visible) and
3811+ ** other screens.
3812+ */
3813+ {"sidebar_indentstr", DT_STR, R_BOTH, UL &SidebarIndentStr, UL " "},
3814+ /*
3815+ ** .pp
3816+ ** This specifies the string that is used to indent items
3817+ ** with sidebar_folderindent= yes
3818+ */
3819+ { "sidebar_visible", DT_BOOL, R_BOTH, OPTSIDEBAR, 0 },
3820+ /*
3821+ ** .pp
3822+ ** This specifies whether or not to show sidebar (left-side list of folders).
3823+ */
3824+ { "sidebar_sort", DT_BOOL, R_BOTH, OPTSIDEBARSORT, 0 },
3825+ /*
3826+ ** .pp
3827+ ** This specifies whether or not to sort the sidebar alphabetically.
3828+ */
3829+ { "sidebar_width", DT_NUM, R_BOTH, UL &SidebarWidth, 0 },
3830+ /*
3831+ ** .pp
3832+ ** The width of the sidebar.
3833+ */
3834+ { "sidebar_shortpath", DT_BOOL, R_BOTH, OPTSIDEBARSHORTPATH, 0 },
3835+ /*
3836+ ** .pp
3837+ ** Should the sidebar shorten the path showed.
3838+ */
3839+ {"sidebar_format", DT_STR, R_NONE, UL &SidebarFormat, UL "%B%?F? [%F]?%* %?N?%N/?%4S"},
3840+ /*
3841+ ** .pp
3842+ ** Format string for the sidebar. The sequences `%N', `%F' and `%S'
3843+ ** will be replaced by the number of new or flagged messages or the total
3844+ ** size of them mailbox. `%B' will be replaced with the name of the mailbox.
3845+ ** The `%!' sequence will be expanded to `!' if there is one flagged message;
3846+ ** to `!!' if there are two flagged messages; and to `n!' for n flagged
3847+ ** messages, n>2.
3848+ */
3849+ { "sidebar_folderindent", DT_BOOL, R_BOTH, OPTSIDEBARFOLDERINDENT, 0 },
3850+ /*
3851+ ** .pp
3852+ ** Should folders be indented in the sidebar.
3853+ */
3854+
3855 { "pgp_use_gpg_agent", DT_BOOL, R_NONE, OPTUSEGPGAGENT, 0},
3856 /*
3857 ** .pp
3858diff -rbuN mutt-1.6.0-orig/mailbox.h mutt-1.6.0/mailbox.h
3859--- mutt-1.6.0-orig/mailbox.h 2016-04-02 14:12:22.000000000 -0400
3860+++ mutt-1.6.0/mailbox.h 2016-04-04 23:42:10.897286656 -0400
3861@@ -27,6 +27,7 @@
3862 #define M_NEWFOLDER (1<<4) /* create a new folder - same as M_APPEND, but uses
3863 * safe_fopen() for mbox-style folders.
3864 */
3865+#define M_PEEK (1<<5) /* revert atime back after taking a look (if applicable) */
3866
3867 /* mx_open_new_message() */
3868 #define M_ADD_FROM (1<<0) /* add a From_ line */
3869diff -rbuN mutt-1.6.0-orig/main.c mutt-1.6.0/main.c
3870--- mutt-1.6.0-orig/main.c 2016-04-02 14:12:22.000000000 -0400
3871+++ mutt-1.6.0/main.c 2016-04-04 23:42:10.900619885 -0400
3872@@ -50,6 +50,7 @@
3873 #include <unistd.h>
3874 #include <errno.h>
3875 #include <sys/stat.h>
3876+#include <limits.h>
3877 #include <sys/utsname.h>
3878
3879 #ifdef HAVE_GETOPT_H
3880@@ -557,7 +558,7 @@
3881
3882 int main (int argc, char **argv)
3883 {
3884- char folder[_POSIX_PATH_MAX] = "";
3885+ char folder[PATH_MAX] = "";
3886 char *subject = NULL;
3887 char *includeFile = NULL;
3888 char *draftFile = NULL;
3889@@ -1184,6 +1185,13 @@
3890 strfcpy (folder, NONULL(Spoolfile), sizeof (folder));
3891 mutt_expand_path (folder, sizeof (folder));
3892
3893+ {
3894+ char tmpfolder[PATH_MAX];
3895+ strfcpy (tmpfolder, folder, sizeof (tmpfolder));
3896+ if(!realpath(tmpfolder, folder))
3897+ strfcpy (folder, tmpfolder, sizeof (tmpfolder));
3898+ }
3899+
3900 mutt_str_replace (&CurrentFolder, folder);
3901 mutt_str_replace (&LastFolder, folder);
3902
3903@@ -1206,6 +1214,7 @@
3904 if((Context = mx_open_mailbox (folder, ((flags & M_RO) || option (OPTREADONLY)) ? M_READONLY : 0, NULL))
3905 || !explicit_folder)
3906 {
3907+ set_curbuffy(folder);
3908 mutt_index_menu ();
3909 if (Context)
3910 FREE (&Context);
3911diff -rbuN mutt-1.6.0-orig/Makefile.am mutt-1.6.0/Makefile.am
3912--- mutt-1.6.0-orig/Makefile.am 2016-04-02 14:12:22.000000000 -0400
3913+++ mutt-1.6.0/Makefile.am 2016-04-04 23:42:30.493345218 -0400
3914@@ -31,6 +31,7 @@
3915 main.c mbox.c menu.c mh.c mx.c pager.c parse.c pattern.c \
3916 postpone.c query.c recvattach.c recvcmd.c \
3917 rfc822.c rfc1524.c rfc2047.c rfc2231.c rfc3676.c \
3918+ sidebar.c \
3919 score.c send.c sendlib.c signal.c sort.c \
3920 status.c system.c thread.c charset.c history.c lib.c \
3921 muttlib.c editmsg.c mbyte.c mutt_idna.c \
3922diff -rbuN mutt-1.6.0-orig/Makefile.in mutt-1.6.0/Makefile.in
3923--- mutt-1.6.0-orig/Makefile.in 2016-04-02 14:14:47.000000000 -0400
3924+++ mutt-1.6.0/Makefile.in 2016-04-04 23:44:13.566813314 -0400
3925@@ -130,6 +130,7 @@
3926 recvcmd.$(OBJEXT) rfc822.$(OBJEXT) rfc1524.$(OBJEXT) \
3927 rfc2047.$(OBJEXT) rfc2231.$(OBJEXT) rfc3676.$(OBJEXT) \
3928 score.$(OBJEXT) send.$(OBJEXT) sendlib.$(OBJEXT) \
3929+ sidebar.$(OBJECT) \
3930 signal.$(OBJEXT) sort.$(OBJEXT) status.$(OBJEXT) \
3931 system.$(OBJEXT) thread.$(OBJEXT) charset.$(OBJEXT) \
3932 history.$(OBJEXT) lib.$(OBJEXT) muttlib.$(OBJEXT) \
3933@@ -486,6 +487,7 @@
3934 postpone.c query.c recvattach.c recvcmd.c \
3935 rfc822.c rfc1524.c rfc2047.c rfc2231.c rfc3676.c \
3936 score.c send.c sendlib.c signal.c sort.c \
3937+ sidebar.c \
3938 status.c system.c thread.c charset.c history.c lib.c \
3939 muttlib.c editmsg.c mbyte.c mutt_idna.c \
3940 url.c ascii.c crypt-mod.c crypt-mod.h safe_asprintf.c
3941@@ -816,6 +818,7 @@
3942 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send.Po@am__quote@
3943 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sendlib.Po@am__quote@
3944 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sha1.Po@am__quote@
3945+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sidebar.Po@am__quote@
3946 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signal.Po@am__quote@
3947 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smime.Po@am__quote@
3948 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smtp.Po@am__quote@
3949diff -rbuN mutt-1.6.0-orig/mbox.c mutt-1.6.0/mbox.c
3950--- mutt-1.6.0-orig/mbox.c 2016-04-02 14:12:22.000000000 -0400
3951+++ mutt-1.6.0/mbox.c 2016-04-04 23:42:10.900619885 -0400
3952@@ -100,6 +100,7 @@
3953 mutt_perror (ctx->path);
3954 return (-1);
3955 }
3956+ ctx->atime = sb.st_atime;
3957 ctx->mtime = sb.st_mtime;
3958 ctx->size = sb.st_size;
3959
3960@@ -251,6 +252,7 @@
3961
3962 ctx->size = sb.st_size;
3963 ctx->mtime = sb.st_mtime;
3964+ ctx->atime = sb.st_atime;
3965
3966 #ifdef NFS_ATTRIBUTE_HACK
3967 if (sb.st_mtime > sb.st_atime)
3968diff -rbuN mutt-1.6.0-orig/menu.c mutt-1.6.0/menu.c
3969--- mutt-1.6.0-orig/menu.c 2016-04-02 14:12:22.000000000 -0400
3970+++ mutt-1.6.0/menu.c 2016-04-04 23:42:10.903953115 -0400
3971@@ -24,6 +24,7 @@
3972 #include "mutt_curses.h"
3973 #include "mutt_menu.h"
3974 #include "mbyte.h"
3975+#include "sidebar.h"
3976
3977 char* SearchBuffers[MENU_MAX];
3978
3979@@ -184,7 +185,7 @@
3980 {
3981 char *scratch = safe_strdup (s);
3982 int shift = option (OPTARROWCURSOR) ? 3 : 0;
3983- int cols = COLS - shift;
3984+ int cols = COLS - shift - SidebarWidth;
3985
3986 mutt_format_string (s, n, cols, cols, FMT_LEFT, ' ', scratch, mutt_strlen (scratch), 1);
3987 s[n - 1] = 0;
3988@@ -237,6 +238,7 @@
3989 int do_color;
3990 int attr;
3991
3992+ draw_sidebar(1);
3993 for (i = menu->top; i < menu->top + menu->pagelen; i++)
3994 {
3995 if (i < menu->max)
3996@@ -247,7 +249,7 @@
3997 menu_pad_string (buf, sizeof (buf));
3998
3999 ATTRSET(attr);
4000- move(i - menu->top + menu->offset, 0);
4001+ move(i - menu->top + menu->offset, SidebarWidth);
4002 do_color = 1;
4003
4004 if (i == menu->current)
4005@@ -270,7 +272,7 @@
4006 else
4007 {
4008 NORMAL_COLOR;
4009- CLEARLINE(i - menu->top + menu->offset);
4010+ CLEARLINE_WIN (i - menu->top + menu->offset);
4011 }
4012 }
4013 NORMAL_COLOR;
4014@@ -287,7 +289,7 @@
4015 return;
4016 }
4017
4018- move (menu->oldcurrent + menu->offset - menu->top, 0);
4019+ move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth);
4020 ATTRSET(menu->color (menu->oldcurrent));
4021
4022 if (option (OPTARROWCURSOR))
4023@@ -299,13 +301,13 @@
4024 {
4025 menu_make_entry (buf, sizeof (buf), menu, menu->oldcurrent);
4026 menu_pad_string (buf, sizeof (buf));
4027- move (menu->oldcurrent + menu->offset - menu->top, 3);
4028+ move (menu->oldcurrent + menu->offset - menu->top, SidebarWidth + 3);
4029 print_enriched_string (menu->color(menu->oldcurrent), (unsigned char *) buf, 1);
4030 }
4031
4032 /* now draw it in the new location */
4033 SETCOLOR(MT_COLOR_INDICATOR);
4034- mvaddstr(menu->current + menu->offset - menu->top, 0, "->");
4035+ mvaddstr(menu->current + menu->offset - menu->top, SidebarWidth, "->");
4036 }
4037 else
4038 {
4039@@ -318,7 +320,7 @@
4040 menu_make_entry (buf, sizeof (buf), menu, menu->current);
4041 menu_pad_string (buf, sizeof (buf));
4042 SETCOLOR(MT_COLOR_INDICATOR);
4043- move(menu->current - menu->top + menu->offset, 0);
4044+ move(menu->current - menu->top + menu->offset, SidebarWidth);
4045 print_enriched_string (menu->color(menu->current), (unsigned char *) buf, 0);
4046 }
4047 menu->redraw &= REDRAW_STATUS;
4048@@ -330,7 +332,7 @@
4049 char buf[LONG_STRING];
4050 int attr = menu->color (menu->current);
4051
4052- move (menu->current + menu->offset - menu->top, 0);
4053+ move (menu->current + menu->offset - menu->top, SidebarWidth);
4054 menu_make_entry (buf, sizeof (buf), menu, menu->current);
4055 menu_pad_string (buf, sizeof (buf));
4056
4057@@ -873,7 +875,7 @@
4058
4059
4060 if (option (OPTARROWCURSOR))
4061- move (menu->current - menu->top + menu->offset, 2);
4062+ move (menu->current - menu->top + menu->offset, SidebarWidth + 2);
4063 else if (option (OPTBRAILLEFRIENDLY))
4064 move (menu->current - menu->top + menu->offset, 0);
4065 else
4066diff -rbuN mutt-1.6.0-orig/mh.c mutt-1.6.0/mh.c
4067--- mutt-1.6.0-orig/mh.c 2016-04-02 14:12:22.000000000 -0400
4068+++ mutt-1.6.0/mh.c 2016-04-04 23:42:10.903953115 -0400
4069@@ -295,6 +295,32 @@
4070 mhs_free_sequences (&mhs);
4071 }
4072
4073+void mh_buffy_update (const char *path, int *msgcount, int *msg_unread, int *msg_flagged, time_t *sb_last_checked)
4074+{
4075+ int i;
4076+ struct mh_sequences mhs;
4077+ memset (&mhs, 0, sizeof (mhs));
4078+
4079+ if(!option(OPTSIDEBAR))
4080+ return;
4081+
4082+ if (mh_read_sequences (&mhs, path) < 0)
4083+ return;
4084+
4085+ msgcount = 0;
4086+ msg_unread = 0;
4087+ msg_flagged = 0;
4088+ for (i = 0; i <= mhs.max; i++)
4089+ msgcount++;
4090+ if (mhs_check (&mhs, i) & MH_SEQ_UNSEEN) {
4091+ msg_unread++;
4092+ }
4093+ if (mhs_check (&mhs, i) & MH_SEQ_FLAGGED)
4094+ msg_flagged++;
4095+ mhs_free_sequences (&mhs);
4096+ *sb_last_checked = time(NULL);
4097+}
4098+
4099 static int mh_mkstemp (CONTEXT * dest, FILE ** fp, char **tgt)
4100 {
4101 int fd;
4102diff -rbuN mutt-1.6.0-orig/mutt_curses.h mutt-1.6.0/mutt_curses.h
4103--- mutt-1.6.0-orig/mutt_curses.h 2016-04-02 14:12:22.000000000 -0400
4104+++ mutt-1.6.0/mutt_curses.h 2016-04-04 23:42:10.907286346 -0400
4105@@ -64,6 +64,7 @@
4106 #undef lines
4107 #endif /* lines */
4108
4109+#define CLEARLINE_WIN(x) move(x,SidebarWidth), clrtoeol()
4110 #define CLEARLINE(x) move(x,0), clrtoeol()
4111 #define CENTERLINE(x,y) move(y, (COLS-strlen(x))/2), addstr(x)
4112 #define BEEP() do { if (option (OPTBEEP)) beep(); } while (0)
4113@@ -124,6 +125,8 @@
4114 MT_COLOR_UNDERLINE,
4115 MT_COLOR_INDEX,
4116 MT_COLOR_PROMPT,
4117+ MT_COLOR_NEW,
4118+ MT_COLOR_FLAGGED,
4119 MT_COLOR_MAX
4120 };
4121
4122diff -rbuN mutt-1.6.0-orig/mutt.h mutt-1.6.0/mutt.h
4123--- mutt-1.6.0-orig/mutt.h 2016-04-02 14:12:22.000000000 -0400
4124+++ mutt-1.6.0/mutt.h 2016-04-04 23:42:10.907286346 -0400
4125@@ -428,6 +428,10 @@
4126 OPTSAVEEMPTY,
4127 OPTSAVENAME,
4128 OPTSCORE,
4129+ OPTSIDEBAR,
4130+ OPTSIDEBARSHORTPATH,
4131+ OPTSIDEBARSORT,
4132+ OPTSIDEBARFOLDERINDENT,
4133 OPTSIGDASHES,
4134 OPTSIGONTOP,
4135 OPTSORTRE,
4136@@ -872,6 +876,7 @@
4137 {
4138 char *path;
4139 FILE *fp;
4140+ time_t atime;
4141 time_t mtime;
4142 off_t size;
4143 off_t vsize;
4144@@ -906,6 +911,7 @@
4145 unsigned int quiet : 1; /* inhibit status messages? */
4146 unsigned int collapsed : 1; /* are all threads collapsed? */
4147 unsigned int closing : 1; /* mailbox is being closed */
4148+ unsigned int peekonly : 1; /* just taking a glance, revert atime */
4149
4150 /* driver hooks */
4151 void *data; /* driver specific data */
4152diff -rbuN mutt-1.6.0-orig/muttlib.c mutt-1.6.0/muttlib.c
4153--- mutt-1.6.0-orig/muttlib.c 2016-04-02 14:12:22.000000000 -0400
4154+++ mutt-1.6.0/muttlib.c 2016-04-04 23:42:10.907286346 -0400
4155@@ -1282,6 +1282,8 @@
4156 pl = pw = 1;
4157
4158 /* see if there's room to add content, else ignore */
4159+ if ( DrawFullLine )
4160+ {
4161 if ((col < COLS && wlen < destlen) || soft)
4162 {
4163 int pad;
4164@@ -1325,6 +1327,52 @@
4165 col += wid;
4166 src += pl;
4167 }
4168+ }
4169+ else
4170+ {
4171+ if ((col < COLS-SidebarWidth && wlen < destlen) || soft)
4172+ {
4173+ int pad;
4174+
4175+ /* get contents after padding */
4176+ mutt_FormatString (buf, sizeof (buf), 0, src + pl, callback, data, flags);
4177+ len = mutt_strlen (buf);
4178+ wid = mutt_strwidth (buf);
4179+
4180+ /* try to consume as many columns as we can, if we don't have
4181+ * memory for that, use as much memory as possible */
4182+ pad = (COLS - SidebarWidth - col - wid) / pw;
4183+ if (pad > 0 && wlen + (pad * pl) + len > destlen)
4184+ pad = ((signed)(destlen - wlen - len)) / pl;
4185+ if (pad > 0)
4186+ {
4187+ while (pad--)
4188+ {
4189+ memcpy (wptr, src, pl);
4190+ wptr += pl;
4191+ wlen += pl;
4192+ col += pw;
4193+ }
4194+ }
4195+ else if (soft && pad < 0)
4196+ {
4197+ /* \0-terminate dest for length computation in mutt_wstr_trunc() */
4198+ *wptr = 0;
4199+ /* make sure right part is at most as wide as display */
4200+ len = mutt_wstr_trunc (buf, destlen, COLS, &wid);
4201+ /* truncate left so that right part fits completely in */
4202+ wlen = mutt_wstr_trunc (dest, destlen - len, col + pad, &col);
4203+ wptr = dest + wlen;
4204+ }
4205+ if (len + wlen > destlen)
4206+ len = mutt_wstr_trunc (buf, destlen - wlen, COLS - SidebarWidth - col, NULL);
4207+ memcpy (wptr, buf, len);
4208+ wptr += len;
4209+ wlen += len;
4210+ col += wid;
4211+ src += pl;
4212+ }
4213+ }
4214 break; /* skip rest of input */
4215 }
4216 else if (ch == '|')
4217diff -rbuN mutt-1.6.0-orig/mutt_menu.h mutt-1.6.0/mutt_menu.h
4218--- mutt-1.6.0-orig/mutt_menu.h 2016-04-02 14:12:22.000000000 -0400
4219+++ mutt-1.6.0/mutt_menu.h 2016-04-04 23:42:10.907286346 -0400
4220@@ -34,6 +34,7 @@
4221 #define REDRAW_FULL (1<<5)
4222 #define REDRAW_BODY (1<<6)
4223 #define REDRAW_SIGWINCH (1<<7)
4224+#define REDRAW_SIDEBAR (1<<8)
4225
4226 #define M_MODEFMT "-- Mutt: %s"
4227
4228diff -rbuN mutt-1.6.0-orig/mx.c mutt-1.6.0/mx.c
4229--- mutt-1.6.0-orig/mx.c 2016-04-02 14:12:22.000000000 -0400
4230+++ mutt-1.6.0/mx.c 2016-04-04 23:42:10.910619575 -0400
4231@@ -580,6 +580,7 @@
4232 * M_APPEND open mailbox for appending
4233 * M_READONLY open mailbox in read-only mode
4234 * M_QUIET only print error messages
4235+ * M_PEEK revert atime where applicable
4236 * ctx if non-null, context struct to use
4237 */
4238 CONTEXT *mx_open_mailbox (const char *path, int flags, CONTEXT *pctx)
4239@@ -602,6 +603,8 @@
4240 ctx->quiet = 1;
4241 if (flags & M_READONLY)
4242 ctx->readonly = 1;
4243+ if (flags & M_PEEK)
4244+ ctx->peekonly = 1;
4245
4246 if (flags & (M_APPEND|M_NEWFOLDER))
4247 {
4248@@ -701,12 +704,25 @@
4249 void mx_fastclose_mailbox (CONTEXT *ctx)
4250 {
4251 int i;
4252+#ifndef BUFFY_SIZE
4253+ struct utimbuf ut;
4254+#endif
4255
4256 if(!ctx)
4257 return;
4258+#ifndef BUFFY_SIZE
4259+ /* fix up the times so buffy won't get confused */
4260+ if (ctx->peekonly && ctx->path && ctx->mtime > ctx->atime)
4261+ {
4262+ ut.actime = ctx->atime;
4263+ ut.modtime = ctx->mtime;
4264+ utime (ctx->path, &ut);
4265+ }
4266+#endif
4267
4268 /* never announce that a mailbox we've just left has new mail. #3290
4269 * XXX: really belongs in mx_close_mailbox, but this is a nice hook point */
4270+ if(!ctx->peekonly)
4271 mutt_buffy_setnotified(ctx->path);
4272
4273 if (ctx->mx_close)
4274@@ -719,6 +735,8 @@
4275 mutt_clear_threads (ctx);
4276 for (i = 0; i < ctx->msgcount; i++)
4277 mutt_free_header (&ctx->hdrs[i]);
4278+ ctx->msgcount -= ctx->deleted;
4279+ set_buffystats(ctx);
4280 FREE (&ctx->hdrs);
4281 FREE (&ctx->v2r);
4282 FREE (&ctx->path);
4283@@ -812,6 +830,10 @@
4284 if (!ctx->hdrs[i]->deleted && ctx->hdrs[i]->read
4285 && !(ctx->hdrs[i]->flagged && option (OPTKEEPFLAGGED)))
4286 read_msgs++;
4287+ if (ctx->hdrs[i]->deleted && !ctx->hdrs[i]->read)
4288+ ctx->unread--;
4289+ if (ctx->hdrs[i]->deleted && ctx->hdrs[i]->flagged)
4290+ ctx->flagged--;
4291 }
4292
4293 if (read_msgs && quadoption (OPT_MOVE) != M_NO)
4294diff -rbuN mutt-1.6.0-orig/mx.h mutt-1.6.0/mx.h
4295--- mutt-1.6.0-orig/mx.h 2016-04-02 14:12:22.000000000 -0400
4296+++ mutt-1.6.0/mx.h 2016-04-04 23:42:10.910619575 -0400
4297@@ -57,6 +57,7 @@
4298 int mh_read_dir (CONTEXT *, const char *);
4299 int mh_sync_mailbox (CONTEXT *, int *);
4300 int mh_check_mailbox (CONTEXT *, int *);
4301+void mh_buffy_update (const char *, int *, int *, int *, time_t *);
4302 int mh_check_empty (const char *);
4303
4304 int maildir_read_dir (CONTEXT *);
4305diff -rbuN mutt-1.6.0-orig/OPS mutt-1.6.0/OPS
4306--- mutt-1.6.0-orig/OPS 2016-04-02 14:12:22.000000000 -0400
4307+++ mutt-1.6.0/OPS 2016-04-04 23:42:10.910619575 -0400
4308@@ -179,3 +179,8 @@
4309 OP_MAIN_SHOW_LIMIT "show currently active limit pattern"
4310 OP_MAIN_COLLAPSE_THREAD "collapse/uncollapse current thread"
4311 OP_MAIN_COLLAPSE_ALL "collapse/uncollapse all threads"
4312+OP_SIDEBAR_SCROLL_UP "scroll the mailbox pane up 1 page"
4313+OP_SIDEBAR_SCROLL_DOWN "scroll the mailbox pane down 1 page"
4314+OP_SIDEBAR_NEXT "go down to next mailbox"
4315+OP_SIDEBAR_PREV "go to previous mailbox"
4316+OP_SIDEBAR_OPEN "open hilighted mailbox"
4317diff -rbuN mutt-1.6.0-orig/pager.c mutt-1.6.0/pager.c
4318--- mutt-1.6.0-orig/pager.c 2016-04-02 14:12:22.000000000 -0400
4319+++ mutt-1.6.0/pager.c 2016-04-04 23:42:10.910619575 -0400
4320@@ -29,6 +29,7 @@
4321 #include "pager.h"
4322 #include "attach.h"
4323 #include "mbyte.h"
4324+#include "sidebar.h"
4325
4326 #include "mutt_crypt.h"
4327
4328@@ -1096,6 +1097,7 @@
4329 wchar_t wc;
4330 mbstate_t mbstate;
4331 int wrap_cols = mutt_term_width ((flags & M_PAGER_NOWRAP) ? 0 : Wrap);
4332+ wrap_cols -= SidebarWidth;
4333
4334 if (check_attachment_marker ((char *)buf) == 0)
4335 wrap_cols = COLS;
4336@@ -1573,6 +1575,7 @@
4337
4338 int bodyoffset = 1; /* offset of first line of real text */
4339 int statusoffset = 0; /* offset for the status bar */
4340+ int statuswidth;
4341 int helpoffset = LINES - 2; /* offset for the help bar. */
4342 int bodylen = LINES - 2 - bodyoffset; /* length of displayable area */
4343
4344@@ -1747,7 +1750,7 @@
4345 if ((redraw & REDRAW_BODY) || topline != oldtopline)
4346 {
4347 do {
4348- move (bodyoffset, 0);
4349+ move (bodyoffset, SidebarWidth);
4350 curline = oldtopline = topline;
4351 lines = 0;
4352 force_redraw = 0;
4353@@ -1760,6 +1763,7 @@
4354 &QuoteList, &q_level, &force_redraw, &SearchRE) > 0)
4355 lines++;
4356 curline++;
4357+ move(lines + bodyoffset, SidebarWidth);
4358 }
4359 last_offset = lineInfo[curline].offset;
4360 } while (force_redraw);
4361@@ -1772,6 +1776,7 @@
4362 addch ('~');
4363 addch ('\n');
4364 lines++;
4365+ move(lines + bodyoffset, SidebarWidth);
4366 }
4367 NORMAL_COLOR;
4368
4369@@ -1789,29 +1794,39 @@
4370 hfi.ctx = Context;
4371 hfi.pager_progress = pager_progress_str;
4372
4373+ statuswidth = COLS - (option(OPTSTATUSONTOP) && PagerIndexLines > 0 ? SidebarWidth : 0);
4374+
4375 if (last_pos < sb.st_size - 1)
4376 snprintf(pager_progress_str, sizeof(pager_progress_str), OFF_T_FMT "%%", (100 * last_offset / sb.st_size));
4377 else
4378 strfcpy(pager_progress_str, (topline == 0) ? "all" : "end", sizeof(pager_progress_str));
4379
4380 /* print out the pager status bar */
4381- move (statusoffset, 0);
4382+ move (statusoffset, SidebarWidth);
4383 SETCOLOR (MT_COLOR_STATUS);
4384+ if(option(OPTSTATUSONTOP) && PagerIndexLines > 0) {
4385+ CLEARLINE_WIN (statusoffset);
4386+ } else {
4387+ CLEARLINE (statusoffset);
4388+ DrawFullLine = 1; /* for mutt_make_string_info */
4389+ }
4390
4391 if (IsHeader (extra) || IsMsgAttach (extra))
4392 {
4393- size_t l1 = COLS * MB_LEN_MAX;
4394+ size_t l1 = statuswidth * MB_LEN_MAX;
4395 size_t l2 = sizeof (buffer);
4396 hfi.hdr = (IsHeader (extra)) ? extra->hdr : extra->bdy->hdr;
4397 mutt_make_string_info (buffer, l1 < l2 ? l1 : l2, NONULL (PagerFmt), &hfi, M_FORMAT_MAKEPRINT);
4398- mutt_paddstr (COLS, buffer);
4399+ mutt_paddstr (statuswidth, buffer);
4400 }
4401 else
4402 {
4403 char bn[STRING];
4404 snprintf (bn, sizeof (bn), "%s (%s)", banner, pager_progress_str);
4405- mutt_paddstr (COLS, bn);
4406+ mutt_paddstr (statuswidth, bn);
4407 }
4408+ if(!option(OPTSTATUSONTOP) || PagerIndexLines == 0)
4409+ DrawFullLine = 0; /* reset */
4410 NORMAL_COLOR;
4411 if (option(OPTTSENABLED) && TSSupported)
4412 {
4413@@ -1827,16 +1842,22 @@
4414 /* redraw the pager_index indicator, because the
4415 * flags for this message might have changed. */
4416 menu_redraw_current (index);
4417+ draw_sidebar(MENU_PAGER);
4418
4419 /* print out the index status bar */
4420 menu_status_line (buffer, sizeof (buffer), index, NONULL(Status));
4421
4422- move (indexoffset + (option (OPTSTATUSONTOP) ? 0 : (indexlen - 1)), 0);
4423+ move (indexoffset + (option (OPTSTATUSONTOP) ? 0 : (indexlen - 1)),
4424+ (option(OPTSTATUSONTOP) ? 0: SidebarWidth));
4425 SETCOLOR (MT_COLOR_STATUS);
4426- mutt_paddstr (COLS, buffer);
4427+ mutt_paddstr (COLS - (option(OPTSTATUSONTOP) ? 0 : SidebarWidth), buffer);
4428 NORMAL_COLOR;
4429 }
4430
4431+ /* if we're not using the index, update every time */
4432+ if ( index == 0 )
4433+ draw_sidebar(MENU_PAGER);
4434+
4435 redraw = 0;
4436
4437 if (option(OPTBRAILLEFRIENDLY)) {
4438@@ -2777,6 +2798,13 @@
4439 mutt_what_key ();
4440 break;
4441
4442+ case OP_SIDEBAR_SCROLL_UP:
4443+ case OP_SIDEBAR_SCROLL_DOWN:
4444+ case OP_SIDEBAR_NEXT:
4445+ case OP_SIDEBAR_PREV:
4446+ scroll_sidebar(ch, MENU_PAGER);
4447+ break;
4448+
4449 default:
4450 ch = -1;
4451 break;
4452diff -rbuN mutt-1.6.0-orig/PATCHES mutt-1.6.0/PATCHES
4453--- mutt-1.6.0-orig/PATCHES 2016-04-02 14:12:22.000000000 -0400
4454+++ mutt-1.6.0/PATCHES 2016-04-04 23:42:10.913952805 -0400
4455@@ -0,0 +1 @@
4456+patch-1.6.0.sidebar.20160404.txt
4457diff -rbuN mutt-1.6.0-orig/pattern.c mutt-1.6.0/pattern.c
4458--- mutt-1.6.0-orig/pattern.c 2016-04-02 14:12:22.000000000 -0400
4459+++ mutt-1.6.0/pattern.c 2016-04-04 23:42:10.913952805 -0400
4460@@ -154,6 +154,10 @@
4461 HEADER *h = ctx->hdrs[msgno];
4462 char *buf;
4463 size_t blen;
4464+#ifdef HAVE_FMEMOPEN
4465+ char *temp;
4466+ size_t tempsize;
4467+#endif
4468
4469 if ((msg = mx_open_message (ctx, msgno)) != NULL)
4470 {
4471@@ -163,12 +167,20 @@
4472 memset (&s, 0, sizeof (s));
4473 s.fpin = msg->fp;
4474 s.flags = M_CHARCONV;
4475+#ifdef HAVE_FMEMOPEN
4476+ if((s.fpout = open_memstream(&temp, &tempsize)) == NULL)
4477+ {
4478+ mutt_perror ("Error opening memstream");
4479+ return (0);
4480+ }
4481+#else
4482 mutt_mktemp (tempfile, sizeof (tempfile));
4483 if ((s.fpout = safe_fopen (tempfile, "w+")) == NULL)
4484 {
4485 mutt_perror (tempfile);
4486 return (0);
4487 }
4488+#endif
4489
4490 if (pat->op != M_BODY)
4491 mutt_copy_header (msg->fp, h, s.fpout, CH_FROM | CH_DECODE, NULL);
4492@@ -184,7 +196,11 @@
4493 if (s.fpout)
4494 {
4495 safe_fclose (&s.fpout);
4496+#ifdef HAVE_FMEMOPEN
4497+ FREE(&temp);
4498+#else
4499 unlink (tempfile);
4500+#endif
4501 }
4502 return (0);
4503 }
4504@@ -193,11 +209,28 @@
4505 mutt_body_handler (h->content, &s);
4506 }
4507
4508+#ifdef HAVE_FMEMOPEN
4509+ fclose(s.fpout);
4510+ lng = tempsize;
4511+
4512+ if(tempsize) {
4513+ if ((fp = fmemopen(temp, tempsize, "r")) == NULL) {
4514+ mutt_perror ("Error re-opening memstream");
4515+ return (0);
4516+ }
4517+ } else { /* fmemopen cannot handle empty buffers */
4518+ if ((fp = safe_fopen ("/dev/null", "r")) == NULL) {
4519+ mutt_perror ("Error opening /dev/null");
4520+ return (0);
4521+ }
4522+ }
4523+#else
4524 fp = s.fpout;
4525 fflush (fp);
4526 fseek (fp, 0, 0);
4527 fstat (fileno (fp), &st);
4528 lng = (long) st.st_size;
4529+#endif
4530 }
4531 else
4532 {
4533@@ -244,7 +277,12 @@
4534 if (option (OPTTHOROUGHSRC))
4535 {
4536 safe_fclose (&fp);
4537+#ifdef HAVE_FMEMOPEN
4538+ if(tempsize)
4539+ FREE (&temp);
4540+#else
4541 unlink (tempfile);
4542+#endif
4543 }
4544 }
4545
4546diff -rbuN mutt-1.6.0-orig/protos.h mutt-1.6.0/protos.h
4547--- mutt-1.6.0-orig/protos.h 2016-04-02 14:12:22.000000000 -0400
4548+++ mutt-1.6.0/protos.h 2016-04-04 23:42:10.913952805 -0400
4549@@ -36,6 +36,13 @@
4550 const char *pager_progress;
4551 };
4552
4553+struct sidebar_entry {
4554+ char box[SHORT_STRING];
4555+ unsigned int size;
4556+ unsigned int new;
4557+ unsigned int flagged;
4558+};
4559+
4560 void mutt_make_string_info (char *, size_t, const char *, struct hdr_format_info *, format_flag);
4561
4562 int mutt_extract_token (BUFFER *, BUFFER *, int);
4563diff -rbuN mutt-1.6.0-orig/sidebar.c mutt-1.6.0/sidebar.c
4564--- mutt-1.6.0-orig/sidebar.c 1969-12-31 19:00:00.000000000 -0500
4565+++ mutt-1.6.0/sidebar.c 2016-04-04 23:42:10.913952805 -0400
4566@@ -0,0 +1,410 @@
4567+/*
4568+ * Copyright (C) ????-2004 Justin Hibbits <jrh29@po.cwru.edu>
4569+ * Copyright (C) 2004 Thomer M. Gil <mutt@thomer.com>
4570+ *
4571+ * This program is free software; you can redistribute it and/or modify
4572+ * it under the terms of the GNU General Public License as published by
4573+ * the Free Software Foundation; either version 2 of the License, or
4574+ * (at your option) any later version.
4575+ *
4576+ * This program is distributed in the hope that it will be useful,
4577+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4578+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4579+ * GNU General Public License for more details.
4580+ *
4581+ * You should have received a copy of the GNU General Public License
4582+ * along with this program; if not, write to the Free Software
4583+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
4584+ */
4585+
4586+
4587+#if HAVE_CONFIG_H
4588+# include "config.h"
4589+#endif
4590+
4591+#include "mutt.h"
4592+#include "mutt_menu.h"
4593+#include "mutt_curses.h"
4594+#include "sidebar.h"
4595+#include "buffy.h"
4596+#include <libgen.h>
4597+#include "keymap.h"
4598+#include <stdbool.h>
4599+
4600+/*BUFFY *CurBuffy = 0;*/
4601+static BUFFY *TopBuffy = 0;
4602+static BUFFY *BottomBuffy = 0;
4603+static int known_lines = 0;
4604+
4605+void calc_boundaries() {
4606+
4607+ BUFFY *tmp = Incoming;
4608+
4609+ int count = LINES - 2 - (option(OPTHELP) ? 1 : 0);
4610+
4611+ if ( known_lines != LINES ) {
4612+ TopBuffy = BottomBuffy = 0;
4613+ known_lines = LINES;
4614+ }
4615+ for ( ; tmp->next != 0; tmp = tmp->next )
4616+ tmp->next->prev = tmp;
4617+
4618+ if ( TopBuffy == 0 && BottomBuffy == 0 )
4619+ TopBuffy = Incoming;
4620+ if ( BottomBuffy == 0 ) {
4621+ BottomBuffy = TopBuffy;
4622+ while ( --count && BottomBuffy->next )
4623+ BottomBuffy = BottomBuffy->next;
4624+ }
4625+ else if ( TopBuffy == CurBuffy->next ) {
4626+ BottomBuffy = CurBuffy;
4627+ tmp = BottomBuffy;
4628+ while ( --count && tmp->prev)
4629+ tmp = tmp->prev;
4630+ TopBuffy = tmp;
4631+ }
4632+ else if ( BottomBuffy == CurBuffy->prev ) {
4633+ TopBuffy = CurBuffy;
4634+ tmp = TopBuffy;
4635+ while ( --count && tmp->next )
4636+ tmp = tmp->next;
4637+ BottomBuffy = tmp;
4638+ }
4639+}
4640+
4641+static const char *
4642+sidebar_format_str (char *dest,
4643+ size_t destlen,
4644+ size_t col,
4645+ char op,
4646+ const char *src,
4647+ const char *prefix,
4648+ const char *ifstring,
4649+ const char *elsestring,
4650+ unsigned long data,
4651+ format_flag flags)
4652+{
4653+/* casting from unsigned long - srsly?! */
4654+struct sidebar_entry *sbe = (struct sidebar_entry *) data;
4655+unsigned int optional;
4656+char fmt[SHORT_STRING], buf[SHORT_STRING];
4657+
4658+optional = flags & M_FORMAT_OPTIONAL;
4659+
4660+switch(op) {
4661+ case 'F':
4662+ if(!optional) {
4663+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
4664+ snprintf (dest, destlen, fmt, sbe->flagged);
4665+ } else if(sbe->flagged == 0) {
4666+ optional = 0;
4667+ }
4668+ break;
4669+
4670+ case '!':
4671+ if(sbe->flagged == 0)
4672+ mutt_format_s(dest, destlen, prefix, "");
4673+ if(sbe->flagged == 1)
4674+ mutt_format_s(dest, destlen, prefix, "!");
4675+ if(sbe->flagged == 2)
4676+ mutt_format_s(dest, destlen, prefix, "!!");
4677+ if(sbe->flagged > 2) {
4678+ snprintf (buf, sizeof (buf), "%d!", sbe->flagged);
4679+ mutt_format_s(dest, destlen, prefix, buf);
4680+ }
4681+ break;
4682+
4683+ case 'S':
4684+ if(!optional) {
4685+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
4686+ snprintf (dest, destlen, fmt, sbe->size);
4687+ } else if (sbe->size == 0) {
4688+ optional = 0;
4689+ }
4690+ break;
4691+
4692+ case 'N':
4693+ if(!optional) {
4694+ snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
4695+ snprintf (dest, destlen, fmt, sbe->new);
4696+ } else if(sbe->new == 0) {
4697+ optional = 0;
4698+ }
4699+ break;
4700+
4701+ case 'B':
4702+ mutt_format_s(dest, destlen, prefix, sbe->box);
4703+ break;
4704+ }
4705+
4706+ if(optional)
4707+ mutt_FormatString (dest, destlen, col, ifstring, sidebar_format_str, (unsigned long) sbe, flags);
4708+ else if (flags & M_FORMAT_OPTIONAL)
4709+ mutt_FormatString (dest, destlen, col, elsestring, sidebar_format_str, (unsigned long) sbe, flags);
4710+
4711+ return (src);
4712+}
4713+
4714+char *make_sidebar_entry(char *box, unsigned int size, unsigned int new, unsigned int flagged) {
4715+ static char *entry = 0;
4716+ struct sidebar_entry sbe;
4717+ int SBvisual;
4718+
4719+ SBvisual = SidebarWidth - strlen(SidebarDelim);
4720+ if (SBvisual < 1)
4721+ return NULL;
4722+
4723+ sbe.new = new;
4724+ sbe.flagged = flagged;
4725+ sbe.size = size;
4726+ strncpy(sbe.box, box, 31);
4727+
4728+ safe_realloc(&entry, SBvisual + 2);
4729+ entry[SBvisual + 1] = '\0';
4730+
4731+ mutt_FormatString (entry, SBvisual+1, 0, SidebarFormat, sidebar_format_str, (unsigned long) &sbe, 0);
4732+
4733+ return entry;
4734+}
4735+
4736+void set_curbuffy(char buf[LONG_STRING])
4737+{
4738+ BUFFY* tmp = CurBuffy = Incoming;
4739+
4740+ if (!Incoming)
4741+ return;
4742+
4743+ while(1) {
4744+ if(!strcmp(tmp->path, buf) || !strcmp(tmp->realpath, buf)) {
4745+ CurBuffy = tmp;
4746+ break;
4747+ }
4748+
4749+ if(tmp->next)
4750+ tmp = tmp->next;
4751+ else
4752+ break;
4753+ }
4754+}
4755+
4756+int draw_sidebar(int menu) {
4757+
4758+ BUFFY *tmp;
4759+#ifndef USE_SLANG_CURSES
4760+ attr_t attrs;
4761+#endif
4762+ short delim_len = strlen(SidebarDelim);
4763+ short color_pair;
4764+
4765+ static bool initialized = false;
4766+ static int prev_show_value;
4767+ static short saveSidebarWidth;
4768+ int lines = 0;
4769+ int SidebarHeight;
4770+
4771+ if(option(OPTSTATUSONTOP) || option(OPTHELP))
4772+ lines++; /* either one will occupy the first line */
4773+
4774+ /* initialize first time */
4775+ if(!initialized) {
4776+ prev_show_value = option(OPTSIDEBAR);
4777+ saveSidebarWidth = SidebarWidth;
4778+ if(!option(OPTSIDEBAR)) SidebarWidth = 0;
4779+ initialized = true;
4780+ }
4781+
4782+ /* save or restore the value SidebarWidth */
4783+ if(prev_show_value != option(OPTSIDEBAR)) {
4784+ if(prev_show_value && !option(OPTSIDEBAR)) {
4785+ saveSidebarWidth = SidebarWidth;
4786+ SidebarWidth = 0;
4787+ } else if(!prev_show_value && option(OPTSIDEBAR)) {
4788+ mutt_buffy_check(1); /* we probably have bad or no numbers */
4789+ SidebarWidth = saveSidebarWidth;
4790+ }
4791+ prev_show_value = option(OPTSIDEBAR);
4792+ }
4793+
4794+
4795+/* if ( SidebarWidth == 0 ) return 0; */
4796+ if (SidebarWidth > 0 && option (OPTSIDEBAR)
4797+ && delim_len >= SidebarWidth) {
4798+ unset_option (OPTSIDEBAR);
4799+ /* saveSidebarWidth = SidebarWidth; */
4800+ if (saveSidebarWidth > delim_len) {
4801+ SidebarWidth = saveSidebarWidth;
4802+ mutt_error (_("Value for sidebar_delim is too long. Disabling sidebar."));
4803+ sleep (2);
4804+ } else {
4805+ SidebarWidth = 0;
4806+ mutt_error (_("Value for sidebar_delim is too long. Disabling sidebar. Please set your sidebar_width to a sane value."));
4807+ sleep (4); /* the advise to set a sane value should be seen long enough */
4808+ }
4809+ saveSidebarWidth = 0;
4810+ return (0);
4811+ }
4812+
4813+ if ( SidebarWidth == 0 || !option(OPTSIDEBAR)) {
4814+ if (SidebarWidth > 0) {
4815+ saveSidebarWidth = SidebarWidth;
4816+ SidebarWidth = 0;
4817+ }
4818+ unset_option(OPTSIDEBAR);
4819+ return 0;
4820+ }
4821+
4822+ /* get attributes for divider */
4823+ SETCOLOR(MT_COLOR_STATUS);
4824+#ifndef USE_SLANG_CURSES
4825+ attr_get(&attrs, &color_pair, 0);
4826+#else
4827+ color_pair = attr_get();
4828+#endif
4829+ SETCOLOR(MT_COLOR_NORMAL);
4830+
4831+ /* draw the divider */
4832+
4833+ SidebarHeight = LINES - 1;
4834+ if(option(OPTHELP) || !option(OPTSTATUSONTOP))
4835+ SidebarHeight--;
4836+
4837+ for ( ; lines < SidebarHeight; lines++ ) {
4838+ move(lines, SidebarWidth - delim_len);
4839+ addstr(NONULL(SidebarDelim));
4840+#ifndef USE_SLANG_CURSES
4841+ mvchgat(lines, SidebarWidth - delim_len, delim_len, 0, color_pair, NULL);
4842+#endif
4843+ }
4844+
4845+ if ( Incoming == 0 ) return 0;
4846+ lines = 0;
4847+ if(option(OPTSTATUSONTOP) || option(OPTHELP))
4848+ lines++; /* either one will occupy the first line */
4849+
4850+ if ( known_lines != LINES || TopBuffy == 0 || BottomBuffy == 0 )
4851+ calc_boundaries(menu);
4852+ if ( CurBuffy == 0 ) CurBuffy = Incoming;
4853+
4854+ tmp = TopBuffy;
4855+
4856+ SETCOLOR(MT_COLOR_NORMAL);
4857+
4858+ for ( ; tmp && lines < SidebarHeight; tmp = tmp->next ) {
4859+ if ( tmp == CurBuffy )
4860+ SETCOLOR(MT_COLOR_INDICATOR);
4861+ else if ( tmp->msg_unread > 0 )
4862+ SETCOLOR(MT_COLOR_NEW);
4863+ else if ( tmp->msg_flagged > 0 )
4864+ SETCOLOR(MT_COLOR_FLAGGED);
4865+ else
4866+ SETCOLOR(MT_COLOR_NORMAL);
4867+
4868+ move( lines, 0 );
4869+ if ( Context && Context->path &&
4870+ (!strcmp(tmp->path, Context->path)||
4871+ !strcmp(tmp->realpath, Context->path)) ) {
4872+ tmp->msg_unread = Context->unread;
4873+ tmp->msgcount = Context->msgcount;
4874+ tmp->msg_flagged = Context->flagged;
4875+ }
4876+ /* check whether Maildir is a prefix of the current folder's path */
4877+ short maildir_is_prefix = 0;
4878+ if ( (strlen(tmp->path) > strlen(Maildir)) &&
4879+ (strncmp(Maildir, tmp->path, strlen(Maildir)) == 0) )
4880+ maildir_is_prefix = 1;
4881+ /* calculate depth of current folder and generate its display name with indented spaces */
4882+ int sidebar_folder_depth = 0;
4883+ char *sidebar_folder_name;
4884+ sidebar_folder_name = option(OPTSIDEBARSHORTPATH) ? mutt_basename(tmp->path) : tmp->path + maildir_is_prefix*(strlen(Maildir) + 1);
4885+ if ( maildir_is_prefix && option(OPTSIDEBARFOLDERINDENT) ) {
4886+ char *tmp_folder_name;
4887+ int i;
4888+ tmp_folder_name = tmp->path + strlen(Maildir) + 1;
4889+ for (i = 0; i < strlen(tmp->path) - strlen(Maildir); i++) {
4890+ if (tmp_folder_name[i] == '/' || tmp_folder_name[i] == '.') sidebar_folder_depth++;
4891+ }
4892+ if (sidebar_folder_depth > 0) {
4893+ if (option(OPTSIDEBARSHORTPATH)) {
4894+ tmp_folder_name = strrchr(tmp->path, '.');
4895+ if (tmp_folder_name == NULL)
4896+ tmp_folder_name = mutt_basename(tmp->path);
4897+ else
4898+ tmp_folder_name++;
4899+ }
4900+ else
4901+ tmp_folder_name = tmp->path + strlen(Maildir) + 1;
4902+ sidebar_folder_name = malloc(strlen(tmp_folder_name) + sidebar_folder_depth*strlen(NONULL(SidebarIndentStr)) + 1);
4903+ sidebar_folder_name[0]=0;
4904+ for (i=0; i < sidebar_folder_depth; i++)
4905+ strncat(sidebar_folder_name, NONULL(SidebarIndentStr), strlen(NONULL(SidebarIndentStr)));
4906+ strncat(sidebar_folder_name, tmp_folder_name, strlen(tmp_folder_name));
4907+ }
4908+ }
4909+ printw( "%.*s", SidebarWidth - delim_len + 1,
4910+ make_sidebar_entry(sidebar_folder_name, tmp->msgcount,
4911+ tmp->msg_unread, tmp->msg_flagged));
4912+ if (sidebar_folder_depth > 0)
4913+ free(sidebar_folder_name);
4914+ lines++;
4915+ }
4916+ SETCOLOR(MT_COLOR_NORMAL);
4917+ for ( ; lines < SidebarHeight; lines++ ) {
4918+ int i = 0;
4919+ move( lines, 0 );
4920+ for ( ; i < SidebarWidth - delim_len; i++ )
4921+ addch(' ');
4922+ }
4923+ return 0;
4924+}
4925+
4926+
4927+void set_buffystats(CONTEXT* Context)
4928+{
4929+ BUFFY *tmp = Incoming;
4930+ while(tmp) {
4931+ if(Context && (!strcmp(tmp->path, Context->path) ||
4932+ !strcmp(tmp->realpath, Context->path))) {
4933+ tmp->msg_unread = Context->unread;
4934+ tmp->msgcount = Context->msgcount;
4935+ tmp->msg_flagged = Context->flagged;
4936+ break;
4937+ }
4938+ tmp = tmp->next;
4939+ }
4940+}
4941+
4942+void scroll_sidebar(int op, int menu)
4943+{
4944+ if(!SidebarWidth) return;
4945+ if(!CurBuffy) return;
4946+
4947+ switch (op) {
4948+ case OP_SIDEBAR_NEXT:
4949+ if ( CurBuffy->next == NULL ) return;
4950+ CurBuffy = CurBuffy->next;
4951+ break;
4952+ case OP_SIDEBAR_PREV:
4953+ if ( CurBuffy->prev == NULL ) return;
4954+ CurBuffy = CurBuffy->prev;
4955+ break;
4956+ case OP_SIDEBAR_SCROLL_UP:
4957+ CurBuffy = TopBuffy;
4958+ if ( CurBuffy != Incoming ) {
4959+ calc_boundaries(menu);
4960+ CurBuffy = CurBuffy->prev;
4961+ }
4962+ break;
4963+ case OP_SIDEBAR_SCROLL_DOWN:
4964+ CurBuffy = BottomBuffy;
4965+ if ( CurBuffy->next ) {
4966+ calc_boundaries(menu);
4967+ CurBuffy = CurBuffy->next;
4968+ }
4969+ break;
4970+ default:
4971+ return;
4972+ }
4973+ calc_boundaries(menu);
4974+ draw_sidebar(menu);
4975+}
4976+
4977diff -rbuN mutt-1.6.0-orig/sidebar.h mutt-1.6.0/sidebar.h
4978--- mutt-1.6.0-orig/sidebar.h 1969-12-31 19:00:00.000000000 -0500
4979+++ mutt-1.6.0/sidebar.h 2016-04-04 23:42:10.913952805 -0400
4980@@ -0,0 +1,36 @@
4981+/*
4982+ * Copyright (C) ????-2004 Justin Hibbits <jrh29@po.cwru.edu>
4983+ * Copyright (C) 2004 Thomer M. Gil <mutt@thomer.com>
4984+ *
4985+ * This program is free software; you can redistribute it and/or modify
4986+ * it under the terms of the GNU General Public License as published by
4987+ * the Free Software Foundation; either version 2 of the License, or
4988+ * (at your option) any later version.
4989+ *
4990+ * This program is distributed in the hope that it will be useful,
4991+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4992+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4993+ * GNU General Public License for more details.
4994+ *
4995+ * You should have received a copy of the GNU General Public License
4996+ * along with this program; if not, write to the Free Software
4997+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
4998+ */
4999+
5000+#ifndef SIDEBAR_H
5001+#define SIDEBAR_H
5002+
5003+struct MBOX_LIST {
5004+ char *path;
5005+ int msgcount;
5006+ int new;
5007+} MBLIST;
5008+
5009+/* parameter is whether or not to go to the status line */
5010+/* used for omitting the last | that covers up the status bar in the index */
5011+int draw_sidebar(int);
5012+void scroll_sidebar(int, int);
5013+void set_curbuffy(char*);
5014+void set_buffystats(CONTEXT*);
5015+
5016+#endif /* SIDEBAR_H */