1 /*
2  * Copyright 1998-2002 Ben Smithurst <ben@smithurst.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * Splits a mbox into its individual messages.
29  */
30 
31 #include "misc.h"
32 
33 const char rcsid[] =
34 	"$BCPS: src/mailutils/mail-split.c,v 1.29 2003/01/19 19:18:25 ben Exp $";
35 
36 int split_maildir(int, char *, FILE *);
37 int split_file(FILE *, FILE *);
38 void usage(void);
39 
40 char *prefix, *suffix;
41 int fflag;
42 
43 int
main(int argc,char * argv[])44 main(int argc, char *argv[]) {
45 	int ch;
46 
47 	/* defaults */
48 	prefix = "%n-";
49 	suffix = "";
50 	fflag = 0;
51 
52 	while ((ch = getopt(argc, argv, "fp:s:")) != -1)
53 		switch (ch) {
54 		case 'f':
55 			fflag = 1;
56 			break;
57 		case 'p':
58 			prefix = optarg;
59 			break;
60 		case 's':
61 			suffix = optarg;
62 			break;
63 		default:
64 			usage();
65 			/* NOTREACHED */
66 		}
67 
68 	argc -= optind;
69 	argv += optind;
70 
71 	return (process(argv, split_file, split_maildir) ? 0 : 1);
72 }
73 
74 int
split_maildir(int reset,char * fn,FILE * in_fp)75 split_maildir(int reset, char *fn, FILE *in_fp) {
76 
77 	if (reset)
78 		warnx("useless on Maildir %s", curfile);
79 
80 	return (RET_NOCHANGE);
81 }
82 
83 /*
84  * Expands the given prefix, number and suffix into `buf', which has
85  * room for `size' characters including NUL. %N in prefix or suffix
86  * expands to the value of `curfile', %n to the last part of `curfile'
87  * (i.e. from the character after the last slash onwards, or the entire
88  * string if it doesn't contain any slashes.)
89  */
90 int
fn_expand(char * buf,int size,char * prefix,char * suffix,int num)91 fn_expand(char *buf, int size, char *prefix, char *suffix, int num) {
92 	char numbuf[32];
93 	char *b, *p;
94 	int len, i;
95 	int plen, slen, nlen, nblen, numlen;
96 
97 	snprintf(numbuf, sizeof numbuf, "%06d", num);
98 	numlen = strlen(numbuf);
99 
100 	plen = strlen(prefix);
101 	slen = strlen(suffix);
102 	nlen = strlen(curfile);
103 	if ((b = strrchr(curfile, '/')) == NULL) {
104 		b = curfile;
105 		nblen = nlen;
106 	} else {
107 		b++;
108 		nblen = strlen(b);
109 	}
110 
111 	/* Do the expansion */
112 	len = 0;
113 	for (i = 0; i < 3; i++) {
114 		/* Second time through the loop, just copy the number */
115 		if (i == 1) {
116 			if (len + numlen >= size)
117 				return (0);
118 
119 			strcpy(buf + len, numbuf);
120 			len += numlen;
121 			continue;
122 		}
123 
124 		/*
125 		 * First time through (i == 0), process the prefix, otherwise
126 		 * (must be third time) process suffix.
127 		 */
128 		for (p = (i ? suffix : prefix); *p != '\0'; p++)
129 			switch (*p) {
130 			case '%':
131 				switch (*++p) {
132 				case 'n':
133 					if (len + nblen >= size)
134 						return (0);
135 					strcpy(buf + len, b);
136 					len += nblen;
137 					break;
138 				case 'N':
139 					if (len + nlen >= size)
140 						return (0);
141 					strcpy(buf + len, curfile);
142 					len += nlen;
143 					break;
144 				case '\0':
145 					p--; /* So that for() loop sees \0 */
146 					/* FALLTHROUGH */
147 				case '%':
148 					if (len + 1 >= size)
149 						return (0);
150 					buf[len++] = '%';
151 					break;
152 				default:
153 					if (len + 2 >= size)
154 						return (0);
155 					buf[len++] = '%';
156 					buf[len++] = *p;
157 					break;
158 				}
159 				break;
160 			default:
161 				if (len + numlen >= size)
162 					return (0);
163 				buf[len++] = *p;
164 				break;
165 			}
166 	}
167 	buf[len] = '\0';
168 
169 	return (1);
170 }
171 
172 int
split_file(FILE * in,FILE * junk)173 split_file(FILE *in, FILE *junk) {
174 	FILE *out;
175 	char *line, filename[MAXPATHLEN];
176 	int e, fd, from_line, num;
177 	size_t len;
178 	mode_t mask;
179 
180 	/* Set a restrictive umask since files containing mail are created */
181 	mask = umask(077);
182 
183 	num = 0;
184 	from_line = 0;
185 	out = NULL;
186 
187 	while ((line = gl_getline(in)) != NULL) {
188 		len = strlen(line);
189 		chop(line, len);
190 
191 		if (is_from(line, 1)) {
192 			num++;
193 			from_line = 1;
194 			if (out) {
195 				fclose(out);
196 				out = NULL;
197 			}
198 
199 			/*
200 			 * build the new filename, increase num until
201 			 * a unique name is found.
202 			 */
203 			for (;;) {
204 				/* Do expansion */
205 				if (!fn_expand(filename, sizeof filename,
206 				  prefix, suffix, num))
207 				{
208 					warnx("fn_expand failed");
209 					umask(mask);
210 					return (RET_LOCALERROR);
211 				}
212 
213 				/* Attempt to create file */
214 				fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
215 				  0600);
216 				if (fd >= 0)
217 					break;
218 
219 				/* EEXIST is ok, anything else isn't. */
220 				if (errno != EEXIST) {
221 					e = errno;
222 					umask(mask);
223 					errno = e;
224 					return (RET_ERROR);
225 				}
226 
227 				/* Protect against overflow. */
228 				if (num == INT_MAX) {
229 					warnx("couldn't get unique name");
230 					umask(mask);
231 					return (RET_LOCALERROR);
232 				}
233 
234 				num++;
235 			}
236 
237 			if ((out = fdopen(fd, "w")) == NULL) {
238 				e = errno;
239 				umask(mask);
240 				errno = e;
241 				return (RET_ERROR);
242 			}
243 		} else
244 			from_line = 0;
245 
246 		if (out && (fflag || !from_line))
247 			fprintf(out, "%s\n", line);
248 	}
249 
250 	gl_destroy(in);
251 
252 	if (out != NULL)
253 		fclose(out);
254 
255 	umask(mask);
256 	return (RET_NOCHANGE);
257 }
258 
259 void
usage(void)260 usage(void) {
261 	fprintf(stderr, "usage: mail-split [-f] [-p prefix] [-s suffix]\n"
262 			"       [files ...]\n");
263 	exit(EX_USAGE);
264 }
265