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 - 2015 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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #ifndef HAVE_AMALGAMATION
42 /* Save/copy the indicated messages at the end of the passed file name.
43 * If mark is true, mark the message "saved" */
44 static int save1(char *str
, int domark
, char const *cmd
,
45 struct ignoretab
*ignoret
, int convert
, int sender_record
,
48 /* Snarf the file from the end of the command line and return a pointer to it.
49 * If there is no file attached, return the mbox file. Put a null in front of
50 * the file name so that the message list processing won't see it, unless the
51 * file name is the only thing on the line, in which case, return 0 in the
52 * reference flag variable */
53 static char * snarf(char *linebuf
, bool_t
*flag
, bool_t usembox
);
55 /* Delete the indicated messages. Set dot to some nice place afterwards */
56 static int delm(int *msgvec
);
58 static int ignore1(char **list
, struct ignoretab
*tab
, char const *which
);
60 /* Print out all currently retained fields */
61 static int igshow(struct ignoretab
*tab
, char const *which
);
63 /* Compare two names for sorting ignored field list */
64 static int igcomp(void const *l
, void const *r
);
67 static int _unignore(char **list
, struct ignoretab
*tab
, char const *which
);
68 static void __unign_all(struct ignoretab
*tab
);
69 static void __unign_one(struct ignoretab
*tab
, char const *name
);
72 save1(char *str
, int domark
, char const *cmd
, struct ignoretab
*ignoret
,
73 int convert
, int sender_record
, int domove
)
75 ui64_t mstats
[1], tstats
[2];
77 int newfile
= 0, last
= 0, *msgvec
, *ip
;
79 char *file
= NULL
, *cp
, *cq
;
80 char const *disp
= "";
82 bool_t success
= FAL0
, f
;
85 msgvec
= salloc((msgCount
+ 2) * sizeof *msgvec
);
87 for (cp
= str
; *cp
!= '\0' && blankchar(*cp
); ++cp
)
91 if ((file
= snarf(str
, &f
, convert
!= SEND_TOFILE
)) == NULL
)
96 *msgvec
= first(0, MMNORM
);
98 } else if (getmsglist(str
, msgvec
, 0) < 0)
101 if (pstate
& PS_HOOK_MASK
) {
105 printf(_("No messages to %s.\n"), cmd
);
110 if ((cp
= nameof(message
+ *msgvec
- 1, 0)) == NULL
) {
111 printf(_("Cannot determine message sender to %s.\n"), cmd
);
115 for (cq
= cp
; *cq
!= '\0' && *cq
!= '@'; cq
++)
118 if (ok_blook(outfolder
)) {
119 size_t sz
= strlen(cp
) +1;
120 file
= salloc(sz
+ 1);
122 memcpy(file
+ 1, cp
, sz
);
127 if ((file
= expand(file
)) == NULL
)
129 if (access(file
, F_OK
) >= 0) {
131 disp
= _("[Appended]");
134 disp
= _("[New file]");
137 obuf
= ((convert
== SEND_TOFILE
) ? Fopen(file
, "a+") : Zopen(file
, "a+"));
139 obuf
= ((convert
== SEND_TOFILE
) ? Fopen(file
, "wx") : Zopen(file
, "wx"));
145 if (!newfile
&& !fstat(fileno(obuf
), &st
) && S_ISREG(st
.st_mode
) &&
146 fseek(obuf
, -2L, SEEK_END
) == 0) {
150 switch (fread(buf
, sizeof *buf
, 2, obuf
)) {
152 if (buf
[1] != '\n') {
178 tstats
[0] = tstats
[1] = 0;
181 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
183 mp
= message
+ *ip
- 1;
184 if (sendmp(mp
, obuf
, ignoret
, NULL
, convert
, mstats
) < 0) {
192 mp
->m_flag
|= MSAVED
;
194 mp
->m_flag
|= MDELETED
| MSAVED
;
198 tstats
[0] += mstats
[0];
199 tstats
[1] += mp
->m_lines
;/* TODO won't work, need target! v15!! */
211 if (Fclose(obuf
) != 0)
215 printf("\"%s\" %s %" /*PRIu64 "/%"*/ PRIu64
" bytes\n",
216 file
, disp
, /*tstats[1], TODO v15: lines written */ tstats
[0]);
221 newfile
= ~(MSAVED
| MDELETED
);
223 for (ip
= msgvec
; *ip
!= 0 &&
224 UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
); ++ip
) {
225 mp
= message
+ *ip
- 1;
226 mp
->m_flag
&= newfile
;
230 if (domove
&& last
&& success
) {
231 setdot(message
+ last
- 1);
232 last
= first(0, MDELETED
);
233 setdot(message
+ (last
!= 0 ? last
- 1 : 0));
237 return (success
== FAL0
);
241 snarf(char *linebuf
, bool_t
*flag
, bool_t usembox
)
246 if ((cp
= laststring(linebuf
, flag
, FAL0
)) == NULL
) {
251 n_err(_("No file specified\n"));
261 int rv
= -1, *ip
, last
;
265 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
266 mp
= message
+ *ip
- 1;
268 mp
->m_flag
|= MDELETED
| MTOUCH
;
269 mp
->m_flag
&= ~(MPRESERVE
| MSAVED
| MBOX
);
273 setdot(message
+ last
- 1);
274 last
= first(0, MDELETED
);
276 setdot(message
+ last
- 1);
287 ignore1(char **list
, struct ignoretab
*tab
, char const *which
)
295 h
= igshow(tab
, which
);
299 for (ap
= list
; *ap
!= 0; ++ap
) {
304 field
= ac_alloc(sz
);
305 i_strcpy(field
, *ap
, sz
);
306 if (member(field
, tab
))
310 igp
= scalloc(1, sizeof *igp
);
311 sz
= strlen(field
) +1;
312 igp
->i_field
= smalloc(sz
);
313 memcpy(igp
->i_field
, field
, sz
);
314 igp
->i_link
= tab
->i_head
[h
];
315 tab
->i_head
[h
] = igp
;
327 igshow(struct ignoretab
*tab
, char const *which
)
334 if (tab
->i_count
== 0) {
335 printf(_("No fields currently being %s.\n"), which
);
339 ring
= salloc((tab
->i_count
+ 1) * sizeof *ring
);
341 for (h
= 0; h
< HSHSIZE
; ++h
)
342 for (igp
= tab
->i_head
[h
]; igp
!= 0; igp
= igp
->i_link
)
343 *ap
++ = igp
->i_field
;
346 qsort(ring
, tab
->i_count
, sizeof *ring
, igcomp
);
348 for (ap
= ring
; *ap
!= NULL
; ++ap
)
356 igcomp(void const *l
, void const *r
)
361 rv
= strcmp(*(char**)UNCONST(l
), *(char**)UNCONST(r
));
367 _unignore(char **list
, struct ignoretab
*tab
, char const *which
)
372 if (tab
->i_count
== 0)
373 printf(_("No fields currently being %s.\n"), which
);
375 while ((field
= *list
++) != NULL
)
376 if (field
[0] == '*' && field
[1] == '\0') {
380 __unign_one(tab
, field
);
386 __unign_all(struct ignoretab
*tab
)
389 struct ignored
*n
, *x
;
392 for (i
= 0; i
< NELEM(tab
->i_head
); ++i
)
393 for (n
= tab
->i_head
[i
]; n
!= NULL
; n
= x
) {
398 memset(tab
, 0, sizeof *tab
);
403 __unign_one(struct ignoretab
*tab
, char const *name
)
405 struct ignored
*ip
, *iq
;
410 for (iq
= NULL
, ip
= tab
->i_head
[h
]; ip
!= NULL
; ip
= ip
->i_link
) {
411 if (!asccasecmp(ip
->i_field
, name
)) {
414 iq
->i_link
= ip
->i_link
;
416 tab
->i_head
[h
] = ip
->i_link
;
429 int list
[2], *ip
, *ip2
, mdot
, *msgvec
= v
, rv
= 1;
434 /* If some messages were supplied, find the first applicable one
435 * following dot using wrap around */
436 mdot
= (int)PTR2SIZE(dot
- message
+ 1);
438 /* Find first message in supplied message list which follows dot */
439 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
440 if ((mb
.mb_threaded
? message
[*ip
- 1].m_threadpos
> dot
->m_threadpos
448 mp
= message
+ *ip2
- 1;
449 if (!(mp
->m_flag
& MMNDEL
)) {
458 printf(_("No messages applicable\n"));
462 /* If this is the first command, select message 1. Note that this must
463 * exist for us to get here at all */
464 if (!(pstate
& PS_SAW_COMMAND
)) {
470 /* Just find the next good message after dot, no wraparound */
471 if (mb
.mb_threaded
== 0) {
472 for (mp
= dot
+ !!(pstate
& PS_DID_PRINT_DOT
);
473 PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
474 if (!(mp
->m_flag
& MMNORM
))
477 /* TODO The threading code had some bugs that caused crashes.
478 * TODO The last thing (before the deep look) happens here,
479 * TODO so let's not trust PS_DID_PRINT_DOT but check & hope it fixes */
480 if ((mp
= dot
) != NULL
&& (pstate
& PS_DID_PRINT_DOT
))
481 mp
= next_in_thread(mp
);
482 while (mp
!= NULL
&& (mp
->m_flag
& MMNORM
))
483 mp
= next_in_thread(mp
);
485 if (mp
== NULL
|| PTRCMP(mp
, >=, message
+ msgCount
)) {
487 printf(_("At EOF\n"));
495 list
[0] = (int)PTR2SIZE(dot
- message
+ 1);
510 rv
= save1(str
, 1, "save", saveignore
, SEND_MBOX
, 0, 0);
522 rv
= save1(str
, 1, "save", saveignore
, SEND_MBOX
, 1, 0);
534 rv
= save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 0, 0);
546 rv
= save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 1, 0);
558 rv
= save1(str
, 0, "move", saveignore
, SEND_MBOX
, 0, 1);
570 rv
= save1(str
, 0, "move", saveignore
, SEND_MBOX
, 1, 1);
582 rv
= save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 0, 0);
594 rv
= save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 1, 0);
606 if (str
== NULL
|| *str
== '\0')
607 str
= savestr("/dev/null");
608 rv
= save1(str
, 0, "write", allignore
, SEND_TOFILE
, 0, 0);
627 int list
[2], rv
= 0, *msgvec
= v
, lastdot
;
630 lastdot
= (int)PTR2SIZE(dot
- message
+ 1);
631 if (delm(msgvec
) >= 0) {
632 list
[0] = (int)PTR2SIZE(dot
- message
+ 1);
633 if (list
[0] > lastdot
) {
639 printf(_("At EOF\n"));
641 printf(_("No more messages\n"));
650 int *msgvec
= v
, *ip
;
654 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
656 mp
= message
+ *ip
- 1;
659 if (mp
->m_flag
& (MDELETED
| MSAVED
))
660 mp
->m_flag
&= ~(MDELETED
| MSAVED
);
662 mp
->m_flag
&= ~MDELETED
;
675 rv
= ignore1(list
, ignore
+ 1, "retained");
687 rv
= ignore1(list
, ignore
, "ignored");
693 c_saveretfield(void *v
)
699 rv
= ignore1(list
, saveignore
+ 1, "retained");
705 c_saveigfield(void *v
)
711 rv
= ignore1(list
, saveignore
, "ignored");
717 c_fwdretfield(void *v
)
723 rv
= ignore1(list
, fwdignore
+ 1, "retained");
729 c_fwdigfield(void *v
)
735 rv
= ignore1(list
, fwdignore
, "ignored");
746 rv
= _unignore((char**)v
, ignore
, "ignored");
757 rv
= _unignore((char**)v
, ignore
+ 1, "retained");
763 c_unsaveignore(void *v
)
768 rv
= _unignore((char**)v
, saveignore
, "ignored");
774 c_unsaveretain(void *v
)
779 rv
= _unignore((char**)v
, saveignore
+ 1, "retained");
785 c_unfwdignore(void *v
)
790 rv
= _unignore((char**)v
, fwdignore
, "ignored");
796 c_unfwdretain(void *v
)
801 rv
= _unignore((char**)v
, fwdignore
+ 1, "retained");