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>.
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
36 #define n_FILE cmd_write
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 n_ignore
const *itp
, 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
);
56 save1(char *str
, int domark
, char const *cmd
, struct n_ignore
const *itp
,
57 int convert
, int sender_record
, int domove
)
59 ui64_t mstats
[1], tstats
[2];
60 enum n_fopen_state fs
;
61 int last
, *msgvec
, *ip
;
64 char const *disp
, *shell
;
66 bool_t success
, isflag
;
74 msgvec
= n_autorec_alloc((msgCount
+ 2) * sizeof *msgvec
);
76 for (cp
= str
; *cp
!= '\0' && spacechar(*cp
); ++cp
)
78 isflag
= (*cp
!= '\0');
80 if ((file
= snarf(str
, &isflag
, convert
!= SEND_TOFILE
)) == NULL
)
82 while(spacechar(*file
))
86 shell
= ok_vlook(SHELL
);
91 *msgvec
= first(0, MMNORM
);
93 } else if (n_getmsglist(str
, msgvec
, 0) < 0)
96 if (n_pstate
& (n_PS_HOOK_MASK
| n_PS_ROBOT
)) {
100 fprintf(n_stdout
, _("No messages to %s.\n"), cmd
);
107 if((cp
= n_header_senderfield_of(message
+ *msgvec
- 1)) == NULL
||
108 (np
= lextract(cp
, GTO
| GSKIN
)) == NULL
){
109 n_err(_("Cannot determine message sender to %s.\n"), cmd
);
114 for (cq
= cp
; *cq
!= '\0' && *cq
!= '@'; cq
++)
117 if (ok_blook(outfolder
)) {
118 size_t sz
= strlen(cp
) +1;
119 file
= n_autorec_alloc(sz
+ 1);
121 memcpy(file
+ 1, cp
, sz
);
126 /* Pipe target is special TODO hacked in later, normalize flow! */
128 if ((obuf
= Popen(file
, "w", shell
, NULL
, 1)) == NULL
) {
137 disp
= A_("[Piped]");
141 if ((file
= fexpand(file
, FEXP_FULL
)) == NULL
)
144 /* TODO all this should be URL and Mailbox-"VFS" based, and then finally
145 * TODO end up as Mailbox()->append(). Unless SEND_TOFILE, of course.
146 * TODO However, URL parse because that file:// prefix check is a HACK! */
147 if(convert
== SEND_TOFILE
&& !is_prefix("file://", file
))
148 file
= savecat("file://", file
);
149 if((obuf
= n_fopen_any(file
, "a+", &fs
)) == NULL
){
154 #if defined HAVE_POP3 && defined HAVE_IMAP
155 if(mb
.mb_type
== MB_POP3
&& (fs
& n_PROTO_MASK
) == n_PROTO_IMAP
){
157 n_err(_("Direct copy from POP3 to IMAP not supported before v15\n"));
162 disp
= (fs
& n_FOPEN_STATE_EXISTS
) ? A_("[Appended]") : A_("[New file]");
164 if((fs
& (n_PROTO_MASK
| n_FOPEN_STATE_EXISTS
)) ==
165 (n_PROTO_FILE
| n_FOPEN_STATE_EXISTS
)){
168 /* TODO RETURN check, but be aware of protocols: v15: Mailbox->lock()!
169 * TODO BETTER yet: should be returned in lock state already! */
170 n_file_lock(fileno(obuf
), FLT_WRITE
, 0,0, UIZ_MAX
);
172 if((xerr
= n_folder_mbox_prepare_append(obuf
, NULL
)) != n_ERR_NONE
){
180 tstats
[0] = tstats
[1] = 0;
182 imap_created_mailbox
= 0;
186 for (ip
= msgvec
; *ip
!= 0 && UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
);
188 mp
= &message
[*ip
- 1];
190 if((fs
& n_PROTO_MASK
) == n_PROTO_IMAP
&&
191 !n_ignore_is_any(n_IGNORE_SAVE
) && imap_thisaccount(file
)){
192 if(imap_copy(mp
, PTR2SIZE(mp
- message
+ 1), file
) == STOP
){
196 mstats
[0] = mp
->m_xsize
;
199 if (sendmp(mp
, obuf
, itp
, NULL
, convert
, mstats
) < 0) {
207 mp
->m_flag
|= MSAVED
;
209 mp
->m_flag
|= MDELETED
| MSAVED
;
213 tstats
[0] += mstats
[0];
214 tstats
[1] += mp
->m_lines
;/* TODO won't work, need target! v15!! */
220 /* TODO Should be a VFS, then n_MBOX knows what to do upon .close()! */
221 if((fs
& n_PROTO_MASK
) == n_PROTO_FILE
&& convert
== SEND_MBOX
)
222 n_folder_mbox_prepare_append(obuf
, NULL
);
232 if (!Pclose(obuf
, TRU1
))
234 } else if (Fclose(obuf
) != 0)
239 if((fs
& n_PROTO_MASK
) == n_PROTO_IMAP
){
240 if(disconnected(file
))
241 disp
= A_("[Queued]");
242 else if(imap_created_mailbox
)
243 disp
= A_("[New file]");
245 disp
= A_("[Appended]");
248 fprintf(n_stdout
, "%s %s %" /*PRIu64 "/%"*/ PRIu64
" bytes\n",
249 n_shexp_quote_cp(file
, FAL0
), disp
,
250 /*tstats[1], TODO v15: lines written */ tstats
[0]);
252 for (ip
= msgvec
; *ip
!= 0 &&
253 UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
); ++ip
) {
254 mp
= message
+ *ip
- 1;
255 mp
->m_flag
&= ~MSAVED
;
258 for (ip
= msgvec
; *ip
!= 0 &&
259 UICMP(z
, PTR2SIZE(ip
- msgvec
), <, msgCount
); ++ip
) {
260 mp
= message
+ *ip
- 1;
261 mp
->m_flag
&= ~(MSAVED
| MDELETED
);
265 if (domove
&& last
&& success
) {
266 setdot(message
+ last
- 1);
267 last
= first(0, MDELETED
);
268 setdot(message
+ (last
!= 0 ? last
- 1 : 0));
272 return (success
== FAL0
);
276 snarf(char *linebuf
, bool_t
*flag
, bool_t usembox
)
281 if ((cp
= laststring(linebuf
, flag
, TRU1
)) == NULL
) {
284 cp
= fexpand("&", FEXP_NVAR
);
286 n_err(_("No file specified\n"));
299 rv
= save1(str
, 1, "save", n_IGNORE_SAVE
, SEND_MBOX
, 0, 0);
311 rv
= save1(str
, 1, "save", n_IGNORE_SAVE
, SEND_MBOX
, 1, 0);
323 rv
= save1(str
, 0, "copy", n_IGNORE_SAVE
, SEND_MBOX
, 0, 0);
335 rv
= save1(str
, 0, "copy", n_IGNORE_SAVE
, SEND_MBOX
, 1, 0);
347 rv
= save1(str
, 0, "move", n_IGNORE_SAVE
, SEND_MBOX
, 0, 1);
359 rv
= save1(str
, 0, "move", n_IGNORE_SAVE
, SEND_MBOX
, 1, 1);
371 rv
= save1(str
, 0, "decrypt", n_IGNORE_SAVE
, SEND_DECRYPT
, 0, 0);
383 rv
= save1(str
, 0, "decrypt", n_IGNORE_SAVE
, SEND_DECRYPT
, 1, 0);
395 if (str
== NULL
|| *str
== '\0')
396 str
= savestr(n_path_devnull
);
397 rv
= save1(str
, 0, "write", n_IGNORE_ALL
, SEND_TOFILE
, 0, 0);