/* $Cambridge: hermes/src/prayer/accountd/mail.c,v 1.1.1.1 2003/04/15 13:00:02 dpc22 Exp $ */
/************************************************
* Prayer - a Webmail Interface *
************************************************/
/* Copyright (c) University of Cambridge 2000 - 2002 */
/* See the file NOTICE for conditions of use and distribution. */
#include "accountd.h"
/* mail_string_add() *****************************************************
*
* Copy string to ptr, add pointer over string
* Up to client to worry about bounds checking and buffer overruns.
* mail_fake_simple is the only client
*
* sp: Targ string. Updated to point to end of copied string.
* s: String to add
************************************************************************/
static void mail_string_add(char **sp, char *s)
{
strcpy(*sp, s);
*sp += strlen(s);
}
/* mail_fake_simple() ****************************************************
*
* Convert simple format .forward files into equivalent MSforward so that
* client doesn't have to worry about the distinguistion.
* stream: iostream connection to client
* file: .forward file to test
*
* Returns:
* NIL => input file was not simple .forward file
* T => .forward file was simple format. OK {literal} to output.
************************************************************************/
static BOOL mail_fake_simple(struct iostream *stream, FILE * file)
{
static char buffer[MAXLENGTH];
static char tmp[2 * MAXLENGTH]; /* Safe upper bound */
char *s;
int c, i, count;
int copy, vacation;
char *address;
/* Get first line from file */
if (!fgets(buffer, MAXLENGTH - 1, file))
return (NIL);
buffer[strlen(buffer) - 1] = '\0'; /* Remove trailing \n */
/* Can't fake Exim filter files */
if (!strncasecmp(buffer, "# Exim filter ", strlen("# Exim filter ")))
return (NIL);
/* Can't fake multiline files */
while ((c = getc(file)) != EOF)
if ((c != '\015') && (c != '\015') && (c != ' ') && (c != '\t'))
return (NIL);
/* Old style formats that we want to support:
*
* \user, vacation-user ==> VACATION action
* \user, address ==> REDIRECT action
*/
copy = NIL;
vacation = NIL;
address = NIL;
s = buffer;
/* Skip leading whitespace */
while (*s == ' ')
s++;
if (*s == '\\') {
/* "\dpc22, something" */
copy = T;
if (!(s = strchr(s, ',')))
return (NIL); /* Something funny going on */
s++;
} else
s = buffer;
/* Skip leading whitespace */
while (*s == ' ')
s++;
if (!strncmp(s, "vacation-", strlen("vacation-"))) {
if (strchr(s, '@')) /* Catch vacation-foo@some.remote.site */
return (NIL);
vacation = T;
address = NIL;
} else
address = s;
/* .forward file passes our regex: convert to equivalent MSforward */
s = tmp;
mail_string_add(&s, "# MSforward file created by prayer\n");
if (vacation)
mail_string_add(&s, "vacation\n");
if (address && address[0]) {
mail_string_add(&s, "redirect\n");
mail_string_add(&s, " address\t");
mail_string_add(&s, address);
mail_string_add(&s, "\n");
if (copy)
mail_string_add(&s, " copy true\n");
}
count = (int) (s - tmp);
ioprintf(stream, "OK {%d}" CRLF, count);
for (i = 0; i < count; i++)
ioputc(tmp[i], stream);
ioputs(stream, "" CRLF);
ioflush(stream);
return (T);
}
/* ====================================================================== */
/* mail_status() *********************************************************
*
* MAILSTATUS command.
* config: Accountd configuration
* stream: iostream connection to client
* line: Arguments to MAILSTATUS: should be empty
*
* Returns: T on success, NIL otherwise
*
* Output:
* OK {literal} => Current mail status as MSforward format
* NO [Error message] => Couldn't determine mail status
* BAD [Error message] => Protocol error
************************************************************************/
BOOL
mail_status(struct config * config, struct iostream * stream, char *line)
{
unsigned long existing, current;
FILE *file;
struct stat sbuf;
int c;
if (!checksum_test(config, &existing, ¤t)) {
ioputs(stream, "OK {0}" CRLF); /* No mail forwarding in place */
ioflush(stream);
return (T);
}
if (existing != 0) {
/* .forward file exists and contains checksum */
if (existing != current) {
ioputs(stream,
"NO Checksum mismatch: manually maintained .forward file?"
CRLF);
ioflush(stream);
return (T);
}
if (stat(config->msforward_name, &sbuf) ||
((file = fopen(config->msforward_name, "r")) == NULL)) {
ioputs(stream,
"NO Inconsistent state: Couldn't open .MSforward file"
CRLF);
ioflush(stream);
return (T);
}
ioprintf(stream, "OK {%lu}" CRLF, (unsigned long) sbuf.st_size);
while ((c = getc(file)) != EOF)
ioputc(c, stream);
ioputs(stream, "" CRLF);
ioflush(stream);
fclose(file);
return (T);
}
/* No checksum in .forward file. Check for trivial case that we can fake
* up as an automatically managed file */
if ((file = fopen(config->forward_name, "r")) == NULL) {
/* Worked a second ago! */
ioputs(stream,
"NO Checksum mismatch: manually maintained .forward file?"
CRLF);
ioflush(stream);
return (T);
}
if (!mail_fake_simple(stream, file)) {
ioputs(stream, "NO Manually maintained .forward file?" CRLF);
ioflush(stream);
}
fclose(file);
return (T);
}
/* ====================================================================== */
/* XXX Should really update temporary files, rename */
/* mail_change() *********************************************************
*
* MAILCHANGE command
* config: accountd configuration
* stream: iostream connection to client
* line: Arguments to MAILCHANGE. Literal containing MSforward
* restricted: T => filters are restricted to "mail" subdirectory
*
* Returns: T on success, NIL otherwise
*
* Output:
* OK [text]. MSforward file uploaded, filter filter generated
* NO [text]. Upload or translation failed
* BAD [text]. Protocol error
************************************************************************/
BOOL
mail_change(struct config * config, struct iostream * stream, char *line)
{
char *size;
FILE *file;
int c, len;
unsigned char *text = NIL;
unsigned char *s;
if (!((size = string_get_token(&line)) && (size[0]))) {
ioputs(stream, "BAD No file size provided" CRLF);
ioflush(stream);
return (T);
}
if (((len = strlen(size)) < 2) ||
(size[0] != '{') || (size[len - 1] != '}')) {
ioputs(stream, "BAD Invalid file size" CRLF);
ioflush(stream);
return (T);
}
/* Check that size[1] -> size[len-1] all digits? */
size[len - 1] = '\0'; /* Probably not needed */
len = atoi(size + 1);
/* Copy filter value to string */
if ((text = malloc(len + 1)) == NIL) {
ioputs(stream, "NO Out of memory" CRLF);
ioflush(stream);
return (T);
}
s = text;
while ((len > 0) && (c = iogetc(stream))) {
*s++ = (unsigned char) c;
len--;
}
*s = '\0';
if (len > 0) {
free(text);
ioprintf(stream, "BAD %d bytes missing from input" CRLF, len);
ioflush(stream);
return (T);
}
/* Check whether filter file actually contains actions */
if (filter_empty((char *) text)) {
free(text);
unlink(config->msforward_name);
unlink(config->forward_name);
ioputs(stream, "OK Filter file disabled" CRLF);
ioflush(stream);
return (T);
}
/* Push filter value into .MSforward file */
if ((file = fopen(config->msforward_name, "w")) == NIL) {
/* Swallow unwanted text */
while ((len > 0) && (c = iogetc(stream)))
len--;
fclose(file);
ioputs(stream, "NO Couldn't open file" CRLF);
ioflush(stream);
return (T);
}
for (s = text; *s; s++)
putc(*s, file);
if (fclose(file) != 0) {
free(text);
ioputs(stream,
"NO Failed to update filter file. Quota problems?" CRLF);
ioflush(stream);
return (T);
}
/* File successfully uploaded. Run update script */
if (filter_write_forward(config, (char *) text))
ioputs(stream, "OK Filter file uploaded" CRLF);
else
ioputs(stream,
"NO Failed to update filter file. Quota problems?" CRLF);
ioflush(stream);
free(text);
return (T);
}
/* ====================================================================== */
/* mail_vacclear() *******************************************************
*
* Clear vacation log file
* config: Accountd configuration
* stream: iostream connection to client
* line: Arguments for VACATION_CLEAR command. Should be empty
*
* Returns: T in all situations
* Output: OK in all situations
************************************************************************/
BOOL
mail_vacclear(struct config * config, struct iostream * stream, char *line)
{
unlink("vacation.log");
unlink("vacation.once.dir");
unlink("vacation.once.pag");
ioputs(stream, "OK Vacation Log cleared" CRLF);
ioflush(stream);
return (T);
}
syntax highlighted by Code2HTML, v. 0.9.1