]> diplodocus.org Git - nmh/blob - uip/mhstore.c
vector.c: Move interface to own file.
[nmh] / uip / mhstore.c
1 /* mhstore.c -- store the contents of MIME messages
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "sbr/path.h"
10 #include "sbr/print_version.h"
11 #include "sbr/print_help.h"
12 #include "sbr/error.h"
13 #include <fcntl.h>
14 #include "h/signals.h"
15 #include "h/md5.h"
16 #include "h/mts.h"
17 #include "h/tws.h"
18 #include "h/mime.h"
19 #include "h/mhparse.h"
20 #include "h/mhcachesbr.h"
21 #include "h/done.h"
22 #include "h/utils.h"
23 #include "mhmisc.h"
24 #include "sbr/m_maildir.h"
25 #include "mhfree.h"
26
27 #define MHSTORE_SWITCHES \
28 X("auto", 0, AUTOSW) \
29 X("noauto", 0, NAUTOSW) \
30 X("check", 0, CHECKSW) \
31 X("nocheck", 0, NCHECKSW) \
32 X("verbose", 0, VERBSW) \
33 X("noverbose", 0, NVERBSW) \
34 X("file file", 0, FILESW) /* interface from show */ \
35 X("outfile outfile", 0, OUTFILESW) \
36 X("part number", 0, PARTSW) \
37 X("type content", 0, TYPESW) \
38 X("prefer content", 0, PREFERSW) \
39 X("noprefer", 0, NPREFERSW) \
40 X("rcache policy", 0, RCACHESW) \
41 X("wcache policy", 0, WCACHESW) \
42 X("version", 0, VERSIONSW) \
43 X("help", 0, HELPSW) \
44 X("clobber always|auto|suffix|ask|never", 0, CLOBBERSW) \
45 X("debug", -5, DEBUGSW) \
46
47 #define X(sw, minchars, id) id,
48 DEFINE_SWITCH_ENUM(MHSTORE);
49 #undef X
50
51 #define X(sw, minchars, id) { sw, minchars, id },
52 DEFINE_SWITCH_ARRAY(MHSTORE, switches);
53 #undef X
54
55
56 #define quitser pipeser
57
58 /* mhparse.c */
59 int debugsw = 0;
60
61 /*
62 * static prototypes
63 */
64 static void pipeser (int);
65
66
67 int
68 main (int argc, char **argv)
69 {
70 int msgnum, *icachesw;
71 bool autosw = false;
72 /* verbosw defaults to 1 for backward compatibility. */
73 bool verbosw = true;
74 const char *clobbersw = "always";
75 char *cp, *file = NULL, *outfile = NULL, *folder = NULL;
76 char *maildir, buf[100], **argp;
77 char **arguments;
78 char *cwd;
79 struct msgs_array msgs = { 0, 0, NULL };
80 struct msgs *mp = NULL;
81 CT ct, *ctp;
82 FILE *fp;
83 int files_not_clobbered;
84 mhstoreinfo_t info;
85
86 if (nmh_init(argv[0], true, true)) { return 1; }
87
88 set_done(freects_done);
89
90 arguments = getarguments (invo_name, argc, argv, 1);
91 argp = arguments;
92
93 /*
94 * Parse arguments
95 */
96 while ((cp = *argp++)) {
97 if (*cp == '-') {
98 switch (smatch (++cp, switches)) {
99 case AMBIGSW:
100 ambigsw (cp, switches);
101 done (1);
102 case UNKWNSW:
103 die("-%s unknown", cp);
104
105 case HELPSW:
106 snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
107 invo_name);
108 print_help (buf, switches, 1);
109 done (0);
110 case VERSIONSW:
111 print_version(invo_name);
112 done (0);
113
114 case AUTOSW:
115 autosw = true;
116 continue;
117 case NAUTOSW:
118 autosw = false;
119 continue;
120
121 case RCACHESW:
122 icachesw = &rcachesw;
123 goto do_cache;
124 case WCACHESW:
125 icachesw = &wcachesw;
126 do_cache:
127 if (!(cp = *argp++) || *cp == '-')
128 die("missing argument to %s", argp[-2]);
129 switch (*icachesw = smatch (cp, cache_policy)) {
130 case AMBIGSW:
131 ambigsw (cp, cache_policy);
132 done (1);
133 case UNKWNSW:
134 die("%s unknown", cp);
135 default:
136 break;
137 }
138 continue;
139
140 case CHECKSW:
141 checksw++;
142 continue;
143 case NCHECKSW:
144 checksw = 0;
145 continue;
146
147 case PARTSW:
148 if (!(cp = *argp++) || *cp == '-')
149 die("missing argument to %s", argp[-2]);
150 if (npart >= NPARTS)
151 die("too many parts (starting with %s), %d max",
152 cp, NPARTS);
153 parts[npart++] = cp;
154 continue;
155
156 case TYPESW:
157 if (!(cp = *argp++) || *cp == '-')
158 die("missing argument to %s", argp[-2]);
159 if (ntype >= NTYPES)
160 die("too many types (starting with %s), %d max",
161 cp, NTYPES);
162 types[ntype++] = cp;
163 continue;
164
165 case PREFERSW:
166 if (!(cp = *argp++) || *cp == '-')
167 die("missing argument to %s", argp[-2]);
168 if (npreferred >= NPREFS)
169 die("too many preferred types (starting with %s), %d max",
170 cp, NPREFS);
171 mime_preference[npreferred].type = cp;
172 cp = strchr(cp, '/');
173 if (cp) *cp++ = '\0';
174 mime_preference[npreferred++].subtype = cp;
175 continue;
176
177 case NPREFERSW:
178 npreferred = 0;
179 continue;
180
181 case FILESW:
182 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
183 die("missing argument to %s", argp[-2]);
184 file = *cp == '-' ? cp : path (cp, TFILE);
185 continue;
186
187 case OUTFILESW:
188 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
189 die("missing argument to %s", argp[-2]);
190 outfile = *cp == '-' ? cp : path (cp, TFILE);
191 continue;
192
193 case VERBSW:
194 verbosw = true;
195 continue;
196 case NVERBSW:
197 verbosw = false;
198 continue;
199 case CLOBBERSW:
200 if (!(cp = *argp++) || *cp == '-')
201 die("missing argument to %s", argp[-2]);
202 clobbersw = cp;
203 continue;
204 case DEBUGSW:
205 debugsw = 1;
206 continue;
207 }
208 }
209 if (*cp == '+' || *cp == '@') {
210 if (folder)
211 die("only one folder at a time!");
212 folder = pluspath (cp);
213 } else
214 app_msgarg(&msgs, cp);
215 }
216
217 /* null terminate the list of acceptable parts/types */
218 parts[npart] = NULL;
219 types[ntype] = NULL;
220
221 /*
222 * Check if we've specified an additional profile
223 */
224 if ((cp = getenv ("MHSTORE"))) {
225 if ((fp = fopen (cp, "r"))) {
226 readconfig(NULL, fp, cp, 0);
227 fclose (fp);
228 } else {
229 admonish ("", "unable to read $MHSTORE profile (%s)", cp);
230 }
231 }
232
233 /*
234 * Read the standard profile setup
235 */
236 if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
237 readconfig(NULL, fp, cp, 0);
238 fclose (fp);
239 }
240
241 /* Check for public cache location */
242 if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
243 cache_public = NULL;
244
245 /* Check for private cache location */
246 if (!(cache_private = context_find (nmhprivcache)))
247 cache_private = ".cache";
248 cache_private = mh_xstrdup(m_maildir(cache_private));
249
250 /*
251 * Cache the current directory before we do any chdirs()'s.
252 */
253 cwd = mh_xstrdup(pwd());
254
255 if (!context_find ("path"))
256 free (path ("./", TFOLDER));
257
258 if (file && msgs.size)
259 die("cannot specify msg and file at same time!");
260
261 /*
262 * check if message is coming from file
263 */
264 if (file) {
265 cts = mh_xcalloc(2, sizeof *cts);
266 ctp = cts;
267
268 if ((ct = parse_mime (file))) {
269 *ctp++ = ct;
270 if (outfile) {
271 ct->c_storage = mh_xstrdup(outfile);
272 }
273 }
274 } else {
275 /*
276 * message(s) are coming from a folder
277 */
278 if (!msgs.size)
279 app_msgarg(&msgs, "cur");
280 if (!folder)
281 folder = getfolder (1);
282 maildir = m_maildir (folder);
283
284 if (chdir (maildir) == NOTOK)
285 adios (maildir, "unable to change directory to");
286
287 /* read folder and create message structure */
288 if (!(mp = folder_read (folder, 1)))
289 die("unable to read folder %s", folder);
290
291 /* check for empty folder */
292 if (mp->nummsg == 0)
293 die("no messages in %s", folder);
294
295 /* parse all the message ranges/sequences and set SELECTED */
296 for (msgnum = 0; msgnum < msgs.size; msgnum++)
297 if (!m_convert (mp, msgs.msgs[msgnum]))
298 done (1);
299 seq_setprev (mp); /* set the previous-sequence */
300
301 cts = mh_xcalloc(mp->numsel + 1, sizeof *cts);
302 ctp = cts;
303
304 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
305 if (is_selected(mp, msgnum)) {
306 char *msgnam;
307
308 msgnam = m_name (msgnum);
309 if ((ct = parse_mime (msgnam))) {
310 *ctp++ = ct;
311 if (outfile) {
312 ct->c_storage = mh_xstrdup(outfile);
313 }
314 }
315 }
316 }
317 }
318
319 if (!*cts)
320 done (1);
321
322 userrs = true;
323 SIGNAL (SIGQUIT, quitser);
324 SIGNAL (SIGPIPE, pipeser);
325
326 /*
327 * Get the associated umask for the relevant contents.
328 */
329 for (ctp = cts; *ctp; ctp++) {
330 struct stat st;
331
332 ct = *ctp;
333 if (type_ok (ct, 1) && !ct->c_umask) {
334 if (stat (ct->c_file, &st) != NOTOK)
335 ct->c_umask = ~(st.st_mode & 0777);
336 else
337 ct->c_umask = ~m_gmprot();
338 }
339 }
340
341 /*
342 * Store the message content
343 */
344 info = mhstoreinfo_create (cts, cwd, clobbersw, autosw, verbosw);
345 store_all_messages (info);
346 files_not_clobbered = mhstoreinfo_files_not_clobbered(info);
347 mhstoreinfo_free(info);
348
349 /* Now free all the structures for the content */
350 for (ctp = cts; *ctp; ctp++)
351 free_content (*ctp);
352
353 free (cts);
354 cts = NULL;
355
356 /* If reading from a folder, do some updating */
357 if (mp) {
358 context_replace (pfolder, folder);/* update current folder */
359 seq_setcur (mp, mp->hghsel); /* update current message */
360 seq_save (mp); /* synchronize sequences */
361 context_save (); /* save the context file */
362 }
363
364 done (files_not_clobbered);
365 return 1;
366 }
367
368
369 static void
370 pipeser (int i)
371 {
372 if (i == SIGQUIT) {
373 fflush (stdout);
374 fprintf (stderr, "\n");
375 fflush (stderr);
376 }
377
378 done (1);
379 /* NOTREACHED */
380 }