#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include "globals.h"
#include "mh.h"
void mh_handle( struct mbox_struct * mb ) {
/* Since we never really intend to quit this function,
* all "global" stuff may go on the stack here */
int mod_time = 0;
int ret = 0;
if ( ! strlen(mb->file) ) {
fprintf(stderr, "asmail: mh_handle: no mailbox directory specified.\n");
mb->status = STAT_FAIL;
signal_update();
pthread_exit(NULL);
}
if ( strlen(mb->file) > (MAX_INPUT_LENGTH-4)) {
fprintf(stderr, "asmail: mh_handle: mailbox directory name is too long.\n");
mb->status = STAT_FAIL;
signal_update();
pthread_exit(NULL);
}
while (1) {
mb->status |= STAT_RUN;
signal_update();
if ( (ret = count_mh( mb, &mod_time )) < 0 ) {
mb->status = STAT_FAIL;
signal_update();
} else {
if ( mb->cnew > 0 ) {
mb->mail = MAIL_NEW;
}
else if ( mb->ctotal ) {
mb->mail = MAIL_OLD;
}
else {
mb->mail = MAIL_NONE;
}
}
if ( mb->status == STAT_RUN )
mb->status = STAT_IDLE;
signal_update();
sleep_check(mb->update);
}
}
int count_mh( struct mbox_struct * mb, int *mod_time ) {
if (mb->flags & FLAG_USE_MH_SEQ) {
return count_mh_sequences(mb, mod_time);
} else {
return count_mh_files(mb);
}
}
int count_mh_files( struct mbox_struct * mb ) {
DIR *dir = NULL;
FILE *f = NULL;
char line[MAX_INPUT_LENGTH+1] = "";
char fname[MAX_INPUT_LENGTH+1] = "";
int ctotal = 0;
int cnew = 0;
struct dirent *dir_entry = NULL;
/* Open the mH directory */
if ((dir = opendir(mb->file)) == NULL)
return(-1);
/* Parse through each file in the directory */
while ((dir_entry = readdir(dir)) != NULL) {
/* Ignore files that are not mH mail files */
if (dir_entry->d_name[0] != '.' && dir_entry->d_name[0] != ',') {
strcpy(fname, mb->file);
/* Make sure the directory name ends in a slash. */
if (fname[strlen(fname) - 1] != '/')
strncat(fname, "/",
MAX_INPUT_LENGTH - strlen(fname));
strncat(fname, dir_entry->d_name,
MAX_INPUT_LENGTH - strlen(fname));
fname[MAX_INPUT_LENGTH] = '\0';
if ( (f = fopen(fname, "r")) == NULL ) {
return(-1);
}
++ctotal;
++cnew;
while ( fgets(line, MAX_INPUT_LENGTH, f) ) {
switch (line[0]) {
case 'S':
if ( !strncmp(line, "Status: ", 8) ) {
if (mb->flags & FLAG_UNREAD_AS_NEW) {
if (!strncmp(line, "Status: RO", 10))
--cnew;
} else if (!strncmp(line, "Status: R", 9)) {
--cnew;
}
/* Once we find the Status header, no need
to keep reading from this file. */
goto close;
}
break;
}
}
close:
fclose(f);
}
}
closedir(dir);
if ( (mb->cnew != cnew) && (cnew != 0) ) {
pthread_mutex_lock(&mb->mutex);
mb->flags |= FLAG_ARRIVED;
pthread_mutex_unlock(&mb->mutex);
}
mb->ctotal = ctotal;
mb->cnew = cnew;
return(cnew);
}
int count_mh_sequences( struct mbox_struct * mb, int *mod_time ) {
struct stat stat_buf;
DIR *dir = NULL;
FILE *f = NULL;
int ctotal = 0;
int cnew = 0;
int lsize = 0;
int i = 0;
struct dirent *dir_entry = NULL;
char *line = NULL;
char **tokens = NULL;
char fname[MAX_INPUT_LENGTH+1] = "";
/* Form the full path to the .mh_sequences file.
Make sure the directory name ends in a slash. */
strcpy(fname, mb->file);
if (fname[strlen(fname) - 1] != '/')
strncat(fname, "/", MAX_INPUT_LENGTH - strlen(fname));
strncat(fname, ".mh_sequences", MAX_INPUT_LENGTH - strlen(fname));
fname[MAX_INPUT_LENGTH] = '\0';
/* Stat the .mh_sequences file to see if anything has changed. */
if ( stat(mb->file, &stat_buf) ) {
/* Yes, we have to keep trying. The mailbox
* may be on a filesystem that experiences
* problems, like NFS. */
fprintf(stderr, "asmail: mh_handle: stat (%s) failed.\n", mb->file);
return(-1);
} else {
if ( stat_buf.st_ctime == *mod_time )
return(0);
else
*mod_time = stat_buf.st_ctime;
}
/* Open the mH directory */
if ((dir = opendir(mb->file)) == NULL)
return(-1);
/* Count the total number of messages in the directory. */
while ((dir_entry = readdir(dir)) != NULL) {
if (dir_entry->d_name[0] != '.' && dir_entry->d_name[0] != ',')
++ctotal;
}
closedir(dir);
/* Now parse the .mh_sequences file to count the number of new messages.
* Although this is a user-configurable option in ~/.mh_profile, we will assume that the
* new messages are contained in the "unseen" sequence (balsa also makes this assumption).
*
* We look for a line beginning with "unseen: " which is followed by
* a space separated list of message numbers, or ranges of message numbers.
* It looks similar to this:
*
* unseen: 1 3 6-10 16-24 30
*/
if ( (f = fopen(fname, "r")) == NULL ) {
fprintf(stderr, "asmail: count_mh_sequences: unable to open sequences file: %s\n", fname);
return(-1);
}
lsize = 256;
line = (char *) malloc(sizeof(char) * lsize);
while(!feof(f)) {
readLine(f, &line, &lsize);
if (strncasecmp(line, "unseen:", 7) == 0) {
tokenizeString(line, lsize, &tokens);
for (i = 1; tokens[i] != '\0'; i++) {
cnew += parse_sequence(tokens[i]);
}
break;
}
}
fclose(f);
free(line);
/* Free the tokens. */
if (tokens != NULL) {
for (i = 0; tokens[i] != '\0'; i++) {
free(tokens[i]);
}
free(tokens);
}
if ( (mb->cnew != cnew) && (cnew != 0) ) {
pthread_mutex_lock(&mb->mutex);
mb->flags |= FLAG_ARRIVED;
pthread_mutex_unlock(&mb->mutex);
}
mb->ctotal = ctotal;
mb->cnew = cnew;
return(cnew);
}
/* Read a line in from the file stream and check to make *
* sure that the entire string was read. The string is *
* terminated by "\n" (LF). If the character array is *
* too short, make it bigger. */
int readLine(FILE *stream, char **line, int *size) {
off_t pos;
char buf[256];
int blen, llen;
pos = ftell(stream);
memset(*line, 0, *size);
llen = 0;
/* Make sure we read the entire line. */
do {
if (fgets(buf, sizeof(buf), stream) == NULL)
break;
blen = strlen(buf);
if (blen + llen <= (*size) - 1) {
strcat(*line, buf);
llen += blen;
}
else {
*size *= 2 ;
llen += blen;
*line = (char *) realloc(*line, *size);
strcat(*line, buf);
}
} while (buf[blen - 1] != '\n'&& !feof(stream));
return 0;
}
/* Return a NULL-terminated array of strings. */
int tokenizeString(char *string, int size, char ***output) {
char *start, *end;
char *p, *q;
int i;
end = string + size;
/* Remove spaces from end of string */
for (p = string; p < end && *p != '\0'; p++);
for (p--; isspace((int)*p); p--);
end = p + 1;
/* Remove spaces from beginning of string */
for (p = string; p < end && isspace((int)*p); p++);
start = p;
/* If start == end, then we have an empty string. */
if (start == end) {
*output = (char **) malloc(sizeof(char *) * 1);
(*output)[0] = '\0';
return 0;
}
/* Count number of tokens */
for (p = end-1, i = 0; p > start; p--) {
if (isspace((int)*p) && !isspace((int)*(p-1))) {
i++;
}
}
/* Number of tokens equals spaces + 1 */
i++;
*output = (char **) malloc(sizeof(char *) * (i + 1));
(*output)[i] = '\0';
/* Copy tokens to the char array */
for (i = 0, p = q = start; p < end && q < end; p = q, i++) {
for (q = p; !isspace((int)*q) && q < end; q++);
/* Copy the token to the string array */
(*output)[i] = (char *) malloc(sizeof(char) * (q - p + 1));
strncpy((*output)[i], p, q-p);
(*output)[i][q-p] = '\0';
/* Forward past any extra spaces between tokens */
for (q++; isspace((int)*q) && q < end; q++);
}
return(0);
}
/* Parse a sequence, either a single number, or a range of numbers. */
int parse_sequence(char *sequence) {
char *p;
int a, b;
for (p = sequence; *p != '\0'; p++) {
/* Found a range. */
if (*p == '-') {
*p = '\0';
p++;
a = atoi(sequence);
b = atoi(p);
return (b-a+1);
}
}
return(1);
}
syntax highlighted by Code2HTML, v. 0.9.1