1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2002-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 /* MH refile command */
18 
19 #include <mh.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 
26 static char prog_doc[] = N_("File messages in other folders");
27 static char args_doc[] = N_("MSGLIST FOLDER [FOLDER...]");
28 
29 int link_flag = 0;
30 int preserve_flag = 0;
31 char *source_file = NULL;
32 mu_list_t folder_name_list = NULL;
33 mu_list_t folder_mbox_list = NULL;
34 
35 void
add_folder(const char * folder)36 add_folder (const char *folder)
37 {
38   if (!folder_name_list && mu_list_create (&folder_name_list))
39     {
40       mu_error (_("cannot create folder list"));
41       exit (1);
42     }
43   mu_list_append (folder_name_list, mu_strdup (folder));
44 }
45 
46 static void
add_folder_option(struct mu_parseopt * po,struct mu_option * opt,const char * arg)47 add_folder_option (struct mu_parseopt *po, struct mu_option *opt,
48 		   const char *arg)
49 {
50   add_folder (arg);
51 }
52 
53 static struct mu_option options[] = {
54   { "folder",  0, N_("FOLDER"), MU_OPTION_DEFAULT,
55     N_("specify folder to operate upon"),
56     mu_c_string, NULL, add_folder_option },
57   { "draft",   0, NULL, MU_OPTION_DEFAULT,
58     N_("use <mh-dir>/draft as the source message"),
59     mu_c_string, &source_file, NULL, "draft" },
60   { "copy",    0, NULL, MU_OPTION_DEFAULT,
61     N_("preserve the source folder copy"),
62     mu_c_bool, &link_flag },
63   { "link",    0, NULL, MU_OPTION_ALIAS },
64   { "preserve", 0, NULL, MU_OPTION_HIDDEN,
65     N_("try to preserve message sequence numbers"),
66     mu_c_bool, NULL, mh_opt_notimpl_warning },
67   { "source", 0, N_("FOLDER"), MU_OPTION_DEFAULT,
68     N_("specify source folder; it will become the current folder after the program exits"),
69     mu_c_string, NULL, mh_opt_set_folder },
70   { "src", 0, NULL, MU_OPTION_ALIAS },
71   { "file", 0, N_("FILE"), MU_OPTION_DEFAULT,
72     N_("use FILE as the source message"),
73     mu_c_string, &source_file },
74   MU_OPTION_END
75 };
76 
77 void
open_folders(void)78 open_folders (void)
79 {
80   int rc;
81   mu_iterator_t itr;
82 
83   if (!folder_name_list)
84     {
85       mu_error (_("no folder specified"));
86       exit (1);
87     }
88 
89   if ((rc = mu_list_create (&folder_mbox_list)) != 0)
90     {
91       mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
92       exit (1);
93     }
94 
95   if ((rc = mu_list_get_iterator (folder_name_list, &itr)) != 0)
96     {
97       mu_diag_funcall (MU_DIAG_ERROR, "mu_list_get_iterator", NULL, rc);
98       exit (1);
99     }
100 
101   for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
102     {
103       char *name = NULL;
104       mu_mailbox_t mbox;
105 
106       mu_iterator_current (itr, (void **)&name);
107       mbox = mh_open_folder (name, MU_STREAM_RDWR|MU_STREAM_CREAT);
108       mu_list_append (folder_mbox_list, mbox);
109       free (name);
110     }
111   mu_iterator_destroy (&itr);
112   mu_list_destroy (&folder_name_list);
113 }
114 
115 void
enumerate_folders(void (* f)(void *,mu_mailbox_t),void * data)116 enumerate_folders (void (*f) (void *, mu_mailbox_t), void *data)
117 {
118   mu_iterator_t itr;
119 
120   if (mu_list_get_iterator (folder_mbox_list, &itr))
121     {
122       mu_error (_("cannot create iterator"));
123       exit (1);
124     }
125 
126   for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
127     {
128       mu_mailbox_t mbox;
129       mu_iterator_current (itr, (void **)&mbox);
130       (*f) (data, mbox);
131     }
132   mu_iterator_destroy (&itr);
133 }
134 
135 void
_close_folder(void * unused,mu_mailbox_t mbox)136 _close_folder (void *unused, mu_mailbox_t mbox)
137 {
138   mu_mailbox_close (mbox);
139   mu_mailbox_destroy (&mbox);
140 }
141 
142 void
close_folders(void)143 close_folders (void)
144 {
145   enumerate_folders (_close_folder, NULL);
146 }
147 
148 void
refile_folder(void * data,mu_mailbox_t mbox)149 refile_folder (void *data, mu_mailbox_t mbox)
150 {
151   mu_message_t msg = data;
152   int rc;
153 
154   rc = mu_mailbox_append_message (mbox, msg);
155   if (rc)
156     {
157       mu_error (_("error appending message: %s"), mu_strerror (rc));
158       exit (1);
159     }
160 }
161 
162 void
refile(mu_message_t msg)163 refile (mu_message_t msg)
164 {
165   enumerate_folders (refile_folder, msg);
166 }
167 
168 int
refile_iterator(size_t num,mu_message_t msg,void * data)169 refile_iterator (size_t num, mu_message_t msg, void *data)
170 {
171   enumerate_folders (refile_folder, msg);
172   if (!link_flag)
173     {
174       mu_attribute_t attr;
175       mu_message_get_attribute (msg, &attr);
176       mu_attribute_set_deleted (attr);
177     }
178   return 0;
179 }
180 
181 int
main(int argc,char ** argv)182 main (int argc, char **argv)
183 {
184   mu_msgset_t msgset;
185   mu_mailbox_t mbox;
186   int status, i, j;
187 
188   mh_getopt (&argc, &argv, options, 0, args_doc, prog_doc, NULL);
189   /* Collect any surplus folders */
190   for (i = j = 0; i < argc; i++)
191     {
192       if (argv[i][0] == '+')
193 	add_folder (argv[i]);
194       else
195 	argv[j++] = argv[i];
196     }
197   argv[j] = NULL;
198   argc = j;
199 
200   open_folders ();
201 
202   if (source_file)
203     {
204       mu_message_t msg;
205 
206       if (argc > 0)
207 	{
208 	  mu_error (_("both message set and source file given"));
209 	  exit (1);
210 	}
211       msg = mh_file_to_message (mu_folder_directory (), source_file);
212       refile (msg);
213       if (!link_flag)
214 	unlink (source_file);
215       status = 0;
216     }
217   else
218     {
219       mbox = mh_open_folder (mh_current_folder (), MU_STREAM_RDWR);
220       mh_msgset_parse (&msgset, mbox, argc, argv, "cur");
221 
222       status = mu_msgset_foreach_message (msgset, refile_iterator, NULL);
223 
224       mh_sequences_elim (msgset);
225 
226       mu_mailbox_expunge (mbox);
227       mu_mailbox_close (mbox);
228       mu_mailbox_destroy (&mbox);
229     }
230 
231   close_folders ();
232 
233   return status;
234 }
235