mutt stable branch with some hacks
at master 1303 lines 32 kB view raw
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}