/*
 * Program:	MBOX mail routines
 *
 * Author:	Mark Crispin
 *		Networks and Distributed Computing
 *		Computing & Communications
 *		University of Washington
 *		Administration Building, AG-44
 *		Seattle, WA  98195
 *		Internet: MRC@CAC.Washington.EDU
 *
 * Date:	10 March 1992
 * Last Edited:	26 April 1992
 *
 * Copyright 1992 by the University of Washington
 *
 *  Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appears in all copies and that both the
 * above copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the University of Washington not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  This software is made
 * available "as is", and
 * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
 * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
 * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <stdio.h>
#include <ctype.h>
#include <pwd.h>
#include <netdb.h>
#include <errno.h>
extern int errno;		/* just in case */
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>
#include "osdep.h"
#include "mail.h"
#include "mbox.h"
#include "bezerk.h"
#include "misc.h"

#define	fsync(fd);	/* sync() */

/* MBOX mail routines */


/* Driver dispatch used by MAIL */

DRIVER mboxdriver = {
  (DRIVER *) NIL,		/* next driver */
  mbox_valid,			/* mailbox is valid for us */
  mbox_find,			/* find mailboxes */
  bezerk_find_bboards,		/* find bboards */
  mbox_open,			/* open mailbox */
  bezerk_close,			/* close mailbox */
  bezerk_fetchfast,		/* fetch message "fast" attributes */
  bezerk_fetchflags,		/* fetch message flags */
  bezerk_fetchenvelope,		/* fetch message envelopes */
  bezerk_fetchheader,		/* fetch message header only */
  bezerk_fetchtext,		/* fetch message body only */
  bezerk_fetchbody,		/* fetch message body section */
  bezerk_setflag,		/* set message flag */
  bezerk_clearflag,		/* clear message flag */
  bezerk_search,		/* search for message based on criteria */
  mbox_ping,			/* ping mailbox to see if still alive */
  mbox_check,			/* check for new messages */
  mbox_expunge,			/* expunge deleted messages */
  bezerk_copy,			/* copy messages to another mailbox */
  bezerk_move,			/* move messages to another mailbox */
  bezerk_gc			/* garbage collect stream */
};

/* Test for valid header */

#define VALID (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
  (s[4] == ' ') && (t = strchr (s+5,'\n')) && (t-s >= 27) && (t[-5] == ' ') &&\
  (t[(rn = -4 * (t[-9] == ' '))-8] == ':') && \
  ((sysv = 3 * (t[rn-11] == ' ')) || t[rn-11] == ':') && \
  (t[rn+sysv-14] == ' ') && (t[rn+sysv-17] == ' ') && \
  (t[rn+sysv-21] == ' ')


/* MBOX mail validate mailbox
 * Accepts: mailbox name
 * Returns: our driver if name is valid, otherwise calls valid in next driver
 */

DRIVER *mbox_valid (name)
	char *name;
{
  int fd;
  char s[MAILTMPLEN];
  char *t;
  int rn,sysv;
  int ret = NIL;
  struct stat sbuf;
				/* only consider INBOX */
  if (!strcmp (ucase (strcpy (s,name)),"INBOX")) {
				/* make what the file name would be */
    sprintf (s,"%s/mbox",getpwuid (geteuid ())->pw_dir);
				/* file exist? */
    if ((stat(s,&sbuf) == 0) && (fd = open (s,O_RDONLY,NIL)) >= 0) {
				/* allow empty or valid file */
      if ((sbuf.st_size == 0) || ((read (fd,s,MAILTMPLEN-1) >= 0) &&
				  (*s == 'F') && VALID)) ret = T;
      close (fd);		/* close the file */
    }
  }
  return ret ? &mboxdriver :
    (mboxdriver.next ? (*mboxdriver.next->valid) (name) : NIL);
}


/* MBOX mail find list of mailboxes
 * Accepts: mail stream
 *	    pattern to search
 */

void mbox_find (stream,pat)
	MAILSTREAM *stream;
	char *pat;
{
  /* Always a no-op since bezerk will do this */
}

/* MBOX mail open
 * Accepts: stream to open
 * Returns: stream on success, NIL on failure
 */

MAILSTREAM *mbox_open (stream)
	MAILSTREAM *stream;
{
  MAILSTREAM *ret;
  unsigned long i = 1;
  unsigned long recent = 0;
  char tmp[MAILTMPLEN];
				/* change mailbox file name */
  sprintf (tmp,"%s/mbox",getpwuid (geteuid ())->pw_dir);
  fs_give ((void **) &stream->mailbox);
  stream->mailbox = cpystr (tmp);
  stream->silent = T;		/* now do the open */
  if (ret = bezerk_open (stream)) {
    mbox_ping (ret);		/* snarf new mail */
				/* notify upper level of mailbox sizes */
    mail_exists (stream,stream->nmsgs);
    while (i <= stream->nmsgs) if (mail_elt (stream,i++)->recent) ++recent;
    mail_recent (stream,recent);/* including recent messages */
  }
  stream->silent = NIL;		/* allow external events now */
  return ret;
}

/* MBOX mail ping mailbox
 * Accepts: MAIL stream
 * Returns: T if stream alive, else NIL
 * No-op for readonly files, since read/writer can expunge it from under us!
 */

long mbox_ping (stream)
	MAILSTREAM *stream;
{
  int fd,sfd;
  char *s;
  long size;
  struct stat sbuf;
  char lock[MAILTMPLEN],slock[MAILTMPLEN];
  if (LOCAL && !stream->readonly && !stream->lock) {
    mm_critical (stream);	/* go critical */
				/* calculate name of bezerk file */
    sprintf (LOCAL->buf,MAILFILE,getpwuid (geteuid ())->pw_name);
    if ((sfd = bezerk_lock (LOCAL->buf,O_RDWR,NIL,slock,LOCK_EX)) >= 0) {
      fstat (sfd,&sbuf);	/* get size of the poop */
      if (size = sbuf.st_size){ /* non-empty? */
				/* yes, read it */
	read (sfd,s = (char *) fs_get (sbuf.st_size + 1),size);
	s[sbuf.st_size] = '\0';	/* tie it off */
	if ((fd = bezerk_lock (LOCAL->fname,O_WRONLY|O_APPEND,NIL,lock,
			       LOCK_EX)) >= 0) {
	  fstat (fd,&sbuf);	/* get current file size before write*/
	  if (write (fd,s,size) >= 0) ftruncate (sfd,0);
	  else {		/* failed */
	    sprintf (LOCAL->buf,"New mail copy failed: %s",strerror (errno));
	    mm_log (LOCAL->buf,ERROR);
	    ftruncate (fd,sbuf.st_size);
	  }
	  fsync (fd);		/* force out the update */
	  bezerk_unlock (fd,NIL,lock);
	}
	fs_give ((void **) &s);	/* flush the poop now */
      }
				/* all done with update */
      bezerk_unlock (sfd,NIL,slock);
    }
    mm_nocritical (stream);	/* release critical */
  }
  return bezerk_ping (stream);	/* do the bezerk routine now */
}

/* MBOX mail check mailbox
 * Accepts: MAIL stream
 */

void mbox_check (stream)
	MAILSTREAM *stream;
{
				/* do local ping, then do bezerk routine */
  if (mbox_ping (stream)) bezerk_check (stream);
}


/* MBOX mail expunge mailbox
 * Accepts: MAIL stream
 */

void mbox_expunge (stream)
	MAILSTREAM *stream;
{
  bezerk_expunge (stream);	/* do expunge */
  mbox_ping (stream);		/* do local ping */
}
