1 /*
2  *  incm - incorporating new mails
3  *
4  *  Author:  Yasunari Momoi <momo@bug.org>
5  *  Created: 2000/10/19
6  */
7 
8 #include "mew.h"
9 
10 private char version_message[] = "version 6.8 20180607 Kazu Yamamoto";
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <stdarg.h>
15 #include <ctype.h>
16 #include <string.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <pwd.h>
20 #include <signal.h>
21 #include <errno.h>
22 
23 #if TIME_WITH_SYS_TIME
24 # include <sys/time.h>
25 # include <time.h>
26 #else
27 # if HAVE_SYS_TIME_H
28 #  include <sys/time.h>
29 # else
30 #  include <time.h>
31 # endif
32 #endif
33 
34 #if HAVE_DIRENT_H
35 # include <dirent.h>
36 #endif
37 
38 #if HAVE_FCNTL_H
39 # include <fcntl.h>
40 #endif
41 
42 #if HAVE_SYS_FILE_H
43 # include <sys/file.h>
44 #endif
45 
46 #if HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 
50 enum MBOXTYPE {
51 	T_UNKNOWN,
52 	T_MAILDIR,
53 	T_MBOX,
54 	T_STDIN,
55 };
56 
57 enum MBOXSTATE {
58 	ST_UNKNOWN,
59 	ST_HEADER,
60 	ST_BODY_AFTER_EMPTY_LINE,
61 	ST_BODY,
62 };
63 
64 #define FBUFSIZ		(BUFSIZ * 32)
65 #ifndef PATH_MAX
66 # define PATH_MAX	1024
67 #endif
68 
69 private char	FileBuf[FBUFSIZ];
70 private char	Mbox[PATH_MAX];
71 private char	MboxLock[PATH_MAX];
72 private char	*InboxDir = NULL;
73 private char	*Suffix = NULL;
74 private int	Use_Suffix = FALSE;
75 private int	Suffix_len;
76 private int	MboxType;
77 private int	Backup;
78 private int	GetCur;
79 private int	UseCL;
80 private int	PreserveUnixFrom;
81 private int	CreateMTime = TRUE;
82 private int	FileMode = S_IRUSR | S_IWUSR;
83 private int	Exit = 0;
84 
85 /****************************************************************
86  *
87  * prototype
88  *
89  */
90 
91 private void	error(const char *, ...);
92 private void	usage(const char *);
93 private void	help(const char *);
94 private void	version(const char *);
95 private void	init_env(int, char **);
96 private int	get_number(char *);
97 private int	get_last_seq(void);
98 private int	compare_string(char **, char **);
99 private int	copyfile(char *, char *);
100 private void	movefile(char *, char *, char *, int);
101 private int	maildir_names(const char *, char **, char **, char **);
102 private int	new_inbox_file(int, char[]);
103 private FILE	*open_new_inbox_file(int *, char[]);
104 private int	get_from_dir(int, char *, char *, int);
105 private int	process_maildir(int);
106 private int	lock_mbox(char *);
107 private void	unlock_mbox(char *);
108 private int	process_mbox(int);
109 private int	process_stdin(int);
110 private void	process(void);
111 private int	check_mbox_type(const char *);
112 private void	sanity_check(void);
113 
114 
115 #if !HAVE_FLOCK
116 # if HAVE_LOCKF
117 #  define flock(a, b)	lockf(a, b, 0)
118 #  define LOCK_EX	F_LOCK
119 # endif
120 #endif
121 
122 #ifndef S_ISDIR
123 # define S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
124 #endif
125 #ifndef S_ISREG
126 # define S_ISREG(m)	(((m) & S_IFMT) == S_IFREG)
127 #endif
128 
129 private char *
Getlogin(void)130 Getlogin(void)
131 {
132 #ifdef HAVE_GETLOGIN
133 	{
134 		char *user;
135 		if ((user = getlogin()) != NULL)
136 			return user;
137 	}
138 #endif
139 #ifdef HAVE_GETPWUID
140 	{
141 		struct passwd *pw;
142 		if ((pw = getpwuid(getuid())) == NULL)
143 			return NULL;
144 		else
145 			return pw->pw_name;
146 	}
147 #endif
148 	return NULL;
149 }
150 
151 private char *
Gethomedir(void)152 Gethomedir(void)
153 {
154 	char *home;
155 
156 	if ((home = getenv("HOME")) != NULL)
157 		return home;
158 #ifdef HAVE_GETPWUID
159 	{
160 		struct passwd *pw;
161 		if ((pw = getpwuid(getuid())) != NULL)
162 			return pw->pw_dir;
163 	}
164 #endif
165 	return NULL;
166 }
167 
168 private void
error(const char * fmt,...)169 error(const char *fmt, ...)
170 {
171 	va_list ap;
172 	if (warn_prog != NULL)
173 		fprintf(stderr, "%s: ", warn_prog);
174 	va_start(ap, fmt);
175 	if (fmt != NULL)
176 		vfprintf(stderr, fmt, ap);
177 	va_end(ap);
178 	fprintf(stderr, "\n");
179 	if (strlen(MboxLock) > 0)
180 		unlock_mbox(MboxLock);
181 	exit(EXIT_FAILURE);
182 }
183 
184 /****************************************************************
185  *
186  * options and usages
187  *
188  */
189 
190 
191 #define LOCK_SUFFIX	".lock"
192 #define MAILDIR		"Maildir"
193 #define MAILDIR_NEW	"new"
194 #define MAILDIR_CUR	"cur"
195 #define MAILDIR_TMP	"tmp"
196 
197 private void
usage(const char * progname)198 usage(const char *progname) {
199 	fprintf(stderr, "Usage: %s [-abchosv] [-d maildir] [-i inboxdir] [-x suffix]\n", progname);
200 }
201 
202 private const char *
203 help_message[] = {
204 	"    -h            Display this help message.",
205 	"    -v            Display the version.",
206 	"    -d <mail>     Path to mbox/maildir.",
207 	"    -m <mail>     Path to mbox/maildir.",
208 	"    -s            Read one mail from stdin instead of mbox/maildir.",
209 	"    -i <inboxdir> Path to inboxdir.",
210 	"    -b            Backup mail.",
211 	"                    mbox: No truncate mbox file.",
212 	"                    maildir: To maildir/cur directory.",
213 	"    -a            Retrieve all mail from maildir/{cur,new} directory.",
214 	"                  (no backup) (for maildir)",
215 	"    -c            Use Content-Length: field. (for mbox)",
216 	"    -u            Don't create inboxdir/.mew-mtime file.",
217 	"    -f            Preserve Unix From (Envelope Sender). (for mbox)",
218 	"    -p <fmode>    Specify file permission. (for mbox)",
219 	"    -o            Use the suffix when creating messages.",
220 	"    -x <suffix>   Use this suffix.",
221 	NULL
222 };
223 
224 private void
help(const char * progname)225 help(const char *progname) {
226 	const char **p = help_message;
227 
228 	fprintf(stderr, "Help: %s\n\n", progname);
229 	fprintf(stderr, " Incorporating new mails.\n\n");
230 	usage(progname);
231 	while (*p) fprintf(stderr, "%s\n", *p++);
232 }
233 
234 private void
version(const char * progname)235 version(const char *progname) {
236 	fprintf(stderr, "%s %s\n", progname, version_message);
237 }
238 
239 private int
check_mbox_type(const char * path)240 check_mbox_type(const char *path)
241 {
242 	struct stat sb;
243 
244 	if (stat(path, &sb))
245 		return T_UNKNOWN;
246 	if (S_ISDIR(sb.st_mode)) {
247 		char* newdir;
248 		char* curdir;
249 
250 		if (maildir_names(path, &newdir, &curdir, NULL))
251 			error("maildir name is not set (%s)", path);
252 		if (stat(newdir, &sb))
253 			return T_UNKNOWN;
254 		if (!S_ISDIR(sb.st_mode) || access(newdir, R_OK|W_OK|X_OK))
255 			return T_UNKNOWN;
256 
257 		if (Backup) {
258 			if (stat(curdir, &sb))
259 				return T_UNKNOWN;
260 			if (!S_ISDIR(sb.st_mode) || access(curdir, W_OK))
261 				return T_UNKNOWN;
262 		}
263 		return T_MAILDIR;
264 	}
265 	else if (S_ISREG(sb.st_mode))
266 		return T_MBOX;
267 	else
268 		return T_UNKNOWN;
269 }
270 
271 private const char *
272 mbox_path_list[] = {
273 	"/var/mail/",
274 	"/var/spool/mail/",
275 	"/usr/spool/mail/",
276 	NULL
277 };
278 
279 private void
search_mbox_path(void)280 search_mbox_path(void)
281 {
282 	char *home, *mail, *user;
283 
284 	if ((home = Gethomedir()) != NULL) {
285 		if (strlen(home) + 9 > PATH_MAX)
286 			error("pathname too long (%s)", home);
287 		snprintf(Mbox, sizeof(Mbox), "%s/%s", home, MAILDIR);
288 		if (check_mbox_type(Mbox) != T_UNKNOWN)
289 			return;
290 	}
291 	if ((mail = getenv("MAIL")) != NULL) {
292 		if (strlen(mail) + 1 > PATH_MAX)
293 			error("pathname too long (%s)", mail);
294 		STRCPY(Mbox, mail);
295 		if (check_mbox_type(Mbox) != T_UNKNOWN)
296 			return;
297 	}
298 	if ((user = Getlogin()) != NULL) {
299 		int i;
300 		for (i = 0; mbox_path_list[i] != NULL; i++) {
301 			if (strlen(mbox_path_list[i]) + strlen(user) + 1
302 			    > PATH_MAX)
303 				error("pathname too long (%s)", user);
304 			snprintf(Mbox, sizeof(Mbox), "%s%s", mbox_path_list[i], user);
305 			if (check_mbox_type(Mbox) != T_UNKNOWN)
306 				return;
307 		}
308 	}
309 	snprintf(Mbox, sizeof(Mbox), ".");
310 	return;
311 }
312 
313 void
sig_exit(int signo)314 sig_exit(int signo)
315 {
316 	Exit = 1;
317 }
318 
319 void
sig_ignore(int signo)320 sig_ignore(int signo)
321 {
322 	/* ignore signal */
323 }
324 
325 private void
set_sighandler(void)326 set_sighandler(void)
327 {
328 	if (signal(SIGHUP, sig_ignore) == SIG_ERR)
329 		error("can't catch SIGHUP\n");
330 	if (signal(SIGINT, sig_exit) == SIG_ERR)
331 		error("can't catch SIGINT\n");
332 	if (signal(SIGALRM, sig_ignore) == SIG_ERR)
333 		error("can't catch SIGALRM\n");
334 	if (signal(SIGTERM, sig_ignore) == SIG_ERR)
335 		error("can't catch SIGTERM\n");
336 }
337 
338 private void
init_env(int argc,char ** argv)339 init_env(int argc, char **argv)
340 {
341 	set_sighandler();
342 	STRDUP(InboxDir, ".");
343 	MboxType = T_UNKNOWN;
344 	Backup = FALSE;
345 	UseCL = FALSE;
346 	search_mbox_path();
347 	STRDUP(Suffix, SUFFIX);
348 	Suffix_len = strlen(Suffix);
349 }
350 
351 private int
get_number(char * s)352 get_number(char *s)
353 {
354 	int num = 0;
355 	unsigned char c;
356 
357 	while ((c = *s) != NUL && isdigit(c)) {
358 		num = num * 10 + c - '0';
359 		s++;
360 	}
361 
362 	if (num == 0)
363 		return 0;
364 	else if (*s == NUL)
365 		return num;
366 	else if (strncmp(s, Suffix, Suffix_len) == 0 && s[Suffix_len] == NUL)
367 		return num;
368 	else
369 		return 0;
370 }
371 
372 private int
get_last_seq(void)373 get_last_seq(void)
374 {
375 	struct dirent *dp;
376 	DIR *dirp;
377 	int last = 0;
378 	int seq;
379 
380 	if ((dirp = opendir(InboxDir)) == NULL)
381 		error("opendir(%s)", InboxDir);
382 	while ((dp = readdir(dirp)) != NULL) {
383 		if ((seq = get_number(dp->d_name)) == 0)
384 			continue;
385 		last = last > seq ? last : seq;
386 	}
387 	closedir(dirp);
388 	return last;
389 }
390 
391 private int
compare_string(char ** i,char ** j)392 compare_string(char **i, char **j)
393 {
394 	return strcmp(*i, *j);
395 }
396 
397 private int
copyfile(char * src,char * dst)398 copyfile(char *src, char *dst)
399 {
400 	struct timeval tv[2];
401 	struct stat sb;
402 	int srcfd, dstfd;
403 	ssize_t rlen, wlen;
404 
405 	if ((srcfd = open(src, O_RDONLY, 0)) < 0)
406 		error("open(%s) for read", src);
407 	if (fstat(srcfd, &sb))
408 		error("fstat(%s)", src);
409 	if ((dstfd = open(dst, O_EXCL | O_CREAT | O_WRONLY | O_TRUNC, 0)) < 0)
410 		error("open(%s) for write", dst);
411 	while ((rlen = read(srcfd, FileBuf, FBUFSIZ)) > 0) {
412 		if ((wlen = write(dstfd, FileBuf, rlen)) != rlen) {
413 			if (close(dstfd))
414 				goto werr;
415 			unlink(dst);
416 			error("write(%s) (read %d bytes/write %d bytes)",
417 			      dst, rlen, wlen);
418 		}
419 	}
420 	if (rlen < 0) {
421 		if (close(dstfd))
422 			goto werr;
423 		unlink(dst);
424 		error("read(%s)", src);
425 	}
426 	close(srcfd);
427 
428 	tv[0].tv_sec = sb.st_atime;
429 	tv[0].tv_usec = 0;
430 	tv[1].tv_sec = sb.st_mtime;
431 	tv[1].tv_usec = 0;
432 #if HAVE_FUTIMES
433 	if (futimes(dstfd, tv))
434 		warning("futimes(%s) failed", dst);
435 #endif
436 #if HAVE_FCHMOD
437 	if (fchmod(dstfd, sb.st_mode))
438 		warning("fchmod(%s) failed", dst);
439 #endif
440 	close(dstfd);
441 #if !HAVE_FUTIMES
442 	if (utimes(dst, tv))
443 		warning("utimes(%s) failed", dst);
444 #endif
445 #if !HAVE_FCHMOD
446 	if (chmod(dst, sb.st_mode))
447 		warning("chmod(%s) failed", dst);
448 #endif
449 	return 0;
450 
451  werr:
452 	close(srcfd);
453 	return -1;
454 }
455 
456 private void
movefile(char * fromfile,char * tofile,char * backupfile,int backup)457 movefile(char *fromfile, char *tofile, char *backupfile, int backup)
458 {
459 	if (backup && backupfile != NULL) {
460 		if (copyfile(fromfile, tofile))
461 			error("copyfile(%s, %s)", fromfile, tofile);
462 		if (rename(fromfile, backupfile))
463 			error("rename(%s, %s)", fromfile, backupfile);
464 	}
465 	else if (backup) {
466 		if (copyfile(fromfile, tofile))
467 			error("copyfile(%s, %s)", fromfile, tofile);
468 	}
469 	else {
470 		if (rename(fromfile, tofile)) {
471 			if (errno != EXDEV)
472 				error("rename(%s, %s)", fromfile, tofile);
473 			if (copyfile(fromfile, tofile))
474 				error("copyfile(%s, %s)", fromfile, tofile);
475 			if (unlink(fromfile))
476 				error("unlink(%s)", fromfile);
477 		}
478 	}
479 }
480 
481 /* maildir has {new,cur,tmp} subdirectory. */
482 private int
maildir_names(const char * maildir,char ** newdir,char ** curdir,char ** tmpdir)483 maildir_names(const char *maildir, char **newdir, char **curdir, char **tmpdir)
484 {
485 	int len = strlen(maildir) + strlen(MAILDIR_NEW) + 2;
486 
487 	if (maildir == NULL || strlen(maildir) <= 0)
488 		return -1;
489 	if (newdir != NULL) {
490 		MALLOC(*newdir, len);
491 		snprintf(*newdir, len, "%s/%s", maildir, MAILDIR_NEW);
492 	}
493 	if (curdir != NULL) {
494 		MALLOC(*curdir, len);
495 		snprintf(*curdir, len, "%s/%s", maildir, MAILDIR_CUR);
496 	}
497 	if (tmpdir != NULL) {
498 		MALLOC(*tmpdir, len);
499 		snprintf(*tmpdir, len, "%s/%s", maildir, MAILDIR_TMP);
500 	}
501 	return 0;
502 }
503 
504 /* *WARNING* inboxfile requires PATH_MAX bytes */
505 private int
new_inbox_file(int seq,char inboxfile[])506 new_inbox_file(int seq, char inboxfile[])
507 {
508 	char num[PATH_MAX];
509 	char *suffix = (Use_Suffix == YES) ? Suffix : "";
510 
511 	do {
512 		snprintf(num, sizeof(num), "%d%s", ++seq, suffix);
513 		if (strlen(InboxDir) + strlen(num) + 2 > PATH_MAX)
514 			error("pathname too long (%s/%s)", InboxDir, num);
515 		snprintf(inboxfile, PATH_MAX, "%s/%s", InboxDir, num);
516 		if (access(inboxfile, F_OK) && errno == ENOENT)
517 			break;
518 	} while (TRUE);
519 	return seq;
520 }
521 
522 /* *WARNING* inboxfile requires PATH_MAX bytes */
523 private FILE *
open_new_inbox_file(int * seq,char inboxfile[])524 open_new_inbox_file(int *seq, char inboxfile[])
525 {
526 	char num[PATH_MAX];
527 	int flag = O_EXCL | O_CREAT | O_WRONLY;
528 	int fd;
529 	FILE *fp = NULL;
530 	char *suffix = (Use_Suffix == YES) ? Suffix : "";
531 
532 	for (;;) {
533 		snprintf(num, sizeof(num), "%d%s", ++*seq, suffix);
534 		if (strlen(InboxDir) + strlen(num) + 2 > PATH_MAX)
535 			error("pathname too long (%s/%s)", InboxDir, num);
536 		snprintf(inboxfile, PATH_MAX, "%s/%s", InboxDir, num);
537 		if ((fd = open(inboxfile, flag, FileMode)) >= 0 ||
538 		    errno != EEXIST)
539 			break;
540 		usleep(rand() % 199);
541 	}
542 	if (fd < 0)
543 		warning("open(%s) for write", inboxfile);
544 	else {
545 		if ((fp = fdopen(fd, FDWRITE)) == NULL)
546 			warning("open(%s) for write", inboxfile);
547 	}
548 	return fp;
549 }
550 
551 private int
get_from_dir(int seq,char * fromdir,char * backupdir,int backup)552 get_from_dir(int seq, char *fromdir, char *backupdir, int backup)
553 {
554 	struct stat sb;
555 	struct dirent *dp;
556 	DIR *dirp;
557 	char mailfile[PATH_MAX];
558 	char inboxfile[PATH_MAX];
559 	char backupfile[PATH_MAX];
560 	char **list;
561 	int listsize = BUFSIZ;
562 	int listend = 0;
563 	int i;
564 
565 	MALLOC(list, sizeof(char *)*listsize);
566 	if ((dirp = opendir(fromdir)) == NULL)
567 		error("opendir(%s)", fromdir);
568 	while ((dp = readdir(dirp)) != NULL) {
569 		if (strlen(fromdir) + strlen(dp->d_name) + 2 > PATH_MAX)
570 			error("pathname too long (%s/%s)",
571 			      fromdir, dp->d_name);
572 		snprintf(mailfile, sizeof(mailfile), "%s/%s", fromdir, dp->d_name);
573 		if (stat(mailfile, &sb))
574 			continue;
575 		if (!(S_ISREG(sb.st_mode) && (sb.st_mode & S_IRUSR)))
576 			continue;
577 		if (listend >= listsize) {
578 			listsize *= 2;
579 			if ((list = (char **)
580 			     realloc(list, sizeof(char *)*listsize)) == NULL)
581 				error("realloc");
582 		}
583 		STRDUP(list[listend++], dp->d_name);
584 	}
585 	closedir(dirp);
586 
587 	qsort(list, listend, sizeof(char *),
588 	      (int (*)(const void *, const void *))compare_string);
589 
590 	for (i = 0; i < listend; i++) {
591 		seq = new_inbox_file(seq, inboxfile);
592 		if (strlen(fromdir) + strlen(list[i]) + 2 > PATH_MAX)
593 			error("pathname too long (%s/%s)",
594 			      fromdir, list[i]);
595 		snprintf(mailfile, sizeof(mailfile), "%s/%s", fromdir, list[i]);
596 		if (backup && backupdir != NULL) {
597 			if (strlen(backupdir) + strlen(list[i]) + 6 > PATH_MAX)
598 				error("pathname too long (%s/%s)",
599 				      backupdir, list[i]);
600 			snprintf(backupfile, sizeof(backupfile), "%s/%s:2,S", backupdir, list[i]);
601 			movefile(mailfile, inboxfile, backupfile, backup);
602 		}
603 		else
604 			movefile(mailfile, inboxfile, NULL, backup);
605 		printf("%d\n", seq);
606 	}
607 	return seq;
608 }
609 
610 private int
process_maildir(int seq)611 process_maildir(int seq)
612 {
613 	char *newdir, *curdir;
614 	if (maildir_names(Mbox, &newdir, &curdir, NULL))
615 		error("maildir name is not set (%s)", Mbox);
616 	if (GetCur)
617 		seq = get_from_dir(seq, curdir, NULL, Backup);
618 	return get_from_dir(seq, newdir, curdir, Backup);
619 }
620 
621 private int
lock_mbox(char * lockfile)622 lock_mbox(char *lockfile)
623 {
624 	int fd;
625 	int retry = 5;
626 
627 	while (TRUE) {
628 	  if ((fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) {
629 			if (errno == EACCES || errno == EROFS)
630 				return 1; /* doesn't need a lockfile, maybe. */
631 			else if (errno != EEXIST)
632 				error("open(%s)", lockfile);
633 			if (retry-- <= 0)
634 				error("can't get lock(%s)", lockfile);
635 		}
636 		else {
637 			/* lock succeeded. */
638 			write(fd, "0", 1);
639 			close(fd);
640 			return 0;
641 		}
642 		sleep(2);
643 	}
644 }
645 
646 private void
unlock_mbox(char * lockfile)647 unlock_mbox(char *lockfile)
648 {
649 	if (strlen(lockfile) > 0)
650 		unlink(lockfile);
651 }
652 
653 private int
process_mbox(int seq)654 process_mbox(int seq)
655 {
656 	char inboxfile[PATH_MAX];
657 	char emptyline[3];
658 	char *ln;
659 	int srcfd, oflag;
660 	FILE *srcfp = NULL;
661 	FILE *dstfp = NULL;
662 	int state = ST_UNKNOWN;
663 	int bytes = -1;		/* UseCL (Content-Length:) */
664 
665 	if (strlen(Mbox) + strlen(LOCK_SUFFIX) + 1 > PATH_MAX)
666 		error("pathname too long (%s%s)", Mbox, LOCK_SUFFIX);
667 	snprintf(MboxLock, sizeof(MboxLock), "%s%s", Mbox, LOCK_SUFFIX);
668 	if (lock_mbox(MboxLock))
669 		MboxLock[0] = '\0'; /* doesn't need a lockfile, maybe. */
670 
671 	oflag = O_RDWR;
672 #if defined(O_EXLOCK)
673 	oflag |= O_EXLOCK;
674 #endif
675 	if ((srcfd = open(Mbox, oflag, 0)) < 0) {
676 		warning("open(%s) for rw/truncate", Mbox);  goto rerr;
677 	}
678 #if !defined(O_EXLOCK) && (HAVE_FLOCK || HAVE_LOCKF)
679 	if (flock(srcfd, LOCK_EX) < 0) {
680 		warning("flock(%s)", Mbox);  goto rerr;
681 	}
682 #endif
683 	if ((srcfp = fdopen(srcfd, FDREAD)) == NULL) {
684 		warning("fdopen(%s) for read", Mbox);  goto rerr;
685 	}
686 
687 	while ((ln = Getline(srcfp)) != NULL) {
688 		if (Exit)
689 			goto werr;
690 		switch (state) {
691 		case ST_UNKNOWN:
692 			if (strncmp(ln, "From ", 5) == 0) {
693 				dstfp = open_new_inbox_file(&seq, inboxfile);
694 				if (dstfp == NULL)
695 					goto rerr;
696 				if (PreserveUnixFrom &&
697 				    fputs(ln, dstfp) == EOF) {
698 					warning("fputs(%s)", inboxfile);
699 					goto werr;
700 				}
701 				state = ST_HEADER;
702 			}
703 			break;
704 		case ST_HEADER:
705 			if (strlen(ln) < 3 &&
706 			    (ln[0] == '\n' || ln[0] == '\r')) {
707 				STRCPY(emptyline, ln);
708 				state = ST_BODY_AFTER_EMPTY_LINE;
709 				break;
710 			}
711 			if (fputs(ln, dstfp) == EOF) {
712 				warning("fputs(%s)", inboxfile);  goto werr;
713 			}
714 			if (UseCL &&
715 			    strncasecmp(ln, "Content-Length", 14) == 0) {
716 				int i;
717 				for (i = 14; i < strlen(ln); i++)
718 					if (isdigit((unsigned char)ln[i]))
719 						break;
720 				bytes = atoi(&ln[i]);
721 			}
722 			break;
723 		case ST_BODY_AFTER_EMPTY_LINE:
724 			if (bytes < 0 && strncmp(ln, "From ", 5) == 0) {
725 				if (fclose(dstfp))
726 					goto rerr;
727 				printf("%d\n", seq);
728 
729 				dstfp = open_new_inbox_file(&seq, inboxfile);
730 				if (dstfp == NULL)
731 					goto rerr;
732 				if (PreserveUnixFrom &&
733 				    fputs(ln, dstfp) == EOF) {
734 					warning("fputs(%s)", inboxfile);
735 					goto werr;
736 				}
737 				state = ST_HEADER;
738 				break;
739 			}
740 			else if (fputs(emptyline, dstfp) == EOF)
741 				goto werr;
742 			/* FALLTHRU */
743 		case ST_BODY:
744 			if (strlen(ln) < 3 &&
745 			    (ln[0] == '\n' || ln[0] == '\r')) {
746 				STRCPY(emptyline, ln);
747 				state = ST_BODY_AFTER_EMPTY_LINE;
748 			}
749 			else
750 				state = ST_BODY;
751 
752 			if (state == ST_BODY && fputs(ln, dstfp) == EOF)
753 				goto werr;
754 			if (bytes >= 0) {
755 				bytes -= strlen(ln);
756 				if (bytes <= 0) {
757 					if (fclose(dstfp))
758 						goto rerr;
759 					dstfp = NULL;
760 					printf("%d\n", seq);
761 					state = ST_UNKNOWN;
762 					bytes = -1;
763 					break;
764 				}
765 			}
766 			break;
767 		}
768 		free(ln);
769 	}
770 	if (dstfp) {
771 		if (fclose(dstfp))
772 			goto rerr;
773 		printf("%d\n", seq);
774 	}
775 	if (!Backup && ftruncate(srcfd, 0)) {
776 		unlock_mbox(MboxLock);
777 		error("ftruncate");
778 	}
779 	fclose(srcfp);
780 	unlock_mbox(MboxLock);
781 	return seq;
782 
783  werr:
784 	if (dstfp)
785 		fclose(dstfp);
786 	unlink(inboxfile);
787  rerr:
788 	if (srcfp)
789 		fclose(srcfp);
790 	unlock_mbox(MboxLock);
791 	error("process_mbox(%s)", Mbox);
792 	return -1;		/* error. not reached */
793 }
794 
795 private int
process_stdin(int seq)796 process_stdin(int seq)
797 {
798 	char inboxfile[PATH_MAX];
799 	char *ln;
800 	FILE *srcfp = stdin;
801 	FILE *dstfp;
802 
803 	if ((dstfp = open_new_inbox_file(&seq, inboxfile)) == NULL)
804 		goto rerr;
805 
806 	while ((ln = Getline(srcfp)) != NULL) {
807 		if (Exit)
808 			goto werr;
809 		if (fputs(ln, dstfp) == EOF) {
810 			warning("fputs(%s)", inboxfile);  goto werr;
811 		}
812 		free(ln);
813 	}
814 
815 	if (dstfp) {
816 		fclose(dstfp);
817 		printf("%d\n", seq);
818 	}
819 	return seq;
820 
821  werr:
822 	if (dstfp)
823 		fclose(dstfp);
824 	unlink(inboxfile);
825  rerr:
826 	error("process_stdin");
827 	return -1;		/* error. not reached */
828 }
829 
830 private void
process(void)831 process(void)
832 {
833 	char mtimefile[PATH_MAX];
834 	FILE *fp;
835 	size_t wb;
836 	int len = strlen(MEW_MTIME_PHRASE);
837 	int seq = get_last_seq();
838 	int newseq = 0;
839 
840 	switch (MboxType) {
841 	case T_MAILDIR:
842 		newseq = process_maildir(seq);
843 		break;
844 	case T_MBOX:
845 		newseq = process_mbox(seq);
846 		break;
847 	case T_STDIN:
848 		newseq = process_stdin(seq);
849 		break;
850 	default:
851 		error("unknown mbox type (%s)", Mbox);
852 	}
853 
854 	/* update .mew-mtime file if new mail arrived */
855 	if (!CreateMTime || newseq <= seq)
856 		return;		/* no new mail */
857 	if (strlen(InboxDir) + strlen(MEW_MTIME_FILE) + 1 > PATH_MAX)
858 		error("pathname too long (%s%s)", InboxDir, MEW_MTIME_FILE);
859 	snprintf(mtimefile, sizeof(mtimefile), "%s/%s", InboxDir, MEW_MTIME_FILE);
860 
861 	if ((fp = fopen(mtimefile, FDWRITE)) == NULL)
862 		error("can't create file (%s)", mtimefile);
863 	if ((wb = fwrite(MEW_MTIME_PHRASE, sizeof(char), len, fp)) != len) {
864 		fclose(fp);
865 		error("fwrite failed (%d, %s)", wb, mtimefile);
866 	}
867 	fclose(fp);
868 }
869 
870 private void
sanity_check(void)871 sanity_check(void)
872 {
873 	struct stat sb;
874 
875 	/* was directory exists? */
876 	if (stat(InboxDir, &sb))
877 		error("stat(%s)", InboxDir);
878 	if (!S_ISDIR(sb.st_mode) || access(InboxDir, W_OK))
879 		error("can't write directory (%s)", InboxDir);
880 
881 	/* mbox type checking */
882 	if (MboxType == T_UNKNOWN &&
883 	    (MboxType = check_mbox_type(Mbox)) == T_UNKNOWN)
884 		error("can't find mbox (%s)", Mbox);
885 }
886 
887 int
main(int argc,char ** argv)888 main(int argc, char **argv)
889 {
890 	extern char *Optarg;
891 	extern int Optind;
892 	char *progname = getprognm(argv[0]);
893 	int ch;
894 
895 	warn_prog = progname;
896 	init_env(argc, argv);
897 
898 	while ((ch = Getopt(argc, argv, "abcd:fhi:m:op:suvx:")) != EOF) {
899 		switch (ch) {
900 		case 'a':
901 			GetCur = TRUE;
902 			break;
903 		case 'b':
904 			Backup = TRUE;
905 			break;
906 		case 'c':
907 			UseCL = TRUE;
908 			break;
909 		case 'd':
910 		case 'm':
911 			if (strlen(Optarg) + 1 > PATH_MAX)
912 				error("pathname too long (%s)", Optarg);
913 			snprintf(Mbox, sizeof(Mbox), "%s", Optarg);
914 			break;
915 		case 'f':
916 			PreserveUnixFrom = TRUE;
917 			break;
918 		case 'i':
919 			STRDUP(InboxDir, Optarg);
920 			break;
921 		case 'o':
922 			Use_Suffix = TRUE;
923 			break;
924 		case 'x':
925 			STRDUP(Suffix, Optarg);
926 			Suffix_len = strlen(Suffix);
927 			break;
928 		case 'p':
929 			sscanf(Optarg, "%i", &FileMode);
930 			break;
931 		case 's':
932 			MboxType = T_STDIN;
933 			break;
934 		case 'u':
935 			CreateMTime = FALSE;
936 			break;
937 		case 'v':
938 			version(progname);
939 			exit(EXIT_SUCCESS);
940 		case 'h':
941 			help(progname);
942 			exit(EXIT_SUCCESS);
943 		default:
944 			usage(progname);
945 			exit(EXIT_SUCCESS);
946 		}
947 	}
948 	argc -= Optind;
949 	argv += Optind;
950 
951 	sanity_check();
952 	process();
953 	return EXIT_SUCCESS;
954 }
955 
956 /*
957  * Copyright (C) 2001-2005 Mew developing team.
958  * All rights reserved.
959  *
960  * Redistribution and use in source and binary forms, with or without
961  * modification, are permitted provided that the following conditions
962  * are met:
963  *
964  * 1. Redistributions of source code must retain the above copyright
965  *    notice, this list of conditions and the following disclaimer.
966  * 2. Redistributions in binary form must reproduce the above copyright
967  *    notice, this list of conditions and the following disclaimer in the
968  *    documentation and/or other materials provided with the distribution.
969  * 3. Neither the name of the team nor the names of its contributors
970  *    may be used to endorse or promote products derived from this software
971  *    without specific prior written permission.
972  *
973  * THIS SOFTWARE IS PROVIDED BY THE TEAM AND CONTRIBUTORS ``AS IS'' AND
974  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
975  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
976  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE TEAM OR CONTRIBUTORS BE
977  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
978  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
979  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
980  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
981  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
982  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
983  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
984  */
985 
986 /*
987  * incm.c ends here
988  */
989