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 - 2016 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
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
);
59 save1(char *str
, int domark
, char const *cmd
, struct ignoretab
*ignoret
,
60 int convert
, int sender_record
, int domove
)
62 ui64_t mstats
[1], tstats
[2];
64 int last
= 0, *msgvec
, *ip
;
66 char *file
= NULL
, *cp
, *cq
;
67 char const *disp
= "", *shell
= NULL
;
69 bool_t success
= FAL0
, isflag
;
72 msgvec
= salloc((msgCount
+ 2) * sizeof *msgvec
);
74 for (cp
= str
; *cp
!= '\0' && spacechar(*cp
); ++cp
)
76 isflag
= (*cp
!= '\0');
78 if ((file
= snarf(str
, &isflag
, convert
!= SEND_TOFILE
)) == NULL
)
80 while(spacechar(*file
))
84 shell
= ok_vlook(SHELL
);
89 *msgvec
= first(0, MMNORM
);
91 } else if (getmsglist(str
, msgvec
, 0) < 0)
94 if (pstate
& (PS_HOOK_MASK
| PS_ROBOT
)) {
98 printf(_("No messages to %s.\n"), cmd
);
103 if ((cp
= nameof(message
+ *msgvec
- 1, 0)) == NULL
) {
104 printf(_("Cannot determine message sender to %s.\n"), cmd
);
108 for (cq
= cp
; *cq
!= '\0' && *cq
!= '@'; cq
++)
111 if (ok_blook(outfolder
)) {
112 size_t sz
= strlen(cp
) +1;
113 file
= salloc(sz
+ 1);
115 memcpy(file
+ 1, cp
, sz
);
120 /* Pipe target is special TODO hacked in later, normalize flow! */
122 if ((obuf
= Popen(file
, "w", shell
, NULL
, 1)) == NULL
) {
134 if ((file
= expand(file
)) == NULL
)
137 obuf
= ((convert
== SEND_TOFILE
) ? Fopen(file
, "a+") : Zopen(file
, "a+"));
139 obuf
= ((convert
== SEND_TOFILE
) ? Fopen(file
, "wx") : Zopen(file
, "wx"));
145 disp
= _("[New file]");
148 disp
= _("[Appended]");
151 /* TODO RETURN check, but be aware of protocols: v15: Mailbox->lock()! */
152 n_file_lock(fileno(obuf
), FLT_WRITE
, 0,0, UIZ_MAX
);
154 if (!isflag
&& !fstat(fileno(obuf
), &st
) && S_ISREG(st
.st_mode
) &&
155 fseek(obuf
, -2L, SEEK_END
) == 0) {
159 switch (fread(buf
, sizeof *buf
, 2, obuf
)) {
161 if (buf
[1] != '\n') {
187 tstats
[0] = tstats
[1] = 0;
190 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
192 mp
= message
+ *ip
- 1;
193 if (sendmp(mp
, obuf
, ignoret
, NULL
, convert
, mstats
) < 0) {
201 mp
->m_flag
|= MSAVED
;
203 mp
->m_flag
|= MDELETED
| MSAVED
;
207 tstats
[0] += mstats
[0];
208 tstats
[1] += mp
->m_lines
;/* TODO won't work, need target! v15!! */
221 if (!Pclose(obuf
, TRU1
))
223 } else if (Fclose(obuf
) != 0)
227 printf("%s %s %" /*PRIu64 "/%"*/ PRIu64
" bytes\n",
228 n_shexp_quote_cp(file
, FAL0
), disp
,
229 /*tstats[1], TODO v15: lines written */ tstats
[0]);
231 for (ip
= msgvec
; *ip
!= 0 &&
232 UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
); ++ip
) {
233 mp
= message
+ *ip
- 1;
234 mp
->m_flag
&= ~MSAVED
;
237 for (ip
= msgvec
; *ip
!= 0 &&
238 UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
); ++ip
) {
239 mp
= message
+ *ip
- 1;
240 mp
->m_flag
&= ~(MSAVED
| MDELETED
);
244 if (domove
&& last
&& success
) {
245 setdot(message
+ last
- 1);
246 last
= first(0, MDELETED
);
247 setdot(message
+ (last
!= 0 ? last
- 1 : 0));
251 return (success
== FAL0
);
255 snarf(char *linebuf
, bool_t
*flag
, bool_t usembox
)
260 if ((cp
= laststring(linebuf
, flag
, TRU1
)) == NULL
) {
265 n_err(_("No file specified\n"));
275 int rv
= -1, *ip
, last
;
279 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
280 mp
= message
+ *ip
- 1;
282 mp
->m_flag
|= MDELETED
| MTOUCH
;
283 mp
->m_flag
&= ~(MPRESERVE
| MSAVED
| MBOX
);
287 setdot(message
+ last
- 1);
288 last
= first(0, MDELETED
);
290 setdot(message
+ last
- 1);
303 int list
[2], *ip
, *ip2
, mdot
, *msgvec
= v
, rv
= 1;
308 /* If some messages were supplied, find the first applicable one
309 * following dot using wrap around */
310 mdot
= (int)PTR2SIZE(dot
- message
+ 1);
312 /* Find first message in supplied message list which follows dot */
313 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
314 if ((mb
.mb_threaded
? message
[*ip
- 1].m_threadpos
> dot
->m_threadpos
322 mp
= message
+ *ip2
- 1;
323 if (!(mp
->m_flag
& MMNDEL
)) {
332 printf(_("No messages applicable\n"));
336 /* If this is the first command, select message 1. Note that this must
337 * exist for us to get here at all */
338 if (!(pstate
& PS_SAW_COMMAND
)) {
344 /* Just find the next good message after dot, no wraparound */
345 if (mb
.mb_threaded
== 0) {
346 for (mp
= dot
+ !!(pstate
& PS_DID_PRINT_DOT
);
347 PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
348 if (!(mp
->m_flag
& MMNORM
))
351 /* TODO The threading code had some bugs that caused crashes.
352 * TODO The last thing (before the deep look) happens here,
353 * TODO so let's not trust PS_DID_PRINT_DOT but check & hope it fixes */
354 if ((mp
= dot
) != NULL
&& (pstate
& PS_DID_PRINT_DOT
))
355 mp
= next_in_thread(mp
);
356 while (mp
!= NULL
&& (mp
->m_flag
& MMNORM
))
357 mp
= next_in_thread(mp
);
359 if (mp
== NULL
|| PTRCMP(mp
, >=, message
+ msgCount
)) {
361 printf(_("At EOF\n"));
369 list
[0] = (int)PTR2SIZE(dot
- message
+ 1);
384 if (*(args
= v
) == '\0' || args
[1] != '\0') {
386 n_err(_("Synopsis: dotmove: up <-> or down <+> by one message\n"));
388 } else switch (args
[0]) {
392 printf(_("At EOF\n"));
394 } else if (getmsglist(UNCONST(/*TODO*/ args
), msgvec
, 0) > 0) {
395 setdot(message
+ msgvec
[0] - 1);
397 rv
= c_headers(msgvec
);
415 rv
= save1(str
, 1, "save", saveignore
, SEND_MBOX
, 0, 0);
427 rv
= save1(str
, 1, "save", saveignore
, SEND_MBOX
, 1, 0);
439 rv
= save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 0, 0);
451 rv
= save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 1, 0);
463 rv
= save1(str
, 0, "move", saveignore
, SEND_MBOX
, 0, 1);
475 rv
= save1(str
, 0, "move", saveignore
, SEND_MBOX
, 1, 1);
487 rv
= save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 0, 0);
499 rv
= save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 1, 0);
511 if (str
== NULL
|| *str
== '\0')
512 str
= savestr("/dev/null");
513 rv
= save1(str
, 0, "write", allignore
, SEND_TOFILE
, 0, 0);
532 int list
[2], rv
= 0, *msgvec
= v
, lastdot
;
535 lastdot
= (int)PTR2SIZE(dot
- message
+ 1);
536 if (delm(msgvec
) >= 0) {
537 list
[0] = (int)PTR2SIZE(dot
- message
+ 1);
538 if (list
[0] > lastdot
) {
544 printf(_("At EOF\n"));
546 printf(_("No more messages\n"));
555 int *msgvec
= v
, *ip
;
559 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
561 mp
= message
+ *ip
- 1;
564 if (mp
->m_flag
& (MDELETED
| MSAVED
))
565 mp
->m_flag
&= ~(MDELETED
| MSAVED
);
567 mp
->m_flag
&= ~MDELETED
;
576 int *msgvec
= v
, *ip
;
579 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
580 setdot(message
+ *ip
- 1);
581 dot
->m_flag
|= MTOUCH
;
582 dot
->m_flag
&= ~MPRESERVE
;
583 pstate
|= PS_DID_PRINT_DOT
;
592 int *msgvec
= v
, *ip
;
595 if (pstate
& PS_EDIT
) {
596 n_err(_("`mbox' can only be used in a system mailbox\n")); /* TODO */
600 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
601 setdot(message
+ *ip
- 1);
602 dot
->m_flag
|= MTOUCH
| MBOX
;
603 dot
->m_flag
&= ~MPRESERVE
;
604 pstate
|= PS_DID_PRINT_DOT
;
614 int *msgvec
= v
, *ip
, mesg
, rv
= 1;
618 if (pstate
& PS_EDIT
) {
619 printf(_("Cannot `preserve' in a system mailbox\n"));
623 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
625 mp
= message
+ mesg
- 1;
626 mp
->m_flag
|= MPRESERVE
;
629 pstate
|= PS_DID_PRINT_DOT
;
640 int *msgvec
= v
, *ip
;
643 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
644 setdot(message
+ *ip
- 1);
645 dot
->m_flag
&= ~(MREAD
| MTOUCH
);
646 dot
->m_flag
|= MSTATUS
;
647 pstate
|= PS_DID_PRINT_DOT
;
656 int *msgvec
= v
, *ip
;
659 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
660 struct message
*mp
= message
+ *ip
- 1;
672 int *msgvec
= v
, *ip
;
675 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
676 m
= message
+ *ip
- 1;
678 if (!(m
->m_flag
& (MFLAG
| MFLAGGED
)))
679 m
->m_flag
|= MFLAG
| MFLAGGED
;
689 int *msgvec
= v
, *ip
;
692 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
693 m
= message
+ *ip
- 1;
695 if (m
->m_flag
& (MFLAG
| MFLAGGED
)) {
696 m
->m_flag
&= ~(MFLAG
| MFLAGGED
);
697 m
->m_flag
|= MUNFLAG
;
708 int *msgvec
= v
, *ip
;
711 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
712 m
= message
+ *ip
- 1;
714 if (!(m
->m_flag
& (MANSWER
| MANSWERED
)))
715 m
->m_flag
|= MANSWER
| MANSWERED
;
722 c_unanswered(void *v
)
725 int *msgvec
= v
, *ip
;
728 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
729 m
= message
+ *ip
- 1;
731 if (m
->m_flag
& (MANSWER
| MANSWERED
)) {
732 m
->m_flag
&= ~(MANSWER
| MANSWERED
);
733 m
->m_flag
|= MUNANSWER
;
744 int *msgvec
= v
, *ip
;
747 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
748 m
= message
+ *ip
- 1;
750 if (!(m
->m_flag
& (MDRAFT
| MDRAFTED
)))
751 m
->m_flag
|= MDRAFT
| MDRAFTED
;
761 int *msgvec
= v
, *ip
;
764 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
765 m
= message
+ *ip
- 1;
767 if (m
->m_flag
& (MDRAFT
| MDRAFTED
)) {
768 m
->m_flag
&= ~(MDRAFT
| MDRAFTED
);
769 m
->m_flag
|= MUNDRAFT
;