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