tin  2.4.4
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.4.4.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

save.c
Go to the documentation of this file.
1 /*
2  * Project : tin - a Usenet reader
3  * Module : save.c
4  * Author : I. Lea & R. Skrenta
5  * Created : 1991-04-01
6  * Updated : 2019-06-05
7  * Notes :
8  *
9  * Copyright (c) 1991-2020 Iain Lea <iain@bricbrac.de>, Rich Skrenta <skrenta@pbm.com>
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  *
16  * 1. Redistributions of source code must retain the above copyright notice,
17  * this list of conditions and the following disclaimer.
18  *
19  * 2. Redistributions in binary form must reproduce the above copyright
20  * notice, this list of conditions and the following disclaimer in the
21  * documentation and/or other materials provided with the distribution.
22  *
23  * 3. Neither the name of the copyright holder nor the names of its
24  * contributors may be used to endorse or promote products derived from
25  * this software without specific prior written permission.
26  *
27  * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 
41 #ifndef TIN_H
42 # include "tin.h"
43 #endif /* !TIN_H */
44 #ifndef TCURSES_H
45 # include "tcurses.h"
46 #endif /* !TCURSES_H */
47 
48 #ifdef HAVE_UUDEVIEW_H
49 # ifndef __UUDEVIEW_H__
50 # include <uudeview.h>
51 # endif /* !__UUDEVIEW_H__ */
52 #endif /* HAVE_UUDEVIEW_H */
53 
54 #ifndef HAVE_LIBUU
55 # undef OFF
56  enum state {
59  OFF,
61  };
62 #endif /* !HAVE_LIBUU */
63 
64 enum action {
69 #ifndef DONT_HAVE_PIPING
70  , PIPE
71 #endif /* !DONT_HAVE_PIPING */
72 };
73 
74 /*
75  * Local prototypes
76  */
77 static FILE *open_save_filename(const char *path, t_bool mbox);
78 static char *build_tree(int depth, int maxlen, int i);
79 static char *generate_savepath(t_part *part);
80 static int build_part_list(t_openartinfo *art);
81 static int get_tagged(int n);
82 static int match_content_type(t_part *part, char *type);
83 static t_bool check_save_mime_type(t_part *part, const char *mime_types);
84 static t_bool decode_save_one(t_part *part, FILE *rawfp, t_bool postproc);
85 static t_bool expand_save_filename(char *outpath, size_t outpath_len, const char *path);
86 static t_bool tag_part(int n);
87 static t_function attachment_left(void);
88 static t_function attachment_right(void);
89 static t_partl *find_part(int n);
90 static void build_attachment_line(int i);
91 static void draw_attachment_arrow(void);
92 static void free_part_list(t_partl *list);
93 static void generate_filename(char *buf, int buflen, const char *suffix);
94 #ifndef DONT_HAVE_PIPING
95  static void pipe_part(const char *savepath);
96 #endif /* !DONT_HAVE_PIPING */
97 static void post_process_uud(void);
98 static void post_process_sh(void);
99 static void process_part(t_part *part, t_openartinfo *art, FILE *outfile, const char *savepath, enum action what);
100 static void process_parts(t_part *part, t_openartinfo *art, enum action what);
101 static void show_attachment_page(void);
102 static void start_viewer(t_part *part, const char *path);
103 static void tag_pattern(void);
104 static void untag_all_parts(void);
105 static void untag_part(int n);
106 static void uudecode_line(const char *buf, FILE *fp);
107 static void view_file(const char *path, const char *file);
108 #ifndef HAVE_LIBUU
109  static void sum_file(const char *path, const char *file);
110 #endif /* !HAVE_LIBUU */
111 
115 
116 /*
117  * Check for articles and say how many new/unread in each group.
118  * or
119  * Start if new/unread articles and return first group with new/unread.
120  * or
121  * Save any new articles to savedir and mark arts read and mail user
122  * and inform how many arts in which groups were saved.
123  * or
124  * Mail any new articles to specified user and mark arts read and mail
125  * user and inform how many arts in which groups were mailed.
126  * Return codes:
127  * CHECK_ANY_NEWS - code to pass to exit() - see manpage for list
128  * START_ANY_NEWS - index in my_group of first group with unread news or -1
129  * MAIL_ANY_NEWS - not checked
130  * SAVE_ANY_NEWS - not checked
131  */
132 int
134  int function,
135  t_bool catchup)
136 {
137  FILE *artfp, *savefp;
138  FILE *fp_log = (FILE *) 0;
139  char *line;
140  char buf[LEN];
141  char path[PATH_LEN];
142  char logfile[PATH_LEN], savefile[PATH_LEN];
143  char subject[HEADER_LEN];
144  int group_count = 0;
145  int i, j;
146  int art_count, hot_count;
147  int saved_arts = 0; /* Total # saved arts */
148  struct t_article *art;
149  struct t_group *group;
150  t_bool log_opened = TRUE;
151  t_bool print_first = verbose;
152  t_bool unread_news = FALSE;
153  time_t epoch;
154 
155  switch (function) {
156  case CHECK_ANY_NEWS:
157  case START_ANY_NEWS:
158  if (verbose)
160  break;
161 
162  case MAIL_ANY_NEWS:
163  joinpath(savefile, sizeof(savefile), TMPDIR, "tin");
164 #ifdef APPEND_PID
165  snprintf(savefile + strlen(savefile), sizeof(savefile) - strlen(savefile), ".%ld", (long) process_id);
166 #endif /* APPEND_PID */
167  /* FALLTHROUGH */
168 
169  case SAVE_ANY_NEWS:
170  joinpath(logfile, sizeof(logfile), rcdir, "log");
171 
172  if (no_write || (fp_log = fopen(logfile, "w")) == NULL) {
173  perror_message(_(txt_cannot_open), logfile);
174  fp_log = stdout;
175  verbose = FALSE;
176  log_opened = FALSE;
177  }
178  fprintf(fp_log, "To: %s\n", userid);
179  (void) time(&epoch);
180  snprintf(subject, sizeof(subject), "Subject: NEWS LOG %s", ctime(&epoch));
181  fprintf(fp_log, "%s\n", subject); /* ctime() includes a \n too */
182  break;
183 
184  default:
185  break;
186  }
187 
188  /*
189  * For each group we subscribe to...
190  */
191  for (i = 0; i < selmenu.max; i++) {
192  art_count = hot_count = 0;
193  group = &active[my_group[i]];
194  /*
195  * FIXME: workaround to get a valid CURR_GROUP
196  * it also points to the currently processed group so that
197  * the correct attributes are used
198  * The correct fix is to get rid of CURR_GROUP
199  */
200  selmenu.curr = i;
201 
202  if (group->bogus || !group->subscribed)
203  continue;
204 
205  if (function == MAIL_ANY_NEWS || function == SAVE_ANY_NEWS) {
206  if (!group->attribute->batch_save)
207  continue;
208 
209  group_count++;
210  snprintf(buf, sizeof(buf), _(txt_saved_groupname), group->name);
211  fprintf(fp_log, "%s", buf);
212  if (verbose)
213  wait_message(0, "%s", buf);
214 
215  if (function == SAVE_ANY_NEWS) {
216  char tmp[PATH_LEN];
217  char *group_path = my_malloc(strlen(group->name) + 2); /* trailing "/\0" */
218 
219  make_group_path(group->name, group_path);
220  if (!strfpath((cmdline.args & CMDLINE_SAVEDIR) ? cmdline.savedir : tinrc.savedir, tmp, sizeof(tmp), group, FALSE))
221  joinpath(tmp, sizeof(tmp), homedir, DEFAULT_SAVEDIR);
222 
223  joinpath(path, sizeof(path), tmp, group_path);
224  free(group_path);
225  create_path(path); /* TODO error handling */
226  }
227  }
228 
229  if (!index_group(group)) {
230  for_each_art(j) {
231  art = &arts[j];
232  FreeAndNull(art->refs);
233  FreeAndNull(art->msgid);
234  }
235  continue;
236  }
237 
238  /*
239  * For each article in this group...
240  */
241  for_each_art(j) {
242  if (arts[j].status != ART_UNREAD)
243  continue;
244 
245  switch (function) {
246  case CHECK_ANY_NEWS:
247  if (print_first) {
248  my_fputc('\n', stdout);
249  print_first = FALSE;
250  }
251  if (!verbose && !catchup) /* we don't need details */
252  return NEWS_AVAIL_EXIT;
253  art_count++;
254  if (arts[j].score >= tinrc.score_select)
255  hot_count++;
256  if (catchup)
257  art_mark(group, &arts[j], ART_READ);
258  break;
259 
260  case START_ANY_NEWS:
261  return i; /* return first group with unread news */
262  /* NOTREACHED */
263 
264  case MAIL_ANY_NEWS:
265  case SAVE_ANY_NEWS:
266  if ((artfp = open_art_fp(group, arts[j].artnum)) == NULL)
267  continue;
268 
269  if (function == SAVE_ANY_NEWS) {
270  snprintf(buf, sizeof(buf), "%"T_ARTNUM_PFMT, arts[j].artnum);
271  joinpath(savefile, sizeof(savefile), path, buf);
272  }
273 
274  if ((savefp = fopen(savefile, "w")) == NULL) {
275  fprintf(fp_log, _(txt_cannot_open), savefile);
276  if (verbose)
277  perror_message(_(txt_cannot_open), savefile);
278  TIN_FCLOSE(artfp);
279  continue;
280  }
281 
283  fprintf(savefp, "To: %s\n", mail_news_user);
284  fprintf(savefp, "Subject: %s\n", arts[j].subject);
285  /*
286  * Reuse some headers to allow threading in mail reader
287  */
288  if (arts[j].msgid)
289  fprintf(savefp, "Message-ID: %s\n", arts[j].msgid);
290  /* fprintf(savefp, "References: %s\n", arts[j].refs); */
291  /*
292  * wrap article in appropriate MIME type
293  */
294  fprintf(savefp, "MIME-Version: 1.0\n");
295  fprintf(savefp, "Content-Type: message/rfc822\n");
296  /*
297  * CTE should be 7bit if the article is in pure
298  * US-ASCII, but this requires previous parsing
299  */
300  fprintf(savefp, "Content-Transfer-Encoding: 8bit\n\n");
301  }
302 
303  snprintf(buf, sizeof(buf), "[%5"T_ARTNUM_PFMT"] %s\n", arts[j].artnum, arts[j].subject);
304  fprintf(fp_log, "%s", buf); /* buf may contain % */
305  if (verbose)
306  wait_message(0, "%s", buf);
307 
308  while ((line = tin_fgets(artfp, FALSE)) != NULL)
309  fprintf(savefp, "%s\n", line); /* TODO: error handling */
310 
311  TIN_FCLOSE(artfp);
312  fclose(savefp);
313  saved_arts++;
314 
315  if (function == MAIL_ANY_NEWS) {
316  strfmailer(mailer, arts[j].subject, mail_news_user, savefile, buf, sizeof(buf), tinrc.mailer_format);
317  invoke_cmd(buf); /* Keep trying after errors */
318  unlink(savefile);
319  }
320  if (catchup)
321  art_mark(group, &arts[j], ART_READ);
322  break;
323 
324  default:
325  break;
326  }
327  }
328 
329  if (art_count) {
330  unread_news = TRUE;
331  if (verbose)
332  wait_message(0, _(txt_saved_group), art_count, hot_count,
333  PLURAL(art_count, txt_article), group->name);
334  }
335  }
336 
337  switch (function) {
338  case CHECK_ANY_NEWS:
339  /*
340  * TODO: shall we return 2 or 0 in the -cZ case?
341  */
342  if (unread_news && !catchup)
343  return NEWS_AVAIL_EXIT;
344  else {
345  if (verbose)
347  return EXIT_SUCCESS;
348  }
349  /* NOTREACHED */
350 
351  case START_ANY_NEWS:
353  return -1;
354  /* NOTREACHED */
355 
356  case MAIL_ANY_NEWS:
357  case SAVE_ANY_NEWS:
358  snprintf(buf, sizeof(buf), _(txt_saved_summary), (function == MAIL_ANY_NEWS ? _(txt_mailed) : _(txt_saved)),
359  saved_arts, PLURAL(saved_arts, txt_article),
360  group_count, PLURAL(group_count, txt_group));
361  fprintf(fp_log, "%s", buf);
362  if (verbose)
363  wait_message(0, "%s", buf);
364 
365  if (log_opened) {
366  fclose(fp_log);
367  if (verbose)
369  strfmailer(mailer, subject, (function == MAIL_ANY_NEWS ? mail_news_user : userid), logfile, buf, sizeof(buf), tinrc.mailer_format);
370  if (invoke_cmd(buf))
371  unlink(logfile);
372  }
373  break;
374 
375  default:
376  break;
377  }
378  return 0;
379 }
380 
381 
382 /*
383  * Do basic validation of a save-to path, handle append/overwrite semantics
384  * and return an opened file handle or NULL if user aborted etc..
385  */
386 static FILE *
388  const char *path,
389  t_bool mbox)
390 {
391  FILE *fp;
392  char keyappend[MAXKEYLEN], keyoverwrite[MAXKEYLEN], keyquit[MAXKEYLEN];
393  char mode[3];
394  struct stat st;
395  t_function func;
396 
397  strcpy(mode, "a+");
398 
399  /*
400  * Mailboxes will always be appended to
401  */
402  if (!mbox && stat(path, &st) != -1) {
403  /*
404  * Admittedly a special case hack, but it saves failing later on
405  */
406  if (S_ISDIR(st.st_mode)) {
408  return NULL;
409  }
410 /* TODO: will this get called every art? Should only be done once/batch */
411 /* TODO: or add an option for defaulting on all future queries */
412 /* TODO: 'truncate' path if query exceeds screen-width */
419 
420  switch (func) {
421  case SAVE_OVERWRITE_FILE:
422  strcpy(mode, "w");
423  break;
424 
425  case GLOBAL_ABORT:
426  case GLOBAL_QUIT:
428  return NULL;
429 
430  default: /* SAVE_APPEND_FILE */
431  break;
432  }
433  if (func == SAVE_OVERWRITE_FILE)
434  tinrc.default_save_mode = 'o';
435  else
436  tinrc.default_save_mode = 'a';
437  }
438 
439  if ((fp = fopen(path, mode)) == NULL) {
441  return NULL;
442  }
443 
444  return fp;
445 }
446 
447 
448 /*
449  * This is where an article is actually copied to disk and processed
450  * We only need to copy the art to disk if we are doing post-processing
451  * 'artinfo' is parsed/cooked article to be saved
452  * 'artptr' points to the article in arts[]
453  * 'mailbox' is set if we are saving to a =mailbox
454  * 'inpath' is the template save path/file to save to
455  * 'max' is the number of articles we are saving
456  * 'post_process' is set if we want post-processing
457  * Expand the path appropriately, taking account of multiple file
458  * extensions and the auto-save with Archive-Name: headers
459  *
460  * Extract binary attachments if !LIBUU
461  * Start viewer if requested
462  * If successful, add entry to the save[] array
463  * Returns:
464  * TRUE or FALSE depending on whether article was saved okay.
465  *
466  * TODO: could we use append_mail() here
467  */
468 t_bool
470  t_openartinfo *artinfo,
471  struct t_article *artptr,
473  const char *inpath,
474  int max,
475  t_bool post_process)
476 {
477  FILE *fp;
478  char from[HEADER_LEN];
479  char path[PATH_LEN];
480  time_t epoch;
482 
483  if (fseek(artinfo->raw, 0L, SEEK_SET) == -1) {
485  return FALSE;
486  }
487 
488  /* The first task is to fixup the filename to be saved too. This is context dependent */
489  strncpy(path, inpath, sizeof(path) - 1);
490 /* fprintf(stderr, "save_and_process_art max=%d num_save=%d starting path=(%s) postproc=%s\n", max, num_save, path, bool_unparse(post_process)); */
491 
492  /*
493  * If using the auto-save feature on an article with Archive-Name,
494  * the path will be: <original-path>/<archive-name>/<part|patch><part#>
495  */
496  if (!is_mailbox && curr_group->attribute->auto_save && artptr->archive) {
497  const char *partprefix;
498  char *ptr;
499  char archpath[PATH_LEN];
500  char filename[NAME_LEN + 1];
501 
502  /*
503  * We need either a part or a patch number, part takes precedence
504  */
505  if (artptr->archive->ispart)
506  partprefix = PATH_PART;
507  else
508  partprefix = PATH_PATCH;
509 
510  /*
511  * Strip off any existing filename
512  */
513  if ((ptr = strrchr(path, DIRSEP)) != NULL)
514  *(ptr + 1) = '\0';
515 
516  /* Add on the archive name as a directory */
517  /* TODO: maybe a s!/!.! on archive-name would be better */
518  joinpath(archpath, sizeof(archpath), path, artptr->archive->name);
519 
520  /* Generate the filename part and append it */
521  snprintf(filename, sizeof(filename), "%s%s", partprefix, artptr->archive->partnum);
522  joinpath(path, sizeof(path), archpath, filename);
523 /*fprintf(stderr, "save_and_process_art archive-name mangled path=(%s)\n", path);*/
524  if (!create_path(path))
525  return FALSE;
526  } else {
527  /*
528  * Mailbox saves are by definition to a single file as are single file
529  * saves. Multiple file saves append a .NNN sequence number to the path
530  * This is backward-contemptibility with older versions of tin
531  */
532  if (!is_mailbox && max > 1) {
533  const char suffixsep = '.';
534 
535  sprintf(&path[strlen(path)], "%c%03d", suffixsep, num_save + 1);
536  }
537  }
538 
539 /* fprintf(stderr, "save_and_process_art expanded path now=(%s)\n", path); */
540 
541  if ((fp = open_save_filename(path, is_mailbox)) == NULL)
542  return FALSE;
543 
544  if (mmdf)
545  fprintf(fp, "%s", MMDFHDRTXT);
546  else {
547  if (artinfo->hdr.from)
548  strip_name(artinfo->hdr.from, from);
549  (void) time(&epoch);
550  fprintf(fp, "From %s %s", from, ctime(&epoch));
551  /*
552  * TODO: add Content-Length: header when using MBOXO
553  * so tin actually write MBOXCL instead of MBOXO?
554  */
555  }
556 
557  if (copy_fp(artinfo->raw, fp)) /* Write tailing newline or MMDF-mailbox separator */
559  else {
560  fclose(fp);
561  unlink(path);
562  return FALSE;
563  }
564 
565  fclose(fp);
566 
567  /*
568  * Saved ok, so fill out a save[] record
569  */
570  if (num_save == max_save - 1)
571  expand_save();
572  save[num_save].path = my_strdup(path);
573  save[num_save].file = strrchr(save[num_save].path, DIRSEP) + 1; /* ptr to filename portion */
575 /* fprintf(stderr, "SAPA (%s) (%s) mbox=%s\n", save[num_save].path, save[num_save].file, bool_unparse(save[num_save].mailbox)); */
576  num_save++; /* NB: num_save is bumped here only */
577 
578  /*
579  * Extract/view parts from multipart articles if required
580  * libuu does this as part of its own processing
581  */
582 #ifndef HAVE_LIBUU
583  if (post_process) {
584 # ifdef USE_CURSES
585  scrollok(stdscr, TRUE);
586 # endif /* USE_CURSES */
587  decode_save_mime(artinfo, TRUE);
588 # ifdef USE_CURSES
589  scrollok(stdscr, FALSE);
590 # endif /* USE_CURSES */
591  }
592 #endif /* !HAVE_LIBUU */
593 
594  return TRUE;
595 }
596 
597 
598 /*
599  * Create the supplied path. Create intermediate directories as needed
600  * Don't create the last component (which would be the filename) unless the
601  * path is / terminated.
602  * Return FALSE if it somehow fails.
603  */
604 t_bool
606  const char *path)
607 {
608  char *buf, *p;
609  struct stat st;
610 
611  if (!strlen(path))
612  return FALSE;
613 
614  buf = my_strdup(path);
615  p = buf + 1;
616 
617  if (!strlen(p)) {
618  free(buf);
619  return FALSE;
620  }
621 
622  while ((p = strchr(p, DIRSEP)) != NULL) {
623  *p = '\0';
624  if (stat(buf, &st) == -1) {
625  if (my_mkdir(buf, (mode_t) (S_IRWXU|S_IRUGO|S_IXUGO)) == -1) {
626  if (errno != EEXIST) {
628  free(buf);
629  return FALSE;
630  }
631  }
632  }
633  *p++ = DIRSEP;
634  }
635  free(buf);
636  return TRUE;
637 }
638 
639 
640 /*
641  * Generate semi-meaningful filename based on sequence number and
642  * Content-(sub)type
643  */
644 static void
646  char *buf,
647  int buflen,
648  const char *suffix)
649 {
650  static int seqno = 0;
651 
652  snprintf(buf, buflen, "%s-%03d.%s", SAVEFILE_PREFIX, seqno++, suffix);
653 }
654 
655 
656 /*
657  * Generate /save/to/path name.
658  *
659  * Return pointer to allocated memory which the caller must free or
660  * NULL if something went wrong.
661  */
662 static char *
664  t_part *part)
665 {
666  char buf[2048];
667  char *savepath;
668  const char *name;
669  t_bool mbox;
670 
671  savepath = my_malloc(PATH_LEN);
672  /*
673  * Get the filename to save to in 'savepath'
674  */
675  if ((name = get_filename(part->params)) == NULL) {
676  char extension[NAME_LEN + 1];
677 
678  lookup_extension(extension, sizeof(extension), content_types[part->type], part->subtype);
679  generate_filename(buf, sizeof(buf), extension);
680  mbox = expand_save_filename(savepath, PATH_LEN, buf);
681  } else
682  mbox = expand_save_filename(savepath, PATH_LEN, name);
683 
684  /*
685  * Not a good idea to dump attachments over a mailbox
686  */
687  if (mbox) {
689  free(savepath);
690  return NULL;
691  }
692 
693  if (!(create_path(savepath))) {
695  free(savepath);
696  return NULL;
697  }
698 
699  return savepath;
700 }
701 
702 
703 /*
704  * Generate a path/filename to save to, using 'path' as input.
705  * The pathname is stored in 'outpath', which should be PATH_LEN in size
706  * Expand metacharacters and use defaults as needed.
707  * Return TRUE if the path is a mailbox, or FALSE otherwise.
708  */
709 static t_bool
711  char *outpath,
712  size_t outpath_len,
713  const char *path)
714 {
715  char base_filename[PATH_LEN];
716  char buf[PATH_LEN];
717  char buf_path[PATH_LEN];
718  int ret;
719 
720  /*
721  * Make sure that externally supplied filename is a filename only and fits
722  * into buffer
723  */
724  STRCPY(buf_path, path);
725  base_name(buf_path, base_filename);
726 
727  /* Build default path to save to */
729  joinpath(buf, sizeof(buf), homedir, DEFAULT_SAVEDIR);
730 
731  /* Join path and filename */
732  joinpath(outpath, outpath_len, buf, base_filename);
733 
734  return (ret == 1); /* should now always evaluate to FALSE */
735 }
736 
737 
738 /*
739  * Post process the articles in save[] according to proc_type_ch
740  * auto_delete is set if we should remove the saved files after processing
741  * This stage can produce a fair bit of output so we allow it to
742  * scroll up the screen rather than waste time displaying it in the
743  * message bar
744  */
745 t_bool
747  t_function proc_type_func,
748  t_bool auto_delete)
749 {
750  if (num_save < 1)
751  return FALSE;
752 
753  EndWin();
754  Raw(FALSE);
756 
757  switch (proc_type_func) {
758  case POSTPROCESS_SHAR:
759  post_process_sh();
760  break;
761 
762  /* This is the default, eg, with AUTOSAVE */
763  case POSTPROCESS_YES:
764  default:
766  break;
767  }
768 
770  my_flush();
771 #ifdef USE_CURSES
772  Raw(TRUE);
773  InitWin();
774 #endif /* USE_CURSES */
775  prompt_continue();
776 #ifndef USE_CURSES
777  Raw(TRUE);
778  InitWin();
779 #endif /* !USE_CURSES */
780 
781  /*
782  * Remove the post-processed files if required
783  */
784  if (auto_delete) {
785  int i;
786 
787  wait_message((tinrc.beginner_level) ? 2 : 1, "%s", _(txt_deleting));
788  cursoroff();
789 
790  for (i = 0; i < num_save; i++)
791  unlink(save[i].path);
792  }
793 
794  return TRUE;
795 }
796 
797 
798 /*
799  * Two implementations .....
800  * The LIBUU case performs multi-file decoding for uue, base64
801  * binhex, qp. This is handled entirely during the post processing phase
802  *
803  * The !LIBUU case only handles multi-file uudecoding, the other MIME
804  * types were handled using the internal MIME parser when the articles
805  * were originally saved
806  */
807 #ifdef HAVE_LIBUU
808 static void
810  void)
811 {
812  FILE *fp_in;
813  char file_out_dir[PATH_LEN];
814  const char *eptr;
815  int i;
816  int count;
817  int errors = 0;
818  uulist *item;
819 
820  /*
821  * Grab the dirname portion
822  */
823  my_strncpy(file_out_dir, save[0].path, save[0].file - save[0].path);
824 
825  UUInitialize();
826 
827  UUSetOption(UUOPT_SAVEPATH, 0, file_out_dir);
828  for (i = 0; i < num_save; i++) {
829  if ((fp_in = fopen(save[i].path, "r")) != NULL) {
830  UULoadFile(save[i].path, NULL, 0); /* Scans file for encoded data */
831  fclose(fp_in);
832  }
833  }
834 
835 # if 0
836  /*
837  * uudeview's "intelligent" multi-part detection
838  * From the uudeview docs: This function is a bunch of heuristics, and I
839  * don't really trust them... should only be called as a last resort on
840  * explicit user request
841  */
842  UUSmerge(0);
843  UUSmerge(1);
844  UUSmerge(99);
845 # endif /* 0 */
846 
847  i = count = 0;
848  item = UUGetFileListItem(i);
849  my_printf(cCRLF);
850 
851  while (item != NULL) {
852  if (UUDecodeFile(item, NULL) == UURET_OK) {
853  char path[PATH_LEN];
854 
855 /* TODO: test for multiple things per article decoded okay? */
856  count++;
857  my_printf(_(txt_uu_success), item->filename);
858  my_printf(cCRLF);
859 
860  /* item->mimetype seems not to be available for uudecoded files etc */
862  joinpath(path, sizeof(path), file_out_dir, item->filename);
863  view_file(path, strrchr(path, DIRSEP) + 1);
864  }
865  } else {
866  errors++;
867  if (item->state & UUFILE_MISPART)
868  eptr = _(txt_libuu_error_missing);
869  else if (item->state & UUFILE_NOBEGIN)
870  eptr = _(txt_libuu_error_no_begin);
871  else if (item->state & UUFILE_NOEND)
872  eptr = _(txt_uu_error_no_end);
873  else if (item->state & UUFILE_NODATA)
874  eptr = _(txt_libuu_error_no_data);
875  else
876  eptr = _(txt_libuu_error_unknown);
877 
878  my_printf(_(txt_uu_error_decode), (item->filename) ? item->filename : item->subfname, eptr);
879  my_printf(cCRLF);
880  }
881  i++;
882  item = UUGetFileListItem(i);
883  my_flush();
884  }
885 
886  my_printf(_(txt_libuu_saved), count, num_save, errors, PLURAL(errors, txt_error));
887  my_printf(cCRLF);
888  UUCleanUp();
889 }
890 
891 #else
892 
893 /*
894  * Open and read all the files in save[]
895  * Scan for uuencode BEGIN lines, decode input as we go along
896  * uuencoded data can span multiple files, and multiple uuencoded
897  * files are supported per batch
898  */
899 static void
901  void)
902 {
903  FILE *fp_in;
904  FILE *fp_out = NULL;
905  char *filename = NULL;
906  char file_out_dir[PATH_LEN];
907  char path[PATH_LEN];
908  char s[LEN], t[LEN], u[LEN];
909  int state = INITIAL;
910  int i;
911  mode_t mode = 0;
912 
913  /*
914  * Grab the dirname portion
915  */
916  my_strncpy(file_out_dir, save[0].path, save[0].file - save[0].path);
917 
918  t[0] = '\0';
919  u[0] = '\0';
920 
921  for (i = 0; i < num_save; i++) {
922  if ((fp_in = fopen(save[i].path, "r")) == NULL)
923  continue;
924 
925  while (fgets(s, (int) sizeof(s), fp_in) != NULL) {
926  switch (state) {
927  case INITIAL:
928  if (strncmp("begin ", s, 6) == 0) {
929  char fmt[15];
930  char name[PATH_LEN];
931  char buf[PATH_LEN];
932 
933  snprintf(fmt, sizeof(fmt), "%%o %%%dc\\n", PATH_LEN - 1);
934  if (sscanf(s + 6, fmt, &mode, name) == 2) {
935  strtok(name, "\n");
936  my_strncpy(buf, name, sizeof(buf) - 1);
937  str_trim(buf);
938  base_name(buf, name);
939  } else
940  name[0] = '\0';
941 
942  if (!mode && !*name) { /* not a valid uu-file at all */
943  state = INITIAL;
944  continue;
945  }
946 
947  if (!*name)
948  generate_filename(name, sizeof(name), "uue");
949 
950  filename = name;
951  expand_save_filename(path, sizeof(path), filename);
952  filename = strrchr(path, DIRSEP) + 1; /* ptr to filename portion */
953  if ((fp_out = fopen(path, "w")) == NULL) {
955  fclose(fp_in);
956  return;
957  }
958  state = MIDDLE;
959  }
960  break;
961 
962  case MIDDLE:
963  /*
964  * TODO: replace hard coded length check (uue lines are not
965  * required to be 60 chars long (45 encoded chars)
966  * ('M' == 60 * 3 / 4 + ' ' == 77))
967  */
968  if (s[0] == 'M')
969  uudecode_line(s, fp_out);
970  else if (STRNCMPEQ("end", s, 3)) {
971  state = END;
972  if (u[0] != 'M')
973  uudecode_line(u, fp_out);
974  if (t[0] != 'M')
975  uudecode_line(t, fp_out);
976  } else /* end */
977  state = OFF; /* OFF => a break in the uuencoded data */
978  break;
979 
980  case OFF:
981  if ((s[0] == 'M') && (t[0] == 'M') && (u[0] == 'M')) {
982  uudecode_line(u, fp_out);
983  uudecode_line(t, fp_out);
984  uudecode_line(s, fp_out);
985  state = MIDDLE; /* Continue output of previously suspended data */
986  } else if (STRNCMPEQ("end", s, 3)) {
987  state = END;
988  if (u[0] != 'M')
989  uudecode_line(u, fp_out);
990  if (t[0] != 'M')
991  uudecode_line(t, fp_out);
992  }
993  break;
994 
995  case END:
996  default:
997  break;
998  } /* switch (state) */
999 
1000  if (state == END) {
1001  /* set the mode after getting rid of dangerous bits */
1002  if (!(mode &= ~(S_ISUID|S_ISGID|S_ISVTX)))
1003  mode = (S_IRUSR|S_IWUSR);
1004 
1005  fchmod(fileno(fp_out), mode);
1006 
1007  fclose(fp_out);
1008  fp_out = NULL;
1009 
1010  my_printf(_(txt_uu_success), filename);
1011  my_printf(cCRLF);
1012  sum_file(path, filename);
1014  view_file(path, filename);
1015  state = INITIAL;
1016  continue;
1017  }
1018 
1019  strcpy(u, t); /* Keep tabs on the last two lines, which typically do not start with M */
1020  strcpy(t, s);
1021 
1022  } /* while (fgets) ... */
1023 
1024  fclose(fp_in);
1025 
1026  } /* for i...num_save */
1027 
1028  /*
1029  * Check if we ran out of data
1030  */
1031  if (fp_out) {
1032  fclose(fp_out);
1034  my_printf(cCRLF);
1035  }
1036  return;
1037 }
1038 
1039 
1040 /*
1041  * Sum file - why do we bother to do this?
1042  * nuke code or add DONT_HAVE_PIPING -tree
1043  */
1044 static void
1046  const char *path,
1047  const char *file)
1048 {
1049 # if defined(HAVE_SUM) && !defined(DONT_HAVE_PIPING)
1050  FILE *fp_in;
1051  char *ext;
1052  char buf[LEN];
1053 
1054  sh_format(buf, sizeof(buf), "%s \"%s\"", DEFAULT_SUM, path);
1055  if ((fp_in = popen(buf, "r")) != NULL) {
1056  buf[0] = '\0';
1057 
1058  /*
1059  * You can't do this with (fgets != NULL)
1060  */
1061  while (!feof(fp_in)) {
1062  fgets(buf, (int) sizeof(buf), fp_in);
1063  if ((ext = strchr(buf, '\n')) != NULL)
1064  *ext = '\0';
1065  }
1066  fflush(fp_in);
1067  pclose(fp_in);
1068 
1069  my_printf(_(txt_checksum_of_file), file, file_size(path), _("bytes"));
1070  my_printf(cCRLF);
1071  my_printf("\t%s%s", buf, cCRLF);
1072  } else {
1074  my_printf(cCRLF);
1075  }
1076  my_flush();
1077 # endif /* HAVE SUM && !DONT_HAVE_PIPING */
1078 }
1079 #endif /* HAVE_LIBUU */
1080 
1081 
1082 /*
1083  * If defined, invoke post processor command
1084  * Create a part structure, with defaults, insert a parameter for the name
1085  */
1086 static void
1088  const char *path,
1089  const char *file)
1090 {
1091  char *ext;
1092  t_part *part;
1093 
1094  part = new_part(NULL);
1095 
1096  if ((ext = strrchr(file, '.')) != NULL)
1097  lookup_mimetype(ext + 1, part); /* Get MIME type/subtype */
1098 
1099  /*
1100  * Needed for the mime-type processor
1101  */
1102  part->params = new_params();
1103  part->params->name = my_strdup("name");
1104  part->params->value = my_strdup(file);
1105 
1106  start_viewer(part, path);
1107  my_printf(cCRLF);
1108 
1109  free_parts(part);
1110 }
1111 
1112 
1113 /* Single character decode. */
1114 #define DEC(Char) (((Char) - ' ') & 077)
1115 /*
1116  * Decode 'buf' - write the uudecoded output to 'fp'
1117  */
1118 static void
1120  const char *buf,
1121  FILE *fp)
1122 {
1123  const char *p = buf;
1124  char ch;
1125  int n;
1126 
1127  n = DEC(*p);
1128 
1129  for (++p; n > 0; p += 4, n -= 3) {
1130  if (n >= 3) {
1131  ch = ((DEC(p[0]) << 2) | (DEC(p[1]) >> 4));
1132  fputc(ch, fp);
1133  ch = ((DEC(p[1]) << 4) | (DEC(p[2]) >> 2));
1134  fputc(ch, fp);
1135  ch = ((DEC(p[2]) << 6) | DEC(p[3]));
1136  fputc(ch, fp);
1137  } else {
1138  if (n >= 1) {
1139  ch = ((DEC(p[0]) << 2) | (DEC(p[1]) >> 4));
1140  fputc(ch, fp);
1141  }
1142  if (n >= 2) {
1143  ch = ((DEC(p[1]) << 4) | (DEC(p[2]) >> 2));
1144  fputc(ch, fp);
1145  }
1146  }
1147  }
1148 }
1149 
1150 
1151 /*
1152  * Unpack /bin/sh archives
1153  * There is no end-of-shar marker so the code reads everything after
1154  * the start marker. This is why shar is handled separately.
1155  * The code assumes shar archives do not span articles
1156  */
1157 static void
1159  void)
1160 {
1161  FILE *fp_in, *fp_out = NULL;
1162  char buf[LEN];
1163  char file_out[PATH_LEN];
1164  char file_out_dir[PATH_LEN];
1165  int i;
1166 
1167  /*
1168  * Grab the dirname portion
1169  */
1170  my_strncpy(file_out_dir, save[0].path, save[0].file - save[0].path);
1171  snprintf(file_out, sizeof(file_out), "%ssh%ld", file_out_dir, (long) process_id);
1172 
1173  for (i = 0; i < num_save; i++) {
1174  if ((fp_in = fopen(save[i].path, "r")) == NULL)
1175  continue;
1176 
1177  wait_message(0, _(txt_extracting_shar), save[i].path);
1178 
1179  while (fgets(buf, (int) sizeof(buf), fp_in) != NULL) {
1180  /* find #!/bin/sh style patterns */
1181  if ((fp_out == NULL) && pcre_exec(shar_regex.re, shar_regex.extra, buf, strlen(buf), 0, 0, NULL, 0) >= 0)
1182  fp_out = fopen(file_out, "w");
1183 
1184  /* write to temp file */
1185  if (fp_out != NULL)
1186  fputs(buf, fp_out);
1187  }
1188  fclose(fp_in);
1189 
1190  if (fp_out == NULL) { /* Didn't extract any shar */
1191  my_fputs(cCRLF, stdout);
1192  continue;
1193  }
1194 
1195  fclose(fp_out);
1196  fp_out = NULL;
1197  sh_format(buf, sizeof(buf), "cd %s; sh %s", file_out_dir, file_out);
1198  my_fputs(cCRLF, stdout);
1199  my_flush();
1200  invoke_cmd(buf); /* Handles its own errors */
1201  unlink(file_out);
1202  }
1203 }
1204 
1205 
1206 /*
1207  * write tailing (MMDF)-mailbox separator
1208  */
1209 void
1211  FILE *fp,
1213 {
1214 #ifdef DEBUG
1215  if (debug & DEBUG_MISC)
1216  error_message(2, "Mailbox=[%d], mailbox_format=[%s]", is_mailbox, txt_mailbox_formats[tinrc.mailbox_format]);
1217 #endif /* DEBUG */
1218 
1219  fprintf(fp, "%s", (is_mailbox && !strcasecmp(txt_mailbox_formats[tinrc.mailbox_format], "MMDF")) ? MMDFHDRTXT : "\n");
1220 }
1221 
1222 
1223 /*
1224  * part needs to have at least content type/subtype and a filename
1225  * path = full path/file (used for substitution in mailcap entries)
1226  */
1227 static void
1229  t_part *part,
1230  const char *path)
1231 {
1232  t_mailcap *foo;
1233 
1234  if ((foo = get_mailcap_entry(part, path)) != NULL) {
1235  if (foo->nametemplate) /* honor nametemplate */
1236  rename_file(path, foo->nametemplate);
1237 
1239  if (foo->needsterminal) {
1240  set_xclick_off();
1241  fflush(stdout);
1242  } else {
1243  if (foo->description)
1244  info_message("%s", foo->description);
1245  }
1246  invoke_cmd(foo->command);
1247  if (foo->needsterminal) {
1248 #ifndef USE_CURSES
1249  EndWin();
1250  Raw(FALSE);
1251 #endif /* !USE_CURSES */
1252  prompt_continue();
1253 #ifndef USE_CURSES
1254  Raw(TRUE);
1255  InitWin();
1256 #endif /* !USE_CURSES */
1257  }
1258  if (foo->nametemplate) /* undo nametemplate, needed as 'save'-prompt is done outside start_viewer */
1259  rename_file(foo->nametemplate, path);
1260  free_mailcap(foo);
1261  } else
1263 }
1264 
1265 
1266 /*
1267  * Decode and save the binary object pointed to in 'part'
1268  * Optionally launch a viewer for it
1269  * Return FALSE if Abort used to skip further viewing/saving
1270  * or other terminal error occurs
1271  */
1272 static t_bool
1274  t_part *part,
1275  FILE *rawfp,
1276  t_bool postproc)
1277 {
1278  FILE *fp;
1279  char buf[2048], buf2[2048];
1280  char *savepath;
1281  int count;
1282  int i;
1283 
1284  /*
1285  * Decode this message part if appropriate
1286  */
1288  /* TODO: skip message if saving multiple files (e.g. save 't'agged) */
1289  wait_message(1, "Skipped %s/%s", content_types[part->type], part->subtype); /* TODO: better msg */
1290  return TRUE;
1291  }
1292 
1293  if ((savepath = generate_savepath(part)) == NULL)
1294  return FALSE;
1295 
1296  /*
1297  * Decode/save the attachment
1298  */
1299  if ((fp = open_save_filename(savepath, FALSE)) == NULL) {
1300  free(savepath);
1301  return FALSE;
1302  }
1303 
1304  if (part->encoding == ENCODING_BASE64)
1305  mmdecode(NULL, 'b', 0, NULL); /* flush */
1306 
1307  fseek(rawfp, part->offset, SEEK_SET);
1308 
1309  for (i = 0; i < part->line_count; i++) {
1310  if ((fgets(buf, sizeof(buf), rawfp)) == NULL)
1311  break;
1312 
1313  /* This should catch cases where people illegally append text etc */
1314  if (buf[0] == '\0')
1315  break;
1316 
1317  switch (part->encoding) {
1318  case ENCODING_QP:
1319  case ENCODING_BASE64:
1320  count = mmdecode(buf, part->encoding == ENCODING_QP ? 'q' : 'b', '\0', buf2);
1321  fwrite(buf2, count, 1, fp);
1322  break;
1323 
1324  case ENCODING_UUE:
1325  /* TODO: if postproc, don't decode these since the traditional uudecoder will get them */
1326  /*
1327  * x-uuencode attachments have all the header info etc which we must ignore
1328  */
1329  if (strncmp(buf, "begin ", 6) != 0 && strncmp(buf, "end\n", 4) != 0 && buf[0] != '\n')
1330  uudecode_line(buf, fp);
1331  break;
1332 
1333  default:
1334  fputs(buf, fp);
1335  }
1336  }
1337  fclose(fp);
1338 
1339  /*
1340  * View the attachment
1341  */
1343  start_viewer(part, savepath);
1344  my_printf(cCRLF);
1345  } else {
1346  snprintf(buf, sizeof(buf), _(txt_view_attachment), savepath, content_types[part->type], part->subtype);
1347  if ((i = prompt_yn(buf, TRUE)) == 1)
1348  start_viewer(part, savepath);
1349  else if (i == -1) { /* Skip rest of attachments */
1350  unlink(savepath);
1351  free(savepath);
1352  return FALSE;
1353  }
1354  }
1355 
1356  /*
1357  * Save the attachment
1358  */
1359  if (postproc && curr_group->attribute->post_process_view) {
1360  my_printf(_(txt_uu_success), savepath);
1361  my_printf(cCRLF);
1362  }
1363  if (!postproc) {
1364  snprintf(buf, sizeof(buf), _(txt_save_attachment), savepath, content_types[part->type], part->subtype);
1365  if ((i = prompt_yn(buf, FALSE)) != 1) {
1366  unlink(savepath);
1367  if (i == -1) { /* Skip rest of attachments */
1368  free(savepath);
1369  return FALSE;
1370  }
1371  }
1372  }
1373  free(savepath);
1374  return TRUE;
1375 }
1376 
1377 
1378 enum match {
1382 };
1383 
1384 /*
1385  * Match a single type/subtype Content pair
1386  * Returns:
1387  * NO = Not matched
1388  * MATCH = Matched
1389  * NOTMATCH = Matched, but !negated
1390  */
1391 static int
1393  t_part *part,
1394  char *type)
1395 {
1396  char *subtype;
1397  int typeindex;
1398  t_bool found = FALSE;
1399  t_bool negate = FALSE;
1400 
1401  /* Check for negation */
1402  if (*type == '!') {
1403  negate = TRUE;
1404  ++type;
1405 
1406  if (!*type) /* Invalid type */
1407  return NO;
1408  }
1409 
1410  /* Split type and subtype */
1411  if ((subtype = strchr(type, '/')) == NULL)
1412  return NO;
1413  *(subtype++) = '\0';
1414 
1415  if (!*type || !*subtype) /* Missing type or subtype */
1416  return NO;
1417 
1418  /* Try and match major */
1419  if (strcmp(type, "*") == 0)
1420  found = TRUE;
1421  else if (((typeindex = content_type(type)) != -1) && typeindex == part->type)
1422  found = TRUE;
1423 
1424  if (!found)
1425  return NO;
1426 
1427  /* Try and match subtype */
1428  found = FALSE;
1429  if (strcmp(subtype, "*") == 0)
1430  found = TRUE;
1431  else if (strcmp(subtype, part->subtype) == 0)
1432  found = TRUE;
1433 
1434  if (!found)
1435  return NO;
1436 
1437  /* We got a match */
1438  if (negate)
1439  return NOTMATCH;
1440 
1441  return MATCH;
1442 }
1443 
1444 
1445 /*
1446  * See if the mime type of this part matches the list of content types to save
1447  * or ignore. Return TRUE if there is a match
1448  * mime_types is a comma separated list of type/subtype pairs. type and/or
1449  * subtype can be a '*' to match any, and a pair can begin with a ! which
1450  * will negate the meaning. We eval all pairs, the rightmost match will
1451  * prevail
1452  */
1453 static t_bool
1455  t_part *part,
1456  const char *mime_types)
1457 {
1458  char *ptr, *pair;
1459  int found;
1460  int retcode;
1461 
1462  if (!mime_types)
1463  return FALSE;
1464 
1465  ptr = my_strdup(mime_types);
1466 
1467  if ((pair = strtok(ptr, ",")) == NULL) {
1468  free(ptr);
1469  return FALSE;
1470  }
1471 
1472  retcode = match_content_type(part, pair);
1473 
1474  while ((pair = strtok(NULL, ",")) != NULL) {
1475  if ((found = match_content_type(part, pair)) != NO)
1476  retcode = found;
1477  }
1478 
1479  free(ptr);
1480  return (retcode == MATCH);
1481 }
1482 
1483 
1484 /*
1485  * decode and save binary MIME attachments from an open article context
1486  * optionally locate and launch a viewer application
1487  * 'postproc' determines the mode of the operation and will be set to
1488  * TRUE when we're called during a [Ss]ave operation and FALSE when
1489  * when just viewing
1490  * When it is TRUE the view option will depend on post_process_view and
1491  * the save is implicit. Feedback will also be printed.
1492  * When it is FALSE then the view/save options will be queried
1493  */
1494 void
1496  t_openartinfo *art,
1497  t_bool postproc)
1498 {
1499  t_part *ptr, *uueptr;
1500 
1501  /*
1502  * Iterate over all the attachments
1503  */
1504  for (ptr = art->hdr.ext; ptr != NULL; ptr = ptr->next) {
1505  /*
1506  * Handle uuencoded sections in this message part.
1507  * Only works when the uuencoded file is entirely within the current
1508  * article.
1509  * We don't do this when postprocessing as the generic uudecode code
1510  * already handles uuencoded data, but TODO: review this
1511  */
1512  if (!postproc) {
1513  for (uueptr = ptr->uue; uueptr != NULL; uueptr = uueptr->next) {
1514  if (!(decode_save_one(uueptr, art->raw, postproc)))
1515  break;
1516  }
1517  }
1518 
1519  /*
1520  * TYPE_MULTIPART is an envelope type, don't process it.
1521  * If we had an UUE part, the "surrounding" text/plain plays
1522  * the role of a multipart part. Check to see if we want to
1523  * save text and if not, skip this part.
1524  */
1525  /* check_save_mime_type() is done in decode_save_one() and the check for ptr->uue must be done unconditionally */
1526  if (ptr->type == TYPE_MULTIPART || (NULL != ptr->uue /* && !check_save_mime_type(ptr, curr_group->attribute->mime_types_to_save) */ ))
1527  continue;
1528 
1529  if (!(decode_save_one(ptr, art->raw, postproc)))
1530  break;
1531  }
1532 }
1533 
1534 
1535 /*
1536  * Attachment menu
1537  */
1538 static void
1540  void)
1541 {
1542  char buf[BUFSIZ];
1543  const char *charset;
1544  int i, tmp_len, max_depth;
1545  t_part *part;
1546 
1548  currmenu = &attmenu;
1549  mark_offset = 0;
1550 
1551  if (attmenu.curr < 0)
1552  attmenu.curr = 0;
1553 
1554  info_len = max_depth = 0;
1555  for (i = 0; i < attmenu.max; ++i) {
1556  part = get_part(i);
1558  tmp_len = strwidth(buf);
1559  charset = get_param(part->params, "charset");
1560  snprintf(buf, sizeof(buf), " %s/%s, %s, %s%s", content_types[part->type], part->subtype, content_encodings[part->encoding], charset ? charset : "", charset ? ", " : "");
1561  tmp_len += strwidth(buf);
1562  if (tmp_len > info_len)
1563  info_len = tmp_len;
1564 
1565  tmp_len = part->depth;
1566  if (tmp_len > max_depth)
1567  max_depth = tmp_len;
1568  }
1569  tmp_len = cCOLS - 13 - MIN((cCOLS - 13) / 2 + 10, max_depth * 2 + 1 + strwidth(_(txt_attachment_no_name)));
1570  if (info_len > tmp_len)
1571  info_len = tmp_len;
1572 
1573  ClearScreen();
1576 
1577  for (i = attmenu.first; i < attmenu.first + NOTESLINES && i < attmenu.max; ++i)
1579 
1581 
1582  if (attmenu.max <= 0) {
1584  return;
1585  }
1586 
1588 }
1589 
1590 
1591 void
1593  t_openartinfo *art)
1594 {
1595  char key[MAXKEYLEN];
1596  t_function func;
1597  t_menu *oldmenu = NULL;
1598  t_part *part;
1599 
1600  if (currmenu)
1601  oldmenu = currmenu;
1602  num_of_tagged_parts = 0;
1603  attmenu.curr = 0;
1605  clear_note_area();
1607  set_xclick_off();
1608 
1609  forever {
1610  switch ((func = handle_keypad(attachment_left, attachment_right, NULL, attachment_keys))) {
1611  case GLOBAL_QUIT:
1613  if (oldmenu)
1614  currmenu = oldmenu;
1615  return;
1616 
1617  case DIGIT_1:
1618  case DIGIT_2:
1619  case DIGIT_3:
1620  case DIGIT_4:
1621  case DIGIT_5:
1622  case DIGIT_6:
1623  case DIGIT_7:
1624  case DIGIT_8:
1625  case DIGIT_9:
1626  if (attmenu.max)
1628  break;
1629 
1630 #ifndef NO_SHELL_ESCAPE
1631  case GLOBAL_SHELL_ESCAPE:
1632  do_shell_escape();
1633  break;
1634 #endif /* !NO_SHELL_ESCAPE */
1635 
1636  case GLOBAL_HELP:
1639  break;
1640 
1641  case GLOBAL_BUGREPORT:
1642  bug_report();
1644  break;
1645 
1646  case GLOBAL_FIRST_PAGE:
1647  top_of_list();
1648  break;
1649 
1650  case GLOBAL_LAST_PAGE:
1651  end_of_list();
1652  break;
1653 
1654  case GLOBAL_REDRAW_SCREEN:
1655  my_retouch();
1657  break;
1658 
1659  case GLOBAL_LINE_DOWN:
1660  move_down();
1661  break;
1662 
1663  case GLOBAL_LINE_UP:
1664  move_up();
1665  break;
1666 
1667  case GLOBAL_PAGE_DOWN:
1668  page_down();
1669  break;
1670 
1671  case GLOBAL_PAGE_UP:
1672  page_up();
1673  break;
1674 
1675  case GLOBAL_SCROLL_DOWN:
1676  scroll_down();
1677  break;
1678 
1679  case GLOBAL_SCROLL_UP:
1680  scroll_up();
1681  break;
1682 
1686  break;
1687 
1691  break;
1692 
1693  case ATTACHMENT_SAVE:
1694  if (attmenu.max) {
1698  }
1699  break;
1700 
1701  case ATTACHMENT_SELECT:
1702  if (attmenu.max) {
1706  }
1707  break;
1708 
1709  case ATTACHMENT_TAG:
1710  if (attmenu.max) {
1711  t_bool tagged;
1712 
1713  tagged = tag_part(attmenu.curr);
1715  if (attmenu.curr + 1 < attmenu.max)
1716  move_down();
1718  }
1719  break;
1720 
1721  case ATTACHMENT_UNTAG:
1722  if (attmenu.max && num_of_tagged_parts) {
1723  untag_all_parts();
1725  }
1726  break;
1727 
1729  if (attmenu.max) {
1730  tag_pattern();
1733  }
1734  break;
1735 
1737  if (attmenu.max) {
1738  int i;
1739 
1740  for (i = attmenu.first; i < attmenu.max; ++i)
1741  tag_part(i);
1744  }
1745  break;
1746 
1749  case GLOBAL_SEARCH_REPEAT:
1752  else if (attmenu.max) {
1753  int new_pos, old_pos = attmenu.curr;
1754 
1756  if (new_pos != old_pos)
1757  move_to_item(new_pos);
1758  }
1759  break;
1760 
1761 #ifndef DONT_HAVE_PIPING
1762  case ATTACHMENT_PIPE:
1763  case GLOBAL_PIPE:
1764  if (attmenu.max) {
1766  process_parts(part, art, func == GLOBAL_PIPE ? PIPE_RAW : PIPE);
1768  }
1769  break;
1770 #endif /* !DONT_HAVE_PIPING */
1771 
1772  default:
1774  break;
1775  }
1776  }
1777 }
1778 
1779 
1780 static t_function
1782  void)
1783 {
1784  return GLOBAL_QUIT;
1785 }
1786 
1787 
1788 static t_function
1790  void)
1791 {
1792  return ATTACHMENT_SELECT;
1793 }
1794 
1795 
1796 static void
1798  void)
1799 {
1801  if (tinrc.info_in_last_line) {
1802  const char *name;
1803  t_part *part;
1804 
1808  } else if (attmenu.curr == attmenu.max - 1)
1810 }
1811 
1812 
1813 static void
1815  int i)
1816 {
1817  char *sptr;
1818  const char *name;
1819  const char *charset;
1820 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1821  char *tmpname;
1822  char *tmpbuf;
1823 #endif /* MULTIBYTE_ABLE && !NOLOCALE */
1824  char buf[BUFSIZ];
1825  char buf2[BUFSIZ];
1826  char *tree = NULL;
1827  int len, namelen, tagged, treelen;
1828  t_part *part;
1829 
1830 #ifdef USE_CURSES
1831  /*
1832  * Allocate line buffer
1833  * make it the same size like in !USE_CURSES case to simplify some code
1834  */
1835 # if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1836  sptr = my_malloc(cCOLS * MB_CUR_MAX + 2);
1837 # else
1838  sptr = my_malloc(cCOLS + 2);
1839 # endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1840 #else
1841  sptr = screen[INDEX2SNUM(i)].col;
1842 #endif /* USE_CURSES */
1843 
1844  part = get_part(i);
1845  namelen = MIN(cCOLS - 13 - info_len - 8, strwidth(_(txt_attachment_no_name)));
1846  tagged = get_tagged(i);
1847 
1848  if (!(name = get_filename(part->params))) {
1849  if (!(name = part->description))
1851  }
1852 
1853  charset = get_param(part->params, "charset");
1854  snprintf(buf2, sizeof(buf2), _(txt_attachment_lines), part->line_count);
1855  /* TODO: make the layout configurable? */
1856  if (!strcmp(content_types[part->type], "text"))
1857  snprintf(buf, sizeof(buf), " %s/%s, %s, %s%s%s", content_types[part->type], part->subtype, content_encodings[part->encoding], charset ? charset : "", charset ? ", " : "", buf2);
1858  else
1859  snprintf(buf, sizeof(buf), " %s/%s, %s, %s", content_types[part->type], part->subtype, content_encodings[part->encoding], buf2);
1860  if (part->depth > 0) {
1861  treelen = cCOLS - 13 - info_len - namelen;
1862  tree = build_tree(part->depth, treelen, i);
1863  }
1864  snprintf(buf2, sizeof(buf2), "%s %s", tagged ? tin_ltoa(tagged, 3) : " ", BlankIfNull(tree));
1865  FreeIfNeeded(tree);
1866  len = strwidth(buf2);
1867  if (namelen + len + info_len + 8 <= cCOLS)
1868  namelen = cCOLS - 8 - info_len - len;
1869 
1870 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1871  tmpname = spart(name, namelen, TRUE);
1872  tmpbuf = spart(buf, info_len, TRUE);
1873  snprintf(sptr, cCOLS * MB_CUR_MAX, " %s %s%*s%*s%s", tin_ltoa(i + 1, 4), buf2, namelen, BlankIfNull(tmpname), info_len, BlankIfNull(tmpbuf), cCRLF);
1874  FreeIfNeeded(tmpname);
1875  FreeIfNeeded(tmpbuf);
1876 #else
1877  snprintf(sptr, cCOLS, " %s %s%-*.*s%*.*s%s", tin_ltoa(i + 1, 4), buf2, namelen, namelen, name, info_len, info_len, buf, cCRLF);
1878 #endif /* MULTIBYTE_ABLE && !NOLOCALE */
1879 
1880  WriteLine(INDEX2LNUM(i), sptr);
1881 
1882 #ifdef USE_CURSES
1883  free(sptr);
1884 #endif /* USE_CURSES */
1885 }
1886 
1887 
1888 /*
1889  * Build attachment tree. Code adopted
1890  * from thread.c:make_prefix().
1891  */
1892 static char *
1894  int depth,
1895  int maxlen,
1896  int i)
1897 {
1898 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1899  char *result;
1900  wchar_t *tree;
1901 #else
1902  char *tree;
1903 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1904  int prefix_ptr, tmpdepth;
1905  int depth_level = 0;
1906  t_bool found = FALSE;
1907  t_partl *lptr, *lptr2;
1908 
1909  lptr2 = find_part(i);
1910  prefix_ptr = depth * 2 - 1;
1911  if (prefix_ptr > maxlen - 1 - !(maxlen % 2)) {
1912  int odd = ((maxlen % 2) ? 0 : 1);
1913 
1914  prefix_ptr -= maxlen - ++depth_level - 2 - odd;
1915  while (prefix_ptr > maxlen - 2 - odd) {
1916  if (depth_level < maxlen / 5)
1917  depth_level++;
1918 
1919  prefix_ptr -= maxlen - depth_level - 2 - odd;
1920  odd = (odd ? 0 : 1);
1921  }
1922  }
1923 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1924  tree = my_malloc(sizeof(wchar_t) * prefix_ptr + 3 * sizeof(wchar_t));
1925  tree[prefix_ptr + 2] = (wchar_t) '\0';
1926 #else
1927  tree = my_malloc(prefix_ptr + 3);
1928  tree[prefix_ptr + 2] = '\0';
1929 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1930  tree[prefix_ptr + 1] = TREE_ARROW;
1931  tree[prefix_ptr] = TREE_HORIZ;
1932  for (lptr = lptr2->next; lptr != NULL; lptr = lptr->next) {
1933  if (lptr->part->depth == depth) {
1934  found = TRUE;
1935  break;
1936  }
1937  if (lptr->part->depth < depth)
1938  break;
1939  }
1940  tree[--prefix_ptr] = found ? TREE_VERT_RIGHT : TREE_UP_RIGHT;
1941  found = FALSE;
1942  for (tmpdepth = depth - 1; prefix_ptr > 1; --tmpdepth) {
1943  for (lptr = lptr2->next; lptr != NULL; lptr = lptr->next) {
1944  if (lptr->part->depth == tmpdepth) {
1945  found = TRUE;
1946  break;
1947  }
1948  if (lptr->part->depth < tmpdepth)
1949  break;
1950  }
1951  tree[--prefix_ptr] = TREE_BLANK;
1952  tree[--prefix_ptr] = found ? TREE_VERT : TREE_BLANK;
1953  found = FALSE;
1954  }
1955  while (depth_level)
1956  tree[--depth_level] = TREE_ARROW_WRAP;
1957 
1958 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1959  result = wchar_t2char(tree);
1960  free(tree);
1961  return result;
1962 #else
1963  return tree;
1964 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1965 }
1966 
1967 
1968 /*
1969  * Find nth attachment in part_list.
1970  * Return pointer to that part.
1971  */
1972 static t_partl *
1974  int n)
1975 {
1976  t_partl *lptr;
1977 
1978  lptr = part_list;
1979  if (attmenu.max >= 1)
1980  lptr = lptr->next;
1981 
1982  while (n-- > 0 && lptr->next)
1983  lptr = lptr->next;
1984 
1985  return lptr;
1986 }
1987 
1988 
1989 t_part *
1991  int n)
1992 {
1993  t_partl *lptr;
1994 
1995  lptr = find_part(n);
1996  return lptr->part;
1997 }
1998 
1999 
2000 static void
2002  void)
2003 {
2004  char buf[BUFSIZ];
2005  char pat[128];
2006  char *prompt;
2007  const char *name;
2008  const char *charset;
2009  struct regex_cache cache = { NULL, NULL };
2010  t_part *part;
2011  t_partl *lptr;
2012 
2013 #if 0
2014  if (num_of_tagged_parts)
2015  untag_all_parts();
2016 #endif /* 0 */
2017 
2020  free(prompt);
2021  return;
2022  }
2023  free(prompt);
2024 
2025  if (STRCMPEQ(tinrc.default_select_pattern, "*")) { /* all */
2026  if (tinrc.wildcard)
2027  STRCPY(pat, ".*");
2028  else
2030  } else
2031  snprintf(pat, sizeof(pat), REGEX_FMT, tinrc.default_select_pattern);
2032 
2033  if (tinrc.wildcard && !(compile_regex(pat, &cache, PCRE_CASELESS)))
2034  return;
2035 
2036  lptr = find_part(0);
2037 
2038  for (; lptr != NULL; lptr = lptr->next) {
2039  part = lptr->part;
2040  if (!(name = get_filename(part->params))) {
2041  if (!(name = part->description))
2043  }
2044  charset = get_param(part->params, "charset");
2045 
2046  snprintf(buf, sizeof(buf), "%s %s/%s %s, %s", name, content_types[part->type], part->subtype, content_encodings[part->encoding], charset ? charset : "");
2047 
2048  if (!match_regex(buf, pat, &cache, TRUE)) {
2049  continue;
2050  }
2051  if (!lptr->tagged)
2052  lptr->tagged = ++num_of_tagged_parts;
2053  }
2054 
2055  if (tinrc.wildcard) {
2056  FreeIfNeeded(cache.re);
2057  FreeIfNeeded(cache.extra);
2058  }
2059 }
2060 
2061 
2062 static int
2064  int n)
2065 {
2066  t_partl *lptr;
2067 
2068  lptr = find_part(n);
2069  return lptr->tagged;
2070 }
2071 
2072 
2073 static t_bool
2075  int n)
2076 {
2077  t_partl *lptr;
2078 
2079  lptr = find_part(n);
2080  if (lptr->tagged) {
2081  untag_part(n);
2082  return FALSE;
2083  } else {
2084  lptr->tagged = ++num_of_tagged_parts;
2085  return TRUE;
2086  }
2087 }
2088 
2089 
2090 static void
2092  int n)
2093 {
2094  int i;
2095  t_partl *curr_part, *lptr;
2096 
2097  lptr = find_part(0);
2098  curr_part = find_part(n);
2099  i = attmenu.max;
2100 
2101  while (i-- > 0 && lptr) {
2102  if (lptr->tagged > curr_part->tagged)
2103  --lptr->tagged;
2104  lptr = lptr->next;
2105  }
2106 
2107  curr_part->tagged = 0;
2109 }
2110 
2111 
2112 static void
2114  void)
2115 {
2116  t_partl *lptr = part_list;
2117 
2118  while (lptr) {
2119  if (lptr->tagged) {
2120  lptr->tagged = 0;
2121  }
2122  lptr = lptr->next;
2123  }
2124  num_of_tagged_parts = 0;
2125 }
2126 
2127 
2128 /*
2129  * Build a linked list which holds pointers to the parts we want deal with.
2130  */
2131 static int
2133  t_openartinfo *art)
2134 {
2135  int i = 0;
2136  t_part *ptr, *uueptr;
2137  t_partl *lptr;
2138 
2139  part_list = my_malloc(sizeof(t_partl));
2140  lptr = part_list;
2141  lptr->part = art->hdr.ext;
2142  lptr->next = NULL;
2143  lptr->tagged = 0;
2144  for (ptr = art->hdr.ext; ptr != NULL; ptr = ptr->next) {
2145  if ((uueptr = ptr->uue) != NULL) {
2146  lptr->next = my_malloc(sizeof(t_partl));
2147  lptr->next->part = ptr;
2148  lptr->next->next = NULL;
2149  lptr->next->tagged = 0;
2150  lptr = lptr->next;
2151  ++i;
2152  for (; uueptr != NULL; uueptr = uueptr->next) {
2153  lptr->next = my_malloc(sizeof(t_partl));
2154  lptr->next->part = uueptr;
2155  lptr->next->next = NULL;
2156  lptr->next->tagged = 0;
2157  lptr = lptr->next;
2158  ++i;
2159  }
2160  }
2161 
2162  if (ptr->uue)
2163  continue;
2164 
2165  lptr->next = my_malloc(sizeof(t_partl));
2166  lptr->next->part = ptr;
2167  lptr->next->next = NULL;
2168  lptr->next->tagged = 0;
2169  lptr = lptr->next;
2170  ++i;
2171  }
2172  return i;
2173 }
2174 
2175 
2176 static void
2178  t_partl *list)
2179 {
2180  while (list->next != NULL) {
2181  free_part_list(list->next);
2182  list->next = NULL;
2183  }
2184  free(list);
2185 }
2186 
2187 
2188 static void
2190  t_part *part,
2191  t_openartinfo *art,
2192  enum action what)
2193 {
2194  FILE *fp;
2195  char *savepath = NULL, *tmppath;
2196  int i, saved_parts = 0;
2197  t_partl *lptr;
2198 
2199  switch (what) {
2200  case SAVE_TAGGED:
2201  for (i = 1; i <= num_of_tagged_parts; i++) {
2202  lptr = part_list;
2203 
2204  while (lptr) {
2205  if (lptr->tagged == i) {
2206  if ((savepath = generate_savepath(lptr->part)) == NULL)
2207  return;
2208 
2209  if ((fp = open_save_filename(savepath, FALSE)) == NULL) {
2210  free(savepath);
2211  return;
2212  }
2213  process_part(lptr->part, art, fp, NULL, SAVE);
2214  free(savepath);
2215  ++saved_parts;
2216  }
2217  lptr = lptr->next;
2218  }
2219  }
2220  break;
2221 
2222  default:
2223  if ((tmppath = generate_savepath(part)) == NULL)
2224  return;
2225 
2226  if (what == SAVE)
2227  savepath = tmppath;
2228  else {
2229  savepath = get_tmpfilename(tmppath);
2230  free(tmppath);
2231  }
2232  if ((fp = open_save_filename(savepath, FALSE)) == NULL) {
2233  free(savepath);
2234  return;
2235  }
2236  process_part(part, art, fp, savepath, what);
2237  break;
2238  }
2239  switch (what) {
2240  case SAVE_TAGGED:
2242  break;
2243 
2244  case SAVE:
2245  wait_message(2, _(txt_attachment_saved), savepath);
2246  free(savepath);
2247  break;
2248 
2249  default:
2250  unlink(savepath);
2251  free(savepath);
2252  break;
2253  }
2254  cursoroff();
2255 }
2256 
2257 
2258 /*
2259  * VIEW/PIPE/SAVE the given part.
2260  *
2261  * PIPE_RAW uses the raw part, otherwise the part is decoded first.
2262  */
2263 static void
2265  t_part *part,
2266  t_openartinfo *art,
2267  FILE *outfile,
2268  const char *savepath,
2269  enum action what)
2270 {
2271  FILE *infile;
2272  char buf[2048], buf2[2048];
2273  int count;
2274  int i, line_count;
2275 #ifdef CHARSET_CONVERSION
2276  char *conv_buf;
2277  const char *network_charset;
2278  size_t line_len;
2279 #endif /* CHARSET_CONVERSION */
2280 
2281  /*
2282  * uuencoded parts must be read from the cooked article,
2283  * otherwise they might be additionally encoded with b64 or qp
2284  */
2285  if (part->encoding == ENCODING_UUE)
2286  infile = art->cooked;
2287  else
2288  infile = art->raw;
2289 
2290  if (what != PIPE_RAW && part->encoding == ENCODING_BASE64)
2291  mmdecode(NULL, 'b', 0, NULL); /* flush */
2292 
2293  fseek(infile, part->offset, SEEK_SET);
2294 
2295  line_count = part->line_count;
2296 
2297  for (i = 0; i < line_count; i++) {
2298  if ((fgets(buf, sizeof(buf), infile)) == NULL)
2299  break;
2300 
2301  /* This should catch cases where people illegally append text etc */
2302  if (buf[0] == '\0')
2303  break;
2304 
2305  /*
2306  * page.c:new_uue() sets offset to the 'begin ...' line
2307  * -> skip over the first line in uuencoded parts
2308  */
2309  if (part->encoding == ENCODING_UUE && i == 0) {
2310  ++line_count;
2311  continue;
2312  }
2313 
2314  if (what != PIPE_RAW) {
2315  switch (part->encoding) {
2316  case ENCODING_QP:
2317  case ENCODING_BASE64:
2318 #ifdef CHARSET_CONVERSION
2319  memset(buf2, '\0', sizeof(buf2));
2320 #endif /* CHARSET_CONVERSION */
2321  if ((count = mmdecode(buf, part->encoding == ENCODING_QP ? 'q' : 'b', '\0', buf2)) > 0) {
2322 #ifdef CHARSET_CONVERSION
2323  if (what != SAVE && what != SAVE_TAGGED && !strncmp(content_types[part->type], "text", 4)) {
2324  line_len = count;
2325  conv_buf = my_strdup(buf2);
2326  network_charset = get_param(part->params, "charset");
2327  process_charsets(&conv_buf, &line_len, network_charset ? network_charset : "US-ASCII", tinrc.mm_local_charset, FALSE);
2328  strncpy(buf2, conv_buf, sizeof(buf2) - 1);
2329  count = strlen(buf2);
2330  free(conv_buf);
2331  }
2332 #endif /* CHARSET_CONVERSION */
2333  fwrite(buf2, count, 1, outfile);
2334  }
2335  break;
2336 
2337  case ENCODING_UUE:
2338  /* TODO: if postproc, don't decode these since the traditional uudecoder will get them */
2339  /*
2340  * x-uuencode attachments have all the header info etc which we must ignore
2341  */
2342  if (strncmp(buf, "begin ", 6) != 0 && strncmp(buf, "end\n", 4) != 0 && buf[0] != '\n')
2344  break;
2345 
2346  default:
2347 #ifdef CHARSET_CONVERSION
2348  if (what != SAVE && what != SAVE_TAGGED && !strncmp(content_types[part->type], "text", 4)) {
2349  conv_buf = my_strdup(buf);
2350  line_len = strlen(conv_buf);
2351  network_charset = get_param(part->params, "charset");
2352  process_charsets(&conv_buf, &line_len, network_charset ? network_charset : "US-ASCII", tinrc.mm_local_charset, FALSE);
2353  strncpy(buf, conv_buf, sizeof(buf) - 1);
2354  free(conv_buf);
2355  }
2356 #endif /* CHARSET_CONVERSION */
2357  fputs(buf, outfile);
2358  }
2359  } else
2360  fputs(buf, outfile);
2361  }
2362 
2363  fclose(outfile);
2364 
2365  switch (what) {
2366  case VIEW:
2367  start_viewer(part, savepath);
2368  break;
2369 
2370 #ifndef DONT_HAVE_PIPING
2371  case PIPE:
2372  case PIPE_RAW:
2373  pipe_part(savepath);
2374  break;
2375 #endif /* !DONT_HAVE_PIPING */
2376 
2377  default:
2378  break;
2379  }
2380 }
2381 
2382 
2383 #ifndef DONT_HAVE_PIPING
2384 static void
2386  const char *savepath)
2387 {
2388  FILE *fp, *pipe_fp;
2389  char *prompt;
2390 
2393  free(prompt);
2394  return;
2395  }
2396  free(prompt);
2397  if ((fp = fopen(savepath, "r")) == NULL)
2398  /* TODO: error message? */
2399  return;
2400  EndWin();
2401  Raw(FALSE);
2402  fflush(stdout);
2404  if ((pipe_fp = popen(tinrc.default_pipe_command, "w")) == NULL) {
2407  Raw(TRUE);
2408  InitWin();
2409  fclose(fp);
2410  return;
2411  }
2412  copy_fp(fp, pipe_fp);
2413  if (errno == EPIPE)
2415  fflush(pipe_fp);
2416  (void) pclose(pipe_fp);
2418  fclose(fp);
2419 # ifdef USE_CURSES
2420  Raw(TRUE);
2421  InitWin();
2422 # endif /* USE_CURSES */
2423  prompt_continue();
2424 # ifndef USE_CURSES
2425  Raw(TRUE);
2426  InitWin();
2427 # endif /* !USE_CURSES */
2428 }
2429 #endif /* !DONT_HAVE_PIPING */
name
const char * name
Definition: signal.c:117
t_config::wildcard
int wildcard
Definition: tinrc.h:155
save_append_overwrite_keys
struct keylist save_append_overwrite_keys
Definition: keymap.c:86
check_start_save_any_news
int check_start_save_any_news(int function, t_bool catchup)
Definition: save.c:133
get_filename
const char * get_filename(t_param *ptr)
Definition: cook.c:353
TMPDIR
#define TMPDIR
Definition: tin.h:2121
t_article
Definition: tin.h:1510
t_config::score_select
int score_select
Definition: tinrc.h:159
open_save_filename
static FILE * open_save_filename(const char *path, t_bool mbox)
Definition: save.c:387
txt_attachment_untagged
constext txt_attachment_untagged[]
Definition: lang.c:92
HIST_SELECT_PATTERN
@ HIST_SELECT_PATTERN
Definition: extern.h:1552
process_part
static void process_part(t_part *part, t_openartinfo *art, FILE *outfile, const char *savepath, enum action what)
Definition: save.c:2264
TREE_HORIZ
#define TREE_HORIZ
Definition: tin.h:930
txt_command_failed
constext txt_command_failed[]
Definition: lang.c:150
strcasecmp
int strcasecmp(const char *p, const char *q)
Definition: string.c:468
EXIT_SUCCESS
#define EXIT_SUCCESS
Definition: tin.h:1273
txt_attachment_menu
constext txt_attachment_menu[]
Definition: lang.c:84
start_viewer
static void start_viewer(t_part *part, const char *path)
Definition: save.c:1228
DEBUG_MISC
#define DEBUG_MISC
Definition: debug.h:54
GLOBAL_PAGE_DOWN
@ GLOBAL_PAGE_DOWN
Definition: keymap.h:200
_
#define _(Text)
Definition: tin.h:94
GLOBAL_SHELL_ESCAPE
@ GLOBAL_SHELL_ESCAPE
Definition: keymap.h:223
pipe_fp
static FILE * pipe_fp
Definition: feed.c:55
NO
@ NO
Definition: save.c:1379
process_parts
static void process_parts(t_part *part, t_openartinfo *art, enum action what)
Definition: save.c:2189
txt_cannot_create
constext txt_cannot_create[]
Definition: lang.c:123
t_attribute::batch_save
unsigned batch_save
Definition: tin.h:1611
INDEX_TOP
#define INDEX_TOP
Definition: tin.h:1008
build_tree
static char * build_tree(int depth, int maxlen, int i)
Definition: save.c:1893
txt_cannot_open_for_saving
constext txt_cannot_open_for_saving[]
Definition: lang.c:129
save_and_process_art
t_bool save_and_process_art(t_openartinfo *artinfo, struct t_article *artptr, t_bool is_mailbox, const char *inpath, int max, t_bool post_process)
Definition: save.c:469
txt_no_prev_search
constext txt_no_prev_search[]
Definition: lang.c:683
my_strdup
char * my_strdup(const char *str)
Definition: string.c:133
txt_is_mailbox
constext txt_is_mailbox[]
Definition: lang.c:554
center_line
void center_line(int line, t_bool inverse, const char *str)
Definition: screen.c:258
t_group
Definition: tin.h:1772
TYPE_MULTIPART
#define TYPE_MULTIPART
Definition: rfc2046.h:48
txt_uu_success
constext txt_uu_success[]
Definition: lang.c:901
free_mailcap
void free_mailcap(t_mailcap *tmailcap)
Definition: rfc1524.c:475
DIGIT_8
@ DIGIT_8
Definition: keymap.h:158
open_art_fp
FILE * open_art_fp(struct t_group *group, t_artnum art)
Definition: rfc2046.c:1501
process_id
pid_t process_id
Definition: init.c:124
bug_report
void bug_report(void)
Definition: global.c:430
VIEW
@ VIEW
Definition: save.c:65
DEFAULT_SUM
#define DEFAULT_SUM
Definition: tin.h:540
GLOBAL_TOGGLE_INFO_LAST_LINE
@ GLOBAL_TOGGLE_INFO_LAST_LINE
Definition: keymap.h:229
PIPE_RAW
@ PIPE_RAW
Definition: save.c:68
ATTACHMENT_SAVE
@ ATTACHMENT_SAVE
Definition: keymap.h:161
MIDDLE
@ MIDDLE
Definition: save.c:58
str_trim
char * str_trim(char *string)
Definition: string.c:532
pcre_exec
int pcre_exec(const pcre *, const pcre_extra *, const char *, int, int, int, int *, int)
Definition: pcre_exec.c:3690
txt_attachment_no_name
constext txt_attachment_no_name[]
Definition: lang.c:86
txt_bad_command
constext txt_bad_command[]
Definition: lang.c:112
txt_post_processing
constext txt_post_processing[]
Definition: lang.c:725
PIPE
@ PIPE
Definition: save.c:70
GLOBAL_SEARCH_SUBJECT_FORWARD
@ GLOBAL_SEARCH_SUBJECT_FORWARD
Definition: keymap.h:220
t_attribute::post_process_view
unsigned post_process_view
Definition: tin.h:1621
untag_all_parts
static void untag_all_parts(void)
Definition: save.c:2113
partlist
Definition: rfc2046.h:116
generate_savepath
static char * generate_savepath(t_part *part)
Definition: save.c:663
view_file
static void view_file(const char *path, const char *file)
Definition: save.c:1087
SAVEFILE_PREFIX
#define SAVEFILE_PREFIX
Definition: tin.h:635
art_mark
void art_mark(struct t_group *group, struct t_article *art, int flag)
Definition: newsrc.c:1571
t_attribute::auto_save
unsigned auto_save
Definition: tin.h:1610
content_encodings
constext * content_encodings[]
Definition: lang.c:1448
t_attribute::ask_for_metamail
unsigned ask_for_metamail
Definition: tin.h:1606
move_down
void move_down(void)
Definition: global.c:110
openartinfo
Definition: rfc2046.h:183
DIGIT_5
@ DIGIT_5
Definition: keymap.h:155
last_search
t_function last_search
Definition: init.c:117
post_process_files
t_bool post_process_files(t_function proc_type_func, t_bool auto_delete)
Definition: save.c:746
screen
struct t_screen * screen
Definition: screen.c:51
txt_group
constext txt_group[]
Definition: lang.c:1217
txt_attachment_saved
constext txt_attachment_saved[]
Definition: lang.c:87
generate_filename
static void generate_filename(char *buf, int buflen, const char *suffix)
Definition: save.c:645
my_flush
#define my_flush()
Definition: tcurses.h:171
ClearScreen
void ClearScreen(void)
Definition: curses.c:410
txt_error_fseek
constext txt_error_fseek[]
Definition: lang.c:194
ENCODING_QP
#define ENCODING_QP
Definition: rfc2046.h:56
S_IWUSR
#define S_IWUSR
Definition: tin.h:2137
DEC
#define DEC(Char)
Definition: save.c:1114
scroll_up
void scroll_up(void)
Definition: global.c:278
cCRLF
#define cCRLF
Definition: tcurses.h:150
partlist::part
t_part * part
Definition: rfc2046.h:117
art
static t_openartinfo * art
Definition: cook.c:78
DIGIT_3
@ DIGIT_3
Definition: keymap.h:153
PATH_PART
#define PATH_PART
Definition: tin.h:592
tinrc
struct t_config tinrc
Definition: init.c:191
content_type
int content_type(char *type)
Definition: rfc2046.c:115
perror_message
void perror_message(const char *fmt,...)
Definition: screen.c:220
INTERACTIVE_WITH_HEADERS
@ INTERACTIVE_WITH_HEADERS
Definition: tin.h:1158
wait_message
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
FreeAndNull
#define FreeAndNull(p)
Definition: tin.h:2204
txt_extracting_shar
constext txt_extracting_shar[]
Definition: lang.c:270
curr_group
struct t_group * curr_group
Definition: group.c:55
t_config::default_select_pattern
char default_select_pattern[LEN]
Definition: tinrc.h:91
mailer
char mailer[PATH_LEN]
Definition: init.c:92
signal_context
int signal_context
Definition: signal.c:105
txt_end_of_attachments
constext txt_end_of_attachments[]
Definition: lang.c:167
txt_cannot_write_to_directory
constext txt_cannot_write_to_directory[]
Definition: lang.c:135
pipe_part
static void pipe_part(const char *savepath)
Definition: save.c:2385
SAVE
@ SAVE
Definition: save.c:66
free_part_list
static void free_part_list(t_partl *list)
Definition: save.c:2177
decode_save_one
static t_bool decode_save_one(t_part *part, FILE *rawfp, t_bool postproc)
Definition: save.c:1273
regex_cache::extra
pcre_extra * extra
Definition: tin.h:1919
txt_append_overwrite_quit
constext txt_append_overwrite_quit[]
Definition: lang.c:52
MIN
#define MIN(a, b)
Definition: tin.h:805
GLOBAL_SCROLL_DOWN
@ GLOBAL_SCROLL_DOWN
Definition: keymap.h:213
free_parts
void free_parts(t_part *ptr)
Definition: rfc2046.c:846
get_tagged
static int get_tagged(int n)
Definition: save.c:2063
fmt_string
char * fmt_string(const char *fmt,...)
Definition: string.c:1379
my_fputc
#define my_fputc(ch, stream)
Definition: tcurses.h:152
ATTACHMENT_TAG
@ ATTACHMENT_TAG
Definition: keymap.h:163
SAVE_APPEND_FILE
@ SAVE_APPEND_FILE
Definition: keymap.h:332
t_config::interactive_mailer
int interactive_mailer
Definition: tinrc.h:256
prompt_item_num
void prompt_item_num(int ch, const char *prompt)
Definition: global.c:200
t_archive::ispart
t_bool ispart
Definition: tin.h:1480
t_save::path
char * path
Definition: tin.h:1923
state
state
Definition: save.c:56
tcurses.h
TIN_FCLOSE
#define TIN_FCLOSE(x)
Definition: tin.h:1037
clear_note_area
void clear_note_area(void)
Definition: group.c:988
invoke_cmd
t_bool invoke_cmd(const char *nam)
Definition: misc.c:793
GLOBAL_SCROLL_UP
@ GLOBAL_SCROLL_UP
Definition: keymap.h:214
txt_checking_for_news
constext txt_checking_for_news[]
Definition: lang.c:144
t_menu::max
int max
Definition: tin.h:2007
info_message
void info_message(const char *fmt,...)
Definition: screen.c:102
t_config::beginner_level
t_bool beginner_level
Definition: tinrc.h:208
get_param
const char * get_param(t_param *list, const char *name)
Definition: rfc2046.c:568
tin.h
txt_mailed
constext txt_mailed[]
Definition: lang.c:614
GLOBAL_LAST_PAGE
@ GLOBAL_LAST_PAGE
Definition: keymap.h:192
CMDLINE_SAVEDIR
#define CMDLINE_SAVEDIR
Definition: tin.h:1094
mail_news_user
char mail_news_user[LEN]
Definition: init.c:90
txt_attachment_tagged
constext txt_attachment_tagged[]
Definition: lang.c:90
t_save::file
char * file
Definition: tin.h:1924
txt_info_no_previous_expression
constext txt_info_no_previous_expression[]
Definition: lang.c:548
DIGIT_4
@ DIGIT_4
Definition: keymap.h:154
expand_save
void expand_save(void)
Definition: memory.c:172
ATTACHMENT_SELECT
@ ATTACHMENT_SELECT
Definition: keymap.h:162
cmdline
struct t_cmdlineopts cmdline
Definition: init.c:189
openartinfo::cooked
FILE * cooked
Definition: rfc2046.h:189
PATH_LEN
#define PATH_LEN
Definition: tin.h:837
param::value
char * value
Definition: rfc2046.h:79
txt_uu_error_decode
constext txt_uu_error_decode[]
Definition: lang.c:899
GLOBAL_TOGGLE_HELP_DISPLAY
@ GLOBAL_TOGGLE_HELP_DISPLAY
Definition: keymap.h:228
action
action
Definition: save.c:64
rcdir
char rcdir[PATH_LEN]
Definition: init.c:100
txt_saved_group
constext txt_saved_group[]
Definition: lang.c:810
prompt_string_default
char * prompt_string_default(const char *prompt, char *def, const char *failtext, int history)
Definition: prompt.c:558
page_up
void page_up(void)
Definition: global.c:130
txt_save_attachment
constext txt_save_attachment[]
Definition: lang.c:806
attmenu
static t_menu attmenu
Definition: save.c:113
forever
#define forever
Definition: tin.h:810
show_help_page
void show_help_page(const int level, const char *title)
Definition: help.c:694
active
struct t_group * active
Definition: memory.c:66
tin_fgets
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:320
part::next
struct part * next
Definition: rfc2046.h:107
Raw
void Raw(int state)
Definition: curses.c:624
part::line_count
int line_count
Definition: rfc2046.h:104
make_group_path
void make_group_path(const char *name, char *path)
Definition: misc.c:2067
part
Definition: rfc2046.h:92
print_art_separator_line
void print_art_separator_line(FILE *fp, t_bool is_mailbox)
Definition: save.c:1210
SAVE_OVERWRITE_FILE
@ SAVE_OVERWRITE_FILE
Definition: keymap.h:333
content_types
constext * content_types[]
Definition: lang.c:1453
STRCMPEQ
#define STRCMPEQ(s1, s2)
Definition: tin.h:816
strwidth
int strwidth(const char *str)
Definition: string.c:1043
t_archive::partnum
char * partnum
Definition: tin.h:1479
new_part
t_part * new_part(t_part *part)
Definition: rfc2046.c:792
partlist::next
struct partlist * next
Definition: rfc2046.h:118
my_strncpy
void my_strncpy(char *p, const char *q, size_t n)
Definition: string.c:190
DEFAULT_SAVEDIR
#define DEFAULT_SAVEDIR
Definition: tin.h:632
CHECK_ANY_NEWS
#define CHECK_ANY_NEWS
Definition: tin.h:1220
param::name
char * name
Definition: rfc2046.h:78
mark_offset
int mark_offset
Definition: screen.c:48
T_ARTNUM_PFMT
#define T_ARTNUM_PFMT
Definition: tin.h:227
file_size
long file_size(const char *file)
Definition: misc.c:2122
homedir
char homedir[PATH_LEN]
Definition: init.c:78
draw_arrow_mark
void draw_arrow_mark(int line)
Definition: screen.c:300
t_article::archive
struct t_archive * archive
Definition: tin.h:1524
txt_saved_groupname
constext txt_saved_groupname[]
Definition: lang.c:811
t_header::subj
char * subj
Definition: rfc2046.h:133
WriteLine
#define WriteLine(row, buffer)
Definition: tcurses.h:174
txt_mailbox_formats
constext * txt_mailbox_formats[]
Definition: lang.c:1557
PLURAL
#define PLURAL(x, y)
Definition: tin.h:1053
cAttachment
@ cAttachment
Definition: tin.h:107
txt_no_viewer_found
constext txt_no_viewer_found[]
Definition: lang.c:697
get_tmpfilename
char * get_tmpfilename(const char *filename)
Definition: misc.c:101
NEWS_AVAIL_EXIT
#define NEWS_AVAIL_EXIT
Definition: tin.h:1280
index_group
t_bool index_group(struct t_group *group)
Definition: art.c:396
DIGIT_7
@ DIGIT_7
Definition: keymap.h:157
NAME_LEN
#define NAME_LEN
Definition: tin.h:852
t_config::mm_local_charset
char mm_local_charset[LEN]
Definition: tinrc.h:103
toggle_mini_help
void toggle_mini_help(int level)
Definition: help.c:1020
check_save_mime_type
static t_bool check_save_mime_type(t_part *part, const char *mime_types)
Definition: save.c:1454
match_content_type
static int match_content_type(t_part *part, char *type)
Definition: save.c:1392
GLOBAL_BUGREPORT
@ GLOBAL_BUGREPORT
Definition: keymap.h:187
handle_keypad
t_function handle_keypad(t_function(*left_action)(void), t_function(*right_action)(void), t_function(*mouse_action)(t_function(*left_action)(void), t_function(*right_action)(void)), const struct keylist keys)
Definition: global.c:355
draw_attachment_arrow
static void draw_attachment_arrow(void)
Definition: save.c:1797
txt_attachment_select
constext txt_attachment_select[]
Definition: lang.c:89
t_mailcap::needsterminal
t_bool needsterminal
Definition: tin.h:2057
ART_READ
#define ART_READ
Definition: tin.h:1320
new_params
t_param * new_params(void)
Definition: rfc2046.c:527
part::description
char * description
Definition: rfc2046.h:101
strfpath
int strfpath(const char *format, char *str, size_t maxsize, struct t_group *group, t_bool expand_all)
Definition: misc.c:1701
t_config::info_in_last_line
t_bool info_in_last_line
Definition: tinrc.h:215
buf
static char buf[16]
Definition: langinfo.c:50
show_attachment_page
static void show_attachment_page(void)
Definition: save.c:1539
TREE_VERT
#define TREE_VERT
Definition: tin.h:932
GLOBAL_PIPE
@ GLOBAL_PIPE
Definition: keymap.h:202
txt_attachment_menu_com
constext txt_attachment_menu_com[]
Definition: lang.c:85
t_menu
Definition: tin.h:2005
GLOBAL_LINE_DOWN
@ GLOBAL_LINE_DOWN
Definition: keymap.h:194
FreeIfNeeded
#define FreeIfNeeded(p)
Definition: tin.h:2203
show_mini_help
void show_mini_help(int level)
Definition: help.c:754
t_archive::name
char * name
Definition: tin.h:1478
ATTACHMENT_TAG_PATTERN
@ ATTACHMENT_TAG_PATTERN
Definition: keymap.h:164
info_len
static int info_len
Definition: save.c:112
t_attribute::savedir
char * savedir
Definition: tin.h:1566
attachment_page
void attachment_page(t_openartinfo *art)
Definition: save.c:1592
post_process_uud
static void post_process_uud(void)
Definition: save.c:900
t_mailcap::nametemplate
char * nametemplate
Definition: tin.h:2052
tag_pattern
static void tag_pattern(void)
Definition: save.c:2001
decode_save_mime
void decode_save_mime(t_openartinfo *art, t_bool postproc)
Definition: save.c:1495
TREE_VERT_RIGHT
#define TREE_VERT_RIGHT
Definition: tin.h:933
my_mkdir
int my_mkdir(char *path, mode_t mode)
Definition: misc.c:708
GLOBAL_LINE_UP
@ GLOBAL_LINE_UP
Definition: keymap.h:195
t_mailcap::description
char * description
Definition: tin.h:2050
txt_view_attachment
constext txt_view_attachment[]
Definition: lang.c:947
outfile
static FILE * outfile
Definition: pcretest.c:139
END
@ END
Definition: save.c:60
top_of_list
void top_of_list(void)
Definition: global.c:182
PCRE_CASELESS
#define PCRE_CASELESS
Definition: pcre.h:98
ATTACHMENT_LEVEL
#define ATTACHMENT_LEVEL
Definition: tin.h:1109
base_name
void base_name(const char *fullpath, char *file)
Definition: misc.c:868
GLOBAL_SEARCH_SUBJECT_BACKWARD
@ GLOBAL_SEARCH_SUBJECT_BACKWARD
Definition: keymap.h:219
txt_art_not_saved
constext txt_art_not_saved[]
Definition: lang.c:61
ATTACHMENT_PIPE
@ ATTACHMENT_PIPE
Definition: keymap.h:160
DIGIT_9
@ DIGIT_9
Definition: keymap.h:159
cCOLS
int cCOLS
Definition: curses.c:53
txt_pipe_to_command
constext txt_pipe_to_command[]
Definition: lang.c:1166
LEN
#define LEN
Definition: tin.h:854
txt_attachment_lines
constext txt_attachment_lines[]
Definition: lang.c:83
txt_post_processing_finished
constext txt_post_processing_finished[]
Definition: lang.c:726
t_function
enum defined_functions t_function
Definition: keymap.h:373
txt_uu_error_no_end
constext txt_uu_error_no_end[]
Definition: lang.c:900
t_cmdlineopts::savedir
char savedir[255]
Definition: tin.h:1470
num_save
int num_save
Definition: memory.c:57
SAVE_TAGGED
@ SAVE_TAGGED
Definition: save.c:67
txt_no_command
constext txt_no_command[]
Definition: lang.c:1164
OFF
@ OFF
Definition: save.c:59
part::type
unsigned type
Definition: rfc2046.h:94
S_IXUGO
#define S_IXUGO
Definition: tin.h:2163
txt_select_pattern
constext txt_select_pattern[]
Definition: lang.c:839
shar_regex
struct regex_cache shar_regex
currmenu
t_menu * currmenu
Definition: init.c:165
t_mailcap
Definition: tin.h:2045
STRNCMPEQ
#define STRNCMPEQ(s1, s2, n)
Definition: tin.h:817
txt_deleting
constext txt_deleting[]
Definition: lang.c:163
cursoroff
void cursoroff(void)
Definition: curses.c:721
ENCODING_UUE
#define ENCODING_UUE
Definition: rfc2046.h:60
GLOBAL_FIRST_PAGE
@ GLOBAL_FIRST_PAGE
Definition: keymap.h:190
txt_attachments_saved
constext txt_attachments_saved[]
Definition: lang.c:88
INITIAL
@ INITIAL
Definition: save.c:57
part::offset
long offset
Definition: rfc2046.h:103
set_xclick_off
void set_xclick_off(void)
Definition: curses.c:703
compile_regex
t_bool compile_regex(const char *regex, struct regex_cache *cache, int options)
Definition: regex.c:111
lookup_extension
t_bool lookup_extension(char *extension, size_t ext_len, const char *major, const char *minor)
Definition: mimetypes.c:169
copy_fp
t_bool copy_fp(FILE *fp_ip, FILE *fp_op)
Definition: misc.c:179
catchup
static t_bool catchup
Definition: main.c:57
is_mailbox
static t_bool is_mailbox
Definition: feed.c:50
sum_file
static void sum_file(const char *path, const char *file)
Definition: save.c:1045
regex_cache::re
pcre * re
Definition: tin.h:1918
my_fputs
#define my_fputs(str, stream)
Definition: tcurses.h:153
func_to_key
char func_to_key(t_function func, const struct keylist keys)
Definition: keymap.c:124
my_retouch
#define my_retouch()
Definition: tcurses.h:173
t_config::default_pipe_command
char default_pipe_command[LEN]
Definition: tinrc.h:73
TREE_ARROW_WRAP
#define TREE_ARROW_WRAP
Definition: tin.h:928
MMDFHDRTXT
#define MMDFHDRTXT
Definition: tin.h:640
save
struct t_save * save
Definition: memory.c:70
strfmailer
int strfmailer(const char *mail_prog, char *subject, char *to, const char *filename, char *dest, size_t maxsize, const char *format)
Definition: misc.c:1795
EndWin
void EndWin(void)
Definition: curses.c:368
BlankIfNull
#define BlankIfNull(p)
Definition: tin.h:2206
txt_attachments_tagged
constext txt_attachments_tagged[]
Definition: lang.c:91
S_ISDIR
#define S_ISDIR(m)
Definition: tin.h:2127
partlist::tagged
int tagged
Definition: rfc2046.h:119
SEEK_SET
#define SEEK_SET
Definition: tin.h:2441
bool_not
#define bool_not(b)
Definition: bool.h:81
attachment_left
static t_function attachment_left(void)
Definition: save.c:1781
get_mailcap_entry
t_mailcap * get_mailcap_entry(t_part *part, const char *path)
Definition: rfc1524.c:68
build_attachment_line
static void build_attachment_line(int i)
Definition: save.c:1814
tag_part
static t_bool tag_part(int n)
Definition: save.c:2074
my_printf
#define my_printf
Definition: tcurses.h:169
t_group::bogus
t_bool bogus
Definition: tin.h:1787
scroll_down
void scroll_down(void)
Definition: global.c:252
MAXKEYLEN
#define MAXKEYLEN
Definition: keymap.h:136
match
match
Definition: save.c:1378
t_cmdlineopts::args
unsigned int args
Definition: tin.h:1471
t_menu::curr
int curr
Definition: tin.h:2006
part::depth
int depth
Definition: rfc2046.h:105
INDEX2LNUM
#define INDEX2LNUM(i)
Definition: tin.h:1009
t_config::savedir
char savedir[PATH_LEN]
Definition: tinrc.h:126
t_group::name
char * name
Definition: tin.h:1773
InitWin
void InitWin(void)
Definition: curses.c:355
find_part
static t_partl * find_part(int n)
Definition: save.c:1973
t_screen::col
char * col
Definition: tin.h:1930
TREE_BLANK
#define TREE_BLANK
Definition: tin.h:929
untag_part
static void untag_part(int n)
Definition: save.c:2091
t_attribute::mime_types_to_save
char * mime_types_to_save
Definition: tin.h:1586
FALSE
#define FALSE
Definition: bool.h:70
txt_cannot_open
constext txt_cannot_open[]
Definition: lang.c:128
ATTACHMENT_UNTAG
@ ATTACHMENT_UNTAG
Definition: keymap.h:166
STRCPY
#define STRCPY(dst, src)
Definition: tin.h:814
create_path
t_bool create_path(const char *path)
Definition: save.c:605
debug
unsigned short debug
Definition: debug.c:51
GLOBAL_PAGE_UP
@ GLOBAL_PAGE_UP
Definition: keymap.h:201
t_config::mailer_format
char mailer_format[PATH_LEN]
Definition: tinrc.h:71
lookup_mimetype
void lookup_mimetype(const char *ext, t_part *part)
Definition: mimetypes.c:105
regex_cache
Definition: tin.h:1917
verbose
int verbose
Definition: init.c:153
rename_file
void rename_file(const char *old_filename, const char *new_filename)
Definition: misc.c:733
tin_ltoa
char * tin_ltoa(t_artnum value, int digits)
Definition: string.c:80
num_of_tagged_parts
static int num_of_tagged_parts
Definition: save.c:112
end_of_list
void end_of_list(void)
Definition: global.c:191
no_write
t_bool no_write
Definition: init.c:144
joinpath
void joinpath(char *result, size_t result_size, const char *dir, const char *file)
Definition: joinpath.c:50
ART_UNREAD
#define ART_UNREAD
Definition: tin.h:1321
suffix
static const char * suffix[]
Definition: pcregrep.c:222
snprintf
#define snprintf
Definition: tin.h:2417
strip_name
void strip_name(const char *from, char *address)
Definition: misc.c:2368
GLOBAL_SEARCH_REPEAT
@ GLOBAL_SEARCH_REPEAT
Definition: keymap.h:216
move_to_item
void move_to_item(int n)
Definition: global.c:227
START_ANY_NEWS
#define START_ANY_NEWS
Definition: tin.h:1221
DIGIT_2
@ DIGIT_2
Definition: keymap.h:152
openartinfo::raw
FILE * raw
Definition: rfc2046.h:188
my_group
int * my_group
Definition: memory.c:64
openartinfo::hdr
struct t_header hdr
Definition: rfc2046.h:185
error_message
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:184
get_part
t_part * get_part(int n)
Definition: save.c:1990
process_charsets
void process_charsets(char **line, size_t *max_line_len, const char *network_charset, const char *local_charset, t_bool conv_tex2iso)
Definition: misc.c:2634
t_config::default_save_mode
int default_save_mode
Definition: tinrc.h:134
t_group::attribute
struct t_attribute * attribute
Definition: tin.h:1790
GLOBAL_HELP
@ GLOBAL_HELP
Definition: keymap.h:191
t_menu::first
int first
Definition: tin.h:2008
GLOBAL_ABORT
@ GLOBAL_ABORT
Definition: keymap.h:186
txt_starting_command
constext txt_starting_command[]
Definition: lang.c:845
S_IRUSR
#define S_IRUSR
Definition: tin.h:2136
part::uue
struct part * uue
Definition: rfc2046.h:106
t_mailcap::command
char * command
Definition: tin.h:2047
attachment_right
static t_function attachment_right(void)
Definition: save.c:1789
REGEX_FMT
#define REGEX_FMT
Definition: tin.h:1016
move_up
void move_up(void)
Definition: global.c:81
DIGIT_6
@ DIGIT_6
Definition: keymap.h:156
txt_mail_log_to
constext txt_mail_log_to[]
Definition: lang.c:611
userid
char userid[PATH_LEN]
Definition: init.c:107
selmenu
t_menu selmenu
Definition: select.c:85
part_list
static t_partl * part_list
Definition: save.c:114
S_IRUGO
#define S_IRUGO
Definition: tin.h:2161
NOTMATCH
@ NOTMATCH
Definition: save.c:1381
PATH_PATCH
#define PATH_PATCH
Definition: tin.h:593
S_ISVTX
#define S_ISVTX
Definition: tin.h:2167
SAVE_ANY_NEWS
#define SAVE_ANY_NEWS
Definition: tin.h:1223
t_config::mailbox_format
int mailbox_format
Definition: tinrc.h:95
txt_saved
constext txt_saved[]
Definition: lang.c:809
TREE_UP_RIGHT
#define TREE_UP_RIGHT
Definition: tin.h:931
build_part_list
static int build_part_list(t_openartinfo *art)
Definition: save.c:2132
t_bool
unsigned t_bool
Definition: bool.h:77
errno
int errno
mmdecode
int mmdecode(const char *what, int encoding, int delimiter, char *where)
Definition: rfc2047.c:147
printascii
char * printascii(char *buf, int ch)
Definition: keymap.c:271
t_save::mailbox
t_bool mailbox
Definition: tin.h:1925
INTERACTIVE_NONE
@ INTERACTIVE_NONE
Definition: tin.h:1157
match_regex
t_bool match_regex(const char *string, char *pattern, struct regex_cache *cache, t_bool icase)
Definition: regex.c:59
prompt_yn
int prompt_yn(const char *prompt, t_bool default_answer)
Definition: prompt.c:165
TRUE
#define TRUE
Definition: bool.h:74
expand_save_filename
static t_bool expand_save_filename(char *outpath, size_t outpath_len, const char *path)
Definition: save.c:710
MAIL_ANY_NEWS
#define MAIL_ANY_NEWS
Definition: tin.h:1222
for_each_art
#define for_each_art(x)
Definition: tin.h:2211
HIST_PIPE_COMMAND
@ HIST_PIPE_COMMAND
Definition: extern.h:1546
part::params
t_param * params
Definition: rfc2046.h:102
GLOBAL_REDRAW_SCREEN
@ GLOBAL_REDRAW_SCREEN
Definition: keymap.h:212
HEADER_LEN
#define HEADER_LEN
Definition: tin.h:857
txt_no_attachments
constext txt_no_attachments[]
Definition: lang.c:669
uudecode_line
static void uudecode_line(const char *buf, FILE *fp)
Definition: save.c:1119
arts
struct t_article * arts
Definition: memory.c:69
prompt_continue
void prompt_continue(void)
Definition: prompt.c:774
POSTPROCESS_SHAR
@ POSTPROCESS_SHAR
Definition: keymap.h:328
t_group::subscribed
t_bool subscribed
Definition: tin.h:1785
txt_there_is_no_news
constext txt_there_is_no_news[]
Definition: lang.c:876
txt_saved_summary
constext txt_saved_summary[]
Definition: lang.c:813
TREE_ARROW
#define TREE_ARROW
Definition: tin.h:927
S_IRWXU
#define S_IRWXU
Definition: tin.h:2135
set_signal_catcher
void set_signal_catcher(int flag)
Definition: signal.c:526
t_header::ext
t_part * ext
Definition: rfc2046.h:146
sh_format
int sh_format(char *dst, size_t len, const char *fmt,...)
Definition: string.c:670
generic_search
int generic_search(t_bool forward, t_bool repeat, int current, int last, int level)
Definition: search.c:196
DIGIT_1
@ DIGIT_1
Definition: keymap.h:151
MATCH
@ MATCH
Definition: save.c:1380
t_header::from
char * from
Definition: rfc2046.h:128
GLOBAL_QUIT
@ GLOBAL_QUIT
Definition: keymap.h:210
part::encoding
unsigned encoding
Definition: rfc2046.h:95
unlink
#define unlink(file)
Definition: tin.h:384
prompt_slk_response
t_function prompt_slk_response(t_function default_func, const struct keylist keys, const char *fmt,...)
Definition: prompt.c:670
ENCODING_BASE64
#define ENCODING_BASE64
Definition: rfc2046.h:57
DIRSEP
#define DIRSEP
Definition: tin.h:2104
NOTESLINES
int NOTESLINES
Definition: signal.c:111
do_shell_escape
void do_shell_escape(void)
Definition: misc.c:542
INDEX2SNUM
#define INDEX2SNUM(i)
Definition: tin.h:1011
page_down
void page_down(void)
Definition: global.c:155
POSTPROCESS_YES
@ POSTPROCESS_YES
Definition: keymap.h:329
ATTACHMENT_TOGGLE_TAGGED
@ ATTACHMENT_TOGGLE_TAGGED
Definition: keymap.h:165
max_save
int max_save
Definition: memory.c:56
attachment_keys
struct keylist attachment_keys
Definition: keymap.c:62
post_process_sh
static void post_process_sh(void)
Definition: save.c:1158
part::subtype
char * subtype
Definition: rfc2046.h:100
my_malloc
#define my_malloc(size)
Definition: tin.h:2196
set_first_screen_item
void set_first_screen_item(void)
Definition: global.c:61