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