mutt stable branch with some hacks
1/*
2 * Copyright (C) 1996-2000,2007,2010,2013 Michael R. Elkins <me@mutt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#if HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "mutt.h"
24#include "mutt_curses.h"
25#include "mutt_menu.h"
26#include "attach.h"
27#include "buffy.h"
28#include "mapping.h"
29#include "sort.h"
30#include "mailbox.h"
31#include "browser.h"
32#ifdef USE_IMAP
33#include "imap.h"
34#endif
35
36#include <stdlib.h>
37#include <dirent.h>
38#include <string.h>
39#include <ctype.h>
40#include <unistd.h>
41#include <sys/stat.h>
42#include <errno.h>
43#include <locale.h>
44
45static const struct mapping_t FolderHelp[] = {
46 { N_("Exit"), OP_EXIT },
47 { N_("Chdir"), OP_CHANGE_DIRECTORY },
48 { N_("Mask"), OP_ENTER_MASK },
49 { N_("Help"), OP_HELP },
50 { NULL, 0 }
51};
52
53typedef struct folder_t
54{
55 struct folder_file *ff;
56 int num;
57} FOLDER;
58
59static char LastDir[_POSIX_PATH_MAX] = "";
60static char LastDirBackup[_POSIX_PATH_MAX] = "";
61
62/* Frees up the memory allocated for the local-global variables. */
63static void destroy_state (struct browser_state *state)
64{
65 int c;
66
67 for (c = 0; c < state->entrylen; c++)
68 {
69 FREE (&((state->entry)[c].name));
70 FREE (&((state->entry)[c].desc));
71 }
72#ifdef USE_IMAP
73 FREE (&state->folder);
74#endif
75 FREE (&state->entry);
76}
77
78static int browser_compare_subject (const void *a, const void *b)
79{
80 struct folder_file *pa = (struct folder_file *) a;
81 struct folder_file *pb = (struct folder_file *) b;
82
83 int r = mutt_strcoll (pa->name, pb->name);
84
85 return ((BrowserSort & SORT_REVERSE) ? -r : r);
86}
87
88static int browser_compare_date (const void *a, const void *b)
89{
90 struct folder_file *pa = (struct folder_file *) a;
91 struct folder_file *pb = (struct folder_file *) b;
92
93 int r = pa->mtime - pb->mtime;
94
95 return ((BrowserSort & SORT_REVERSE) ? -r : r);
96}
97
98static int browser_compare_size (const void *a, const void *b)
99{
100 struct folder_file *pa = (struct folder_file *) a;
101 struct folder_file *pb = (struct folder_file *) b;
102
103 int r = pa->size - pb->size;
104
105 return ((BrowserSort & SORT_REVERSE) ? -r : r);
106}
107
108static void browser_sort (struct browser_state *state)
109{
110 int (*f) (const void *, const void *);
111
112 switch (BrowserSort & SORT_MASK)
113 {
114 case SORT_ORDER:
115 return;
116 case SORT_DATE:
117 f = browser_compare_date;
118 break;
119 case SORT_SIZE:
120 f = browser_compare_size;
121 break;
122 case SORT_SUBJECT:
123 default:
124 f = browser_compare_subject;
125 break;
126 }
127 qsort (state->entry, state->entrylen, sizeof (struct folder_file), f);
128}
129
130static int link_is_dir (const char *folder, const char *path)
131{
132 struct stat st;
133 char fullpath[_POSIX_PATH_MAX];
134
135 mutt_concat_path (fullpath, folder, path, sizeof (fullpath));
136
137 if (stat (fullpath, &st) == 0)
138 return (S_ISDIR (st.st_mode));
139 else
140 return 0;
141}
142
143static const char *
144folder_format_str (char *dest, size_t destlen, size_t col, int cols, char op, const char *src,
145 const char *fmt, const char *ifstring, const char *elsestring,
146 unsigned long data, format_flag flags)
147{
148 char fn[SHORT_STRING], tmp[SHORT_STRING], permission[11];
149 char date[SHORT_STRING], *t_fmt;
150 time_t tnow;
151 FOLDER *folder = (FOLDER *) data;
152 struct passwd *pw;
153 struct group *gr;
154 int optional = (flags & MUTT_FORMAT_OPTIONAL);
155
156 switch (op)
157 {
158 case 'C':
159 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
160 snprintf (dest, destlen, tmp, folder->num + 1);
161 break;
162
163 case 'd':
164 case 'D':
165 if (folder->ff->local)
166 {
167 int do_locales = TRUE;
168
169 if (op == 'D') {
170 t_fmt = NONULL(DateFmt);
171 if (*t_fmt == '!') {
172 ++t_fmt;
173 do_locales = FALSE;
174 }
175 } else {
176 tnow = time (NULL);
177 t_fmt = tnow - folder->ff->mtime < 31536000 ? "%b %d %H:%M" : "%b %d %Y";
178 }
179
180 if (!do_locales)
181 setlocale (LC_TIME, "C");
182 strftime (date, sizeof (date), t_fmt, localtime (&folder->ff->mtime));
183 if (!do_locales)
184 setlocale (LC_TIME, "");
185
186 mutt_format_s (dest, destlen, fmt, date);
187 }
188 else
189 mutt_format_s (dest, destlen, fmt, "");
190 break;
191
192 case 'f':
193 {
194 char *s;
195#ifdef USE_IMAP
196 if (folder->ff->imap)
197 s = NONULL (folder->ff->desc);
198 else
199#endif
200 s = NONULL (folder->ff->name);
201
202 snprintf (fn, sizeof (fn), "%s%s", s,
203 folder->ff->local ? (S_ISLNK (folder->ff->mode) ? "@" :
204 (S_ISDIR (folder->ff->mode) ? "/" :
205 ((folder->ff->mode & S_IXUSR) != 0 ? "*" : ""))) : "");
206
207 mutt_format_s (dest, destlen, fmt, fn);
208 break;
209 }
210 case 'F':
211 if (folder->ff->local)
212 {
213 snprintf (permission, sizeof (permission), "%c%c%c%c%c%c%c%c%c%c",
214 S_ISDIR(folder->ff->mode) ? 'd' : (S_ISLNK(folder->ff->mode) ? 'l' : '-'),
215 (folder->ff->mode & S_IRUSR) != 0 ? 'r': '-',
216 (folder->ff->mode & S_IWUSR) != 0 ? 'w' : '-',
217 (folder->ff->mode & S_ISUID) != 0 ? 's' : (folder->ff->mode & S_IXUSR) != 0 ? 'x': '-',
218 (folder->ff->mode & S_IRGRP) != 0 ? 'r' : '-',
219 (folder->ff->mode & S_IWGRP) != 0 ? 'w' : '-',
220 (folder->ff->mode & S_ISGID) != 0 ? 's' : (folder->ff->mode & S_IXGRP) != 0 ? 'x': '-',
221 (folder->ff->mode & S_IROTH) != 0 ? 'r' : '-',
222 (folder->ff->mode & S_IWOTH) != 0 ? 'w' : '-',
223 (folder->ff->mode & S_ISVTX) != 0 ? 't' : (folder->ff->mode & S_IXOTH) != 0 ? 'x': '-');
224 mutt_format_s (dest, destlen, fmt, permission);
225 }
226#ifdef USE_IMAP
227 else if (folder->ff->imap)
228 {
229 /* mark folders with subfolders AND mail */
230 snprintf (permission, sizeof (permission), "IMAP %c",
231 (folder->ff->inferiors && folder->ff->selectable) ? '+' : ' ');
232 mutt_format_s (dest, destlen, fmt, permission);
233 }
234#endif
235 else
236 mutt_format_s (dest, destlen, fmt, "");
237 break;
238
239 case 'g':
240 if (folder->ff->local)
241 {
242 if ((gr = getgrgid (folder->ff->gid)))
243 mutt_format_s (dest, destlen, fmt, gr->gr_name);
244 else
245 {
246 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
247 snprintf (dest, destlen, tmp, folder->ff->gid);
248 }
249 }
250 else
251 mutt_format_s (dest, destlen, fmt, "");
252 break;
253
254 case 'l':
255 if (folder->ff->local)
256 {
257 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
258 snprintf (dest, destlen, tmp, folder->ff->nlink);
259 }
260 else
261 mutt_format_s (dest, destlen, fmt, "");
262 break;
263
264 case 'm':
265 if (!optional)
266 {
267 if (folder->ff->has_buffy)
268 {
269 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
270 snprintf (dest, destlen, tmp, folder->ff->msg_count);
271 }
272 else
273 mutt_format_s (dest, destlen, fmt, "");
274 }
275 else if (!folder->ff->msg_count)
276 optional = 0;
277 break;
278
279 case 'N':
280 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
281 snprintf (dest, destlen, tmp, folder->ff->new ? 'N' : ' ');
282 break;
283
284 case 'n':
285 if (!optional)
286 {
287 if (folder->ff->has_buffy)
288 {
289 snprintf (tmp, sizeof (tmp), "%%%sd", fmt);
290 snprintf (dest, destlen, tmp, folder->ff->msg_unread);
291 }
292 else
293 mutt_format_s (dest, destlen, fmt, "");
294 }
295 else if (!folder->ff->msg_unread)
296 optional = 0;
297 break;
298
299 case 's':
300 if (folder->ff->local)
301 {
302 mutt_pretty_size(fn, sizeof(fn), folder->ff->size);
303 snprintf (tmp, sizeof (tmp), "%%%ss", fmt);
304 snprintf (dest, destlen, tmp, fn);
305 }
306 else
307 mutt_format_s (dest, destlen, fmt, "");
308 break;
309
310 case 't':
311 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
312 snprintf (dest, destlen, tmp, folder->ff->tagged ? '*' : ' ');
313 break;
314
315 case 'u':
316 if (folder->ff->local)
317 {
318 if ((pw = getpwuid (folder->ff->uid)))
319 mutt_format_s (dest, destlen, fmt, pw->pw_name);
320 else
321 {
322 snprintf (tmp, sizeof (tmp), "%%%sld", fmt);
323 snprintf (dest, destlen, tmp, folder->ff->uid);
324 }
325 }
326 else
327 mutt_format_s (dest, destlen, fmt, "");
328 break;
329
330 default:
331 snprintf (tmp, sizeof (tmp), "%%%sc", fmt);
332 snprintf (dest, destlen, tmp, op);
333 break;
334 }
335
336 if (optional)
337 mutt_FormatString (dest, destlen, col, cols, ifstring, folder_format_str, data, 0);
338 else if (flags & MUTT_FORMAT_OPTIONAL)
339 mutt_FormatString (dest, destlen, col, cols, elsestring, folder_format_str, data, 0);
340
341 return (src);
342}
343
344static void add_folder (MUTTMENU *m, struct browser_state *state,
345 const char *name, const struct stat *s, BUFFY *b)
346{
347 if (state->entrylen == state->entrymax)
348 {
349 /* need to allocate more space */
350 safe_realloc (&state->entry,
351 sizeof (struct folder_file) * (state->entrymax += 256));
352 memset (&state->entry[state->entrylen], 0,
353 sizeof (struct folder_file) * 256);
354 if (m)
355 m->data = state->entry;
356 }
357
358 if (s != NULL)
359 {
360 (state->entry)[state->entrylen].mode = s->st_mode;
361 (state->entry)[state->entrylen].mtime = s->st_mtime;
362 (state->entry)[state->entrylen].size = s->st_size;
363 (state->entry)[state->entrylen].gid = s->st_gid;
364 (state->entry)[state->entrylen].uid = s->st_uid;
365 (state->entry)[state->entrylen].nlink = s->st_nlink;
366
367 (state->entry)[state->entrylen].local = 1;
368 }
369
370 if (b)
371 {
372 (state->entry)[state->entrylen].has_buffy = 1;
373 (state->entry)[state->entrylen].new = b->new;
374 (state->entry)[state->entrylen].msg_count = b->msg_count;
375 (state->entry)[state->entrylen].msg_unread = b->msg_unread;
376 }
377
378 (state->entry)[state->entrylen].name = safe_strdup (name);
379 (state->entry)[state->entrylen].desc = safe_strdup (name);
380#ifdef USE_IMAP
381 (state->entry)[state->entrylen].imap = 0;
382#endif
383 (state->entrylen)++;
384}
385
386static void init_state (struct browser_state *state, MUTTMENU *menu)
387{
388 state->entrylen = 0;
389 state->entrymax = 256;
390 state->entry = (struct folder_file *) safe_calloc (state->entrymax, sizeof (struct folder_file));
391#ifdef USE_IMAP
392 state->imap_browse = 0;
393#endif
394 if (menu)
395 menu->data = state->entry;
396}
397
398static int examine_directory (MUTTMENU *menu, struct browser_state *state,
399 char *d, const char *prefix)
400{
401 struct stat s;
402 DIR *dp;
403 struct dirent *de;
404 char buffer[_POSIX_PATH_MAX + SHORT_STRING];
405 BUFFY *tmp;
406
407 while (stat (d, &s) == -1)
408 {
409 if (errno == ENOENT)
410 {
411 /* The last used directory is deleted, try to use the parent dir. */
412 char *c = strrchr (d, '/');
413
414 if (c && (c > d))
415 {
416 *c = 0;
417 continue;
418 }
419 }
420 mutt_perror (d);
421 return (-1);
422 }
423
424 if (!S_ISDIR (s.st_mode))
425 {
426 mutt_error (_("%s is not a directory."), d);
427 return (-1);
428 }
429
430 mutt_buffy_check (0);
431
432 if ((dp = opendir (d)) == NULL)
433 {
434 mutt_perror (d);
435 return (-1);
436 }
437
438 init_state (state, menu);
439
440 while ((de = readdir (dp)) != NULL)
441 {
442 if (mutt_strcmp (de->d_name, ".") == 0)
443 continue; /* we don't need . */
444
445 if (prefix && *prefix && mutt_strncmp (prefix, de->d_name, mutt_strlen (prefix)) != 0)
446 continue;
447 if (!((regexec (Mask.rx, de->d_name, 0, NULL, 0) == 0) ^ Mask.not))
448 continue;
449
450 mutt_concat_path (buffer, d, de->d_name, sizeof (buffer));
451 if (lstat (buffer, &s) == -1)
452 continue;
453
454 if ((! S_ISREG (s.st_mode)) && (! S_ISDIR (s.st_mode)) &&
455 (! S_ISLNK (s.st_mode)))
456 continue;
457
458 tmp = Incoming;
459 while (tmp && mutt_strcmp (buffer, tmp->path))
460 tmp = tmp->next;
461 if (tmp && Context &&
462 !mutt_strcmp (tmp->realpath, Context->realpath))
463 {
464 tmp->msg_count = Context->msgcount;
465 tmp->msg_unread = Context->unread;
466 }
467 add_folder (menu, state, de->d_name, &s, tmp);
468 }
469 closedir (dp);
470 browser_sort (state);
471 return 0;
472}
473
474static int examine_mailboxes (MUTTMENU *menu, struct browser_state *state)
475{
476 struct stat s;
477 char buffer[LONG_STRING];
478 BUFFY *tmp = Incoming;
479
480 if (!Incoming)
481 return (-1);
482 mutt_buffy_check (0);
483
484 init_state (state, menu);
485
486 do
487 {
488 if (Context &&
489 !mutt_strcmp (tmp->realpath, Context->realpath))
490 {
491 tmp->msg_count = Context->msgcount;
492 tmp->msg_unread = Context->unread;
493 }
494
495#ifdef USE_IMAP
496 if (mx_is_imap (tmp->path))
497 {
498 add_folder (menu, state, tmp->path, NULL, tmp);
499 continue;
500 }
501#endif
502#ifdef USE_POP
503 if (mx_is_pop (tmp->path))
504 {
505 add_folder (menu, state, tmp->path, NULL, tmp);
506 continue;
507 }
508#endif
509 if (lstat (tmp->path, &s) == -1)
510 continue;
511
512 if ((! S_ISREG (s.st_mode)) && (! S_ISDIR (s.st_mode)) &&
513 (! S_ISLNK (s.st_mode)))
514 continue;
515
516 if (mx_is_maildir (tmp->path))
517 {
518 struct stat st2;
519 char md[_POSIX_PATH_MAX];
520
521 snprintf (md, sizeof (md), "%s/new", tmp->path);
522 if (stat (md, &s) < 0)
523 s.st_mtime = 0;
524 snprintf (md, sizeof (md), "%s/cur", tmp->path);
525 if (stat (md, &st2) < 0)
526 st2.st_mtime = 0;
527 if (st2.st_mtime > s.st_mtime)
528 s.st_mtime = st2.st_mtime;
529 }
530
531 strfcpy (buffer, NONULL(tmp->path), sizeof (buffer));
532 mutt_pretty_mailbox (buffer, sizeof (buffer));
533
534 add_folder (menu, state, buffer, &s, tmp);
535 }
536 while ((tmp = tmp->next));
537 browser_sort (state);
538 return 0;
539}
540
541static int select_file_search (MUTTMENU *menu, regex_t *re, int n)
542{
543 return (regexec (re, ((struct folder_file *) menu->data)[n].name, 0, NULL, 0));
544}
545
546static void folder_entry (char *s, size_t slen, MUTTMENU *menu, int num)
547{
548 FOLDER folder;
549
550 folder.ff = &((struct folder_file *) menu->data)[num];
551 folder.num = num;
552
553 mutt_FormatString (s, slen, 0, MuttIndexWindow->cols, NONULL(FolderFormat), folder_format_str,
554 (unsigned long) &folder, MUTT_FORMAT_ARROWCURSOR);
555}
556
557static void init_menu (struct browser_state *state, MUTTMENU *menu, char *title,
558 size_t titlelen, int buffy)
559{
560 char path[_POSIX_PATH_MAX];
561
562 menu->max = state->entrylen;
563
564 if(menu->current >= menu->max)
565 menu->current = menu->max - 1;
566 if (menu->current < 0)
567 menu->current = 0;
568 if (menu->top > menu->current)
569 menu->top = 0;
570
571 menu->tagged = 0;
572
573 if (buffy)
574 snprintf (title, titlelen, _("Mailboxes [%d]"), mutt_buffy_check (0));
575 else
576 {
577 strfcpy (path, LastDir, sizeof (path));
578 mutt_pretty_mailbox (path, sizeof (path));
579#ifdef USE_IMAP
580 if (state->imap_browse && option (OPTIMAPLSUB))
581 snprintf (title, titlelen, _("Subscribed [%s], File mask: %s"),
582 path, NONULL (Mask.pattern));
583 else
584#endif
585 snprintf (title, titlelen, _("Directory [%s], File mask: %s"),
586 path, NONULL(Mask.pattern));
587 }
588 menu->redraw = REDRAW_FULL;
589}
590
591static int file_tag (MUTTMENU *menu, int n, int m)
592{
593 struct folder_file *ff = &(((struct folder_file *)menu->data)[n]);
594 int ot;
595 if (S_ISDIR (ff->mode) || (S_ISLNK (ff->mode) && link_is_dir (LastDir, ff->name)))
596 {
597 mutt_error _("Can't attach a directory!");
598 return 0;
599 }
600
601 ot = ff->tagged;
602 ff->tagged = (m >= 0 ? m : !ff->tagged);
603
604 return ff->tagged - ot;
605}
606
607void _mutt_select_file (char *f, size_t flen, int flags, char ***files, int *numfiles)
608{
609 char buf[_POSIX_PATH_MAX];
610 char prefix[_POSIX_PATH_MAX] = "";
611 char helpstr[LONG_STRING];
612 char title[STRING];
613 struct browser_state state;
614 MUTTMENU *menu;
615 struct stat st;
616 int i, killPrefix = 0;
617 int multiple = (flags & MUTT_SEL_MULTI) ? 1 : 0;
618 int folder = (flags & MUTT_SEL_FOLDER) ? 1 : 0;
619 int buffy = (flags & MUTT_SEL_BUFFY) ? 1 : 0;
620
621 buffy = buffy && folder;
622
623 memset (&state, 0, sizeof (struct browser_state));
624
625 if (!folder)
626 strfcpy (LastDirBackup, LastDir, sizeof (LastDirBackup));
627
628 if (*f)
629 {
630 mutt_expand_path (f, flen);
631#ifdef USE_IMAP
632 if (mx_is_imap (f))
633 {
634 init_state (&state, NULL);
635 state.imap_browse = 1;
636 if (!imap_browse (f, &state))
637 strfcpy (LastDir, state.folder, sizeof (LastDir));
638 }
639 else
640 {
641#endif
642 for (i = mutt_strlen (f) - 1; i > 0 && f[i] != '/' ; i--);
643 if (i > 0)
644 {
645 if (f[0] == '/')
646 {
647 if (i > sizeof (LastDir) - 1) i = sizeof (LastDir) - 1;
648 strncpy (LastDir, f, i);
649 LastDir[i] = 0;
650 }
651 else
652 {
653 getcwd (LastDir, sizeof (LastDir));
654 safe_strcat (LastDir, sizeof (LastDir), "/");
655 safe_strncat (LastDir, sizeof (LastDir), f, i);
656 }
657 }
658 else
659 {
660 if (f[0] == '/')
661 strcpy (LastDir, "/"); /* __STRCPY_CHECKED__ */
662 else
663 getcwd (LastDir, sizeof (LastDir));
664 }
665
666 if (i <= 0 && f[0] != '/')
667 strfcpy (prefix, f, sizeof (prefix));
668 else
669 strfcpy (prefix, f + i + 1, sizeof (prefix));
670 killPrefix = 1;
671#ifdef USE_IMAP
672 }
673#endif
674 }
675 else
676 {
677 if (!folder)
678 getcwd (LastDir, sizeof (LastDir));
679 else if (!LastDir[0])
680 strfcpy (LastDir, NONULL(Maildir), sizeof (LastDir));
681
682#ifdef USE_IMAP
683 if (!buffy && mx_is_imap (LastDir))
684 {
685 init_state (&state, NULL);
686 state.imap_browse = 1;
687 imap_browse (LastDir, &state);
688 browser_sort (&state);
689 }
690 else
691#endif
692 {
693 i = mutt_strlen (LastDir);
694 while (i && LastDir[--i] == '/')
695 LastDir[i] = '\0';
696 if (!LastDir[0])
697 getcwd (LastDir, sizeof (LastDir));
698 }
699 }
700
701 *f = 0;
702
703 if (buffy)
704 {
705 if (examine_mailboxes (NULL, &state) == -1)
706 goto bail;
707 }
708 else
709#ifdef USE_IMAP
710 if (!state.imap_browse)
711#endif
712 if (examine_directory (NULL, &state, LastDir, prefix) == -1)
713 goto bail;
714
715 menu = mutt_new_menu (MENU_FOLDER);
716 menu->make_entry = folder_entry;
717 menu->search = select_file_search;
718 menu->title = title;
719 menu->data = state.entry;
720 if (multiple)
721 menu->tag = file_tag;
722
723 menu->help = mutt_compile_help (helpstr, sizeof (helpstr), MENU_FOLDER,
724 FolderHelp);
725
726 init_menu (&state, menu, title, sizeof (title), buffy);
727
728 FOREVER
729 {
730 switch (i = mutt_menuLoop (menu))
731 {
732 case OP_GENERIC_SELECT_ENTRY:
733
734 if (!state.entrylen)
735 {
736 mutt_error _("No files match the file mask");
737 break;
738 }
739
740 if (S_ISDIR (state.entry[menu->current].mode) ||
741 (S_ISLNK (state.entry[menu->current].mode) &&
742 link_is_dir (LastDir, state.entry[menu->current].name))
743#ifdef USE_IMAP
744 || state.entry[menu->current].inferiors
745#endif
746 )
747 {
748 /* make sure this isn't a MH or maildir mailbox */
749 if (buffy)
750 {
751 strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
752 mutt_expand_path (buf, sizeof (buf));
753 }
754#ifdef USE_IMAP
755 else if (state.imap_browse)
756 {
757 strfcpy (buf, state.entry[menu->current].name, sizeof (buf));
758 }
759#endif
760 else
761 mutt_concat_path (buf, LastDir, state.entry[menu->current].name, sizeof (buf));
762
763 if ((mx_get_magic (buf) <= 0)
764#ifdef USE_IMAP
765 || state.entry[menu->current].inferiors
766#endif
767 )
768 {
769 char OldLastDir[_POSIX_PATH_MAX];
770
771 /* save the old directory */
772 strfcpy (OldLastDir, LastDir, sizeof (OldLastDir));
773
774 if (mutt_strcmp (state.entry[menu->current].name, "..") == 0)
775 {
776 if (mutt_strcmp ("..", LastDir + mutt_strlen (LastDir) - 2) == 0)
777 strcat (LastDir, "/.."); /* __STRCAT_CHECKED__ */
778 else
779 {
780 char *p = strrchr (LastDir + 1, '/');
781
782 if (p)
783 *p = 0;
784 else
785 {
786 if (LastDir[0] == '/')
787 LastDir[1] = 0;
788 else
789 strcat (LastDir, "/.."); /* __STRCAT_CHECKED__ */
790 }
791 }
792 }
793 else if (buffy)
794 {
795 strfcpy (LastDir, state.entry[menu->current].name, sizeof (LastDir));
796 mutt_expand_path (LastDir, sizeof (LastDir));
797 }
798#ifdef USE_IMAP
799 else if (state.imap_browse)
800 {
801 int n;
802 ciss_url_t url;
803
804 strfcpy (LastDir, state.entry[menu->current].name,
805 sizeof (LastDir));
806 /* tack on delimiter here */
807 n = strlen (LastDir)+1;
808
809 /* special case "" needs no delimiter */
810 url_parse_ciss (&url, state.entry[menu->current].name);
811 if (url.path &&
812 (state.entry[menu->current].delim != '\0') &&
813 (n < sizeof (LastDir)))
814 {
815 LastDir[n] = '\0';
816 LastDir[n-1] = state.entry[menu->current].delim;
817 }
818 }
819#endif
820 else
821 {
822 char tmp[_POSIX_PATH_MAX];
823 mutt_concat_path (tmp, LastDir, state.entry[menu->current].name, sizeof (tmp));
824 strfcpy (LastDir, tmp, sizeof (LastDir));
825 }
826
827 destroy_state (&state);
828 if (killPrefix)
829 {
830 prefix[0] = 0;
831 killPrefix = 0;
832 }
833 buffy = 0;
834#ifdef USE_IMAP
835 if (state.imap_browse)
836 {
837 init_state (&state, NULL);
838 state.imap_browse = 1;
839 imap_browse (LastDir, &state);
840 browser_sort (&state);
841 menu->data = state.entry;
842 }
843 else
844#endif
845 if (examine_directory (menu, &state, LastDir, prefix) == -1)
846 {
847 /* try to restore the old values */
848 strfcpy (LastDir, OldLastDir, sizeof (LastDir));
849 if (examine_directory (menu, &state, LastDir, prefix) == -1)
850 {
851 strfcpy (LastDir, NONULL(Homedir), sizeof (LastDir));
852 goto bail;
853 }
854 }
855 menu->current = 0;
856 menu->top = 0;
857 init_menu (&state, menu, title, sizeof (title), buffy);
858 break;
859 }
860 }
861
862 if (buffy)
863 {
864 strfcpy (f, state.entry[menu->current].name, flen);
865 mutt_expand_path (f, flen);
866 }
867#ifdef USE_IMAP
868 else if (state.imap_browse)
869 strfcpy (f, state.entry[menu->current].name, flen);
870#endif
871 else
872 mutt_concat_path (f, LastDir, state.entry[menu->current].name, flen);
873
874 /* Fall through to OP_EXIT */
875
876 case OP_EXIT:
877
878 if (multiple)
879 {
880 char **tfiles;
881 int i, j;
882
883 if (menu->tagged)
884 {
885 *numfiles = menu->tagged;
886 tfiles = safe_calloc (*numfiles, sizeof (char *));
887 for (i = 0, j = 0; i < state.entrylen; i++)
888 {
889 struct folder_file ff = state.entry[i];
890 char full[_POSIX_PATH_MAX];
891 if (ff.tagged)
892 {
893 mutt_concat_path (full, LastDir, ff.name, sizeof (full));
894 mutt_expand_path (full, sizeof (full));
895 tfiles[j++] = safe_strdup (full);
896 }
897 }
898 *files = tfiles;
899 }
900 else if (f[0]) /* no tagged entries. return selected entry */
901 {
902 *numfiles = 1;
903 tfiles = safe_calloc (*numfiles, sizeof (char *));
904 mutt_expand_path (f, flen);
905 tfiles[0] = safe_strdup (f);
906 *files = tfiles;
907 }
908 }
909
910 destroy_state (&state);
911 mutt_menuDestroy (&menu);
912 goto bail;
913
914 case OP_BROWSER_TELL:
915 if(state.entrylen)
916 mutt_message("%s", state.entry[menu->current].name);
917 break;
918
919#ifdef USE_IMAP
920 case OP_BROWSER_SUBSCRIBE:
921 imap_subscribe (state.entry[menu->current].name, 1);
922 break;
923
924 case OP_BROWSER_UNSUBSCRIBE:
925 imap_subscribe (state.entry[menu->current].name, 0);
926 break;
927
928 case OP_BROWSER_TOGGLE_LSUB:
929 if (option (OPTIMAPLSUB))
930 unset_option (OPTIMAPLSUB);
931 else
932 set_option (OPTIMAPLSUB);
933
934 mutt_unget_event (0, OP_CHECK_NEW);
935 break;
936
937 case OP_CREATE_MAILBOX:
938 if (!state.imap_browse)
939 {
940 mutt_error (_("Create is only supported for IMAP mailboxes"));
941 break;
942 }
943
944 if (!imap_mailbox_create (LastDir))
945 {
946 /* TODO: find a way to detect if the new folder would appear in
947 * this window, and insert it without starting over. */
948 destroy_state (&state);
949 init_state (&state, NULL);
950 state.imap_browse = 1;
951 imap_browse (LastDir, &state);
952 browser_sort (&state);
953 menu->data = state.entry;
954 menu->current = 0;
955 menu->top = 0;
956 init_menu (&state, menu, title, sizeof (title), buffy);
957 MAYBE_REDRAW (menu->redraw);
958 }
959 /* else leave error on screen */
960 break;
961
962 case OP_RENAME_MAILBOX:
963 if (!state.entry[menu->current].imap)
964 mutt_error (_("Rename is only supported for IMAP mailboxes"));
965 else
966 {
967 int nentry = menu->current;
968
969 if (imap_mailbox_rename (state.entry[nentry].name) >= 0)
970 {
971 destroy_state (&state);
972 init_state (&state, NULL);
973 state.imap_browse = 1;
974 imap_browse (LastDir, &state);
975 browser_sort (&state);
976 menu->data = state.entry;
977 menu->current = 0;
978 menu->top = 0;
979 init_menu (&state, menu, title, sizeof (title), buffy);
980 MAYBE_REDRAW (menu->redraw);
981 }
982 }
983 break;
984
985 case OP_DELETE_MAILBOX:
986 if (!state.entry[menu->current].imap)
987 mutt_error (_("Delete is only supported for IMAP mailboxes"));
988 else
989 {
990 char msg[SHORT_STRING];
991 IMAP_MBOX mx;
992 int nentry = menu->current;
993
994 imap_parse_path (state.entry[nentry].name, &mx);
995 if (!mx.mbox)
996 {
997 mutt_error _("Cannot delete root folder");
998 break;
999 }
1000 snprintf (msg, sizeof (msg), _("Really delete mailbox \"%s\"?"),
1001 mx.mbox);
1002 if (mutt_yesorno (msg, MUTT_NO) == MUTT_YES)
1003 {
1004 if (!imap_delete_mailbox (Context, mx))
1005 {
1006 /* free the mailbox from the browser */
1007 FREE (&((state.entry)[nentry].name));
1008 FREE (&((state.entry)[nentry].desc));
1009 /* and move all other entries up */
1010 if (nentry+1 < state.entrylen)
1011 memmove (state.entry + nentry, state.entry + nentry + 1,
1012 sizeof (struct folder_file) * (state.entrylen - (nentry+1)));
1013 memset (&state.entry[state.entrylen - 1], 0,
1014 sizeof (struct folder_file));
1015 state.entrylen--;
1016 mutt_message _("Mailbox deleted.");
1017 init_menu (&state, menu, title, sizeof (title), buffy);
1018 MAYBE_REDRAW (menu->redraw);
1019 }
1020 }
1021 else
1022 mutt_message _("Mailbox not deleted.");
1023 FREE (&mx.mbox);
1024 }
1025 break;
1026#endif
1027
1028 case OP_CHANGE_DIRECTORY:
1029
1030 strfcpy (buf, LastDir, sizeof (buf));
1031#ifdef USE_IMAP
1032 if (!state.imap_browse)
1033#endif
1034 {
1035 /* add '/' at the end of the directory name if not already there */
1036 int len=mutt_strlen(LastDir);
1037 if (len && LastDir[len-1] != '/' && sizeof (buf) > len)
1038 buf[len]='/';
1039 }
1040
1041 if (mutt_get_field (_("Chdir to: "), buf, sizeof (buf), MUTT_FILE) == 0 &&
1042 buf[0])
1043 {
1044 buffy = 0;
1045 mutt_expand_path (buf, sizeof (buf));
1046#ifdef USE_IMAP
1047 if (mx_is_imap (buf))
1048 {
1049 strfcpy (LastDir, buf, sizeof (LastDir));
1050 destroy_state (&state);
1051 init_state (&state, NULL);
1052 state.imap_browse = 1;
1053 imap_browse (LastDir, &state);
1054 browser_sort (&state);
1055 menu->data = state.entry;
1056 menu->current = 0;
1057 menu->top = 0;
1058 init_menu (&state, menu, title, sizeof (title), buffy);
1059 }
1060 else
1061#endif
1062 {
1063 if (*buf != '/')
1064 {
1065 /* in case dir is relative, make it relative to LastDir,
1066 * not current working dir */
1067 char tmp[_POSIX_PATH_MAX];
1068 mutt_concat_path (tmp, LastDir, buf, sizeof (tmp));
1069 strfcpy (buf, tmp, sizeof (buf));
1070 }
1071 if (stat (buf, &st) == 0)
1072 {
1073 if (S_ISDIR (st.st_mode))
1074 {
1075 destroy_state (&state);
1076 if (examine_directory (menu, &state, buf, prefix) == 0)
1077 strfcpy (LastDir, buf, sizeof (LastDir));
1078 else
1079 {
1080 mutt_error _("Error scanning directory.");
1081 if (examine_directory (menu, &state, LastDir, prefix) == -1)
1082 {
1083 mutt_menuDestroy (&menu);
1084 goto bail;
1085 }
1086 }
1087 menu->current = 0;
1088 menu->top = 0;
1089 init_menu (&state, menu, title, sizeof (title), buffy);
1090 }
1091 else
1092 mutt_error (_("%s is not a directory."), buf);
1093 }
1094 else
1095 mutt_perror (buf);
1096 }
1097 }
1098 MAYBE_REDRAW (menu->redraw);
1099 break;
1100
1101 case OP_ENTER_MASK:
1102
1103 strfcpy (buf, NONULL(Mask.pattern), sizeof (buf));
1104 if (mutt_get_field (_("File Mask: "), buf, sizeof (buf), 0) == 0)
1105 {
1106 regex_t *rx = (regex_t *) safe_malloc (sizeof (regex_t));
1107 char *s = buf;
1108 int not = 0, err;
1109
1110 buffy = 0;
1111 /* assume that the user wants to see everything */
1112 if (!buf[0])
1113 strfcpy (buf, ".", sizeof (buf));
1114 SKIPWS (s);
1115 if (*s == '!')
1116 {
1117 s++;
1118 SKIPWS (s);
1119 not = 1;
1120 }
1121
1122 if ((err = REGCOMP (rx, s, REG_NOSUB)) != 0)
1123 {
1124 regerror (err, rx, buf, sizeof (buf));
1125 FREE (&rx);
1126 mutt_error ("%s", buf);
1127 }
1128 else
1129 {
1130 mutt_str_replace (&Mask.pattern, buf);
1131 regfree (Mask.rx);
1132 FREE (&Mask.rx);
1133 Mask.rx = rx;
1134 Mask.not = not;
1135
1136 destroy_state (&state);
1137#ifdef USE_IMAP
1138 if (state.imap_browse)
1139 {
1140 init_state (&state, NULL);
1141 state.imap_browse = 1;
1142 imap_browse (LastDir, &state);
1143 browser_sort (&state);
1144 menu->data = state.entry;
1145 init_menu (&state, menu, title, sizeof (title), buffy);
1146 }
1147 else
1148#endif
1149 if (examine_directory (menu, &state, LastDir, NULL) == 0)
1150 init_menu (&state, menu, title, sizeof (title), buffy);
1151 else
1152 {
1153 mutt_error _("Error scanning directory.");
1154 mutt_menuDestroy (&menu);
1155 goto bail;
1156 }
1157 killPrefix = 0;
1158 if (!state.entrylen)
1159 {
1160 mutt_error _("No files match the file mask");
1161 break;
1162 }
1163 }
1164 }
1165 MAYBE_REDRAW (menu->redraw);
1166 break;
1167
1168 case OP_SORT:
1169 case OP_SORT_REVERSE:
1170
1171 {
1172 int resort = 1;
1173 int reverse = (i == OP_SORT_REVERSE);
1174
1175 switch (mutt_multi_choice ((reverse) ?
1176 _("Reverse sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? ") :
1177 _("Sort by (d)ate, (a)lpha, si(z)e or do(n)'t sort? "),
1178 _("dazn")))
1179 {
1180 case -1: /* abort */
1181 resort = 0;
1182 break;
1183
1184 case 1: /* (d)ate */
1185 BrowserSort = SORT_DATE;
1186 break;
1187
1188 case 2: /* (a)lpha */
1189 BrowserSort = SORT_SUBJECT;
1190 break;
1191
1192 case 3: /* si(z)e */
1193 BrowserSort = SORT_SIZE;
1194 break;
1195
1196 case 4: /* do(n)'t sort */
1197 BrowserSort = SORT_ORDER;
1198 resort = 0;
1199 break;
1200 }
1201 if (resort)
1202 {
1203 BrowserSort |= reverse ? SORT_REVERSE : 0;
1204 browser_sort (&state);
1205 menu->redraw = REDRAW_FULL;
1206 }
1207 break;
1208 }
1209
1210 case OP_TOGGLE_MAILBOXES:
1211 buffy = 1 - buffy;
1212
1213 case OP_CHECK_NEW:
1214 destroy_state (&state);
1215 prefix[0] = 0;
1216 killPrefix = 0;
1217
1218 if (buffy)
1219 {
1220 if (examine_mailboxes (menu, &state) == -1)
1221 goto bail;
1222 }
1223#ifdef USE_IMAP
1224 else if (mx_is_imap (LastDir))
1225 {
1226 init_state (&state, NULL);
1227 state.imap_browse = 1;
1228 imap_browse (LastDir, &state);
1229 browser_sort (&state);
1230 menu->data = state.entry;
1231 }
1232#endif
1233 else if (examine_directory (menu, &state, LastDir, prefix) == -1)
1234 goto bail;
1235 init_menu (&state, menu, title, sizeof (title), buffy);
1236 break;
1237
1238 case OP_BUFFY_LIST:
1239 mutt_buffy_list ();
1240 break;
1241
1242 case OP_BROWSER_NEW_FILE:
1243
1244 snprintf (buf, sizeof (buf), "%s/", LastDir);
1245 if (mutt_get_field (_("New file name: "), buf, sizeof (buf), MUTT_FILE) == 0)
1246 {
1247 strfcpy (f, buf, flen);
1248 destroy_state (&state);
1249 mutt_menuDestroy (&menu);
1250 goto bail;
1251 }
1252 MAYBE_REDRAW (menu->redraw);
1253 break;
1254
1255 case OP_BROWSER_VIEW_FILE:
1256 if (!state.entrylen)
1257 {
1258 mutt_error _("No files match the file mask");
1259 break;
1260 }
1261
1262#ifdef USE_IMAP
1263 if (state.entry[menu->current].selectable)
1264 {
1265 strfcpy (f, state.entry[menu->current].name, flen);
1266 destroy_state (&state);
1267 mutt_menuDestroy (&menu);
1268 goto bail;
1269 }
1270 else
1271#endif
1272 if (S_ISDIR (state.entry[menu->current].mode) ||
1273 (S_ISLNK (state.entry[menu->current].mode) &&
1274 link_is_dir (LastDir, state.entry[menu->current].name)))
1275 {
1276 mutt_error _("Can't view a directory");
1277 break;
1278 }
1279 else
1280 {
1281 BODY *b;
1282 char buf[_POSIX_PATH_MAX];
1283
1284 mutt_concat_path (buf, LastDir, state.entry[menu->current].name, sizeof (buf));
1285 b = mutt_make_file_attach (buf);
1286 if (b != NULL)
1287 {
1288 mutt_view_attachment (NULL, b, MUTT_REGULAR, NULL, NULL, 0);
1289 mutt_free_body (&b);
1290 menu->redraw = REDRAW_FULL;
1291 }
1292 else
1293 mutt_error _("Error trying to view file");
1294 }
1295 }
1296 }
1297
1298 bail:
1299
1300 if (!folder)
1301 strfcpy (LastDir, LastDirBackup, sizeof (LastDir));
1302
1303}