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 - 2014 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
40 #ifndef HAVE_AMALGAMATION
46 /* Save/copy the indicated messages at the end of the passed file name.
47 * If mark is true, mark the message "saved" */
48 static int save1(char *str
, int domark
, char const *cmd
,
49 struct ignoretab
*ignore
, int convert
, int sender_record
,
52 /* Snarf the file from the end of the command line and return a pointer to it.
53 * If there is no file attached, return the mbox file. Put a null in front of
54 * the file name so that the message list processing won't see it, unless the
55 * file name is the only thing on the line, in which case, return 0 in the
56 * reference flag variable */
57 static char * snarf(char *linebuf
, bool_t
*flag
, bool_t usembox
);
59 /* Delete the indicated messages. Set dot to some nice place afterwards */
60 static int delm(int *msgvec
);
62 static int ignore1(char **list
, struct ignoretab
*tab
, char const *which
);
64 /* Print out all currently retained fields */
65 static int igshow(struct ignoretab
*tab
, char const *which
);
67 /* Compare two names for sorting ignored field list */
68 static int igcomp(const void *l
, const void *r
);
70 static void unignore_one(char const *name
, struct ignoretab
*tab
);
71 static int unignore1(char **list
, struct ignoretab
*tab
, char const *which
);
74 save1(char *str
, int domark
, char const *cmd
, struct ignoretab
*ignoret
,
75 int convert
, int sender_record
, int domove
)
77 off_t mstats
[2], tstats
[2];
79 int newfile
= 0, compressed
= 0, last
= 0, *msgvec
, *ip
;
81 char *file
= NULL
, *cp
, *cq
;
82 char const *disp
= "";
85 bool_t success
= FAL0
, f
;
88 msgvec
= salloc((msgCount
+ 2) * sizeof *msgvec
);
90 for (cp
= str
; *cp
!= '\0' && blankchar(*cp
); ++cp
)
94 if ((file
= snarf(str
, &f
, convert
!= SEND_TOFILE
)) == NULL
)
99 *msgvec
= first(0, MMNORM
);
105 printf(tr(23, "No messages to %s.\n"), cmd
);
109 } else if (getmsglist(str
, msgvec
, 0) < 0)
116 printf("No applicable messages.\n");
121 if ((cp
= nameof(&message
[*msgvec
- 1], 0)) == NULL
) {
122 printf(tr(24, "Cannot determine message sender to %s.\n"), cmd
);
126 for (cq
= cp
; *cq
!= '\0' && *cq
!= '@'; cq
++)
129 if (ok_blook(outfolder
)) {
130 size_t sz
= strlen(cp
) + 1;
131 file
= salloc(sz
+ 1);
133 memcpy(file
+ 1, cp
, sz
);
138 if ((file
= expand(file
)) == NULL
)
140 prot
= which_protocol(file
);
141 if (prot
!= PROTO_IMAP
) {
142 if (access(file
, 0) >= 0) {
144 disp
= tr(25, "[Appended]");
147 disp
= tr(26, "[New file]");
151 obuf
= ((convert
== SEND_TOFILE
) ? Fopen(file
, "a+")
152 : Zopen(file
, "a+", &compressed
));
154 obuf
= ((convert
== SEND_TOFILE
) ? Fopen(file
, "wx")
155 : Zopen(file
, "wx", &compressed
));
163 disp
= tr(25, "[Appended]");
165 if (!newfile
&& fstat(fileno(obuf
), &st
) && S_ISREG(st
.st_mode
) &&
166 fseek(obuf
, -2L, SEEK_END
) == 0) {
170 switch (fread(buf
, sizeof *buf
, 2, obuf
)) {
172 if (buf
[1] != '\n') {
197 tstats
[0] = tstats
[1] = 0;
198 imap_created_mailbox
= 0;
200 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
202 mp
= &message
[*ip
- 1];
203 if (prot
== PROTO_IMAP
&& ignoret
[0].i_count
== 0 &&
204 ignoret
[1].i_count
== 0
205 #ifdef HAVE_IMAP /* TODO revisit */
206 && imap_thisaccount(file
)
210 if (imap_copy(mp
, *ip
, file
) == STOP
)
215 mstats
[1] = mp
->m_xsize
;
217 } else if (sendmp(mp
, obuf
, ignoret
, NULL
, convert
, mstats
) < 0) {
224 mp
->m_flag
|= MSAVED
;
226 mp
->m_flag
|= MDELETED
| MSAVED
;
229 tstats
[0] += mstats
[0];
230 tstats
[1] += mstats
[1];
238 if (Fclose(obuf
) != 0)
243 if (prot
== PROTO_IMAP
|| prot
== PROTO_MAILDIR
) {
246 ((prot
== PROTO_IMAP
) && disconnected(file
)) ? "[Queued]" :
248 (imap_created_mailbox
? "[New file]" : "[Appended]"));
250 printf("\"%s\" %s ", file
, disp
);
252 printf("%lu", (ul_it
)tstats
[0]);
254 printf(tr(27, "binary"));
255 printf("/%lu\n", (ul_it
)tstats
[1]);
260 newfile
= ~(MSAVED
| MDELETED
);
262 for (ip
= msgvec
; *ip
!= 0 &&
263 UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
); ++ip
) {
264 mp
= &message
[*ip
- 1];
265 mp
->m_flag
&= newfile
;
269 if (domove
&& last
&& success
) {
270 setdot(&message
[last
- 1]);
271 last
= first(0, MDELETED
);
272 setdot(&message
[last
? last
- 1 : 0]);
276 return (success
== FAL0
);
280 snarf(char *linebuf
, bool_t
*flag
, bool_t usembox
)
285 if ((cp
= laststring(linebuf
, flag
, 0)) == NULL
) {
290 fprintf(stderr
, tr(28, "No file specified.\n"));
300 int rv
= -1, *ip
, last
;
304 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
305 mp
= &message
[*ip
- 1];
307 mp
->m_flag
|= MDELETED
| MTOUCH
;
308 mp
->m_flag
&= ~(MPRESERVE
| MSAVED
| MBOX
);
312 setdot(&message
[last
- 1]);
313 last
= first(0, MDELETED
);
315 setdot(&message
[last
- 1]);
326 ignore1(char **list
, struct ignoretab
*tab
, char const *which
)
334 h
= igshow(tab
, which
);
338 for (ap
= list
; *ap
!= 0; ++ap
) {
343 field
= ac_alloc(sz
+ 1);
344 i_strcpy(field
, *ap
, sz
+ 1);
345 assert(field
[sz
] == '\0');
346 if (member(field
, tab
))
350 igp
= scalloc(1, sizeof *igp
);
351 sz
= strlen(field
) + 1;
352 igp
->i_field
= smalloc(sz
);
353 memcpy(igp
->i_field
, field
, sz
);
354 igp
->i_link
= tab
->i_head
[h
];
355 tab
->i_head
[h
] = igp
;
367 igshow(struct ignoretab
*tab
, char const *which
)
374 if (tab
->i_count
== 0) {
375 printf(tr(34, "No fields currently being %s.\n"), which
);
379 ring
= salloc((tab
->i_count
+ 1) * sizeof *ring
);
381 for (h
= 0; h
< HSHSIZE
; h
++)
382 for (igp
= tab
->i_head
[h
]; igp
!= 0; igp
= igp
->i_link
)
383 *ap
++ = igp
->i_field
;
386 qsort(ring
, tab
->i_count
, sizeof *ring
, igcomp
);
388 for (ap
= ring
; *ap
!= NULL
; ++ap
)
396 igcomp(const void *l
, const void *r
)
401 rv
= strcmp(*(char**)UNCONST(l
), *(char**)UNCONST(r
));
407 unignore_one(char const *name
, struct ignoretab
*tab
)
409 struct ignore
*ip
, *iq
;
414 for (iq
= NULL
, ip
= tab
->i_head
[h
]; ip
!= NULL
; ip
= ip
->i_link
) {
415 if (asccasecmp(ip
->i_field
, name
) == 0) {
418 iq
->i_link
= ip
->i_link
;
420 tab
->i_head
[h
] = ip
->i_link
;
431 unignore1(char **list
, struct ignoretab
*tab
, char const *which
)
435 if (tab
->i_count
== 0) {
436 printf(tr(34, "No fields currently being %s.\n"), which
);
440 while (*list
!= NULL
)
441 unignore_one(*list
++, tab
);
450 int list
[2], *ip
, *ip2
, mdot
, *msgvec
= v
, rv
= 1;
455 /* If some messages were supplied, find the first applicable one
456 * following dot using wrap around */
457 mdot
= (int)PTR2SIZE(dot
- message
+ 1);
459 /* Find first message in supplied message list which follows dot */
460 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
461 if ((mb
.mb_threaded
? message
[*ip
- 1].m_threadpos
> dot
->m_threadpos
469 mp
= &message
[*ip2
- 1];
470 if ((mp
->m_flag
& (MDELETED
| MHIDDEN
)) == 0) {
479 printf(tr(21, "No messages applicable\n"));
483 /* If this is the first command, select message 1. Note that this must
484 * exist for us to get here at all */
491 /* Just find the next good message after dot, no wraparound */
492 if (mb
.mb_threaded
== 0) {
493 for (mp
= dot
+ did_print_dot
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
494 if (!(mp
->m_flag
& (MDELETED
| MSAVED
| MHIDDEN
)))
499 mp
= next_in_thread(mp
);
500 while (mp
&& mp
->m_flag
& (MDELETED
| MSAVED
| MHIDDEN
))
501 mp
= next_in_thread(mp
);
503 if (mp
== NULL
|| PTRCMP(mp
, >=, message
+ msgCount
)) {
505 printf(tr(22, "At EOF\n"));
513 list
[0] = (int)PTR2SIZE(dot
- message
+ 1);
528 rv
= save1(str
, 1, "save", saveignore
, SEND_MBOX
, 0, 0);
540 rv
= save1(str
, 1, "save", saveignore
, SEND_MBOX
, 1, 0);
552 rv
= save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 0, 0);
564 rv
= save1(str
, 0, "copy", saveignore
, SEND_MBOX
, 1, 0);
576 rv
= save1(str
, 0, "move", saveignore
, SEND_MBOX
, 0, 1);
588 rv
= save1(str
, 0, "move", saveignore
, SEND_MBOX
, 1, 1);
600 rv
= save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 0, 0);
612 rv
= save1(str
, 0, "decrypt", saveignore
, SEND_DECRYPT
, 1, 0);
624 if (str
== NULL
|| *str
== '\0')
625 str
= savestr("/dev/null");
626 rv
= save1(str
, 0, "write", allignore
, SEND_TOFILE
, 0, 0);
645 int list
[2], rv
= 0, *msgvec
= v
, lastdot
;
648 lastdot
= dot
- message
+ 1;
649 if (delm(msgvec
) >= 0) {
650 list
[0] = (int)PTR2SIZE(dot
- message
+ 1);
651 if (list
[0] > lastdot
) {
657 printf(tr(29, "At EOF\n"));
659 printf(tr(30, "No more messages\n"));
668 int *msgvec
= v
, *ip
;
672 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
674 mp
= &message
[*ip
- 1];
677 if (mp
->m_flag
& (MDELETED
| MSAVED
))
678 mp
->m_flag
&= ~(MDELETED
| MSAVED
);
680 mp
->m_flag
&= ~MDELETED
;
682 if (mb
.mb_type
== MB_IMAP
|| mb
.mb_type
== MB_CACHE
)
683 imap_undelete(mp
, *ip
);
697 rv
= ignore1(list
, ignore
+ 1, "retained");
709 rv
= ignore1(list
, ignore
, "ignored");
715 saveretfield(void *v
)
721 rv
= ignore1(list
, saveignore
+ 1, "retained");
733 rv
= ignore1(list
, saveignore
, "ignored");
745 rv
= ignore1(list
, fwdignore
+ 1, "retained");
757 rv
= ignore1(list
, fwdignore
, "ignored");
768 rv
= unignore1((char**)v
, ignore
, "ignored");
779 rv
= unignore1((char**)v
, ignore
+ 1, "retained");
785 unsaveignore(void *v
)
790 rv
= unignore1((char**)v
, saveignore
, "ignored");
796 unsaveretain(void *v
)
801 rv
= unignore1((char**)v
, saveignore
+ 1, "retained");
812 rv
= unignore1((char**)v
, fwdignore
, "ignored");
823 rv
= unignore1((char**)v
, fwdignore
+ 1, "retained");
828 /* vim:set fenc=utf-8:s-it-mode */