1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ User commands which save, copy, write (parts of) messages.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #define n_FILE cmd_write
39 #ifndef HAVE_AMALGAMATION
43 /* Save/copy the indicated messages at the end of the passed file name.
44 * If mark is true, mark the message "saved" */
45 static int a_cwrite_save1(void *vp
, struct n_ignore
const *itp
,
46 int convert
, bool_t domark
, bool_t domove
);
49 a_cwrite_save1(void *vp
, struct n_ignore
const *itp
,
50 int convert
, bool_t domark
, bool_t domove
)
52 ui64_t mstats
[1], tstats
[2];
53 enum n_fopen_state fs
;
57 char const *shell
, *disp
;
59 int last
, *msgvec
, *ip
;
60 struct n_cmd_arg
*cap
;
61 struct n_cmd_arg_ctx
*cacp
;
67 msgvec
= cap
->ca_arg
.ca_msglist
;
72 if(!(cap
->ca_ent_flags
[0] & n_CMD_ARG_DESC_MSGLIST_AND_TARGET
)){
75 if((cp
= n_header_senderfield_of(message
+ *msgvec
- 1)) == NULL
||
76 (np
= lextract(cp
, GTO
| GSKIN
)) == NULL
){
77 n_err(_("Cannot determine message sender to %s.\n"),
78 cacp
->cac_desc
->cad_name
);
83 for (cq
= cp
; *cq
!= '\0' && *cq
!= '@'; cq
++)
86 if (ok_blook(outfolder
)) {
87 size_t sz
= strlen(cp
) +1;
88 file
= n_autorec_alloc(sz
+ 1);
90 memcpy(file
+ 1, cp
, sz
);
95 if((file
= cap
->ca_arg
.ca_str
.s
)[0] == '\0')
96 file
= fexpand("&", FEXP_NVAR
);
98 while(spacechar(*file
))
102 shell
= ok_vlook(SHELL
);
104 /* Pipe target is special TODO hacked in later, normalize flow! */
105 if((obuf
= Popen(file
, "w", shell
, NULL
, 1)) == NULL
){
108 n_perr(file
, esave
= n_err_no
);
112 disp
= A_("[Piped]");
117 if ((file
= fexpand(file
, FEXP_FULL
)) == NULL
)
120 /* TODO all this should be URL and Mailbox-"VFS" based, and then finally
121 * TODO end up as Mailbox()->append(). Unless SEND_TOFILE, of course.
122 * TODO However, URL parse because that file:// prefix check is a HACK! */
123 if(convert
== SEND_TOFILE
&& !is_prefix("file://", file
))
124 file
= savecat("file://", file
);
125 if((obuf
= n_fopen_any(file
, "a+", &fs
)) == NULL
){
130 #if defined HAVE_POP3 && defined HAVE_IMAP
131 if(mb
.mb_type
== MB_POP3
&& (fs
& n_PROTO_MASK
) == n_PROTO_IMAP
){
133 n_err(_("Direct copy from POP3 to IMAP not supported before v15\n"));
138 disp
= (fs
& n_FOPEN_STATE_EXISTS
) ? A_("[Appended]") : A_("[New file]");
140 if((fs
& (n_PROTO_MASK
| n_FOPEN_STATE_EXISTS
)) ==
141 (n_PROTO_FILE
| n_FOPEN_STATE_EXISTS
)){
144 /* TODO RETURN check, but be aware of protocols: v15: Mailbox->lock()!
145 * TODO BETTER yet: should be returned in lock state already! */
146 n_file_lock(fileno(obuf
), FLT_WRITE
, 0,0, UIZ_MAX
);
148 if((xerr
= n_folder_mbox_prepare_append(obuf
, NULL
)) != n_ERR_NONE
){
156 tstats
[0] = tstats
[1] = 0;
158 imap_created_mailbox
= 0;
161 n_autorec_relax_create();
162 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
163 mp
= &message
[*ip
- 1];
165 if((fs
& n_PROTO_MASK
) == n_PROTO_IMAP
&&
166 !n_ignore_is_any(n_IGNORE_SAVE
) && imap_thisaccount(file
)){
167 if(imap_copy(mp
, PTR2SIZE(mp
- message
+ 1), file
) == STOP
){
171 mstats
[0] = mp
->m_xsize
;
174 if (sendmp(mp
, obuf
, itp
, NULL
, convert
, mstats
) < 0) {
178 n_autorec_relax_unroll();
182 mp
->m_flag
|= MSAVED
;
184 mp
->m_flag
|= MDELETED
| MSAVED
;
188 tstats
[0] += mstats
[0];
189 tstats
[1] += mp
->m_lines
;/* TODO won't work, need target! v15!! */
191 n_autorec_relax_gut();
195 /* TODO Should be a VFS, then n_MBOX knows what to do upon .close()! */
196 if((fs
& n_PROTO_MASK
) == n_PROTO_FILE
&& convert
== SEND_MBOX
)
197 n_folder_mbox_prepare_append(obuf
, NULL
);
207 if (!Pclose(obuf
, TRU1
))
209 } else if (Fclose(obuf
) != 0)
214 if((fs
& n_PROTO_MASK
) == n_PROTO_IMAP
){
215 if(disconnected(file
))
216 disp
= A_("[Queued]");
217 else if(imap_created_mailbox
)
218 disp
= A_("[New file]");
220 disp
= A_("[Appended]");
223 fprintf(n_stdout
, "%s %s %" /*PRIu64 "/%"*/ PRIu64
" bytes\n",
224 n_shexp_quote_cp(file
, FAL0
), disp
,
225 /*tstats[1], TODO v15: lines written */ tstats
[0]);
227 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
228 mp
= message
+ *ip
- 1;
229 mp
->m_flag
&= ~MSAVED
;
232 for (ip
= msgvec
; *ip
!= 0; ++ip
) {
233 mp
= message
+ *ip
- 1;
234 mp
->m_flag
&= ~(MSAVED
| MDELETED
);
238 if (domove
&& last
&& success
) {
239 setdot(message
+ last
- 1);
240 last
= first(0, MDELETED
);
241 setdot(message
+ (last
!= 0 ? last
- 1 : 0));
245 return (success
== FAL0
);
253 rv
= a_cwrite_save1(vp
, n_IGNORE_SAVE
, SEND_MBOX
, TRU1
, FAL0
);
263 rv
= a_cwrite_save1(vp
, n_IGNORE_SAVE
, SEND_MBOX
, TRU1
, FAL0
);
273 rv
= a_cwrite_save1(vp
, n_IGNORE_SAVE
, SEND_MBOX
, FAL0
, FAL0
);
283 rv
= a_cwrite_save1(vp
, n_IGNORE_SAVE
, SEND_MBOX
, FAL0
, FAL0
);
293 rv
= a_cwrite_save1(vp
, n_IGNORE_SAVE
, SEND_MBOX
, FAL0
, TRU1
);
303 rv
= a_cwrite_save1(vp
, n_IGNORE_SAVE
, SEND_MBOX
, FAL0
, TRU1
);
313 rv
= a_cwrite_save1(vp
, n_IGNORE_SAVE
, SEND_DECRYPT
, FAL0
, FAL0
);
323 rv
= a_cwrite_save1(vp
, n_IGNORE_SAVE
, SEND_DECRYPT
, FAL0
, FAL0
);
331 struct n_cmd_arg
*cap
;
332 struct n_cmd_arg_ctx
*cacp
;
335 if((cap
= (cacp
= vp
)->cac_arg
->ca_next
)->ca_arg
.ca_str
.s
[0] == '\0')
336 cap
->ca_arg
.ca_str
.s
= savestrbuf(n_path_devnull
,
337 cap
->ca_arg
.ca_str
.l
= strlen(n_path_devnull
));
339 rv
= a_cwrite_save1(vp
, n_IGNORE_ALL
, SEND_TOFILE
, FAL0
, FAL0
);