1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * 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 REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 static int save1(char *str
, int domark
, char const *cmd
,
45 struct ignoretab
*ignore
, int convert
,
46 int sender_record
, int domove
);
47 static char * snarf(char *linebuf
, bool_t
*flag
, bool_t usembox
);
48 static int delm(int *msgvec
);
50 static void clob1(int n
);
52 static int ignore1(char **list
, struct ignoretab
*tab
, char const *which
);
53 static int igshow(struct ignoretab
*tab
, char const *which
);
54 static int igcomp(const void *l
, const void *r
);
55 static void unignore_one(const char *name
, struct ignoretab
*tab
);
56 static int unignore1(char **list
, struct ignoretab
*tab
,
60 * If any arguments were given, go to the next applicable argument
61 * following dot, otherwise, go to the next applicable message.
62 * If given as first command with no arguments, print first message.
75 * If some messages were supplied, find the
76 * first applicable one following dot using
80 mdot
= dot
- &message
[0] + 1;
83 * Find the first message in the supplied
84 * message list which follows dot.
87 for (ip
= msgvec
; *ip
!= 0; ip
++) {
90 * Work around an optimizer bug in Cray Standard C Version 4.0.3 (057126).
91 * Otherwise, SIGFPE is received when mb.mb_threaded != 0.
93 #pragma _CRI suppress ip
95 if (mb
.mb_threaded
? message
[*ip
-1].m_threadpos
>
104 mp
= &message
[*ip2
- 1];
105 if ((mp
->m_flag
& (MDELETED
|MHIDDEN
)) == 0) {
114 printf(tr(21, "No messages applicable\n"));
119 * If this is the first command, select message 1.
120 * Note that this must exist for us to get here at all.
130 * Just find the next good message after dot, no
134 if (mb
.mb_threaded
== 0) {
135 for (mp
= dot
+ did_print_dot
; mp
< &message
[msgCount
]; mp
++)
136 if ((mp
->m_flag
& (MDELETED
|MSAVED
|MHIDDEN
)) == 0)
141 mp
= next_in_thread(mp
);
142 while (mp
&& mp
->m_flag
& (MDELETED
|MSAVED
|MHIDDEN
))
143 mp
= next_in_thread(mp
);
145 if (mp
== NULL
|| mp
>= &message
[msgCount
]) {
147 printf(tr(22, "At EOF\n"));
156 list
[0] = dot
- &message
[0] + 1;
162 * Save a message in a file. Mark the message as saved
163 * so we can discard when the user quits.
170 return save1(str
, 1, "save", saveignore
, SEND_MBOX
, 0, 0);
178 return save1(str
, 1, "save", saveignore
, SEND_MBOX
, 1, 0);
182 * Copy a message to a file without affected its saved-ness
189 return save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 0, 0);
197 return save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 1, 0);
201 * Move a message to a file.
208 return save1(str
, 0, "move", saveignore
, SEND_MBOX
, 0, 1);
216 return save1(str
, 0, "move", saveignore
, SEND_MBOX
, 1, 1);
220 * Decrypt and copy a message to a file.
227 return save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 0, 0);
235 return save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 1, 0);
239 * Save/copy the indicated messages at the end of the passed file name.
240 * If mark is true, mark the message "saved."
243 save1(char *str
, int domark
, char const *cmd
, struct ignoretab
*ignoret
,
244 int convert
, int sender_record
, int domove
)
246 off_t mstats
[2], tstats
[2];
248 int newfile
= 0, compressed
= 0, success
= 1, last
= 0, *msgvec
, *ip
;
250 char *file
= NULL
, *cp
, *cq
;
251 char const *disp
= "";
257 msgvec
= (int *)salloc((msgCount
+ 2) * sizeof *msgvec
);
259 for (cp
= str
; *cp
&& blankchar(*cp
); cp
++)
263 if ((file
= snarf(str
, &f
, convert
!= SEND_TOFILE
)) == NULL
)
267 *msgvec
= first(0, MMNORM
);
271 printf(tr(23, "No messages to %s.\n"), cmd
);
275 } else if (getmsglist(str
, msgvec
, 0) < 0)
280 printf("No applicable messages.\n");
284 if ((cp
= nameof(&message
[*msgvec
- 1], 0)) == NULL
) {
286 "Cannot determine message sender to %s.\n"),
290 for (cq
= cp
; *cq
&& *cq
!= '@'; cq
++);
292 if (value("outfolder")) {
293 size_t sz
= strlen(cp
) + 1;
294 file
= salloc(sz
+ 1);
296 memcpy(file
+ 1, cp
, sz
);
300 if ((file
= expand(file
)) == NULL
)
302 prot
= which_protocol(file
);
303 if (prot
!= PROTO_IMAP
) {
304 if (access(file
, 0) >= 0) {
306 disp
= tr(25, "[Appended]");
309 disp
= tr(26, "[New file]");
312 if ((obuf
= convert
== SEND_TOFILE
? Fopen(file
, "a+") :
313 Zopen(file
, "a+", &compressed
)) == NULL
) {
314 if ((obuf
= convert
== SEND_TOFILE
? Fopen(file
, "wx") :
315 Zopen(file
, "wx", &compressed
)) == NULL
) {
322 disp
= tr(25, "[Appended]");
324 if (!newfile
&& fstat(fileno(obuf
), &st
) &&
325 S_ISREG(st
.st_mode
) &&
326 fseek(obuf
, -2L, SEEK_END
) == 0) {
330 switch (fread(buf
, sizeof *buf
, 2, obuf
)) {
332 if (buf
[1] != '\n') {
356 tstats
[0] = tstats
[1] = 0;
357 imap_created_mailbox
= 0;
359 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
360 mp
= &message
[*ip
- 1];
361 if (prot
== PROTO_IMAP
&&
362 ignoret
[0].i_count
== 0 &&
363 ignoret
[1].i_count
== 0
364 #ifdef HAVE_IMAP /* TODO revisit */
365 && imap_thisaccount(file
)
369 if (imap_copy(mp
, *ip
, file
) == STOP
)
374 mstats
[1] = mp
->m_xsize
;
376 } else if (sendmp(mp
, obuf
, ignoret
, NULL
,
377 convert
, mstats
) < 0) {
384 mp
->m_flag
|= MSAVED
;
386 mp
->m_flag
|= MDELETED
|MSAVED
;
389 tstats
[0] += mstats
[0];
390 tstats
[1] += mstats
[1];
398 if (Fclose(obuf
) != 0)
403 if (prot
== PROTO_IMAP
|| prot
== PROTO_MAILDIR
) {
406 ((prot
== PROTO_IMAP
) && disconnected(file
))
409 (imap_created_mailbox
? "[New file]"
412 printf("\"%s\" %s ", file
, disp
);
414 printf("%lu", (long)tstats
[0]);
416 printf(tr(27, "binary"));
417 printf("/%lu\n", (long)tstats
[1]);
419 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
420 mp
= &message
[*ip
- 1];
421 mp
->m_flag
&= ~MSAVED
;
424 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
425 mp
= &message
[*ip
- 1];
426 mp
->m_flag
&= ~(MSAVED
|MDELETED
);
429 if (domove
&& last
&& success
) {
430 setdot(&message
[last
-1]);
431 last
= first(0, MDELETED
);
432 setdot(&message
[last
? last
-1 : 0]);
434 return(success
== 0);
438 * Write the indicated messages at the end of the passed
439 * file name, minus header and trailing blank line.
440 * This is the MIME save function.
447 if (str
== NULL
|| *str
== '\0')
448 str
= savestr("/dev/null");
449 return (save1(str
, 0, "write", allignore
, SEND_TOFILE
, 0, 0));
453 * Snarf the file from the end of the command line and
454 * return a pointer to it. If there is no file attached,
455 * return the mbox file. Put a null in front of the file
456 * name so that the message list processing won't see it,
457 * unless the file name is the only thing on the line, in
458 * which case, return 0 in the reference flag variable.
461 snarf(char *linebuf
, bool_t
*flag
, bool_t usembox
)
465 if ((cp
= laststring(linebuf
, flag
, 0)) == NULL
) {
470 fprintf(stderr
, tr(28, "No file specified.\n"));
487 * Delete messages, then type the new dot.
496 lastdot
= dot
- &message
[0] + 1;
497 if (delm(msgvec
) >= 0) {
498 list
[0] = dot
- &message
[0] + 1;
499 if (list
[0] > lastdot
) {
504 printf(tr(29, "At EOF\n"));
506 printf(tr(30, "No more messages\n"));
511 * Delete the indicated messages.
512 * Set dot to some nice place afterwards.
513 * Internal interface.
523 for (ip
= msgvec
; *ip
!= 0; ip
++) {
524 mp
= &message
[*ip
- 1];
526 mp
->m_flag
|= MDELETED
|MTOUCH
;
527 mp
->m_flag
&= ~(MPRESERVE
|MSAVED
|MBOX
);
531 setdot(&message
[last
-1]);
532 last
= first(0, MDELETED
);
534 setdot(&message
[last
-1]);
544 * Following can't happen -- it keeps lint happy
551 * Undelete the indicated messages.
560 for (ip
= msgvec
; *ip
&& ip
-msgvec
< msgCount
; ip
++) {
561 mp
= &message
[*ip
- 1];
564 if (mp
->m_flag
& (MDELETED
|MSAVED
))
565 mp
->m_flag
&= ~(MDELETED
|MSAVED
);
567 mp
->m_flag
&= ~MDELETED
;
569 if (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)
570 imap_undelete(mp
, *ip
);
578 * Interactively dump core on "core"
586 extern int wait_status
;
590 switch (pid
= fork()) {
598 (void)printf(tr(31, "Okie dokie"));
599 (void)fflush(stdout
);
600 (void)wait_child(pid
);
602 if (WCOREDUMP(wait_status
))
603 (void)printf(tr(32, " -- Core dumped.\n"));
605 (void)printf(tr(33, " -- Can't dump core.\n"));
617 for (cp
= buf
; PTRCMP(cp
, <, buf
+ 512); ++cp
)
623 * Clobber as many bytes of stack as the user requests.
634 times
= (atoi(argv
[0]) + 511) / 512;
638 #endif /* HAVE_ASSERTS */
641 * Add the given header fields to the retained list.
642 * If no arguments, print the current list of retained fields.
649 return ignore1(list
, ignore
+ 1, "retained");
653 * Add the given header fields to the ignored list.
654 * If no arguments, print the current list of ignored fields.
661 return ignore1(list
, ignore
, "ignored");
665 saveretfield(void *v
)
669 return ignore1(list
, saveignore
+ 1, "retained");
677 return ignore1(list
, saveignore
, "ignored");
685 return ignore1(list
, fwdignore
+ 1, "retained");
693 return ignore1(list
, fwdignore
, "ignored");
697 ignore1(char **list
, struct ignoretab
*tab
, char const *which
)
704 return igshow(tab
, which
);
705 for (ap
= list
; *ap
!= 0; ap
++) {
710 field
= ac_alloc(sz
+ 1);
711 i_strcpy(field
, *ap
, sz
+ 1);
713 if (member(field
, tab
)) {
718 igp
= (struct ignore
*)scalloc(1, sizeof (struct ignore
));
719 sz
= strlen(field
) + 1;
720 igp
->i_field
= smalloc(sz
);
721 memcpy(igp
->i_field
, field
, sz
);
722 igp
->i_link
= tab
->i_head
[h
];
723 tab
->i_head
[h
] = igp
;
731 * Print out all currently retained fields.
734 igshow(struct ignoretab
*tab
, char const *which
)
740 if (tab
->i_count
== 0) {
741 printf(tr(34, "No fields currently being %s.\n"), which
);
745 ring
= (char **)salloc((tab
->i_count
+ 1) * sizeof (char *));
747 for (h
= 0; h
< HSHSIZE
; h
++)
748 for (igp
= tab
->i_head
[h
]; igp
!= 0; igp
= igp
->i_link
)
749 *ap
++ = igp
->i_field
;
751 qsort(ring
, tab
->i_count
, sizeof (char *), igcomp
);
752 for (ap
= ring
; *ap
!= 0; ap
++)
758 * Compare two names for sorting ignored field list.
761 igcomp(const void *l
, const void *r
)
763 return (strcmp(*(char**)UNCONST(l
), *(char**)UNCONST(r
)));
769 return unignore1((char **)v
, ignore
, "ignored");
775 return unignore1((char **)v
, ignore
+ 1, "retained");
779 unsaveignore(void *v
)
781 return unignore1((char **)v
, saveignore
, "ignored");
785 unsaveretain(void *v
)
787 return unignore1((char **)v
, saveignore
+ 1, "retained");
793 return unignore1((char **)v
, fwdignore
, "ignored");
799 return unignore1((char **)v
, fwdignore
+ 1, "retained");
803 unignore_one(const char *name
, struct ignoretab
*tab
)
805 struct ignore
*ip
, *iq
= NULL
;
808 for (ip
= tab
->i_head
[h
]; ip
; ip
= ip
->i_link
) {
809 if (asccasecmp(ip
->i_field
, name
) == 0) {
812 iq
->i_link
= ip
->i_link
;
814 tab
->i_head
[h
] = ip
->i_link
;
824 unignore1(char **list
, struct ignoretab
*tab
, char const *which
)
826 if (tab
->i_count
== 0) {
827 printf(tr(34, "No fields currently being %s.\n"), which
);
831 unignore_one(*list
++, tab
);