#include #include #include #include #include #include #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); }