/*
* Copyright 1998-2002 Ben Smithurst <ben@smithurst.org>
* 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);
}
syntax highlighted by Code2HTML, v. 0.9.1