2
* incm - incorporating new mails
4
* Author: Yasunari Momoi <momo@bug.org>
10
private char version_message[] = "version 4.1 20030217 Yasunari Momoi";
18
#include <sys/types.h>
23
#if TIME_WITH_SYS_TIME
24
# include <sys/time.h>
28
# include <sys/time.h>
43
# include <sys/file.h>
60
ST_BODY_AFTER_EMPTY_LINE,
64
#define FBUFSIZ (BUFSIZ * 32)
66
# define PATH_MAX 1024
69
private char FileBuf[FBUFSIZ];
70
private char InboxDir[PATH_MAX];
71
private char Mbox[PATH_MAX];
72
private char MboxLock[PATH_MAX];
77
private int CreateMTime = TRUE;
80
/****************************************************************
86
private void error(const char *, ...);
87
private void usage(const char *);
88
private void help(const char *);
89
private void version(const char *);
90
private void init_env(int, char **);
91
private int is_number(unsigned char *);
92
private int get_last_seq(void);
93
private int compare_string(char **, char **);
94
private void copyfile(char *, char *);
95
private void movefile(char *, char *, char *, int);
96
private int maildir_names(const char *, char **, char **, char **);
97
private int new_inbox_file(int, char[]);
98
private FILE *open_new_inbox_file(int *, char[]);
99
private int get_from_dir(int, char *, char *, int);
100
private int process_maildir(int);
101
private int lock_mbox(char *);
102
private void unlock_mbox(char *);
103
private int process_mbox(int);
104
private int process_stdin(int);
105
private void process(void);
106
private int check_mbox_type(const char *);
107
private void sanity_check(void);
112
# define flock(a, b) lockf(a, b, 0)
113
# define LOCK_EX F_LOCK
118
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
121
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
130
if ((user = getlogin()) != NULL)
137
if ((pw = getpwuid(getuid())) == NULL)
151
if ((home = getenv("HOME")) != NULL)
156
if ((pw = getpwuid(getuid())) == NULL)
167
strdup(const char *str)
170
MALLOC(t, strlen(str) + 1);
178
error(const char *fmt, ...)
181
if (warn_prog != NULL)
182
fprintf(stderr, "%s: ", warn_prog);
185
vfprintf(stderr, fmt, ap);
187
fprintf(stderr, "\n");
188
if (strlen(MboxLock) > 0)
189
unlock_mbox(MboxLock);
193
/****************************************************************
200
#define LOCK_SUFFIX ".lock"
201
#define MAILDIR "Maildir"
202
#define MAILDIR_NEW "new"
203
#define MAILDIR_CUR "cur"
204
#define MAILDIR_TMP "tmp"
207
usage(const char *progname) {
208
fprintf(stderr, "Usage: %s [-abchsv] [-d maildir] [-i inboxdir]\n", progname);
213
" -h Display this help message.",
214
" -v Display the version.",
215
" -d <mail> Path to mbox/maildir.",
216
" -m <mail> Path to mbox/maildir.",
217
" -s Read one mail from stdin instead of mbox/maildir.",
218
" -i <inboxdir> Path to inboxdir.",
220
" mbox: No truncate mbox file.",
221
" maildir: To maildir/cur directory.",
222
" -a Retrieve all mail from maildir/{cur,new} directory.",
223
" (no backup) (for maildir)",
224
" -c Use Content-Length: field. (for mbox)",
225
" -u Don't create inboxdir/.mew-mtime file.",
230
help(const char *progname) {
231
const char **p = help_message;
233
fprintf(stderr, "Help: %s\n\n", progname);
234
fprintf(stderr, " Incorporating new mails.\n\n");
236
while (*p) fprintf(stderr, "%s\n", *p++);
240
version(const char *progname) {
241
fprintf(stderr, "%s %s\n", progname, version_message);
245
check_mbox_type(const char *path)
251
if (S_ISDIR(sb.st_mode)) {
255
if (maildir_names(path, &newdir, &curdir, NULL))
256
error("maildir name is not set (%s)", path);
257
if (stat(newdir, &sb))
259
if (!S_ISDIR(sb.st_mode) || access(newdir, R_OK|W_OK|X_OK))
263
if (stat(curdir, &sb))
265
if (!S_ISDIR(sb.st_mode) || access(curdir, W_OK))
270
else if (S_ISREG(sb.st_mode))
285
search_mbox_path(void)
287
char *home, *mail, *user;
289
if ((home = Gethomedir()) != NULL) {
290
if (strlen(home) + 9 > PATH_MAX)
291
error("pathname too long (%s)", home);
292
sprintf(Mbox, "%s/%s", home, MAILDIR);
293
if (check_mbox_type(Mbox) != T_UNKNOWN)
296
if ((mail = getenv("MAIL")) != NULL) {
297
if (strlen(mail) + 1 > PATH_MAX)
298
error("pathname too long (%s)", mail);
300
if (check_mbox_type(Mbox) != T_UNKNOWN)
303
if ((user = Getlogin()) != NULL) {
305
for (i = 0; mbox_path_list[i] != NULL; i++) {
306
if (strlen(mbox_path_list[i]) + strlen(user) + 1
308
error("pathname too long (%s)", user);
309
sprintf(Mbox, "%s%s", mbox_path_list[i], user);
310
if (check_mbox_type(Mbox) != T_UNKNOWN)
325
sig_ignore(int signo)
333
if (signal(SIGHUP, sig_ignore) == SIG_ERR)
334
error("can't catch SIGHUP\n");
335
if (signal(SIGINT, sig_exit) == SIG_ERR)
336
error("can't catch SIGINT\n");
337
if (signal(SIGALRM, sig_ignore) == SIG_ERR)
338
error("can't catch SIGALRM\n");
339
if (signal(SIGTERM, sig_ignore) == SIG_ERR)
340
error("can't catch SIGTERM\n");
344
init_env(int argc, char **argv)
347
sprintf(InboxDir, ".");
348
MboxType = T_UNKNOWN;
355
is_number(unsigned char *str)
360
} while (*++str != '\0');
372
if ((dirp = opendir(InboxDir)) == NULL)
373
error("opendir(%s)", InboxDir);
374
while ((dp = readdir(dirp)) != NULL) {
375
if (!is_number(dp->d_name))
377
seq = atoi(dp->d_name);
378
last = last > seq ? last : seq;
385
compare_string(char **i, char **j)
387
return strcmp(*i, *j);
391
copyfile(char *src, char *dst)
393
struct timeval tv[2];
398
if ((srcfd = open(src, O_RDONLY, 0)) < 0)
399
error("open(%s) for read", src);
400
if (fstat(srcfd, &sb))
401
error("fstat(%s)", src);
402
if ((dstfd = open(dst, O_EXCL | O_CREAT | O_WRONLY | O_TRUNC, 0)) < 0)
403
error("open(%s) for write", dst);
404
while ((rlen = read(srcfd, FileBuf, FBUFSIZ)) > 0) {
405
if ((wlen = write(dstfd, FileBuf, rlen)) != rlen) {
408
error("write(%s) (read %d bytes/write %d bytes)",
415
error("read(%s)", src);
419
tv[0].tv_sec = sb.st_atime;
421
tv[1].tv_sec = sb.st_mtime;
424
if (futimes(dstfd, tv))
425
warning("futimes(%s) failed", dst);
428
if (fchmod(dstfd, sb.st_mode))
429
warning("fchmod(%s) failed", dst);
434
warning("utimes(%s) failed", dst);
437
if (chmod(dst, sb.st_mode))
438
warning("chmod(%s) failed", dst);
443
movefile(char *fromfile, char *tofile, char *backupfile, int backup)
445
if (backup && backupfile != NULL) {
446
copyfile(fromfile, tofile);
447
if (rename(fromfile, backupfile))
448
error("rename(%s, %s)", fromfile, backupfile);
451
copyfile(fromfile, tofile);
454
if (rename(fromfile, tofile)) {
456
error("rename(%s, %s)", fromfile, tofile);
457
copyfile(fromfile, tofile);
458
if (unlink(fromfile))
459
error("unlink(%s)", fromfile);
464
/* maildir has {new,cur,tmp} subdirectory. */
466
maildir_names(const char *maildir, char **newdir, char **curdir, char **tmpdir)
468
int len = strlen(maildir) + strlen(MAILDIR_NEW) + 2;
470
if (maildir == NULL || strlen(maildir) <= 0)
472
if (newdir != NULL) {
473
MALLOC(*newdir, len);
474
sprintf(*newdir, "%s/%s", maildir, MAILDIR_NEW);
476
if (curdir != NULL) {
477
MALLOC(*curdir, len);
478
sprintf(*curdir, "%s/%s", maildir, MAILDIR_CUR);
480
if (tmpdir != NULL) {
481
MALLOC(*tmpdir, len);
482
sprintf(*tmpdir, "%s/%s", maildir, MAILDIR_TMP);
487
/* *WARNING* inboxfile requires PATH_MAX bytes */
489
new_inbox_file(int seq, char inboxfile[])
493
sprintf(num, "%d", ++seq);
494
if (strlen(InboxDir) + strlen(num) + 2 > PATH_MAX)
495
error("pathname too long (%s/%s)", InboxDir, num);
496
sprintf(inboxfile, "%s/%s", InboxDir, num);
497
if (access(inboxfile, F_OK) && errno == ENOENT)
503
/* *WARNING* inboxfile requires PATH_MAX bytes */
505
open_new_inbox_file(int *seq, char inboxfile[])
508
int flag = O_EXCL | O_CREAT | O_WRONLY;
509
int mode = S_IRUSR | S_IRGRP | S_IROTH |
510
S_IWUSR | S_IWGRP | S_IWOTH;
515
sprintf(num, "%d", ++*seq);
516
if (strlen(InboxDir) + strlen(num) + 2 > PATH_MAX)
517
error("pathname too long (%s/%s)", InboxDir, num);
518
sprintf(inboxfile, "%s/%s", InboxDir, num);
519
if ((fd = open(inboxfile, flag, mode)) >= 0 ||
522
usleep(rand() % 199);
525
warning("open(%s) for write", inboxfile);
527
if ((fp = fdopen(fd, FDWRITE)) == NULL)
528
warning("open(%s) for write", inboxfile);
534
get_from_dir(int seq, char *fromdir, char *backupdir, int backup)
539
char mailfile[PATH_MAX];
540
char inboxfile[PATH_MAX];
541
char backupfile[PATH_MAX];
543
int listsize = BUFSIZ;
547
MALLOC(list, sizeof(char *)*listsize);
548
if ((dirp = opendir(fromdir)) == NULL)
549
error("opendir(%s)", fromdir);
550
while ((dp = readdir(dirp)) != NULL) {
551
if (strlen(fromdir) + strlen(dp->d_name) + 2 > PATH_MAX)
552
error("pathname too long (%s/%s)",
553
fromdir, dp->d_name);
554
sprintf(mailfile, "%s/%s", fromdir, dp->d_name);
555
if (stat(mailfile, &sb))
557
if (!(S_ISREG(sb.st_mode) && (sb.st_mode & S_IRUSR)))
559
if (listend >= listsize) {
561
if ((list = (char **)
562
realloc(list, sizeof(char *)*listsize)) == NULL)
565
if ((list[listend++] = strdup(dp->d_name)) == NULL)
566
error("strdup(%s)", dp->d_name);
570
qsort(list, listend, sizeof(char *),
571
(int (*)(const void *, const void *))compare_string);
573
for (i = 0; i < listend; i++) {
574
seq = new_inbox_file(seq, inboxfile);
575
if (strlen(fromdir) + strlen(list[i]) + 2 > PATH_MAX)
576
error("pathname too long (%s/%s)",
578
sprintf(mailfile, "%s/%s", fromdir, list[i]);
579
if (backup && backupdir != NULL) {
580
if (strlen(backupdir) + strlen(list[i]) + 6 > PATH_MAX)
581
error("pathname too long (%s/%s)",
583
sprintf(backupfile, "%s/%s:2,S", backupdir, list[i]);
584
movefile(mailfile, inboxfile, backupfile, backup);
587
movefile(mailfile, inboxfile, NULL, backup);
594
process_maildir(int seq)
596
char *newdir, *curdir;
597
if (maildir_names(Mbox, &newdir, &curdir, NULL))
598
error("maildir name is not set (%s)", Mbox);
600
seq = get_from_dir(seq, curdir, NULL, Backup);
601
return get_from_dir(seq, newdir, curdir, Backup);
605
lock_mbox(char *lockfile)
611
if ((fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL)) < 0) {
612
if (errno == EACCES || errno == EROFS)
613
return 1; /* doesn't need a lockfile, maybe. */
614
else if (errno != EEXIST)
615
error("open(%s)", lockfile);
617
error("can't get lock(%s)", lockfile);
620
/* lock succeeded. */
630
unlock_mbox(char *lockfile)
632
if (strlen(lockfile) > 0)
637
process_mbox(int seq)
639
char inboxfile[PATH_MAX];
645
int state = ST_UNKNOWN;
646
int bytes = -1; /* UseCL (Content-Length:) */
648
if (strlen(Mbox) + strlen(LOCK_SUFFIX) + 1 > PATH_MAX)
649
error("pathname too long (%s%s)", Mbox, LOCK_SUFFIX);
650
sprintf(MboxLock, "%s%s", Mbox, LOCK_SUFFIX);
651
if (lock_mbox(MboxLock))
652
MboxLock[0] = '\0'; /* doesn't need a lockfile, maybe. */
655
#if defined(O_EXLOCK)
658
if ((srcfd = open(Mbox, oflag, 0)) < 0) {
659
warning("open(%s) for rw/truncate", Mbox); goto rerr;
661
#if !defined(O_EXLOCK) && (HAVE_FLOCK || HAVE_LOCKF)
662
if (flock(srcfd, LOCK_EX) < 0) {
663
warning("flock(%s)", Mbox); goto rerr;
666
if ((srcfp = fdopen(srcfd, FDREAD)) == NULL) {
667
warning("fdopen(%s) for read", Mbox); goto rerr;
670
while ((ln = getline(srcfp)) != NULL) {
675
if (strncmp(ln, "From ", 5) == 0) {
676
dstfp = open_new_inbox_file(&seq, inboxfile);
683
if (strlen(ln) < 3 &&
684
(ln[0] == '\n' || ln[0] == '\r')) {
685
strcpy(emptyline, ln);
686
state = ST_BODY_AFTER_EMPTY_LINE;
689
if (fputs(ln, dstfp) == EOF) {
690
warning("fputs(%s)", inboxfile); goto werr;
693
strncasecmp(ln, "Content-Length", 14) == 0) {
695
for (i = 14; i < strlen(ln); i++)
696
if (isdigit((unsigned char)ln[i]))
698
bytes = atoi(&ln[i]);
701
case ST_BODY_AFTER_EMPTY_LINE:
702
if (bytes < 0 && strncmp(ln, "From ", 5) == 0) {
706
dstfp = open_new_inbox_file(&seq, inboxfile);
712
else if (fputs(emptyline, dstfp) == EOF)
716
if (strlen(ln) < 3 &&
717
(ln[0] == '\n' || ln[0] == '\r')) {
718
strcpy(emptyline, ln);
719
state = ST_BODY_AFTER_EMPTY_LINE;
724
if (state == ST_BODY && fputs(ln, dstfp) == EOF)
729
fclose(dstfp); dstfp = NULL;
744
if (!Backup && ftruncate(srcfd, 0)) {
745
unlock_mbox(MboxLock);
749
unlock_mbox(MboxLock);
759
unlock_mbox(MboxLock);
760
error("process_mbox(%s)", Mbox);
761
return -1; /* error. not reached */
765
process_stdin(int seq)
767
char inboxfile[PATH_MAX];
772
if ((dstfp = open_new_inbox_file(&seq, inboxfile)) == NULL)
775
while ((ln = getline(srcfp)) != NULL) {
778
if (fputs(ln, dstfp) == EOF) {
779
warning("fputs(%s)", inboxfile); goto werr;
795
error("process_stdin");
796
return -1; /* error. not reached */
802
char mtimefile[PATH_MAX];
805
int len = strlen(MEW_MTIME_PHRASE);
806
int seq = get_last_seq();
811
newseq = process_maildir(seq);
814
newseq = process_mbox(seq);
817
newseq = process_stdin(seq);
820
error("unknown mbox type (%s)", Mbox);
823
/* update .mew-mtime file if new mail arrived */
824
if (!CreateMTime || newseq <= seq)
825
return; /* no new mail */
826
if (strlen(InboxDir) + strlen(MEW_MTIME_FILE) + 1 > PATH_MAX)
827
error("pathname too long (%s%s)", InboxDir, MEW_MTIME_FILE);
828
sprintf(mtimefile, "%s/%s", InboxDir, MEW_MTIME_FILE);
830
if ((fp = fopen(mtimefile, FDWRITE)) == NULL)
831
error("can't create file (%s)", mtimefile);
832
if ((wb = fwrite(MEW_MTIME_PHRASE, sizeof(char), len, fp)) != len) {
834
error("fwrite failed (%d, %s)", wb, mtimefile);
844
/* was directory exists? */
845
if (stat(InboxDir, &sb))
846
error("stat(%s)", InboxDir);
847
if (!S_ISDIR(sb.st_mode) || access(InboxDir, W_OK))
848
error("can't write directory (%s)", InboxDir);
850
/* mbox type checking */
851
if (MboxType == T_UNKNOWN &&
852
(MboxType = check_mbox_type(Mbox)) == T_UNKNOWN)
853
error("can't find mbox (%s)", Mbox);
857
main(int argc, char **argv)
861
char *progname = getprognm(argv[0]);
864
warn_prog = progname;
865
init_env(argc, argv);
867
while ((ch = Getopt(argc, argv, "abcd:hi:m:suvp")) != EOF) {
880
if (strlen(Optarg) + 1 > PATH_MAX)
881
error("pathname too long (%s)", Optarg);
882
sprintf(Mbox, "%s", Optarg);
885
if (strlen(Optarg) + 1 > PATH_MAX)
886
error("pathname too long (%s)", Optarg);
887
sprintf(InboxDir, "%s", Optarg);
889
case 'p': /* for debug */
890
printf("InboxDir: %s\n", InboxDir);
891
printf("Mbox: %s\n", Mbox);
919
* Copyright (C) 2001-2003 Mew developing team.
920
* All rights reserved.
922
* Redistribution and use in source and binary forms, with or without
923
* modification, are permitted provided that the following conditions
926
* 1. Redistributions of source code must retain the above copyright
927
* notice, this list of conditions and the following disclaimer.
928
* 2. Redistributions in binary form must reproduce the above copyright
929
* notice, this list of conditions and the following disclaimer in the
930
* documentation and/or other materials provided with the distribution.
931
* 3. Neither the name of the team nor the names of its contributors
932
* may be used to endorse or promote products derived from this software
933
* without specific prior written permission.
935
* THIS SOFTWARE IS PROVIDED BY THE TEAM AND CONTRIBUTORS ``AS IS'' AND
936
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
937
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
938
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TEAM OR CONTRIBUTORS BE
939
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
940
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
941
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
942
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
943
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
944
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
945
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.