tin  2.6.1
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.6.1.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 : 2021-02-25
7 * Notes :
8 *
9 * Copyright (c) 1991-2022 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 {
60 END
61 };
62#endif /* !HAVE_LIBUU */
63
64enum action {
69#ifndef DONT_HAVE_PIPING
71#endif /* !DONT_HAVE_PIPING */
72};
73
74/*
75 * Local prototypes
76 */
77static FILE *open_save_filename(const char *path, t_bool mbox);
78static char *build_tree(int depth, int maxlen, int i);
79static char *generate_savepath(t_part *part);
81static int get_tagged(int n);
82static int match_content_type(t_part *part, char *type);
83static t_bool check_save_mime_type(t_part *part, const char *mime_types);
84static t_bool decode_save_one(t_part *part, FILE *rawfp, t_bool postproc);
85static t_bool expand_save_filename(char *outpath, size_t outpath_len, const char *path);
86static t_bool tag_part(int n);
87static t_function attachment_left(void);
88static t_function attachment_right(void);
89static t_partl *find_part(int n);
90static void build_attachment_line(int i);
91static void draw_attachment_arrow(void);
92static void free_part_list(t_partl *list);
93static 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 */
97static void post_process_uud(void);
98static void post_process_sh(void);
99static void process_part(t_part *part, t_openartinfo *art, FILE *outfile, const char *savepath, enum action what);
100static void process_parts(t_part *part, t_openartinfo *art, enum action what);
101static void show_attachment_page(void);
102static void start_viewer(t_part *part, const char *path);
103static void tag_pattern(void);
104static void untag_all_parts(void);
105static void untag_part(int n);
106static void uudecode_line(const char *buf, FILE *fp);
107static 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 */
132int
134 int function,
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 = (t_bool) 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) {
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: %s\n", MIME_SUPPORTED_VERSION);
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 */
386static 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;
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) {
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 }
435 else
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 * 'mailbox' is set if we are saving to a =mailbox
453 * 'inpath' is the template save path/file to save to
454 * 'max' is the number of articles we are saving
455 * 'post_process' is set if we want post-processing
456 * Expand the path appropriately, taking account of multiple file
457 * extensions.
458 *
459 * Extract binary attachments if !LIBUU
460 * Start viewer if requested
461 * If successful, add entry to the save[] array
462 * Returns:
463 * TRUE or FALSE depending on whether article was saved okay.
464 *
465 * TODO: could we use append_mail() here
466 */
467t_bool
469 t_openartinfo *artinfo,
471 const char *inpath,
472 int max,
473 t_bool post_process)
474{
475 FILE *fp;
476 char from[HEADER_LEN];
477 char path[PATH_LEN];
478 time_t epoch;
480
481 if (fseek(artinfo->raw, 0L, SEEK_SET) == -1) {
483 return FALSE;
484 }
485
486 /* The first task is to fixup the filename to be saved too. This is context dependent */
487 strncpy(path, inpath, sizeof(path) - 1);
488/* 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)); */
489
490 /*
491 * Mailbox saves are by definition to a single file as are single file
492 * saves. Multiple file saves append a .NNN sequence number to the path
493 * This is backward-contemptibility with older versions of tin
494 */
495 if (!is_mailbox && max > 1) {
496 const char suffixsep = '.';
497
498 sprintf(&path[strlen(path)], "%c%03d", suffixsep, num_save + 1);
499 }
500
501/* fprintf(stderr, "save_and_process_art expanded path now=(%s)\n", path); */
502
503 if ((fp = open_save_filename(path, is_mailbox)) == NULL)
504 return FALSE;
505
506 if (mmdf)
507 fprintf(fp, "%s", MMDFHDRTXT);
508 else {
509 if (artinfo->hdr.from)
510 strip_name(artinfo->hdr.from, from);
511 else /* shouldn't show up */
512 snprintf(from, sizeof(from), "%s@%s", PATHMASTER, get_host_name());
513
514 (void) time(&epoch);
515 fprintf(fp, "From %s %s", from, ctime(&epoch));
516 /*
517 * TODO: add Content-Length: header when using MBOXO
518 * so tin actually write MBOXCL instead of MBOXO?
519 */
520 }
521
522 /*
523 * TODO: currently does not quote From_ line in the !mmdf case
524 * like append_mail() (may it break postprocessing?) but
525 * then at least in the (is_mailbox && !post_process && !mmdf)
526 * case it should be done.
527 */
528 if (copy_fp(artinfo->raw, fp)) /* Write tailing newline or MMDF-mailbox separator */
530 else {
531 fclose(fp);
532 unlink(path);
533 return FALSE;
534 }
535
536 fclose(fp);
537
538 /*
539 * Saved ok, so fill out a save[] record
540 */
541 if (num_save == max_save - 1)
542 expand_save();
543 save[num_save].path = my_strdup(path);
544 save[num_save].file = strrchr(save[num_save].path, DIRSEP) + 1; /* ptr to filename portion */
546/* fprintf(stderr, "SAPA (%s) (%s) mbox=%s\n", save[num_save].path, save[num_save].file, bool_unparse(save[num_save].mailbox)); */
547 num_save++; /* NB: num_save is bumped here only */
548
549 /*
550 * Extract/view parts from multipart articles if required
551 * libuu does this as part of its own processing
552 */
553#ifndef HAVE_LIBUU
554 if (post_process) {
555# ifdef USE_CURSES
556 scrollok(stdscr, TRUE);
557# endif /* USE_CURSES */
558 decode_save_mime(artinfo, TRUE);
559# ifdef USE_CURSES
560 scrollok(stdscr, FALSE);
561# endif /* USE_CURSES */
562 }
563#endif /* !HAVE_LIBUU */
564
565 return TRUE;
566}
567
568
569/*
570 * Create the supplied path. Create intermediate directories as needed
571 * Don't create the last component (which would be the filename) unless the
572 * path is / terminated.
573 * Return FALSE if it somehow fails.
574 */
575t_bool
577 const char *path)
578{
579 char *buf, *p;
580 struct stat st;
581
582 if (!strlen(path))
583 return FALSE;
584
585 buf = my_strdup(path);
586 p = buf + 1;
587
588 if (!strlen(p)) {
589 free(buf);
590 return FALSE;
591 }
592
593 while ((p = strchr(p, DIRSEP)) != NULL) {
594 *p = '\0';
595 if (stat(buf, &st) == -1) {
596 if (my_mkdir(buf, (mode_t) (S_IRWXU|S_IRUGO|S_IXUGO)) == -1) {
597 if (errno != EEXIST) {
599 free(buf);
600 return FALSE;
601 }
602 }
603 }
604 *p++ = DIRSEP;
605 }
606 free(buf);
607 return TRUE;
608}
609
610
611/*
612 * Generate semi-meaningful filename based on sequence number and
613 * Content-(sub)type
614 */
615static void
617 char *buf,
618 int buflen,
619 const char *suffix)
620{
621 static int seqno = 0;
622
623 snprintf(buf, (size_t) buflen, "%s-%03d.%s", SAVEFILE_PREFIX, seqno++, suffix);
624}
625
626
627/*
628 * Generate /save/to/path name.
629 *
630 * Return pointer to allocated memory which the caller must free or
631 * NULL if something went wrong.
632 */
633static char *
635 t_part *part)
636{
637 char buf[2048];
638 char *savepath;
639 const char *name;
640 t_bool mbox;
641
642 savepath = my_malloc(PATH_LEN);
643 /*
644 * Get the filename to save to in 'savepath'
645 */
646 if ((name = get_filename(part->params)) == NULL) {
647 char extension[NAME_LEN + 1];
648
649 lookup_extension(extension, sizeof(extension), content_types[part->type], part->subtype);
650 generate_filename(buf, sizeof(buf), extension);
651 mbox = expand_save_filename(savepath, PATH_LEN, buf);
652 } else
653 mbox = expand_save_filename(savepath, PATH_LEN, name);
654
655 /*
656 * Not a good idea to dump attachments over a mailbox
657 */
658 if (mbox) {
660 free(savepath);
661 return NULL;
662 }
663
664 if (!(create_path(savepath))) {
666 free(savepath);
667 return NULL;
668 }
669
670 return savepath;
671}
672
673
674/*
675 * Generate a path/filename to save to, using 'path' as input.
676 * The pathname is stored in 'outpath', which should be PATH_LEN in size
677 * Expand metacharacters and use defaults as needed.
678 * Return TRUE if the path is a mailbox, or FALSE otherwise.
679 */
680static t_bool
682 char *outpath,
683 size_t outpath_len,
684 const char *path)
685{
686 char base_filename[PATH_LEN];
687 char buf[PATH_LEN];
688 char buf_path[PATH_LEN];
689 int ret;
690
691 /*
692 * Make sure that externally supplied filename is a filename only and fits
693 * into buffer
694 */
695 STRCPY(buf_path, path);
696 base_name(buf_path, base_filename);
697
698 /* Build default path to save to */
701
702 /* Join path and filename */
703 joinpath(outpath, outpath_len, buf, base_filename);
704
705 return (ret == 1); /* should now always evaluate to FALSE */
706}
707
708
709/*
710 * Post process the articles in save[] according to proc_type_ch
711 * auto_delete is set if we should remove the saved files after processing
712 * This stage can produce a fair bit of output so we allow it to
713 * scroll up the screen rather than waste time displaying it in the
714 * message bar
715 */
716t_bool
718 t_function proc_type_func,
719 t_bool auto_delete)
720{
721 if (num_save < 1)
722 return FALSE;
723
724 EndWin();
725 Raw(FALSE);
727
728 switch (proc_type_func) {
729 case POSTPROCESS_SHAR:
731 break;
732
733 /* This is the default, eg, with AUTOSAVE */
734 case POSTPROCESS_YES:
735 default:
737 break;
738 }
739
741 my_flush();
742#ifdef USE_CURSES
743 Raw(TRUE);
744 InitWin();
745#endif /* USE_CURSES */
747#ifndef USE_CURSES
748 Raw(TRUE);
749 InitWin();
750#endif /* !USE_CURSES */
751
752 /*
753 * Remove the post-processed files if required
754 */
755 if (auto_delete) {
756 int i;
757
759 cursoroff();
760
761 for (i = 0; i < num_save; i++)
762 unlink(save[i].path);
763 }
764
765 return TRUE;
766}
767
768
769/*
770 * Two implementations .....
771 * The LIBUU case performs multi-file decoding for uue, base64
772 * binhex, qp. This is handled entirely during the post processing phase
773 *
774 * The !LIBUU case only handles multi-file uudecoding, the other MIME
775 * types were handled using the internal MIME parser when the articles
776 * were originally saved
777 */
778#ifdef HAVE_LIBUU
779static void
781 void)
782{
783 FILE *fp_in;
784 char file_out_dir[PATH_LEN];
785 const char *eptr;
786 int i;
787 int count;
788 int errors = 0;
789 uulist *item;
790
791 /*
792 * Grab the dirname portion
793 */
794 my_strncpy(file_out_dir, save[0].path, save[0].file - save[0].path);
795
796 UUInitialize();
797
798 UUSetOption(UUOPT_SAVEPATH, 0, file_out_dir);
799 for (i = 0; i < num_save; i++) {
800 if ((fp_in = fopen(save[i].path, "r")) != NULL) {
801 UULoadFile(save[i].path, NULL, 0); /* Scans file for encoded data */
802 fclose(fp_in);
803 }
804 }
805
806# if 0
807 /*
808 * uudeview's "intelligent" multi-part detection
809 * From the uudeview docs: This function is a bunch of heuristics, and I
810 * don't really trust them... should only be called as a last resort on
811 * explicit user request
812 */
813 UUSmerge(0);
814 UUSmerge(1);
815 UUSmerge(99);
816# endif /* 0 */
817
818 i = count = 0;
819 item = UUGetFileListItem(i);
821
822 while (item != NULL) {
823 if (UUDecodeFile(item, NULL) == UURET_OK) {
824 char path[PATH_LEN];
825
826/* TODO: test for multiple things per article decoded okay? */
827 count++;
828 my_printf(_(txt_uu_success), item->filename);
830
831 /* item->mimetype seems not to be available for uudecoded files etc */
833 joinpath(path, sizeof(path), file_out_dir, item->filename);
834 view_file(path, strrchr(path, DIRSEP) + 1);
835 }
836 } else {
837 errors++;
838 if (item->state & UUFILE_MISPART)
839 eptr = _(txt_libuu_error_missing);
840 else if (item->state & UUFILE_NOBEGIN)
841 eptr = _(txt_libuu_error_no_begin);
842 else if (item->state & UUFILE_NOEND)
843 eptr = _(txt_uu_error_no_end);
844 else if (item->state & UUFILE_NODATA)
845 eptr = _(txt_libuu_error_no_data);
846 else
847 eptr = _(txt_libuu_error_unknown);
848
849 my_printf(_(txt_uu_error_decode), (item->filename) ? item->filename : item->subfname, eptr);
851 }
852 i++;
853 item = UUGetFileListItem(i);
854 my_flush();
855 }
856
857 my_printf(_(txt_libuu_saved), count, num_save, errors, PLURAL(errors, txt_error));
859 UUCleanUp();
860}
861
862#else
863
864/*
865 * Open and read all the files in save[]
866 * Scan for uuencode BEGIN lines, decode input as we go along
867 * uuencoded data can span multiple files, and multiple uuencoded
868 * files are supported per batch
869 */
870static void
872 void)
873{
874 FILE *fp_in;
875 FILE *fp_out = NULL;
876 char *filename = NULL;
877 char file_out_dir[PATH_LEN];
878 char path[PATH_LEN];
879 char s[LEN], t[LEN], u[LEN];
880 int state = INITIAL;
881 int i;
882 mode_t mode = 0;
883
884 /*
885 * Grab the dirname portion
886 */
887 my_strncpy(file_out_dir, save[0].path, (size_t) (save[0].file - save[0].path));
888
889 t[0] = '\0';
890 u[0] = '\0';
891
892 for (i = 0; i < num_save; i++) {
893 if ((fp_in = fopen(save[i].path, "r")) == NULL)
894 continue;
895
896 while (fgets(s, (int) sizeof(s), fp_in) != NULL) {
897 switch (state) {
898 case INITIAL:
899 if (strncmp("begin ", s, 6) == 0) {
900 char fmt[15];
901 char name[PATH_LEN];
902 char buf[PATH_LEN];
903
904 snprintf(fmt, sizeof(fmt), "%%o %%%dc\\n", PATH_LEN - 1);
905 if (sscanf(s + 6, fmt, &mode, name) == 2) {
906 strtok(name, "\n");
907 my_strncpy(buf, name, sizeof(buf) - 1);
908 str_trim(buf);
910 } else
911 name[0] = '\0';
912
913 if (!mode && !*name) { /* not a valid uu-file at all */
914 state = INITIAL;
915 continue;
916 }
917
918 if (!*name)
919 generate_filename(name, sizeof(name), "uue");
920
921 filename = name;
922 expand_save_filename(path, sizeof(path), filename);
923 filename = strrchr(path, DIRSEP) + 1; /* ptr to filename portion */
924 if ((fp_out = fopen(path, "w")) == NULL) {
926 fclose(fp_in);
927 return;
928 }
929 state = MIDDLE;
930 }
931 break;
932
933 case MIDDLE:
934 /*
935 * TODO: replace hard coded length check (uue lines are not
936 * required to be 60 chars long (45 encoded chars)
937 * ('M' == 60 * 3 / 4 + ' ' == 77))
938 */
939 if (s[0] == 'M')
940 uudecode_line(s, fp_out);
941 else if (STRNCMPEQ("end", s, 3)) {
942 state = END;
943 if (u[0] != 'M')
944 uudecode_line(u, fp_out);
945 if (t[0] != 'M')
946 uudecode_line(t, fp_out);
947 } else /* end */
948 state = OFF; /* OFF => a break in the uuencoded data */
949 break;
950
951 case OFF:
952 if ((s[0] == 'M') && (t[0] == 'M') && (u[0] == 'M')) {
953 uudecode_line(u, fp_out);
954 uudecode_line(t, fp_out);
955 uudecode_line(s, fp_out);
956 state = MIDDLE; /* Continue output of previously suspended data */
957 } else if (STRNCMPEQ("end", s, 3)) {
958 state = END;
959 if (u[0] != 'M')
960 uudecode_line(u, fp_out);
961 if (t[0] != 'M')
962 uudecode_line(t, fp_out);
963 }
964 break;
965
966 case END:
967 default:
968 break;
969 } /* switch (state) */
970
971 if (state == END) {
972 /* set the mode after getting rid of dangerous bits */
973 if (!(mode &= ~(S_ISUID|S_ISGID|S_ISVTX)))
974 mode = (S_IRUSR|S_IWUSR);
975
976# ifdef HAVE_FCHMOD
977 fchmod(fileno(fp_out), mode);
978# else
979# ifdef HAVE_CHMOD
980 chmod(path, mode);
981# endif /* HAVE_CHMOD */
982# endif /* HAVE_FCHMOD */
983
984 fclose(fp_out);
985 fp_out = NULL;
986
987 my_printf(_(txt_uu_success), filename);
989 sum_file(path, filename);
991 view_file(path, filename);
992 state = INITIAL;
993 continue;
994 }
995
996 strcpy(u, t); /* Keep tabs on the last two lines, which typically do not start with M */
997 strcpy(t, s);
998
999 } /* while (fgets) ... */
1000
1001 fclose(fp_in);
1002
1003 } /* for i...num_save */
1004
1005 /*
1006 * Check if we ran out of data
1007 */
1008 if (fp_out) {
1009 fclose(fp_out);
1012 }
1013 return;
1014}
1015
1016
1017/*
1018 * Sum file - why do we bother to do this?
1019 * nuke code or add DONT_HAVE_PIPING -tree
1020 */
1021static void
1023 const char *path,
1024 const char *file)
1025{
1026# if defined(HAVE_SUM) && !defined(DONT_HAVE_PIPING)
1027 FILE *fp_in;
1028 char *ext;
1029 char buf[LEN];
1030
1031 sh_format(buf, sizeof(buf), "%s \"%s\"", DEFAULT_SUM, path);
1032 if ((fp_in = popen(buf, "r")) != NULL) {
1033 buf[0] = '\0';
1034
1035 /*
1036 * You can't do this with (fgets != NULL)
1037 */
1038 while (!feof(fp_in)) {
1039 fgets(buf, (int) sizeof(buf), fp_in);
1040 if ((ext = strchr(buf, '\n')) != NULL)
1041 *ext = '\0';
1042 }
1043 fflush(fp_in);
1044 pclose(fp_in);
1045
1046 my_printf(_(txt_checksum_of_file), file, file_size(path), _("bytes"));
1048 my_printf("\t%s%s", buf, cCRLF);
1049 } else {
1052 }
1053 my_flush();
1054# endif /* HAVE SUM && !DONT_HAVE_PIPING */
1055}
1056#endif /* HAVE_LIBUU */
1057
1058
1059/*
1060 * If defined, invoke post processor command
1061 * Create a part structure, with defaults, insert a parameter for the name
1062 */
1063static void
1065 const char *path,
1066 const char *file)
1067{
1068 char *ext;
1069 t_part *part;
1070
1071 part = new_part(NULL);
1072
1073 if ((ext = strrchr(file, '.')) != NULL)
1074 lookup_mimetype(ext + 1, part); /* Get MIME type/subtype */
1075
1076 /*
1077 * Needed for the mime-type processor
1078 */
1079 part->params = new_params();
1080 part->params->name = my_strdup("name");
1081 part->params->value = my_strdup(file);
1082
1083 start_viewer(part, path);
1085
1087}
1088
1089
1090/* Single character decode. */
1091#define DEC(Char) (((Char) - ' ') & 077)
1092/*
1093 * Decode 'buf' - write the uudecoded output to 'fp'
1094 */
1095static void
1097 const char *buf,
1098 FILE *fp)
1099{
1100 const char *p = buf;
1101 char ch;
1102 int n;
1103
1104 n = DEC(*p);
1105
1106 for (++p; n > 0; p += 4, n -= 3) {
1107 if (n >= 3) {
1108 ch = (char) ((DEC(p[0]) << 2) | (DEC(p[1]) >> 4));
1109 fputc(ch, fp);
1110 ch = (char) ((DEC(p[1]) << 4) | (DEC(p[2]) >> 2));
1111 fputc(ch, fp);
1112 ch = (char) ((DEC(p[2]) << 6) | DEC(p[3]));
1113 fputc(ch, fp);
1114 } else {
1115 if (n >= 1) {
1116 ch = (char) ((DEC(p[0]) << 2) | (DEC(p[1]) >> 4));
1117 fputc(ch, fp);
1118 }
1119 if (n >= 2) {
1120 ch = (char) ((DEC(p[1]) << 4) | (DEC(p[2]) >> 2));
1121 fputc(ch, fp);
1122 }
1123 }
1124 }
1125}
1126
1127
1128/*
1129 * Unpack /bin/sh archives
1130 * There is no end-of-shar marker so the code reads everything after
1131 * the start marker. This is why shar is handled separately.
1132 * The code assumes shar archives do not span articles
1133 */
1134static void
1136 void)
1137{
1138 FILE *fp_in, *fp_out = NULL;
1139 char buf[LEN];
1140 char file_out[PATH_LEN];
1141 char file_out_dir[PATH_LEN];
1142 int i;
1143
1144 /*
1145 * Grab the dirname portion
1146 */
1147 my_strncpy(file_out_dir, save[0].path, (size_t) (save[0].file - save[0].path));
1148 snprintf(file_out, sizeof(file_out), "%ssh%ld", file_out_dir, (long) process_id);
1149
1150 for (i = 0; i < num_save; i++) {
1151 if ((fp_in = fopen(save[i].path, "r")) == NULL)
1152 continue;
1153
1154 wait_message(0, _(txt_extracting_shar), save[i].path);
1155
1156 while (fgets(buf, (int) sizeof(buf), fp_in) != NULL) {
1157 /* find #!/bin/sh style patterns */
1158 if ((fp_out == NULL) && pcre_exec(shar_regex.re, shar_regex.extra, buf, (int) strlen(buf), 0, 0, NULL, 0) >= 0)
1159 fp_out = fopen(file_out, "w");
1160
1161 /* write to temp file */
1162 if (fp_out != NULL)
1163 fputs(buf, fp_out);
1164 }
1165 fclose(fp_in);
1166
1167 if (fp_out == NULL) { /* Didn't extract any shar */
1168 my_fputs(cCRLF, stdout);
1169 continue;
1170 }
1171
1172 fclose(fp_out);
1173 fp_out = NULL;
1174 sh_format(buf, sizeof(buf), "cd %s; sh %s", file_out_dir, file_out);
1175 my_fputs(cCRLF, stdout);
1176 my_flush();
1177 invoke_cmd(buf); /* Handles its own errors */
1178 unlink(file_out);
1179 }
1180}
1181
1182
1183/*
1184 * write tailing (MMDF)-mailbox separator
1185 */
1186void
1188 FILE *fp,
1190{
1191#ifdef DEBUG
1192 if (debug & DEBUG_MISC)
1193 error_message(2, "Mailbox=[%s], mailbox_format=[%s]", bool_unparse(is_mailbox), txt_mailbox_formats[tinrc.mailbox_format]);
1194#endif /* DEBUG */
1195
1196 fprintf(fp, "%s", (is_mailbox && !strcasecmp(txt_mailbox_formats[tinrc.mailbox_format], "MMDF")) ? MMDFHDRTXT : "\n");
1197}
1198
1199
1200/*
1201 * part needs to have at least content type/subtype and a filename
1202 * path = full path/file (used for substitution in mailcap entries)
1203 */
1204static void
1206 t_part *part,
1207 const char *path)
1208{
1209 t_mailcap *foo;
1210
1211 if ((foo = get_mailcap_entry(part, path)) != NULL) {
1212 if (foo->nametemplate) /* honor nametemplate */
1213 rename_file(path, foo->nametemplate);
1214
1216 if (foo->needsterminal) {
1218 fflush(stdout);
1219 } else {
1220 if (foo->description)
1221 info_message("%s", foo->description);
1222 }
1223 invoke_cmd(foo->command);
1224 if (foo->needsterminal) {
1225#ifndef USE_CURSES
1226 EndWin();
1227 Raw(FALSE);
1228#endif /* !USE_CURSES */
1230#ifndef USE_CURSES
1231 Raw(TRUE);
1232 InitWin();
1233#endif /* !USE_CURSES */
1234 }
1235 if (foo->nametemplate) /* undo nametemplate, needed as 'save'-prompt is done outside start_viewer */
1236 rename_file(foo->nametemplate, path);
1237 free_mailcap(foo);
1238 } else
1240}
1241
1242
1243/*
1244 * Decode and save the binary object pointed to in 'part'
1245 * Optionally launch a viewer for it
1246 * Return FALSE if Abort used to skip further viewing/saving
1247 * or other terminal error occurs
1248 */
1249static t_bool
1251 t_part *part,
1252 FILE *rawfp,
1253 t_bool postproc)
1254{
1255 FILE *fp;
1256 char buf[2048], buf2[2048];
1257 char *savepath;
1258 int count;
1259 int i;
1260
1261 /*
1262 * Decode this message part if appropriate
1263 */
1265 /* TODO: skip message if saving multiple files (e.g. save 't'agged) */
1266 wait_message(1, "Skipped %s/%s", content_types[part->type], part->subtype); /* TODO: better msg */
1267 return TRUE;
1268 }
1269
1270 if ((savepath = generate_savepath(part)) == NULL)
1271 return FALSE;
1272
1273 /*
1274 * Decode/save the attachment
1275 */
1276 if ((fp = open_save_filename(savepath, FALSE)) == NULL) {
1277 free(savepath);
1278 return FALSE;
1279 }
1280
1282 mmdecode(NULL, 'b', 0, NULL); /* flush */
1283
1284 fseek(rawfp, part->offset, SEEK_SET);
1285
1286 for (i = 0; i < part->line_count; i++) {
1287 if ((fgets(buf, sizeof(buf), rawfp)) == NULL)
1288 break;
1289
1290 /* This should catch cases where people illegally append text etc */
1291 if (buf[0] == '\0')
1292 break;
1293
1294 switch (part->encoding) {
1295 case ENCODING_QP:
1296 case ENCODING_BASE64:
1297 count = mmdecode(buf, part->encoding == ENCODING_QP ? 'q' : 'b', '\0', buf2);
1298 fwrite(buf2, (size_t) count, 1, fp);
1299 break;
1300
1301 case ENCODING_UUE:
1302 /* TODO: if postproc, don't decode these since the traditional uudecoder will get them */
1303 /*
1304 * x-uuencode attachments have all the header info etc which we must ignore
1305 */
1306 if (strncmp(buf, "begin ", 6) != 0 && strncmp(buf, "end\n", 4) != 0 && buf[0] != '\n')
1307 uudecode_line(buf, fp);
1308 break;
1309
1310 default:
1311 fputs(buf, fp);
1312 }
1313 }
1314 fclose(fp);
1315
1316 /*
1317 * View the attachment
1318 */
1320 start_viewer(part, savepath);
1322 } else {
1324 if ((i = prompt_yn(buf, TRUE)) == 1)
1325 start_viewer(part, savepath);
1326 else if (i == -1) { /* Skip rest of attachments */
1327 unlink(savepath);
1328 free(savepath);
1329 return FALSE;
1330 }
1331 }
1332
1333 /*
1334 * Save the attachment
1335 */
1336 if (postproc && curr_group->attribute->post_process_view) {
1337 my_printf(_(txt_uu_success), savepath);
1339 }
1340 if (!postproc) {
1342 if ((i = prompt_yn(buf, FALSE)) != 1) {
1343 unlink(savepath);
1344 if (i == -1) { /* Skip rest of attachments */
1345 free(savepath);
1346 return FALSE;
1347 }
1348 }
1349 }
1350 free(savepath);
1351 return TRUE;
1352}
1353
1354
1355enum match {
1358 NOTMATCH
1360
1361/*
1362 * Match a single type/subtype Content pair
1363 * Returns:
1364 * NO = Not matched
1365 * MATCH = Matched
1366 * NOTMATCH = Matched, but !negated
1367 */
1368static int
1370 t_part *part,
1371 char *type)
1372{
1373 char *subtype;
1374 int typeindex;
1375 t_bool found = FALSE;
1376 t_bool negate = FALSE;
1377
1378 /* Check for negation */
1379 if (*type == '!') {
1380 negate = TRUE;
1381 ++type;
1382
1383 if (!*type) /* Invalid type */
1384 return NO;
1385 }
1386
1387 /* Split type and subtype */
1388 if ((subtype = strchr(type, '/')) == NULL)
1389 return NO;
1390 *(subtype++) = '\0';
1391
1392 if (!*type || !*subtype) /* Missing type or subtype */
1393 return NO;
1394
1395 /* Try and match major */
1396 if (strcmp(type, "*") == 0)
1397 found = TRUE;
1398 else if (((typeindex = content_type(type)) != -1) && typeindex == part->type)
1399 found = TRUE;
1400
1401 if (!found)
1402 return NO;
1403
1404 /* Try and match subtype */
1405 found = FALSE;
1406 if (strcmp(subtype, "*") == 0)
1407 found = TRUE;
1408 else if (strcmp(subtype, part->subtype) == 0)
1409 found = TRUE;
1410
1411 if (!found)
1412 return NO;
1413
1414 /* We got a match */
1415 if (negate)
1416 return NOTMATCH;
1417
1418 return MATCH;
1419}
1420
1421
1422/*
1423 * See if the mime type of this part matches the list of content types to save
1424 * or ignore. Return TRUE if there is a match
1425 * mime_types is a comma separated list of type/subtype pairs. type and/or
1426 * subtype can be a '*' to match any, and a pair can begin with a ! which
1427 * will negate the meaning. We eval all pairs, the rightmost match will
1428 * prevail
1429 */
1430static t_bool
1432 t_part *part,
1433 const char *mime_types)
1434{
1435 char *ptr, *pair;
1436 int found;
1437 int retcode;
1438
1439 if (!mime_types)
1440 return FALSE;
1441
1442 ptr = my_strdup(mime_types);
1443
1444 if ((pair = strtok(ptr, ",")) == NULL) {
1445 free(ptr);
1446 return FALSE;
1447 }
1448
1449 retcode = match_content_type(part, pair);
1450
1451 while ((pair = strtok(NULL, ",")) != NULL) {
1452 if ((found = match_content_type(part, pair)) != NO)
1453 retcode = found;
1454 }
1455
1456 free(ptr);
1457 return (retcode == MATCH);
1458}
1459
1460
1461/*
1462 * decode and save binary MIME attachments from an open article context
1463 * optionally locate and launch a viewer application
1464 * 'postproc' determines the mode of the operation and will be set to
1465 * TRUE when we're called during a [Ss]ave operation and FALSE when
1466 * when just viewing
1467 * When it is TRUE the view option will depend on post_process_view and
1468 * the save is implicit. Feedback will also be printed.
1469 * When it is FALSE then the view/save options will be queried
1470 */
1471void
1474 t_bool postproc)
1475{
1476 t_part *ptr, *uueptr;
1477
1478 /*
1479 * Iterate over all the attachments
1480 */
1481 for (ptr = art->hdr.ext; ptr != NULL; ptr = ptr->next) {
1482 /*
1483 * Handle uuencoded sections in this message part.
1484 * Only works when the uuencoded file is entirely within the current
1485 * article.
1486 * We don't do this when postprocessing as the generic uudecode code
1487 * already handles uuencoded data, but TODO: review this
1488 */
1489 if (!postproc) {
1490 for (uueptr = ptr->uue; uueptr != NULL; uueptr = uueptr->next) {
1491 if (!(decode_save_one(uueptr, art->raw, postproc)))
1492 break;
1493 }
1494 }
1495
1496 /*
1497 * TYPE_MULTIPART is an envelope type, don't process it.
1498 * If we had an UUE part, the "surrounding" text/plain plays
1499 * the role of a multipart part. Check to see if we want to
1500 * save text and if not, skip this part.
1501 */
1502 /* check_save_mime_type() is done in decode_save_one() and the check for ptr->uue must be done unconditionally */
1503 if (ptr->type == TYPE_MULTIPART || (NULL != ptr->uue /* && !check_save_mime_type(ptr, curr_group->attribute->mime_types_to_save) */ ))
1504 continue;
1505
1506 if (!(decode_save_one(ptr, art->raw, postproc)))
1507 break;
1508 }
1509}
1510
1511
1512/*
1513 * Attachment menu
1514 */
1515static void
1517 void)
1518{
1519 char buf[BUFSIZ];
1520 const char *charset;
1521 int i, tmp_len, max_depth;
1522 t_part *part;
1523
1525 currmenu = &attmenu;
1526 mark_offset = 0;
1527
1528 if (attmenu.curr < 0)
1529 attmenu.curr = 0;
1530
1531 info_len = max_depth = 0;
1532 for (i = 0; i < attmenu.max; ++i) {
1533 part = get_part(i);
1535 tmp_len = strwidth(buf);
1536 charset = get_param(part->params, "charset");
1537 snprintf(buf, sizeof(buf), " %s/%s, %s, %s%s", content_types[part->type], part->subtype, content_encodings[part->encoding], charset ? charset : "", charset ? ", " : "");
1538 tmp_len += strwidth(buf);
1539 if (tmp_len > info_len)
1540 info_len = tmp_len;
1541
1542 tmp_len = part->depth;
1543 if (tmp_len > max_depth)
1544 max_depth = tmp_len;
1545 }
1546 tmp_len = cCOLS - 13 - MIN((cCOLS - 13) / 2 + 10, max_depth * 2 + 1 + strwidth(_(txt_attachment_no_name)));
1547 if (info_len > tmp_len)
1548 info_len = tmp_len;
1549
1550 ClearScreen();
1553
1554 for (i = attmenu.first; i < attmenu.first + NOTESLINES && i < attmenu.max; ++i)
1556
1558
1559 if (attmenu.max <= 0) {
1561 return;
1562 }
1563
1565}
1566
1567
1568void
1571{
1572 char key[MAXKEYLEN];
1574 t_menu *oldmenu = NULL;
1575 t_part *part;
1576
1577 if (currmenu)
1578 oldmenu = currmenu;
1580 attmenu.curr = 0;
1585
1586 forever {
1588 case GLOBAL_QUIT:
1590 if (oldmenu)
1591 currmenu = oldmenu;
1592 return;
1593
1594 case DIGIT_1:
1595 case DIGIT_2:
1596 case DIGIT_3:
1597 case DIGIT_4:
1598 case DIGIT_5:
1599 case DIGIT_6:
1600 case DIGIT_7:
1601 case DIGIT_8:
1602 case DIGIT_9:
1603 if (attmenu.max)
1605 break;
1606
1607#ifndef NO_SHELL_ESCAPE
1610 break;
1611#endif /* !NO_SHELL_ESCAPE */
1612
1613 case GLOBAL_HELP:
1616 break;
1617
1618 case GLOBAL_BUGREPORT:
1619 bug_report();
1621 break;
1622
1623 case GLOBAL_FIRST_PAGE:
1624 top_of_list();
1625 break;
1626
1627 case GLOBAL_LAST_PAGE:
1628 end_of_list();
1629 break;
1630
1632 my_retouch();
1634 break;
1635
1636 case GLOBAL_LINE_DOWN:
1637 move_down();
1638 break;
1639
1640 case GLOBAL_LINE_UP:
1641 move_up();
1642 break;
1643
1644 case GLOBAL_PAGE_DOWN:
1645 page_down();
1646 break;
1647
1648 case GLOBAL_PAGE_UP:
1649 page_up();
1650 break;
1651
1652 case GLOBAL_SCROLL_DOWN:
1653 scroll_down();
1654 break;
1655
1656 case GLOBAL_SCROLL_UP:
1657 scroll_up();
1658 break;
1659
1663 break;
1664
1668 break;
1669
1670 case ATTACHMENT_SAVE:
1671 if (attmenu.max) {
1675 }
1676 break;
1677
1678 case ATTACHMENT_SELECT:
1679 if (attmenu.max) {
1683 }
1684 break;
1685
1686 case ATTACHMENT_TAG:
1687 if (attmenu.max) {
1688 t_bool tagged;
1689
1690 tagged = tag_part(attmenu.curr);
1692 if (attmenu.curr + 1 < attmenu.max)
1693 move_down();
1695 }
1696 break;
1697
1698 case ATTACHMENT_UNTAG:
1702 }
1703 break;
1704
1706 if (attmenu.max) {
1707 tag_pattern();
1710 }
1711 break;
1712
1714 if (attmenu.max) {
1715 int i;
1716
1717 for (i = attmenu.first; i < attmenu.max; ++i)
1718 tag_part(i);
1721 }
1722 break;
1723
1729 else if (attmenu.max) {
1730 int new_pos, old_pos = attmenu.curr;
1731
1733 if (new_pos != old_pos)
1734 move_to_item(new_pos);
1735 }
1736 break;
1737
1738#ifndef DONT_HAVE_PIPING
1739 case ATTACHMENT_PIPE:
1740 case GLOBAL_PIPE:
1741 if (attmenu.max) {
1745 }
1746 break;
1747#endif /* !DONT_HAVE_PIPING */
1748
1749 default:
1751 break;
1752 }
1753 }
1754}
1755
1756
1757static t_function
1759 void)
1760{
1761 return GLOBAL_QUIT;
1762}
1763
1764
1765static t_function
1767 void)
1768{
1769 return ATTACHMENT_SELECT;
1770}
1771
1772
1773static void
1775 void)
1776{
1779 const char *name;
1780 t_part *part;
1781
1785 } else if (attmenu.curr == attmenu.max - 1)
1787}
1788
1789
1790static void
1792 int i)
1793{
1794 char *sptr;
1795 const char *name;
1796 const char *charset;
1797#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1798 char *tmpname;
1799 char *tmpbuf;
1800#endif /* MULTIBYTE_ABLE && !NOLOCALE */
1801 char buf[BUFSIZ];
1802 char buf2[BUFSIZ];
1803 char *tree = NULL;
1804 int len, namelen, tagged, treelen;
1805 t_part *part;
1806
1807#ifdef USE_CURSES
1808 /*
1809 * Allocate line buffer
1810 * make it the same size like in !USE_CURSES case to simplify some code
1811 */
1812# if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1813 sptr = my_malloc(cCOLS * MB_CUR_MAX + 2);
1814# else
1815 sptr = my_malloc(cCOLS + 2);
1816# endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1817#else
1818 sptr = screen[INDEX2SNUM(i)].col;
1819#endif /* USE_CURSES */
1820
1821 part = get_part(i);
1822 namelen = MIN(cCOLS - 13 - info_len - 8, strwidth(_(txt_attachment_no_name)));
1823 tagged = get_tagged(i);
1824
1825 if (!(name = get_filename(part->params))) {
1826 if (!(name = part->description))
1828 }
1829
1830 charset = get_param(part->params, "charset");
1831 snprintf(buf2, sizeof(buf2), _(txt_attachment_lines), part->line_count);
1832 /* TODO: make the layout configurable? */
1833 if (!strcmp(content_types[part->type], "text"))
1834 snprintf(buf, sizeof(buf), " %s/%s, %s, %s%s%s", content_types[part->type], part->subtype, content_encodings[part->encoding], charset ? charset : "", charset ? ", " : "", buf2);
1835 else
1836 snprintf(buf, sizeof(buf), " %s/%s, %s, %s", content_types[part->type], part->subtype, content_encodings[part->encoding], buf2);
1837 if (part->depth > 0) {
1838 treelen = cCOLS - 13 - info_len - namelen;
1839 tree = build_tree(part->depth, treelen, i);
1840 }
1841 snprintf(buf2, sizeof(buf2), "%s %s", tagged ? tin_ltoa(tagged, 3) : " ", BlankIfNull(tree));
1842 FreeIfNeeded(tree);
1843 len = strwidth(buf2);
1844 if (namelen + len + info_len + 8 <= cCOLS)
1845 namelen = cCOLS - 8 - info_len - len;
1846
1847#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1848 tmpname = spart(name, namelen, TRUE);
1849 tmpbuf = spart(buf, info_len, TRUE);
1850 snprintf(sptr, (size_t) cCOLS * MB_CUR_MAX, " %s %s%*s%*s%s", tin_ltoa(i + 1, 4), buf2, namelen, BlankIfNull(tmpname), info_len, BlankIfNull(tmpbuf), cCRLF);
1851 FreeIfNeeded(tmpname);
1852 FreeIfNeeded(tmpbuf);
1853#else
1854 snprintf(sptr, cCOLS, " %s %s%-*.*s%*.*s%s", tin_ltoa(i + 1, 4), buf2, namelen, namelen, name, info_len, info_len, buf, cCRLF);
1855#endif /* MULTIBYTE_ABLE && !NOLOCALE */
1856
1857 WriteLine(INDEX2LNUM(i), sptr);
1858
1859#ifdef USE_CURSES
1860 free(sptr);
1861#endif /* USE_CURSES */
1862}
1863
1864
1865/*
1866 * Build attachment tree. Code adopted
1867 * from thread.c:make_prefix().
1868 */
1869static char *
1871 int depth,
1872 int maxlen,
1873 int i)
1874{
1875#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1876 char *result;
1877 wchar_t *tree;
1878#else
1879 char *tree;
1880#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1881 int prefix_ptr, tmpdepth;
1882 int depth_level = 0;
1883 t_bool found = FALSE;
1884 t_partl *lptr, *lptr2;
1885
1886 lptr2 = find_part(i);
1887 prefix_ptr = depth * 2 - 1;
1888 if (prefix_ptr > maxlen - 1 - !(maxlen % 2)) {
1889 int odd = ((maxlen % 2) ? 0 : 1);
1890
1891 prefix_ptr -= maxlen - ++depth_level - 2 - odd;
1892 while (prefix_ptr > maxlen - 2 - odd) {
1893 if (depth_level < maxlen / 5)
1894 depth_level++;
1895
1896 prefix_ptr -= maxlen - depth_level - 2 - odd;
1897 odd = (odd ? 0 : 1);
1898 }
1899 }
1900#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1901 tree = my_malloc(sizeof(wchar_t) * (size_t) prefix_ptr + 3 * sizeof(wchar_t));
1902 tree[prefix_ptr + 2] = (wchar_t) '\0';
1903#else
1904 tree = my_malloc(prefix_ptr + 3);
1905 tree[prefix_ptr + 2] = '\0';
1906#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1907 tree[prefix_ptr + 1] = TREE_ARROW;
1908 tree[prefix_ptr] = TREE_HORIZ;
1909 for (lptr = lptr2->next; lptr != NULL; lptr = lptr->next) {
1910 if (lptr->part->depth == depth) {
1911 found = TRUE;
1912 break;
1913 }
1914 if (lptr->part->depth < depth)
1915 break;
1916 }
1917 tree[--prefix_ptr] = found ? TREE_VERT_RIGHT : TREE_UP_RIGHT;
1918 found = FALSE;
1919 for (tmpdepth = depth - 1; prefix_ptr > 1; --tmpdepth) {
1920 for (lptr = lptr2->next; lptr != NULL; lptr = lptr->next) {
1921 if (lptr->part->depth == tmpdepth) {
1922 found = TRUE;
1923 break;
1924 }
1925 if (lptr->part->depth < tmpdepth)
1926 break;
1927 }
1928 tree[--prefix_ptr] = TREE_BLANK;
1929 tree[--prefix_ptr] = found ? TREE_VERT : TREE_BLANK;
1930 found = FALSE;
1931 }
1932 while (depth_level)
1933 tree[--depth_level] = TREE_ARROW_WRAP;
1934
1935#if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
1936 result = wchar_t2char(tree);
1937 free(tree);
1938 return result;
1939#else
1940 return tree;
1941#endif /* MULTIBYTE_ABLE && !NO_LOCALE */
1942}
1943
1944
1945/*
1946 * Find nth attachment in part_list.
1947 * Return pointer to that part.
1948 */
1949static t_partl *
1951 int n)
1952{
1953 t_partl *lptr;
1954
1955 lptr = part_list;
1956 if (attmenu.max >= 1)
1957 lptr = lptr->next;
1958
1959 while (n-- > 0 && lptr->next)
1960 lptr = lptr->next;
1961
1962 return lptr;
1963}
1964
1965
1966t_part *
1968 int n)
1969{
1970 t_partl *lptr;
1971
1972 lptr = find_part(n);
1973 return lptr->part;
1974}
1975
1976
1977static void
1979 void)
1980{
1981 char buf[BUFSIZ];
1982 char pat[128];
1983 char *prompt;
1984 const char *name;
1985 const char *charset;
1986 struct regex_cache cache = { NULL, NULL };
1987 t_part *part;
1988 t_partl *lptr;
1989
1990#if 0
1993#endif /* 0 */
1994
1997 free(prompt);
1998 return;
1999 }
2000 free(prompt);
2001
2002 if (STRCMPEQ(tinrc.default_select_pattern, "*")) { /* all */
2003 if (tinrc.wildcard)
2004 STRCPY(pat, ".*");
2005 else
2007 } else
2008 snprintf(pat, sizeof(pat), REGEX_FMT, tinrc.default_select_pattern);
2009
2010 if (tinrc.wildcard && !(compile_regex(pat, &cache, PCRE_CASELESS)))
2011 return;
2012
2013 lptr = find_part(0);
2014
2015 for (; lptr != NULL; lptr = lptr->next) {
2016 part = lptr->part;
2017 if (!(name = get_filename(part->params))) {
2018 if (!(name = part->description))
2020 }
2021 charset = get_param(part->params, "charset");
2022
2023 snprintf(buf, sizeof(buf), "%s %s/%s %s, %s", name, content_types[part->type], part->subtype, content_encodings[part->encoding], charset ? charset : "");
2024
2025 if (!match_regex(buf, pat, &cache, TRUE))
2026 continue;
2027
2028 if (!lptr->tagged)
2029 lptr->tagged = ++num_of_tagged_parts;
2030 }
2031
2032 if (tinrc.wildcard) {
2033 FreeIfNeeded(cache.re);
2034 FreeIfNeeded(cache.extra);
2035 }
2036}
2037
2038
2039static int
2041 int n)
2042{
2043 t_partl *lptr;
2044
2045 lptr = find_part(n);
2046 return lptr->tagged;
2047}
2048
2049
2050static t_bool
2052 int n)
2053{
2054 t_partl *lptr;
2055
2056 lptr = find_part(n);
2057 if (lptr->tagged) {
2058 untag_part(n);
2059 return FALSE;
2060 } else {
2061 lptr->tagged = ++num_of_tagged_parts;
2062 return TRUE;
2063 }
2064}
2065
2066
2067static void
2069 int n)
2070{
2071 int i;
2072 t_partl *curr_part, *lptr;
2073
2074 lptr = find_part(0);
2075 curr_part = find_part(n);
2076 i = attmenu.max;
2077
2078 while (i-- > 0 && lptr) {
2079 if (lptr->tagged > curr_part->tagged)
2080 --lptr->tagged;
2081 lptr = lptr->next;
2082 }
2083
2084 curr_part->tagged = 0;
2086}
2087
2088
2089static void
2091 void)
2092{
2093 t_partl *lptr = part_list;
2094
2095 while (lptr) {
2096 if (lptr->tagged)
2097 lptr->tagged = 0;
2098
2099 lptr = lptr->next;
2100 }
2102}
2103
2104
2105/*
2106 * Build a linked list which holds pointers to the parts we want deal with.
2107 */
2108static int
2111{
2112 int i = 0;
2113 t_part *ptr, *uueptr;
2114 t_partl *lptr;
2115
2116 part_list = my_malloc(sizeof(t_partl));
2117 lptr = part_list;
2118 lptr->part = art->hdr.ext;
2119 lptr->next = NULL;
2120 lptr->tagged = 0;
2121 for (ptr = art->hdr.ext; ptr != NULL; ptr = ptr->next) {
2122 if ((uueptr = ptr->uue) != NULL) {
2123 lptr->next = my_malloc(sizeof(t_partl));
2124 lptr->next->part = ptr;
2125 lptr->next->next = NULL;
2126 lptr->next->tagged = 0;
2127 lptr = lptr->next;
2128 ++i;
2129 for (; uueptr != NULL; uueptr = uueptr->next) {
2130 lptr->next = my_malloc(sizeof(t_partl));
2131 lptr->next->part = uueptr;
2132 lptr->next->next = NULL;
2133 lptr->next->tagged = 0;
2134 lptr = lptr->next;
2135 ++i;
2136 }
2137 }
2138
2139 if (ptr->uue)
2140 continue;
2141
2142 lptr->next = my_malloc(sizeof(t_partl));
2143 lptr->next->part = ptr;
2144 lptr->next->next = NULL;
2145 lptr->next->tagged = 0;
2146 lptr = lptr->next;
2147 ++i;
2148 }
2149 return i;
2150}
2151
2152
2153static void
2155 t_partl *list)
2156{
2157 while (list->next != NULL) {
2158 free_part_list(list->next);
2159 list->next = NULL;
2160 }
2161 free(list);
2162}
2163
2164
2165static void
2167 t_part *part,
2169 enum action what)
2170{
2171 FILE *fp;
2172 char *savepath = NULL, *tmppath;
2173 int i, saved_parts = 0;
2174 t_partl *lptr;
2175
2176 switch (what) {
2177 case SAVE_TAGGED:
2178 for (i = 1; i <= num_of_tagged_parts; i++) {
2179 lptr = part_list;
2180
2181 while (lptr) {
2182 if (lptr->tagged == i) {
2183 if ((savepath = generate_savepath(lptr->part)) == NULL)
2184 return;
2185
2186 if ((fp = open_save_filename(savepath, FALSE)) == NULL) {
2187 free(savepath);
2188 return;
2189 }
2190 process_part(lptr->part, art, fp, NULL, SAVE);
2191 free(savepath);
2192 ++saved_parts;
2193 }
2194 lptr = lptr->next;
2195 }
2196 }
2197 break;
2198
2199 default:
2200 if ((tmppath = generate_savepath(part)) == NULL)
2201 return;
2202
2203 if (what == SAVE)
2204 savepath = tmppath;
2205 else {
2206 savepath = get_tmpfilename(tmppath);
2207 free(tmppath);
2208 }
2209 if ((fp = open_save_filename(savepath, FALSE)) == NULL) {
2210 free(savepath);
2211 return;
2212 }
2213 process_part(part, art, fp, savepath, what);
2214 break;
2215 }
2216 switch (what) {
2217 case SAVE_TAGGED:
2219 break;
2220
2221 case SAVE:
2222 wait_message(2, _(txt_attachment_saved), savepath);
2223 free(savepath);
2224 break;
2225
2226 default:
2227 unlink(savepath);
2228 free(savepath);
2229 break;
2230 }
2231 cursoroff();
2232}
2233
2234
2235/*
2236 * VIEW/PIPE/SAVE the given part.
2237 *
2238 * PIPE_RAW uses the raw part, otherwise the part is decoded first.
2239 */
2240static void
2242 t_part *part,
2244 FILE *outfile,
2245 const char *savepath,
2246 enum action what)
2247{
2248 FILE *infile;
2249 char buf[2048], buf2[2048];
2250 int count;
2251 int i, line_count;
2252#ifdef CHARSET_CONVERSION
2253 char *conv_buf;
2254 const char *network_charset;
2255 size_t line_len;
2256#endif /* CHARSET_CONVERSION */
2257
2258 /*
2259 * uuencoded parts must be read from the cooked article,
2260 * otherwise they might be additionally encoded with b64 or qp
2261 */
2262 if (part->encoding == ENCODING_UUE)
2263 infile = art->cooked;
2264 else
2265 infile = art->raw;
2266
2267 if (what != PIPE_RAW && part->encoding == ENCODING_BASE64)
2268 mmdecode(NULL, 'b', 0, NULL); /* flush */
2269
2270 fseek(infile, part->offset, SEEK_SET);
2271
2272 line_count = part->line_count;
2273
2274 for (i = 0; i < line_count; i++) {
2275 if ((fgets(buf, sizeof(buf), infile)) == NULL)
2276 break;
2277
2278 /* This should catch cases where people illegally append text etc */
2279 if (buf[0] == '\0')
2280 break;
2281
2282 /*
2283 * page.c:new_uue() sets offset to the 'begin ...' line
2284 * -> skip over the first line in uuencoded parts
2285 */
2286 if (part->encoding == ENCODING_UUE && i == 0) {
2287 ++line_count;
2288 continue;
2289 }
2290
2291 if (what != PIPE_RAW) {
2292 switch (part->encoding) {
2293 case ENCODING_QP:
2294 case ENCODING_BASE64:
2295#ifdef CHARSET_CONVERSION
2296 memset(buf2, '\0', sizeof(buf2));
2297#endif /* CHARSET_CONVERSION */
2298 if ((count = mmdecode(buf, part->encoding == ENCODING_QP ? 'q' : 'b', '\0', buf2)) > 0) {
2299#ifdef CHARSET_CONVERSION
2300 if (what != SAVE && what != SAVE_TAGGED && !strncmp(content_types[part->type], "text", 4)) {
2301 line_len = (size_t) count;
2302 conv_buf = my_strdup(buf2);
2303 network_charset = get_param(part->params, "charset");
2304 process_charsets(&conv_buf, &line_len, network_charset ? network_charset : "US-ASCII", tinrc.mm_local_charset, FALSE);
2305 strncpy(buf2, conv_buf, sizeof(buf2) - 1);
2306 count = (int) strlen(buf2);
2307 free(conv_buf);
2308 }
2309#endif /* CHARSET_CONVERSION */
2310 fwrite(buf2, (size_t) count, 1, outfile);
2311 }
2312 break;
2313
2314 case ENCODING_UUE:
2315 /* TODO: if postproc, don't decode these since the traditional uudecoder will get them */
2316 /*
2317 * x-uuencode attachments have all the header info etc which we must ignore
2318 */
2319 if (strncmp(buf, "begin ", 6) != 0 && strncmp(buf, "end\n", 4) != 0 && buf[0] != '\n')
2320 uudecode_line(buf, outfile);
2321 break;
2322
2323 default:
2324#ifdef CHARSET_CONVERSION
2325 if (what != SAVE && what != SAVE_TAGGED && !strncmp(content_types[part->type], "text", 4)) {
2326 conv_buf = my_strdup(buf);
2327 line_len = strlen(conv_buf);
2328 network_charset = get_param(part->params, "charset");
2329 process_charsets(&conv_buf, &line_len, network_charset ? network_charset : "US-ASCII", tinrc.mm_local_charset, FALSE);
2330 strncpy(buf, conv_buf, sizeof(buf) - 1);
2331 free(conv_buf);
2332 }
2333#endif /* CHARSET_CONVERSION */
2334 fputs(buf, outfile);
2335 }
2336 } else
2337 fputs(buf, outfile);
2338 }
2339
2340 fclose(outfile);
2341
2342 switch (what) {
2343 case VIEW:
2344 start_viewer(part, savepath);
2345 break;
2346
2347#ifndef DONT_HAVE_PIPING
2348 case PIPE:
2349 case PIPE_RAW:
2350 pipe_part(savepath);
2351 break;
2352#endif /* !DONT_HAVE_PIPING */
2353
2354 default:
2355 break;
2356 }
2357}
2358
2359
2360#ifndef DONT_HAVE_PIPING
2361static void
2363 const char *savepath)
2364{
2365 FILE *fp, *pipe_fp;
2366 char *prompt;
2367
2368 prompt = fmt_string(_(txt_pipe_to_command), (size_t) cCOLS - (strlen(_(txt_pipe_to_command)) + 30), tinrc.default_pipe_command);
2370 free(prompt);
2371 return;
2372 }
2373 free(prompt);
2374 if ((fp = fopen(savepath, "r")) == NULL)
2375 /* TODO: error message? */
2376 return;
2377 EndWin();
2378 Raw(FALSE);
2379 fflush(stdout);
2381 if ((pipe_fp = popen(tinrc.default_pipe_command, "w")) == NULL) {
2384 Raw(TRUE);
2385 InitWin();
2386 fclose(fp);
2387 return;
2388 }
2389 copy_fp(fp, pipe_fp);
2390 if (errno == EPIPE)
2392 fflush(pipe_fp);
2393 (void) pclose(pipe_fp);
2395 fclose(fp);
2396# ifdef USE_CURSES
2397 Raw(TRUE);
2398 InitWin();
2399# endif /* USE_CURSES */
2401# ifndef USE_CURSES
2402 Raw(TRUE);
2403 InitWin();
2404# endif /* !USE_CURSES */
2405}
2406#endif /* !DONT_HAVE_PIPING */
unsigned t_bool
Definition: bool.h:77
#define bool_not(b)
Definition: bool.h:81
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
#define bool_unparse(b)
Definition: bool.h:83
static t_openartinfo * art
Definition: cook.c:78
#define DEBUG_MISC
Definition: debug.h:54
constext txt_attachment_untagged[]
Definition: lang.c:92
constext txt_cannot_create[]
Definition: lang.c:123
@ HIST_PIPE_COMMAND
Definition: extern.h:1566
@ HIST_SELECT_PATTERN
Definition: extern.h:1572
t_function last_search
Definition: init.c:117
int verbose
Definition: init.c:154
constext txt_pipe_to_command[]
Definition: lang.c:1179
constext txt_attachment_menu_com[]
Definition: lang.c:85
constext txt_bad_command[]
Definition: lang.c:112
constext txt_is_mailbox[]
Definition: lang.c:563
int * my_group
Definition: memory.c:64
int NOTESLINES
Definition: signal.c:111
constext txt_checking_for_news[]
Definition: lang.c:144
constext txt_end_of_attachments[]
Definition: lang.c:167
constext txt_saved_groupname[]
Definition: lang.c:824
constext txt_deleting[]
Definition: lang.c:163
constext txt_extracting_shar[]
Definition: lang.c:273
constext txt_attachment_lines[]
Definition: lang.c:83
char homedir[PATH_LEN]
Definition: init.c:78
t_menu selmenu
Definition: select.c:84
pid_t process_id
Definition: init.c:125
constext txt_saved_group[]
Definition: lang.c:823
struct regex_cache shar_regex
struct t_article * arts
Definition: memory.c:69
constext txt_attachment_tagged[]
Definition: lang.c:90
constext txt_save_attachment[]
Definition: lang.c:819
constext txt_attachment_menu[]
Definition: lang.c:84
constext txt_no_attachments[]
Definition: lang.c:680
constext txt_append_overwrite_quit[]
Definition: lang.c:52
int num_save
Definition: memory.c:57
constext txt_error_fseek[]
Definition: lang.c:195
constext txt_attachment_no_name[]
Definition: lang.c:86
constext * txt_mailbox_formats[]
Definition: lang.c:1570
constext txt_saved_summary[]
Definition: lang.c:826
constext txt_select_pattern[]
Definition: lang.c:852
constext txt_uu_success[]
Definition: lang.c:914
constext * content_encodings[]
Definition: lang.c:1461
char mailer[PATH_LEN]
Definition: init.c:92
struct t_save * save
Definition: memory.c:70
constext txt_group[]
Definition: lang.c:1230
constext txt_post_processing_finished[]
Definition: lang.c:741
constext txt_art_not_saved[]
Definition: lang.c:61
constext txt_cannot_open[]
Definition: lang.c:128
char userid[PATH_LEN]
Definition: init.c:107
constext txt_info_no_previous_expression[]
Definition: lang.c:557
constext txt_attachment_select[]
Definition: lang.c:89
constext txt_mailed[]
Definition: lang.c:623
int signal_context
Definition: signal.c:105
constext txt_there_is_no_news[]
Definition: lang.c:889
constext txt_no_viewer_found[]
Definition: lang.c:708
int mark_offset
Definition: screen.c:48
t_menu * currmenu
Definition: init.c:166
constext txt_post_processing[]
Definition: lang.c:740
char rcdir[PATH_LEN]
Definition: init.c:100
constext txt_command_failed[]
Definition: lang.c:150
constext txt_no_prev_search[]
Definition: lang.c:694
int cCOLS
Definition: curses.c:53
char mail_news_user[LEN]
Definition: init.c:90
constext txt_cannot_open_for_saving[]
Definition: lang.c:129
constext txt_attachments_saved[]
Definition: lang.c:88
constext * content_types[]
Definition: lang.c:1466
int max_save
Definition: memory.c:56
constext txt_uu_error_decode[]
Definition: lang.c:912
constext txt_no_command[]
Definition: lang.c:1177
constext txt_view_attachment[]
Definition: lang.c:960
constext txt_saved[]
Definition: lang.c:822
struct t_group * curr_group
Definition: group.c:55
constext txt_attachment_saved[]
Definition: lang.c:87
struct t_config tinrc
Definition: init.c:192
unsigned short debug
Definition: debug.c:51
constext txt_mail_log_to[]
Definition: lang.c:620
constext txt_uu_error_no_end[]
Definition: lang.c:913
constext txt_starting_command[]
Definition: lang.c:858
struct t_screen * screen
Definition: screen.c:51
constext txt_cannot_write_to_directory[]
Definition: lang.c:135
t_bool no_write
Definition: init.c:145
struct t_cmdlineopts cmdline
Definition: init.c:190
constext txt_attachments_tagged[]
Definition: lang.c:91
struct t_group * active
Definition: memory.c:66
static FILE * pipe_fp
Definition: feed.c:55
static t_bool is_mailbox
Definition: feed.c:50
#define MAXKEYLEN
Definition: keymap.h:136
t_function prompt_slk_response(t_function default_func, const struct keylist keys, const char *fmt,...)
Definition: prompt.c:699
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
struct keylist attachment_keys
Definition: keymap.c:62
struct keylist save_append_overwrite_keys
Definition: keymap.c:87
@ GLOBAL_SCROLL_UP
Definition: keymap.h:214
@ POSTPROCESS_SHAR
Definition: keymap.h:329
@ DIGIT_7
Definition: keymap.h:157
@ SAVE_APPEND_FILE
Definition: keymap.h:333
@ GLOBAL_SHELL_ESCAPE
Definition: keymap.h:223
@ DIGIT_3
Definition: keymap.h:153
@ GLOBAL_PAGE_UP
Definition: keymap.h:201
@ ATTACHMENT_UNTAG
Definition: keymap.h:166
@ DIGIT_6
Definition: keymap.h:156
@ GLOBAL_PIPE
Definition: keymap.h:202
@ ATTACHMENT_SELECT
Definition: keymap.h:162
@ GLOBAL_SEARCH_SUBJECT_FORWARD
Definition: keymap.h:220
@ GLOBAL_LINE_DOWN
Definition: keymap.h:194
@ POSTPROCESS_YES
Definition: keymap.h:330
@ ATTACHMENT_PIPE
Definition: keymap.h:160
@ GLOBAL_SCROLL_DOWN
Definition: keymap.h:213
@ GLOBAL_HELP
Definition: keymap.h:191
@ DIGIT_2
Definition: keymap.h:152
@ GLOBAL_SEARCH_SUBJECT_BACKWARD
Definition: keymap.h:219
@ ATTACHMENT_TAG
Definition: keymap.h:163
@ GLOBAL_TOGGLE_HELP_DISPLAY
Definition: keymap.h:228
@ DIGIT_9
Definition: keymap.h:159
@ GLOBAL_PAGE_DOWN
Definition: keymap.h:200
@ ATTACHMENT_SAVE
Definition: keymap.h:161
@ SAVE_OVERWRITE_FILE
Definition: keymap.h:334
@ GLOBAL_ABORT
Definition: keymap.h:186
@ GLOBAL_SEARCH_REPEAT
Definition: keymap.h:216
@ GLOBAL_QUIT
Definition: keymap.h:210
@ GLOBAL_FIRST_PAGE
Definition: keymap.h:190
@ GLOBAL_REDRAW_SCREEN
Definition: keymap.h:212
@ DIGIT_8
Definition: keymap.h:158
@ GLOBAL_TOGGLE_INFO_LAST_LINE
Definition: keymap.h:229
@ GLOBAL_LAST_PAGE
Definition: keymap.h:192
@ DIGIT_1
Definition: keymap.h:151
@ ATTACHMENT_TOGGLE_TAGGED
Definition: keymap.h:165
@ GLOBAL_LINE_UP
Definition: keymap.h:195
@ DIGIT_4
Definition: keymap.h:154
@ DIGIT_5
Definition: keymap.h:155
@ ATTACHMENT_TAG_PATTERN
Definition: keymap.h:164
@ GLOBAL_BUGREPORT
Definition: keymap.h:187
char func_to_key(t_function func, const struct keylist keys)
Definition: keymap.c:125
enum defined_functions t_function
Definition: keymap.h:375
#define PrintFuncKey(buf, func, keys)
Definition: keymap.h:445
static char buf[16]
Definition: langinfo.c:50
static t_bool catchup
Definition: main.c:57
int errno
FILE * open_art_fp(struct t_group *group, t_artnum art)
Definition: rfc2046.c:1520
t_part * new_part(t_part *part)
Definition: rfc2046.c:792
void scroll_down(void)
Definition: global.c:252
void move_to_item(int n)
Definition: global.c:227
void page_up(void)
Definition: global.c:130
void prompt_item_num(int ch, const char *prompt)
Definition: global.c:200
void draw_arrow_mark(int line)
Definition: screen.c:352
int mmdecode(const char *what, int encoding, int delimiter, char *where)
Definition: rfc2047.c:147
void center_line(int line, t_bool inverse, const char *str)
Definition: screen.c:298
void show_mini_help(int level)
Definition: help.c:798
int strfmailer(const char *mail_prog, char *subject, char *to, const char *filename, char *dest, size_t maxsize, const char *format)
Definition: misc.c:1793
void prompt_continue(void)
Definition: prompt.c:803
char * prompt_string_default(const char *prompt, char *def, const char *failtext, int history)
Definition: prompt.c:587
void make_group_path(const char *name, char *path)
Definition: misc.c:2078
char * str_trim(char *string)
Definition: string.c:539
int content_type(char *type)
Definition: rfc2046.c:115
void show_help_page(const int level, const char *title)
Definition: help.c:734
const char * get_host_name(void)
Definition: header.c:52
t_bool index_group(struct t_group *group)
Definition: art.c:396
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:2656
void base_name(const char *fullpath, char *file)
Definition: misc.c:860
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:224
void art_mark(struct t_group *group, struct t_article *art, int flag)
Definition: newsrc.c:1611
void perror_message(const char *fmt,...)
Definition: screen.c:260
t_bool lookup_extension(char *extension, size_t ext_len, const char *major, const char *minor)
Definition: mimetypes.c:169
int sh_format(char *dst, size_t len, const char *fmt,...)
Definition: string.c:677
void ClearScreen(void)
Definition: curses.c:410
void do_shell_escape(void)
Definition: misc.c:547
void scroll_up(void)
Definition: global.c:278
void cursoroff(void)
Definition: curses.c:721
void info_message(const char *fmt,...)
Definition: screen.c:102
t_bool invoke_cmd(const char *nam)
Definition: misc.c:813
t_bool copy_fp(FILE *fp_ip, FILE *fp_op)
Definition: misc.c:179
void EndWin(void)
Definition: curses.c:368
t_bool match_regex(const char *string, char *pattern, struct regex_cache *cache, t_bool icase)
Definition: regex.c:59
void set_signal_catcher(int flag)
Definition: signal.c:527
void joinpath(char *result, size_t result_size, const char *dir, const char *file)
Definition: joinpath.c:50
const char * get_param(t_param *list, const char *name)
Definition: rfc2046.c:568
void toggle_mini_help(int level)
Definition: help.c:1078
void lookup_mimetype(const char *ext, t_part *part)
Definition: mimetypes.c:105
char * my_strdup(const char *str)
Definition: string.c:139
t_param * new_params(void)
Definition: rfc2046.c:527
int generic_search(t_bool forward, t_bool repeat, int current, int last, int level)
Definition: search.c:196
void Raw(int state)
Definition: curses.c:624
t_mailcap * get_mailcap_entry(t_part *part, const char *path)
Definition: rfc1524.c:68
void rename_file(const char *old_filename, const char *new_filename)
Definition: misc.c:742
int my_mkdir(char *path, mode_t mode)
Definition: misc.c:717
void expand_save(void)
Definition: memory.c:176
void page_down(void)
Definition: global.c:155
void move_up(void)
Definition: global.c:81
void set_xclick_off(void)
Definition: curses.c:703
void clear_note_area(void)
Definition: group.c:1051
void free_parts(t_part *ptr)
Definition: rfc2046.c:846
const char * get_filename(t_param *ptr)
Definition: cook.c:353
void InitWin(void)
Definition: curses.c:355
void move_down(void)
Definition: global.c:110
char * tin_fgets(FILE *fp, t_bool header)
Definition: read.c:317
int strwidth(const char *str)
Definition: string.c:1050
void set_first_screen_item(void)
Definition: global.c:61
int strfpath(const char *format, char *str, size_t maxsize, struct t_group *group, t_bool expand_all)
Definition: misc.c:1699
void end_of_list(void)
Definition: global.c:191
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
void my_strncpy(char *p, const char *q, size_t n)
Definition: string.c:196
t_bool compile_regex(const char *regex, struct regex_cache *cache, int options)
Definition: regex.c:111
void free_mailcap(t_mailcap *tmailcap)
Definition: rfc1524.c:475
int strcasecmp(const char *p, const char *q)
Definition: string.c:475
void top_of_list(void)
Definition: global.c:182
void strip_name(const char *from, char *address)
Definition: misc.c:2390
char * get_tmpfilename(const char *filename)
Definition: misc.c:101
long file_size(const char *file)
Definition: misc.c:2133
int prompt_yn(const char *prompt, t_bool default_answer)
Definition: prompt.c:165
char * fmt_string(const char *fmt,...)
Definition: string.c:1386
char * tin_ltoa(t_artnum value, int digits)
Definition: string.c:80
void bug_report(void)
Definition: global.c:430
#define ENCODING_BASE64
Definition: rfc2046.h:57
#define TYPE_MULTIPART
Definition: rfc2046.h:48
#define ENCODING_QP
Definition: rfc2046.h:56
#define ENCODING_UUE
Definition: rfc2046.h:60
#define MIME_SUPPORTED_VERSION
Definition: rfc2046.h:44
static void show_attachment_page(void)
Definition: save.c:1516
static t_bool check_save_mime_type(t_part *part, const char *mime_types)
Definition: save.c:1431
static void pipe_part(const char *savepath)
Definition: save.c:2362
static void process_parts(t_part *part, t_openartinfo *art, enum action what)
Definition: save.c:2166
static t_partl * part_list
Definition: save.c:114
#define DEC(Char)
Definition: save.c:1091
static void draw_attachment_arrow(void)
Definition: save.c:1774
action
Definition: save.c:64
@ VIEW
Definition: save.c:65
@ SAVE
Definition: save.c:66
@ PIPE
Definition: save.c:70
@ SAVE_TAGGED
Definition: save.c:67
@ PIPE_RAW
Definition: save.c:68
static t_menu attmenu
Definition: save.c:113
t_bool post_process_files(t_function proc_type_func, t_bool auto_delete)
Definition: save.c:717
static t_bool decode_save_one(t_part *part, FILE *rawfp, t_bool postproc)
Definition: save.c:1250
void decode_save_mime(t_openartinfo *art, t_bool postproc)
Definition: save.c:1472
static int info_len
Definition: save.c:112
t_part * get_part(int n)
Definition: save.c:1967
t_bool create_path(const char *path)
Definition: save.c:576
static void post_process_uud(void)
Definition: save.c:871
static t_partl * find_part(int n)
Definition: save.c:1950
static void view_file(const char *path, const char *file)
Definition: save.c:1064
static void build_attachment_line(int i)
Definition: save.c:1791
void attachment_page(t_openartinfo *art)
Definition: save.c:1569
static t_bool expand_save_filename(char *outpath, size_t outpath_len, const char *path)
Definition: save.c:681
static void start_viewer(t_part *part, const char *path)
Definition: save.c:1205
static void sum_file(const char *path, const char *file)
Definition: save.c:1022
static int build_part_list(t_openartinfo *art)
Definition: save.c:2109
static FILE * open_save_filename(const char *path, t_bool mbox)
Definition: save.c:387
static t_function attachment_left(void)
Definition: save.c:1758
static void post_process_sh(void)
Definition: save.c:1135
t_bool save_and_process_art(t_openartinfo *artinfo, t_bool is_mailbox, const char *inpath, int max, t_bool post_process)
Definition: save.c:468
static void uudecode_line(const char *buf, FILE *fp)
Definition: save.c:1096
static t_function attachment_right(void)
Definition: save.c:1766
static void free_part_list(t_partl *list)
Definition: save.c:2154
static int match_content_type(t_part *part, char *type)
Definition: save.c:1369
static void process_part(t_part *part, t_openartinfo *art, FILE *outfile, const char *savepath, enum action what)
Definition: save.c:2241
static t_bool tag_part(int n)
Definition: save.c:2051
match
Definition: save.c:1355
@ NO
Definition: save.c:1356
@ MATCH
Definition: save.c:1357
@ NOTMATCH
Definition: save.c:1358
static void untag_all_parts(void)
Definition: save.c:2090
static void generate_filename(char *buf, int buflen, const char *suffix)
Definition: save.c:616
int check_start_save_any_news(int function, t_bool catchup)
Definition: save.c:133
state
Definition: save.c:56
@ MIDDLE
Definition: save.c:58
@ INITIAL
Definition: save.c:57
@ OFF
Definition: save.c:59
@ END
Definition: save.c:60
static int num_of_tagged_parts
Definition: save.c:112
void print_art_separator_line(FILE *fp, t_bool is_mailbox)
Definition: save.c:1187
static void tag_pattern(void)
Definition: save.c:1978
static void untag_part(int n)
Definition: save.c:2068
static char * generate_savepath(t_part *part)
Definition: save.c:634
static char * build_tree(int depth, int maxlen, int i)
Definition: save.c:1870
static int get_tagged(int n)
Definition: save.c:2040
void(* func)(SIG_ARGS)
Definition: signal.c:176
const char * name
Definition: signal.c:117
FILE * cooked
Definition: rfc2046.h:189
struct t_header hdr
Definition: rfc2046.h:185
FILE * raw
Definition: rfc2046.h:188
char * name
Definition: rfc2046.h:78
char * value
Definition: rfc2046.h:79
Definition: rfc2046.h:93
long offset
Definition: rfc2046.h:103
unsigned type
Definition: rfc2046.h:94
char * subtype
Definition: rfc2046.h:100
int line_count
Definition: rfc2046.h:104
struct part * uue
Definition: rfc2046.h:106
int depth
Definition: rfc2046.h:105
char * description
Definition: rfc2046.h:101
t_param * params
Definition: rfc2046.h:102
unsigned encoding
Definition: rfc2046.h:95
struct part * next
Definition: rfc2046.h:107
t_part * part
Definition: rfc2046.h:117
int tagged
Definition: rfc2046.h:119
struct partlist * next
Definition: rfc2046.h:118
pcre_extra * extra
Definition: tin.h:1963
pcre * re
Definition: tin.h:1962
Definition: tin.h:1533
unsigned batch_save
Definition: tin.h:1658
char * mime_types_to_save
Definition: tin.h:1634
unsigned post_process_view
Definition: tin.h:1668
char * savedir
Definition: tin.h:1614
unsigned ask_for_metamail
Definition: tin.h:1654
char savedir[255]
Definition: tin.h:1502
unsigned int args
Definition: tin.h:1503
char default_pipe_command[LEN]
Definition: tinrc.h:85
int mailbox_format
Definition: tinrc.h:107
int interactive_mailer
Definition: tinrc.h:266
char savedir[PATH_LEN]
Definition: tinrc.h:138
int default_save_mode
Definition: tinrc.h:146
int wildcard
Definition: tinrc.h:167
char mm_local_charset[LEN]
Definition: tinrc.h:115
char mailer_format[PATH_LEN]
Definition: tinrc.h:83
char default_select_pattern[LEN]
Definition: tinrc.h:103
t_bool info_in_last_line
Definition: tinrc.h:226
t_bool beginner_level
Definition: tinrc.h:219
int score_select
Definition: tinrc.h:171
Definition: tin.h:1816
t_bool bogus
Definition: tin.h:1831
struct t_attribute * attribute
Definition: tin.h:1834
t_bool subscribed
Definition: tin.h:1829
char * name
Definition: tin.h:1817
char * subj
Definition: rfc2046.h:133
t_part * ext
Definition: rfc2046.h:146
char * from
Definition: rfc2046.h:128
char * description
Definition: tin.h:2100
char * command
Definition: tin.h:2097
char * nametemplate
Definition: tin.h:2102
t_bool needsterminal
Definition: tin.h:2107
Definition: tin.h:2055
int curr
Definition: tin.h:2056
int first
Definition: tin.h:2058
int max
Definition: tin.h:2057
char * path
Definition: tin.h:1967
char * file
Definition: tin.h:1968
t_bool mailbox
Definition: tin.h:1969
char * col
Definition: tin.h:1974
#define my_flush()
Definition: tcurses.h:177
#define cCRLF
Definition: tcurses.h:156
#define WriteLine(row, buffer)
Definition: tcurses.h:180
#define my_fputs(str, stream)
Definition: tcurses.h:159
#define my_retouch()
Definition: tcurses.h:179
#define my_printf
Definition: tcurses.h:175
#define my_fputc(ch, stream)
Definition: tcurses.h:158
#define PLURAL(x, y)
Definition: tin.h:1064
#define STRCMPEQ(s1, s2)
Definition: tin.h:822
#define LEN
Definition: tin.h:860
#define START_ANY_NEWS
Definition: tin.h:1233
#define SEEK_SET
Definition: tin.h:2512
#define TIN_FCLOSE(x)
Definition: tin.h:1048
#define TREE_BLANK
Definition: tin.h:940
#define TMPDIR
Definition: tin.h:2170
@ cAttachment
Definition: tin.h:107
#define DIRSEP
Definition: tin.h:2154
#define TREE_VERT
Definition: tin.h:943
#define STRCPY(dst, src)
Definition: tin.h:820
#define MIN(a, b)
Definition: tin.h:811
#define NEWS_AVAIL_EXIT
Definition: tin.h:1305
#define for_each_art(x)
Definition: tin.h:2260
#define NAME_LEN
Definition: tin.h:858
#define CAST_BOOL(value)
Definition: tin.h:1588
#define TREE_ARROW
Definition: tin.h:938
#define INDEX2LNUM(i)
Definition: tin.h:1020
#define PATHMASTER
Definition: tin.h:601
#define my_malloc(size)
Definition: tin.h:2245
#define unlink(file)
Definition: tin.h:387
#define S_IXUGO
Definition: tin.h:2212
#define EXIT_SUCCESS
Definition: tin.h:1298
#define S_ISDIR(m)
Definition: tin.h:2176
#define FreeIfNeeded(p)
Definition: tin.h:2252
#define DEFAULT_SAVEDIR
Definition: tin.h:638
#define INDEX2SNUM(i)
Definition: tin.h:1022
#define TREE_HORIZ
Definition: tin.h:941
#define MAIL_ANY_NEWS
Definition: tin.h:1234
#define REGEX_FMT
Definition: tin.h:1027
#define S_IRUSR
Definition: tin.h:2185
#define ATTACHMENT_LEVEL
Definition: tin.h:1120
#define _(Text)
Definition: tin.h:94
#define forever
Definition: tin.h:816
#define CMDLINE_SAVEDIR
Definition: tin.h:1105
#define PATH_LEN
Definition: tin.h:843
#define SAVE_ANY_NEWS
Definition: tin.h:1235
#define S_ISVTX
Definition: tin.h:2216
@ INTERACTIVE_NONE
Definition: tin.h:1169
@ INTERACTIVE_WITH_HEADERS
Definition: tin.h:1170
#define snprintf
Definition: tin.h:2464
#define FreeAndNull(p)
Definition: tin.h:2253
#define TREE_ARROW_WRAP
Definition: tin.h:939
#define TREE_VERT_RIGHT
Definition: tin.h:944
#define HEADER_LEN
Definition: tin.h:863
#define T_ARTNUM_PFMT
Definition: tin.h:230
#define INDEX_TOP
Definition: tin.h:1019
#define ART_READ
Definition: tin.h:1345
#define CHECK_ANY_NEWS
Definition: tin.h:1232
#define S_IRUGO
Definition: tin.h:2210
#define S_IWUSR
Definition: tin.h:2186
#define STRNCMPEQ(s1, s2, n)
Definition: tin.h:823
#define DEFAULT_SUM
Definition: tin.h:543
#define MMDFHDRTXT
Definition: tin.h:646
#define ART_UNREAD
Definition: tin.h:1346
#define TREE_UP_RIGHT
Definition: tin.h:942
#define BlankIfNull(p)
Definition: tin.h:2255
#define S_IRWXU
Definition: tin.h:2184
#define SAVEFILE_PREFIX
Definition: tin.h:641