/* * Copyright 1998-2002 Ben Smithurst * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * check for unread mail in all mailboxes under ~/mail */ static const char rcsid[] = "$BCPS: src/mailutils/newmail.c,v 1.61 2003/01/19 19:18:25 ben Exp $"; #include "misc.h" void usage(void); void check_file(char *); #define SIG_EXIT() { \ warnx("exiting because of signal"); \ exit(1); \ } int hlen, mlen, total_newmail, some_newmail; char *home, *mail, *inbox; int batch, count, thorough, mailbox_fd; int main(int argc, char *argv[]) { int ch; TREE *tree; struct passwd *pw; char *mail_env; batch = count = thorough = 0; some_newmail = total_newmail = 0; mailbox_fd = -1; sig_count = 0; signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); while ((ch = getopt(argc, argv, "cf:st")) != -1) switch (ch) { case 'c': count = 1; if (thorough < 1) thorough = 1; break; case 'f': if (thorough < 2) thorough = 2; mailbox_fd = atoi(optarg); if (mailbox_fd < 0) errx(1, "Bad fd for -f option"); break; case 's': batch = 1; break; case 't': thorough++; break; default: usage(); } argc -= optind; argv += optind; /* -f option mustn't have real files specified as well */ if (mailbox_fd >= 0 && argc != 0) errx(1, "must specify no files with -f option"); /* -s option needs exactly one file to work on */ if (batch && argc != 1 && mailbox_fd < 0) errx(1, "must specify exactly one file for -s option"); tree = NULL; /* find mail directory */ home = homedir(); mail = mailpath(); hlen = strlen(home); mlen = strlen(mail); /* check for this case early */ if (mailbox_fd >= 0) { check_file("user supplied fd"); return (0); } /* get inbox filename */ if ((mail_env = getenv("MAIL")) != NULL) { MALLOC(inbox, strlen(mail_env) + 1); strcpy(inbox, mail_env); } else { if ((pw = getpwuid(getuid())) == NULL) err(1, "couldn't get username"); MALLOC(inbox, sizeof _PATH_MAILDIR + strlen(pw->pw_name) + 1); strcpy(inbox, _PATH_MAILDIR); strcat(inbox, "/"); strcat(inbox, pw->pw_name); } if (argc == 0) files_to_tree(&tree, mail); else if (argc == 1) { tree = NULL; check_file(argv[0]); } else array_to_tree(&tree, argv); check_tree(tree, check_file); if (sig_count > 0) SIG_EXIT(); /* * check inbox, if it wasn't checked earlier, and if no files * on command line. */ if (argc == 0 && inbox[0] != '\0') check_file(inbox); if (count && total_newmail > 0) printf("\nOverall, %d new message%s\n", total_newmail, (total_newmail == 1) ? "" : "s"); return (0); } static void check_file0(char *file) { FILE *fp; char *line; int statusfound, newmail, header, maildir = 0; size_t len; struct stat sb; struct timeval times[2]; /* usec for atime/mtime always zero */ times[0].tv_usec = times[1].tv_usec = 0; newmail = 0; maildir = (mailbox_fd < 0) && is_maildir(file); if (maildir) { char buf[MAXPATHLEN]; DIR *d; struct dirent *dp; snprintf(buf, sizeof buf, "%s/new", file); if ((d = opendir(buf)) == NULL) { warn("%s", buf); return; } while ((dp = readdir(d)) != NULL) { if (dp->d_name[0] != '.') { newmail++; if (!count) break; } } total_newmail += newmail; closedir(d); /* check for bad files */ if (thorough > 0) { char *n; const char *ok[] = { ".", "..", "cur", "new", "tmp" }; const int nok = sizeof(ok) / sizeof(ok[0]); int i; if ((d = opendir(file)) == NULL) { warn("%s", file); return; } while ((dp = readdir(d)) != NULL) { n = dp->d_name; for (i = 0; i < nok; i++) if (strcmp(n, ok[i]) == 0) break; if (i >= nok) warnx("%s: contains bad file %s", file, n); } closedir(d); } } else if (mailbox_fd < 0) { /* stuff not done if user gives -f option */ if (stat(file, &sb) < 0) { warn("%s", file); return; } /* exit quickly if zero size */ if (sb.st_size == 0) { if (batch) exit(2); else return; } /* ignore ~/mail/sent-mail */ if (strlen(file) == mlen + 10 && strncmp(file, mail, mlen) == 0 && strcmp(file + mlen, "/sent-mail") == 0) return; if (!S_ISREG(sb.st_mode)) { warnx("%s not a regular file", file); return; } if (thorough < 2) newmail = sb.st_mtime > sb.st_atime; } if (!maildir && (thorough >= 2 || (!newmail && thorough == 1) || (newmail && count))) { if (count) newmail = 0; if (mailbox_fd >= 0) { if ((fp = fdopen(mailbox_fd, "r+")) == NULL) { warn("fdopen: %d", mailbox_fd); return; } } else if ((fp = fopen(file, "r+")) == NULL) { warn("%s", file); return; } /* lock the file to prevent updates while checking */ if (mailbox_fd < 0 && !mailboxlock(file, 0, fileno(fp), LF_GET)) { warn("mailboxlock: %s", file); fclose(fp); return; } header = 1; statusfound = 0; while ((line = gl_getline(fp)) != NULL) { len = strlen(line); if (sig_count > 0) break; chop(line, len); if (!header && is_from(line, 1)) { header = 1; statusfound = 0; } else if (header && strncasecmp(line, "Status:", 7) == 0 && strchr(line + 7, 'R') != NULL) statusfound = 1; else if (header && *line == '\0') { header = 0; if (!statusfound) newmail++; } if (newmail && !count) break; } total_newmail += newmail; gl_destroy(fp); fclose(fp); if (mailbox_fd < 0) { times[1].tv_sec = sb.st_mtime; /* if there's new mail, but mtime <= atime, fudge it */ if (newmail && sb.st_mtime <= sb.st_atime) times[0].tv_sec = sb.st_mtime - 1; else if (!newmail && sb.st_mtime >= sb.st_atime) times[0].tv_sec = sb.st_mtime + 1; else times[0].tv_sec = sb.st_atime; if (utimes(file, times) < 0) warn("utimes: %s", file); mailboxlock(file, 0, -1, LF_REL); } if (sig_count > 0) SIG_EXIT(); } if (newmail) { if (!batch && !some_newmail) printf("You have new mail in:\n"); some_newmail = 1; if (batch) exit(0); printf(" %s", print_mailbox(file)); if (count) printf(" (%d)", newmail); printf("\n"); } else if (batch) exit(2); } void check_file(char *file) { check_file0(file); /* * if we check the user's inbox here, reset the inbox string so * it isn't checked again. */ if (strcmp(file, inbox) == 0) inbox[0] = '\0'; } void usage(void) { fprintf(stderr, "usage: newmail [-f fd] [-cst] [mailbox ...]\n"); exit(EX_USAGE); }