Removed RFC 1864 (Content-MD5) support. I.e. -check switches.
[mmh] / uip / mhtest.c
1 /*
2 ** mhtest.c -- test harness for MIME routines
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <fcntl.h>
11 #include <h/signals.h>
12 #include <errno.h>
13 #include <signal.h>
14 #include <h/tws.h>
15 #include <h/mime.h>
16 #include <h/mhparse.h>
17 #include <h/mhcachesbr.h>
18 #include <h/utils.h>
19
20 static struct swit switches[] = {
21 #define VERBSW  0
22         { "verbose", 0 },
23 #define NVERBSW  1
24         { "noverbose", 0 },
25 #define FILESW  2
26         { "file file", 0 },
27 #define OUTFILESW  3
28         { "outfile file", 0 },
29 #define PARTSW  4
30         { "part number", 0 },
31 #define TYPESW  5
32         { "type content", 0 },
33 #define RCACHESW  6
34         { "rcache policy", 0 },
35 #define WCACHESW  7
36         { "wcache policy", 0 },
37 #define VERSIONSW  8
38         { "version", 0 },
39 #define HELPSW  9
40         { "help", 0 },
41 #define DEBUGSW  10
42         { "debug", -5 },
43         { NULL, 0 }
44 };
45
46
47 int ebcdicsw = 0;  /* hack for linking purposes */
48
49 /* mhparse.c */
50 extern char *tmp;  /* directory to place temp files */
51
52 /* mhcachesbr.c */
53 extern int rcachesw;
54 extern int wcachesw;
55 extern char *cache_public;
56 extern char *cache_private;
57
58 /* mhmisc.c */
59 extern int npart;
60 extern int ntype;
61 extern char *parts[NPARTS + 1];
62 extern char *types[NTYPES + 1];
63 extern int userrs;
64
65 /*
66 ** This is currently needed to keep mhparse happy.
67 ** This needs to be changed.
68 */
69 pid_t xpid  = 0;
70
71 int debugsw = 0;
72 int verbosw = 0;
73
74 #define quitser pipeser
75
76 /* mhparse.c */
77 CT parse_mime(char *);
78
79 /* mhoutsbr.c */
80 int output_message(CT, char *);
81
82 /* mhmisc.c */
83 int part_ok(CT, int);
84 int type_ok(CT, int);
85 void set_endian(void);
86 void flush_errors(void);
87
88 /* mhfree.c */
89 void free_content(CT);
90 extern CT *cts;
91 void freects_done(int) NORETURN;
92
93 /*
94 ** static prototypes
95 */
96 static int write_content(CT *, char *);
97 static void pipeser(int);
98
99
100 int
101 main(int argc, char **argv)
102 {
103         int msgnum, *icachesw;
104         char *cp, *file = NULL, *folder = NULL;
105         char *maildir, buf[100], *outfile = NULL;
106         char **argp, **arguments;
107         struct msgs_array msgs = { 0, 0, NULL };
108         struct msgs *mp = NULL;
109         CT ct, *ctp;
110
111         done=freects_done;
112
113 #ifdef LOCALE
114         setlocale(LC_ALL, "");
115 #endif
116         invo_name = mhbasename(argv[0]);
117
118         /* read user profile/context */
119         context_read();
120
121         arguments = getarguments(invo_name, argc, argv, 1);
122         argp = arguments;
123
124         /*
125         ** Parse arguments
126         */
127         while ((cp = *argp++)) {
128                 if (*cp == '-') {
129                         switch (smatch(++cp, switches)) {
130                         case AMBIGSW:
131                                 ambigsw(cp, switches);
132                                 done(1);
133                         case UNKWNSW:
134                                 adios(NULL, "-%s unknown", cp);
135
136                         case HELPSW:
137                                 snprintf(buf, sizeof(buf), "%s [+folder] [msgs] [switches]", invo_name);
138                                 print_help(buf, switches, 1);
139                                 done(1);
140                         case VERSIONSW:
141                                 print_version(invo_name);
142                                 done(1);
143
144                         case RCACHESW:
145                                 icachesw = &rcachesw;
146                                 goto do_cache;
147                         case WCACHESW:
148                                 icachesw = &wcachesw;
149 do_cache:
150                                 if (!(cp = *argp++) || *cp == '-')
151                                         adios(NULL, "missing argument to %s",
152                                                         argp[-2]);
153                                 switch (*icachesw = smatch(cp, caches)) {
154                                 case AMBIGSW:
155                                         ambigsw(cp, caches);
156                                         done(1);
157                                 case UNKWNSW:
158                                         adios(NULL, "%s unknown", cp);
159                                 default:
160                                         break;
161                                 }
162                                 continue;
163
164                         case PARTSW:
165                                 if (!(cp = *argp++) || *cp == '-')
166                                         adios(NULL, "missing argument to %s", argp[-2]);
167                                 if (npart >= NPARTS)
168                                         adios(NULL, "too many parts (starting with %s), %d max", cp, NPARTS);
169                                 parts[npart++] = cp;
170                                 continue;
171
172                         case TYPESW:
173                                 if (!(cp = *argp++) || *cp == '-')
174                                         adios(NULL, "missing argument to %s", argp[-2]);
175                                 if (ntype >= NTYPES)
176                                         adios(NULL, "too many types (starting with %s), %d max",
177                                                    cp, NTYPES);
178                                 types[ntype++] = cp;
179                                 continue;
180
181                         case FILESW:
182                                 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
183                                         adios(NULL, "missing argument to %s",
184                                                         argp[-2]);
185                                 file = *cp == '-' ? cp : getcpy(expanddir(cp));
186                                 continue;
187
188                         case OUTFILESW:
189                                 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
190                                         adios(NULL, "missing argument to %s",
191                                                         argp[-2]);
192                                 outfile = *cp == '-' ? cp : getcpy(expanddir(cp));
193                                 continue;
194
195                         case VERBSW:
196                                 verbosw = 1;
197                                 continue;
198                         case NVERBSW:
199                                 verbosw = 0;
200                                 continue;
201                         case DEBUGSW:
202                                 debugsw = 1;
203                                 continue;
204                         }
205                 }
206                 if (*cp == '+' || *cp == '@') {
207                         if (folder)
208                                 adios(NULL, "only one folder at a time!");
209                         else
210                                 folder = getcpy(expandfol(cp));
211                 } else
212                         app_msgarg(&msgs, cp);
213         }
214
215         /* null terminate the list of acceptable parts/types */
216         parts[npart] = NULL;
217         types[ntype] = NULL;
218
219         set_endian();
220
221         if (outfile == NULL)
222                 adios(NULL, "must specify output file");
223
224         /* Check for public cache location */
225         if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
226                 cache_public = NULL;
227
228         /* Check for private cache location */
229         if (!(cache_private = context_find(nmhprivcache)))
230                 cache_private = ".cache";
231         cache_private = getcpy(toabsdir(cache_private));
232
233         /*
234         ** Check for storage directory.  If specified,
235         ** then store temporary files there.  Else we
236         ** store them in standard nmh directory.
237         */
238         if ((cp = context_find(nmhstorage)) && *cp)
239                 tmp = concat(cp, "/", invo_name, NULL);
240         else
241                 tmp = getcpy(toabsdir(invo_name));
242
243         if (file && msgs.size)
244                 adios(NULL, "cannot specify msg and file at same time!");
245
246         /*
247         ** check if message is coming from file
248         */
249         if (file) {
250                 if (!(cts = (CT *) calloc((size_t) 2, sizeof(*cts))))
251                         adios(NULL, "out of memory");
252                 ctp = cts;
253
254                 if ((ct = parse_mime(file)))
255                         *ctp++ = ct;
256         } else {
257                 /*
258                 ** message(s) are coming from a folder
259                 */
260                 if (!msgs.size)
261                         app_msgarg(&msgs, seq_cur);
262                 if (!folder)
263                         folder = getcurfol();
264                 maildir = toabsdir(folder);
265
266                 if (chdir(maildir) == NOTOK)
267                         adios(maildir, "unable to change directory to");
268
269                 /* read folder and create message structure */
270                 if (!(mp = folder_read(folder)))
271                         adios(NULL, "unable to read folder %s", folder);
272
273                 /* check for empty folder */
274                 if (mp->nummsg == 0)
275                         adios(NULL, "no messages in %s", folder);
276
277                 /* parse all the message ranges/sequences and set SELECTED */
278                 for (msgnum = 0; msgnum < msgs.size; msgnum++)
279                         if (!m_convert(mp, msgs.msgs[msgnum]))
280                                 done(1);
281                 seq_setprev(mp);  /* set the previous-sequence */
282
283                 if (!(cts = (CT *) calloc((size_t) (mp->numsel + 1),
284                                 sizeof(*cts))))
285                         adios(NULL, "out of memory");
286                 ctp = cts;
287
288                 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
289                         if (is_selected(mp, msgnum)) {
290                                 char *msgnam;
291
292                                 msgnam = m_name(msgnum);
293                                 if ((ct = parse_mime(msgnam)))
294                                         *ctp++ = ct;
295                         }
296                 }
297         }
298
299         if (!*cts)
300                 done(1);
301
302         userrs = 1;
303         SIGNAL(SIGQUIT, quitser);
304         SIGNAL(SIGPIPE, pipeser);
305
306         /*
307         ** Get the associated umask for the relevant contents.
308         */
309         for (ctp = cts; *ctp; ctp++) {
310                 struct stat st;
311
312                 ct = *ctp;
313                 if (type_ok(ct, 1) && !ct->c_umask) {
314                         if (stat(ct->c_file, &st) != NOTOK)
315                                 ct->c_umask = ~(st.st_mode & 0777);
316                         else
317                                 ct->c_umask = ~m_gmprot();
318                 }
319         }
320
321         /*
322         ** Write the content to a file
323         */
324         write_content(cts, outfile);
325
326         /* Now free all the structures for the content */
327         for (ctp = cts; *ctp; ctp++)
328                 free_content(*ctp);
329
330         free((char *) cts);
331         cts = NULL;
332
333         /* If reading from a folder, do some updating */
334         if (mp) {
335                 context_replace(curfolder, folder); /* update current folder */
336                 seq_setcur(mp, mp->hghsel);  /* update current message */
337                 seq_save(mp);  /* synchronize sequences  */
338                 context_save();  /* save the context file  */
339         }
340
341         done(0);
342         return 1;
343 }
344
345
346 static int
347 write_content(CT *cts, char *outfile)
348 {
349         CT ct, *ctp;
350
351         for (ctp = cts; *ctp; ctp++) {
352                 ct = *ctp;
353                 output_message(ct, outfile);
354         }
355
356         flush_errors();
357         return OK;
358 }
359
360
361 static void
362 pipeser(int i)
363 {
364         if (i == SIGQUIT) {
365                 unlink("core");
366                 fflush(stdout);
367                 fprintf(stderr, "\n");
368                 fflush(stderr);
369         }
370
371         done(1);
372         /* NOTREACHED */
373 }