/*
* WMMail - Window Maker Mail
*
* Copyright (c) 1996, 1997, 1998 Per Liden
* Copyright (c) 1997, 1998 Bryan Chan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* mbox.c: functions to handle UNIX-style mailboxes
*
* $Id: mbox.c,v 1.2 2000/07/03 08:49:24 bryan.chan Exp $
*
*/
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <utime.h>
#include <unistd.h>
#include <proplist.h>
#include "wmmail.h"
#include "wmutil.h"
#ifdef MBOX_SUPPORT
/*
* Supported key(s) in the Options dictionary:
*
* MailboxHasInternalData
* CheckTimeStampOnly
* Path
*
* We can classify a given mailbox as being in one of four states.
* The states and their corresponding effects on wmmail_status are
* as follows:
*
* - no mailbox or empty mailbox NO_MAIL
* - no new mails OLD_MAIL
* - same number of new mails or fewer no change
* - more new mails than last time NEW_MAIL
*
* Or, if CheckTimeStampOnly is Yes:
*
* - no mailbox or empty mailbox NO_MAIL
* - smaller than last time but non-zero OLD_MAIL
* - read after most recent write OLD_MAIL
* - no read after most recent write and
* - same size as last time no change
* - bigger than last time NEW_MAIL
*
* We assume that overriding automatically changes wmmail_status
* to OLD_MAIL.
*
*/
/* internal functions */
static int check_time_stamp(Mailbox *, int *, int *, int *);
static int get_new_count(Mailbox *, int *, int *, int *);
static int count_mail(Mailbox *, char *);
#define GET_ENTRY(x,y,z) { proplist_t sub_key = PLMakeString(z); \
y = PLGetDictionaryEntry(x, sub_key); \
PLRelease(sub_key); }
int MBOX_check(Mailbox *mailbox, int *beep, int *redraw, int *run)
{
proplist_t check_time_stamp_only;
int okay, forced_no = False;
GET_ENTRY(mailbox->options, check_time_stamp_only, "CheckTimeStampOnly");
if ( check_time_stamp_only != NULL && PLIsString(check_time_stamp_only) &&
!strcasecmp(PLGetString(check_time_stamp_only), "Yes") )
{
if (num_of_msg_mode != SHOW_NONE)
{
croak("cannot count messages when CheckTimeStampOnly is \"Yes\"; "
"\"No\" assumed");
okay = get_new_count(mailbox, beep, redraw, run);
forced_no = True;
}
else
okay = check_time_stamp(mailbox, beep, redraw, run);
}
else
okay = get_new_count(mailbox, beep, redraw, run);
if ( forced_no ||
( check_time_stamp_only != NULL &&
( !PLIsString(check_time_stamp_only) ||
( strcasecmp(PLGetString(check_time_stamp_only), "Yes") &&
strcasecmp(PLGetString(check_time_stamp_only), "No") ) ) ) )
{
proplist_t label;
if (!forced_no)
croak("boolean expected for key \"CheckTimeStampOnly\"; "
"\"No\" assumed");
label = PLMakeString("CheckTimeStampOnly");
check_time_stamp_only = PLMakeString("No");
PLInsertDictionaryEntry(mailbox->options, label, check_time_stamp_only);
PLRelease(check_time_stamp_only);
PLRelease(label);
}
return okay;
}
static int check_time_stamp(Mailbox *mailbox, int *beep, int *redraw, int *run)
{
proplist_t path;
struct stat t;
char *mailbox_path;
int prev_status;
prev_status = mailbox->status;
GET_ENTRY(mailbox->options, path, "Path");
if (path == NULL)
{
croak("mailbox \"%s\" missing option \"Path\"; ignored", mailbox->name);
return False;
}
else if (!PLIsString(path))
{
croak("mailbox \"%s\" has invalid path; ignored", mailbox->name);
return False;
}
else
mailbox_path = expand_path(PLGetString(path));
if (!stat(mailbox_path, &t))
{
#ifdef DEBUG
croak("%s: %s", mailbox->name, mailbox_path);
croak("size: %d mtime: %d atime: %d", t.st_size, t.st_mtime, t.st_atime);
#endif
if (t.st_size == 0)
{
mailbox->status = NO_MAIL;
}
else if (t.st_size < mailbox->size)
{
/* mailbox smaller in size; some mails have been deleted */
mailbox->status = OLD_MAIL;
}
else if (t.st_atime > t.st_mtime)
{
/* mailbox read after most recent write */
mailbox->status = OLD_MAIL;
}
else if (t.st_size > mailbox->size)
{
/* mailbox modified after most recent read, and larger in size */
/* this implies the arrival of some new mails */
if (mailbox->status == NEW_MAIL && always_new_mail_exec)
*run |= True;
else if (mailbox->status == NEW_MAIL)
*run |= False;
else
*run |= True;
*beep = True;
mailbox->status = NEW_MAIL;
}
/* else no change */
mailbox->size = t.st_size; /* record size of mailbox */
}
else
{
croak("cannot open mailbox \"%s\"; ignored", mailbox->name);
wfree(mailbox_path);
return False;
}
*redraw |= (prev_status != mailbox->status);
mailbox->last_update = time(NULL);
wfree(mailbox_path);
return True;
}
static int get_new_count(Mailbox *mailbox, int *beep, int *redraw, int *run)
{
proplist_t path;
char *mailbox_path;
int prev_status,
prev_new_mail_count;
prev_status = mailbox->status;
prev_new_mail_count = mailbox->new_mail_count;
GET_ENTRY(mailbox->options, path, "Path");
if (path == NULL)
{
croak("mailbox \"%s\" missing option \"Path\"; ignored", mailbox->name);
return False;
}
else if (!PLIsString(path))
{
croak("mailbox \"%s\" has invalid path; ignored", mailbox->name);
return False;
}
else
mailbox_path = expand_path(PLGetString(path));
if (count_mail(mailbox, mailbox_path))
{
if (mailbox->total_mail_count == 0)
{
/* there is no mail in the mailbox */
mailbox->status = NO_MAIL;
}
else if (mailbox->new_mail_count == 0)
{
/* there are no new mails */
mailbox->status = OLD_MAIL;
}
else if (mailbox->new_mail_count > prev_new_mail_count)
{
/* new mails have arrived! */
if (mailbox->status == NEW_MAIL && always_new_mail_exec)
*run |= True;
else if (mailbox->status == NEW_MAIL)
*run |= False;
else
*run |= True;
*beep = True;
mailbox->status = NEW_MAIL;
}
/* else no change */
}
else
{
croak("cannot open mailbox \"%s\"; ignored", mailbox->name);
wfree(mailbox_path);
return False;
}
*redraw |= (prev_status != mailbox->status);
wfree(mailbox_path);
return True;
}
static int count_mail(Mailbox *mailbox, char *mailbox_path)
{
proplist_t has_internal_data;
FILE *file = NULL;
struct stat t;
struct utimbuf xtime;
char buf[13];
int end_of_header = False,
is_old = False;
int m = 0, /* mail_count */
n = 0; /* new_mail_count */
#ifdef DEBUG
croak("%s: %s", mailbox->name, mailbox_path);
#endif
GET_ENTRY(mailbox->options, has_internal_data, "MailboxHasInternalData");
/* only count the mails in the mailbox if it was modified after last update */
if (!stat(mailbox_path, &t) && t.st_ctime <= mailbox->last_update)
#ifdef DEBUG
{
croak("ctime: %d last_update: %d", t.st_ctime, mailbox->last_update);
return True;
}
#else
return True;
#endif
/* save atime */
xtime.actime = t.st_atime;
if ((file = fopen(mailbox_path, "r")) == NULL)
return False;
while (fgets(buf, 12, file) != NULL)
{
if (!strncmp(buf, "From ", 5))
{
end_of_header = False;
m++;
/* do not increment n if the _previous_ mail was already read */
/* always assume the first mail has not been read */
if (is_old)
is_old = False;
else
n++;
}
else if (!strcmp(buf, "\n"))
{
/* the first blank line marks the end of headers */
end_of_header = True;
}
else if (!strncmp(buf, "Status:", 7) && index(buf + 7, 'R'))
{
/* set is_old to True only if we are still reading
the headers; otherwise, is_old is not affected */
is_old |= !end_of_header;
}
if (buf[strlen(buf) - 1] != '\n')
{
/* skip everything until the next newline */
int i;
for (i = '\0'; i != '\n' && i != EOF; i = fgetc(file));
}
}
/* must correct the obiwan error by decrementing n if the last mail is old */
if (is_old)
n--;
/* if an mbox file contains a dummy message, discount it */
if ( has_internal_data != NULL && PLIsString(has_internal_data) &&
!strcasecmp(PLGetString(has_internal_data), "Yes") )
m--;
else if ( has_internal_data != NULL && (!PLIsString(has_internal_data) ||
( strcasecmp(PLGetString(has_internal_data), "Yes") &&
strcasecmp(PLGetString(has_internal_data), "No") )) )
{
proplist_t label;
croak("boolean expected for key \"MailboxHasInternalData\"; "
"\"No\" assumed");
label = PLMakeString("MailboxHasInternalData");
has_internal_data = PLMakeString("No");
PLInsertDictionaryEntry(mailbox->options, label, has_internal_data);
PLRelease(has_internal_data);
PLRelease(label);
}
fclose(file);
stat(mailbox_path, &t);
xtime.modtime = t.st_mtime; /* save mtime */
utime(mailbox_path, &xtime); /* reset atime and mtime */
mailbox->last_update = time(NULL);
mailbox->total_mail_count = m;
mailbox->new_mail_count = n;
#ifdef DEBUG
croak("mail count: %d/%d",
mailbox->new_mail_count, mailbox->total_mail_count);
#endif
return True;
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1