1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "mail.h"
18 #include <mailutils/locker.h>
19
20 /*
21 * c[opy] [file]
22 * c[opy] [msglist] file
23 * C[opy] [msglist]
24 */
25
26 struct append_stat
27 {
28 size_t size;
29 size_t lines;
30 };
31
32 static int
append_to_mailbox(mu_url_t url,msgset_t * msglist,int mark,struct append_stat * totals)33 append_to_mailbox (mu_url_t url, msgset_t *msglist, int mark,
34 struct append_stat *totals)
35 {
36 int status;
37 mu_mailbox_t mbx;
38 msgset_t *mp;
39 size_t size;
40 mu_message_t msg;
41 mu_url_t url_copy;
42
43 mu_url_dup (url, &url_copy);
44 if ((status = mu_mailbox_create_from_url (&mbx, url_copy)) != 0)
45 {
46 mu_url_destroy (&url_copy);
47 mu_error (_("Cannot create mailbox %s: %s"), mu_url_to_string (url),
48 mu_strerror (status));
49 return 1;
50 }
51 mu_mailbox_attach_ticket (mbx);
52 if ((status = mu_mailbox_open (mbx, MU_STREAM_CREAT | MU_STREAM_APPEND)) != 0)
53 {
54 mu_error (_("Cannot open mailbox %s: %s"), mu_url_to_string (url),
55 mu_strerror (status));
56 mu_mailbox_destroy (&mbx);
57 return 1;
58 }
59
60 for (mp = msglist; mp; mp = mp->next)
61 {
62 status = util_get_message (mbox, msgset_msgno (mp), &msg);
63 if (status)
64 break;
65
66 status = mu_mailbox_append_message (mbx, msg);
67 if (status)
68 {
69 mu_error (_("Cannot append message: %s"), mu_strerror (status));
70 break;
71 }
72
73 mu_message_size (msg, &size);
74 totals->size += size;
75 mu_message_lines (msg, &size);
76 totals->lines += size;
77
78 if (mark)
79 {
80 mu_attribute_t attr;
81 mu_message_get_attribute (msg, &attr);
82 mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SAVED);
83 }
84 }
85
86 mu_mailbox_close (mbx);
87 mu_mailbox_destroy (&mbx);
88 return 0;
89 }
90
91 static int
append_to_file(char const * filename,msgset_t * msglist,int mark,struct append_stat * totals)92 append_to_file (char const *filename, msgset_t *msglist, int mark,
93 struct append_stat *totals)
94 {
95 int status;
96 msgset_t *mp;
97 mu_stream_t ostr, mstr;
98 mu_message_t msg;
99 mu_locker_t locker;
100 mu_locker_hints_t hints = {
101 .flags = MU_LOCKER_FLAG_TYPE | MU_LOCKER_FLAG_RETRY,
102 .type = MU_LOCKER_TYPE_KERNEL
103 };
104 mu_stream_stat_buffer stat;
105
106 status = mu_file_stream_create (&ostr, filename,
107 MU_STREAM_CREAT|MU_STREAM_APPEND);
108 if (status)
109 {
110 mu_error (_("Cannot open output file %s: %s"),
111 filename, mu_strerror (status));
112 return 1;
113 }
114
115 status = mu_locker_create_ext (&locker, filename, &hints);
116 if (status)
117 {
118 mu_error (_("Cannot create locker %s: %s"),
119 filename, mu_strerror (status));
120 mu_stream_unref (ostr);
121 return 1;
122 }
123 mu_locker_lock_mode (locker, mu_lck_exc);
124
125 status = mu_locker_lock (locker);
126 if (status)
127 {
128 mu_error (_("Cannot lock %s: %s"),
129 filename, mu_strerror (status));
130 mu_locker_destroy (&locker);
131 mu_stream_unref (ostr);
132 return 1;
133 }
134
135 mu_stream_set_stat (ostr,
136 MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT) |
137 MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUTLN),
138 stat);
139
140 for (mp = msglist; mp; mp = mp->next)
141 {
142 mu_envelope_t env;
143 const char *s, *d;
144 int n;
145
146 status = util_get_message (mbox, msgset_msgno (mp), &msg);
147 if (status)
148 break;
149
150 status = mu_message_get_envelope (msg, &env);
151 if (status)
152 {
153 mu_error (_("Cannot get envelope: %s"), mu_strerror (status));
154 break;
155 }
156
157 status = mu_envelope_sget_sender (env, &s);
158 if (status)
159 {
160 mu_error (_("Cannot get envelope sender: %s"), mu_strerror (status));
161 break;
162 }
163
164 status = mu_envelope_sget_date (env, &d);
165 if (status)
166 {
167 mu_error (_("Cannot get envelope date: %s"), mu_strerror (status));
168 break;
169 }
170
171 status = mu_stream_printf (ostr, "From %s %s\n%n", s, d, &n);
172 if (status)
173 {
174 mu_error (_("Write error: %s"), mu_strerror (status));
175 break;
176 }
177
178 status = mu_message_get_streamref (msg, &mstr);
179 if (status)
180 {
181 mu_error (_("Cannot get message: %s"), mu_strerror (status));
182 break;
183 }
184
185 status = mu_stream_copy_nl (ostr, mstr, 0, NULL);
186 if (status)
187 {
188 mu_error (_("Cannot append message: %s"), mu_strerror (status));
189 break;
190 }
191
192 mu_stream_unref (mstr);
193
194 if (mark)
195 {
196 mu_attribute_t attr;
197 mu_message_get_attribute (msg, &attr);
198 mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SAVED);
199 }
200 }
201
202 mu_stream_close (ostr);
203 mu_stream_unref (ostr);
204
205 mu_locker_unlock (locker);
206 mu_locker_destroy (&locker);
207
208 totals->size = stat[MU_STREAM_STAT_OUT];
209 totals->lines = stat[MU_STREAM_STAT_OUTLN];
210
211 return 0;
212 }
213
214 /*
215 * mail_copy0() is shared between mail_copy() and mail_save().
216 * argc, argv -- argument count & vector
217 * mark -- whether we should mark the message as saved.
218 */
219 int
mail_copy0(int argc,char ** argv,int mark)220 mail_copy0 (int argc, char **argv, int mark)
221 {
222 mu_url_t url;
223 msgset_t *msglist = NULL;
224 struct append_stat totals = { 0, 0 };
225 int rc;
226 char *filename, *storage = NULL;
227
228 if (mu_isupper (argv[0][0]))
229 {
230 if (msgset_parse (argc, argv, MSG_NODELETED, &msglist))
231 return 1;
232 storage = util_get_sender (msgset_msgno (msglist), 1);
233 filename = util_outfolder_name (storage);
234 if (filename)
235 {
236 free (storage);
237 storage = filename;
238 }
239 else
240 filename = storage;
241 }
242 else
243 {
244 filename = argc >= 2 ? argv[--argc] : getenv ("MBOX");
245 if (msgset_parse (argc, argv, MSG_NODELETED, &msglist))
246 return 1;
247 }
248
249 rc = mail_expand_name (filename, &url);
250 if (rc == 0)
251 {
252 filename = (char*) mu_url_to_string (url);
253 if (mu_url_is_scheme (url, "file") || mu_url_is_scheme (url, "mbox"))
254 rc = append_to_file (filename, msglist, mark, &totals);
255 else
256 rc = append_to_mailbox (url, msglist, mark, &totals);
257 if (rc == 0)
258 mu_printf ("\"%s\" %3lu/%-5lu\n", filename,
259 (unsigned long) totals.lines, (unsigned long) totals.size);
260 mu_url_destroy (&url);
261 }
262 free (storage);
263 msgset_free (msglist);
264 return rc;
265 }
266
267 int
mail_copy(int argc,char ** argv)268 mail_copy (int argc, char **argv)
269 {
270 return mail_copy0 (argc, argv, 0);
271 }
272