make-config.in: complete path (leftover of [807f64e2], 2015-12-26!)
[s-mailx.git] / cmd-write.c
blob2e0cda58510458b2bf7277ea3941c78fc406146b
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
7 */
8 /*
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
14 * are met:
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
34 * SUCH DAMAGE.
36 #undef n_FILE
37 #define n_FILE cmd_write
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
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);
48 static int
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;
54 struct message *mp;
55 char *file, *cp, *cq;
56 FILE *obuf;
57 char const *shell, *disp;
58 bool_t success;
59 int last, *msgvec, *ip;
60 struct n_cmd_arg *cap;
61 struct n_cmd_arg_ctx *cacp;
62 NYD2_ENTER;
64 cacp = vp;
65 cap = cacp->cac_arg;
66 last = 0;
67 msgvec = cap->ca_arg.ca_msglist;
68 success = FAL0;
69 shell = NULL;
70 file = NULL;
72 if(!(cap->ca_ent_flags[0] & n_CMD_ARG_DESC_MSGLIST_AND_TARGET)){
73 struct name *np;
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);
79 goto jleave;
81 cp = np->n_name;
83 for (cq = cp; *cq != '\0' && *cq != '@'; cq++)
85 *cq = '\0';
86 if (ok_blook(outfolder)) {
87 size_t sz = strlen(cp) +1;
88 file = n_autorec_alloc(sz + 1);
89 file[0] = '+';
90 memcpy(file + 1, cp, sz);
91 } else
92 file = cp;
93 }else{
94 cap = cap->ca_next;
95 if((file = cap->ca_arg.ca_str.s)[0] == '\0')
96 file = fexpand("&", FEXP_NVAR);
98 while(spacechar(*file))
99 ++file;
100 if (*file == '|') {
101 ++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){
106 int esave;
108 n_perr(file, esave = n_err_no);
109 n_err_no = esave;
110 goto jleave;
112 disp = A_("[Piped]");
113 goto jsend;
117 if ((file = fexpand(file, FEXP_FULL)) == NULL)
118 goto jleave;
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){
126 n_perr(file, 0);
127 goto jleave;
130 #if defined HAVE_POP3 && defined HAVE_IMAP
131 if(mb.mb_type == MB_POP3 && (fs & n_PROTO_MASK) == n_PROTO_IMAP){
132 Fclose(obuf);
133 n_err(_("Direct copy from POP3 to IMAP not supported before v15\n"));
134 goto jleave;
136 #endif
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)){
142 int xerr;
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){
149 n_perr(file, xerr);
150 goto jleave;
154 jsend:
155 success = TRU1;
156 tstats[0] = tstats[1] = 0;
157 #ifdef HAVE_IMAP
158 imap_created_mailbox = 0;
159 #endif
161 n_autorec_relax_create();
162 for (ip = msgvec; *ip != 0; ++ip) {
163 mp = &message[*ip - 1];
164 #ifdef HAVE_IMAP
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){
168 success = FAL0;
169 goto jferr;
171 mstats[0] = mp->m_xsize;
172 }else
173 #endif
174 if (sendmp(mp, obuf, itp, NULL, convert, mstats) < 0) {
175 success = FAL0;
176 goto jferr;
178 n_autorec_relax_unroll();
180 touch(mp);
181 if (domark)
182 mp->m_flag |= MSAVED;
183 if (domove) {
184 mp->m_flag |= MDELETED | MSAVED;
185 last = *ip;
188 tstats[0] += mstats[0];
189 tstats[1] += mp->m_lines;/* TODO won't work, need target! v15!! */
191 n_autorec_relax_gut();
193 fflush(obuf);
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);
199 if (ferror(obuf)) {
200 jferr:
201 n_perr(file, 0);
202 if (!success)
203 srelax_rele();
204 success = FAL0;
206 if (shell != NULL) {
207 if (!Pclose(obuf, TRU1))
208 success = FAL0;
209 } else if (Fclose(obuf) != 0)
210 success = FAL0;
212 if (success) {
213 #ifdef HAVE_IMAP
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]");
219 else
220 disp = A_("[Appended]");
222 #endif
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]);
226 } else if (domark) {
227 for (ip = msgvec; *ip != 0; ++ip) {
228 mp = message + *ip - 1;
229 mp->m_flag &= ~MSAVED;
231 } else if (domove) {
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));
243 jleave:
244 NYD2_LEAVE;
245 return (success == FAL0);
248 FL int
249 c_save(void *vp){
250 int rv;
251 NYD_ENTER;
253 rv = a_cwrite_save1(vp, n_IGNORE_SAVE, SEND_MBOX, TRU1, FAL0);
254 NYD_LEAVE;
255 return rv;
258 FL int
259 c_Save(void *vp){
260 int rv;
261 NYD_ENTER;
263 rv = a_cwrite_save1(vp, n_IGNORE_SAVE, SEND_MBOX, TRU1, FAL0);
264 NYD_LEAVE;
265 return rv;
268 FL int
269 c_copy(void *vp){
270 int rv;
271 NYD_ENTER;
273 rv = a_cwrite_save1(vp, n_IGNORE_SAVE, SEND_MBOX, FAL0, FAL0);
274 NYD_LEAVE;
275 return rv;
278 FL int
279 c_Copy(void *vp){
280 int rv;
281 NYD_ENTER;
283 rv = a_cwrite_save1(vp, n_IGNORE_SAVE, SEND_MBOX, FAL0, FAL0);
284 NYD_LEAVE;
285 return rv;
288 FL int
289 c_move(void *vp){
290 int rv;
291 NYD_ENTER;
293 rv = a_cwrite_save1(vp, n_IGNORE_SAVE, SEND_MBOX, FAL0, TRU1);
294 NYD_LEAVE;
295 return rv;
298 FL int
299 c_Move(void *vp){
300 int rv;
301 NYD_ENTER;
303 rv = a_cwrite_save1(vp, n_IGNORE_SAVE, SEND_MBOX, FAL0, TRU1);
304 NYD_LEAVE;
305 return rv;
308 FL int
309 c_decrypt(void *vp){
310 int rv;
311 NYD_ENTER;
313 rv = a_cwrite_save1(vp, n_IGNORE_SAVE, SEND_DECRYPT, FAL0, FAL0);
314 NYD_LEAVE;
315 return rv;
318 FL int
319 c_Decrypt(void *vp){
320 int rv;
321 NYD_ENTER;
323 rv = a_cwrite_save1(vp, n_IGNORE_SAVE, SEND_DECRYPT, FAL0, FAL0);
324 NYD_LEAVE;
325 return rv;
328 FL int
329 c_write(void *vp){
330 int rv;
331 struct n_cmd_arg *cap;
332 struct n_cmd_arg_ctx *cacp;
333 NYD_ENTER;
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);
340 NYD_LEAVE;
341 return rv;
344 /* s-it-mode */