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