Newer
Older
imapext / src / osdep / unix / maildir.c
@yuuji@gentei.org yuuji@gentei.org on 14 Sep 2009 46 KB [mq]: imapext
/*
 * Maildir Module for PINE 4.0x - fourth release, use with CARE! 
 *
 * Author:      Mattias Larsson <ml@techno.org>
 *
 * Version:     21.07.98
 *
 * Please read the README.maildir file before using this module!
 *
 * If you have any questions, please e-mail ml@techno.org 
 *
 * Multiple inboxes patch by Dean Gaudet <dgaudet@arctic.org>
 *
 * =================================================
 *
 * Based on the IMAP2 maildir routines by:
 *
 * Author:      Eric Green
 *              Bloodhounds International Inc.
 *              thrytis@imaxx.net
 *
 * Additional contributions from:
 *              Aidas Kasparas (kaspar@soften.ktu.lt)
 *
 * Date:        27 April 1997
 * Last Edited: 13 June 1997
 *
 * Based (heavily) on mh.c and other c-client library files by Mark Crispin:
 *
 *              Mark Crispin
 *              Networks and Distributed Computing
 *              Computing & Communications
 *              University of Washington
 *              Administration Building, AG-44
 *              Seattle, WA  98195
 *              Internet: MRC@CAC.Washington.EDU
 *
 * Copyright 1995 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.
 *
 */

/* CONFIGURABLE OPTIONS - PLEASE CHECK THESE OUT */

#define NO_MAILDIR_FIDDLE	/* disallow Maildir with Maildir in the
				name. This is useful in an ISP setup
				using the IMAP daemon. #undef it if you
				are running a normal pine and know what
				you are doing */

/* #define NO_ABSOLUTE_PATHS	/* if you define this, all paths
				use your HOMEDIR is the root instead
				of the actual root of the machine. This
				is also useful in an ISP setup with
				IMAP */

#undef NO_UID_VALIDITIY		/* define this if you want the UID's not
				to be persistent over sessions. Use this
				if you use another client to read the
				maildir that screws up the special way
				in which we store UIDs. Do not enable
				unless you are sure you need it. */
	
/* END CONFIGURATION */

#define MTA_DEBUG	/* debugging sent to stdout */
#undef MTA_DEBUG

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
extern int errno;		/* just in case */
#include "mail.h"
#include "osdep.h"
#include <pwd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <dirent.h>
#include <utime.h>
#include "maildir.h"
#include "misc.h"
#include "dummy.h"

/* Driver dispatch used by MAIL */

DRIVER maildirdriver = {
  "maildir",			/* driver name */
				/* driver flags */
  DR_MAIL|DR_LOCAL|DR_NOFAST|DR_NAMESPACE,
  (DRIVER *) NIL,		/* next driver */
  maildir_valid,			/* mailbox is valid for us */
  maildir_parameters,		/* manipulate parameters */
  NIL,			/* scan mailboxes */
  maildir_list,			/* find mailboxes */
  maildir_lsub,			/* find subscribed mailboxes */
  maildir_sub,			/* subscribe to mailbox */
  maildir_unsub,		/* unsubscribe from mailbox */
  maildir_create,			/* create mailbox */
  maildir_delete,			/* delete mailbox */
  maildir_rename,			/* rename mailbox */
  NIL,				/* status of mailbox */
  maildir_open,			/* open mailbox */
  maildir_close,		/* close mailbox */
  maildir_fast,			/* fetch message "fast" attributes */
  NIL,				/* fetch message flags */
  NIL,				/* fetch overview */
  NIL,				/* fetch message envelopes */
  maildir_fetchheader,		/* fetch message header */
  maildir_fetchtext,		/* fetch message body */
  NIL,				/* fetch partial message text */
  NIL,				/* unique identifier */
  NIL,				/* message number */
  NIL,				/* modify flags */
  maildir_flagmsg,		/* per-message modify flags */
  NIL,				/* search for message based on criteria */
  NIL,				/* sort messages */
  NIL,				/* thread messages */
  maildir_ping,			/* ping mailbox to see if still alive */
  maildir_check,		/* check for new messages */
  maildir_expunge,			/* expunge deleted messages */
  maildir_copy,			/* copy messages to another mailbox */
  maildir_append,			/* append string message to mailbox */
  maildir_gc				/* garbage collect stream */
};

				/* prototype stream */
MAILSTREAM maildirproto = {&maildirdriver};

/* Check validity of mailbox
 */
 
DRIVER *maildir_valid (char *name)
{
  return maildir_isvalid(name,T) ? &maildirdriver : NIL;
}

int maildir_isvalid (char *name,long justname)
{
  char tmp[MAILTMPLEN];
  struct stat sbuf;
  
  if (!name || (!*name) || 
      ((*name == '#') && 
       (*(name+1) == 0 ||
	(*(name+1) != 'm' && *(name+1) != 'M') ||
	(*(name+2) != 'd' && *(name+1) != 'D') ||
	*(name+3) != '/')) || (*name == '.'))
    return NIL;
  
  /* okay, anything containing the name Maildir will be ignored
     this is to prevent anyone from fiddling with their incoming Maildir
     directly, it should be accessed via the INBOX alias */

  #ifdef NO_MAILDIR_FIDDLE
  if (strstr(name, "Maildir")) {
	return NIL; 
	}
  #endif
 				/* If we are requested only to check 
  				   if the name is appropriate then we
  				   have done! */
  if (justname && *name == '#') return T;
  

				/* must be valid local mailbox */
  if ((*name != '*') && (*name != '{') &&
      maildir_file (tmp,name) &&
				/* assume its maildir if its a dir */
      stat (tmp,&sbuf) == 0 && S_ISDIR (sbuf.st_mode))
    return T;

				/* INBOX is for default Maildir */
  if (!strcmp (ucase (strncpy (tmp,name,MAILTMPLEN)), "INBOX") &&
      (stat (maildir_file (tmp,name),&sbuf) == 0) &&
      S_ISDIR (sbuf.st_mode))
    return T;

  return NIL;
}
#ifdef QMAIL
#ifndef QMAILCONTROL
# define QMAILCONTROL "/var/qmail/control"
#endif
#ifndef DOTQMAIL
# ifdef POSTFIX
#  define DOTQMAIL ".forward"
# else
#  define DOTQMAIL ".qmail"
# endif
#endif
#ifndef XADDR_DELIM
# ifdef POSTFIX
#  define XADDR_DELIM "+"
# else
#  define XADDR_DELIM "-"
# endif
#endif

/* Convert virtual domain user
 */
char* conv_virtualdomain(char *account) {
  char *dom = strchr(account, '@'), *p;
  char vd[MAILTMPLEN+1], rewrite[MAILTMPLEN+1], previous[MAILTMPLEN+1];
  FILE *vdfd;
  int match=0;
  snprintf(vd, MAILTMPLEN, "%s/%s", QMAILCONTROL, "virtualdomains");
  if (NIL == dom) return account;
  dom++;		/* set position of domain part beginning */
  if (dom && NIL != (vdfd = fopen (vd, "r"))) {
    char buf[MAILTMPLEN+1], *s;
    int l = strlen(dom);
    int L = strlen(account);
    while ((s=fgets(buf, MAILTMPLEN, vdfd))) {
      if (p=strchr(s, '#'))
        *p = '\0';			/* zap comments */
      if (!strchr(buf, ':'))
        continue;
      while (s && (strrchr(s, '\n') || strrchr(s, '\r') || strrchr(s, ' ')))
        s[strlen(s)-1] = '\0';
      if (!strncmp(account, s, L) && s[L] == ':' && s[L+1]) { /* user matches */
	match = 3;
        snprintf(rewrite, MAILTMPLEN, "%s%s%s", s+L+1, XADDR_DELIM, account);
	break;
      }
      if (!strncmp(dom, s, l) && s[l] == ':' && s[l+1]) { /* domain matches */
        match = 2;
	snprintf(rewrite, MAILTMPLEN, "%s%s%s", s+l+1, XADDR_DELIM, account);
	continue;
      }
      if (match < 2 && s[0] == '.') { /* if domain described in wildcard */
        if (p=strchr(s, ':')) {
	  *p = '\0';
	  if (!strcmp(dom+(strlen(dom)-strlen(s)), s)) {
	    if (match == 0
	        || strlen(previous) < strlen(s)) {
	      match = 1;
	      strncpy(previous, s, MAILTMPLEN);
	      snprintf(rewrite, MAILTMPLEN, "%s%s%s", p+1, XADDR_DELIM, account);
	    }
	  }
	}
      }
    }
    fclose(vdfd);
    if (match) {
      p = strchr(rewrite, '@');
      /* fprintf(stderr, "m=%d, rwr=[%s]\n", match, rewrite); */
      if (p) {
        *p = '\0';
      }
      /* fprintf(stderr, "rwr=[%s]\n", rewrite); */
      s = cpystr(rewrite);
      memset(vd, 0, sizeof(vd));
      memset(rewrite, 0, sizeof(rewrite));
      memset(previous, 0, sizeof(previous));
      return s;
    }
  }
  return account; /* return itself */
}
/* -------------------------------------------------
   Qmail virtual domain hack ends
   ------------------------------------------------- */
#endif

char *maildir_getmaildir() {
  static char dir[MAILTMPLEN];
  extern char *mymailsuffix();
  static char qfile[MAILTMPLEN];
  FILE *qmail;
  dir[0] = '\0';
  snprintf(qfile, MAILTMPLEN, "%s/%s", myhomedir(), DOTQMAIL);
  strncat(qfile, mymailsuffix(), MAILTMPLEN);
  qmail = fopen(qfile, "r");
  if (NULL != qmail) {	/* if ~/.qmail file exists */
    char buf[MAILTMPLEN], *s;
    while ((s=fgets(buf, MAILTMPLEN, qmail))) {
	/* Skip preceding spaces and trailing newlines */
	while (*s && (*s == ' ' || *s == '\t')) {s++;}
	while (*s && (s[strlen(s)-1] == '\r' || s[strlen(s)-1] == '\n')) {
	s[strlen(s)-1] = '\0';
	}
	if (*s == '#') continue;
	if (s[strlen(s)-1] != '/') continue; /* Skip non maildir format */
        if (*s == '~' && *(s+1) == '/')
            *s = '.';           /* Convert `~/' to `./' (for Postfix) */
	strncpy(dir, s, MAILTMPLEN);
        break;
    }
    fclose(qmail);
  }
#ifdef DEFAULTMAILDIR
  else {
    strncpy(dir, DEFAULTMAILDIR, MAILTMPLEN);
  }
#endif
  if (dir[0]) {
    return dir;
  } else {
#ifdef DEFAULTMAILDIR
    snprintf(dir, MAILTMPLEN, "%s/%s/", myhomedir(), DEFAULTMAILDIR);
#else
    snprintf(dir, MAILTMPLEN, "%s/%s/", myhomedir(), MAILDIRPATH);
#endif
    return dir;
  }
}

/* Maildir mail generate file string
 */

char *maildir_file (char *dst,char *name)
{
  char tmp[MAILTMPLEN];
  
  if (strlen (name) > 3 &&	/* safe do other comparisons */
      (*name == '#') &&
      (name[1] == 'm' || name[1] == 'M') &&
      (name[2] == 'd' || name[2] == 'D') &&
      (name[3] == '/'))
    name += 4;

#ifdef NO_ABSOLUTE_PATHS  
  if (*name == '/') {	
  /* we do not want to accept / absolute paths, so lets strip the first
     / ... */
    snprintf(dst, MAILTMPLEN, "%s/%s/cur", myhomedir(), name+1);

/*    strncpy (dst, name, MAILTMPLEN - 2);
    strncat (dst, "/cur", MAILTMPLEN - 2);
    dst[MAILTMPLEN - 1] = '\0'; */
  }
  else
    snprintf (dst, MAILTMPLEN, "%s/%s/cur",myhomedir (),
	    strcmp (ucase (strcnpy (tmp, name, MAILTMPLEN)), "INBOX") ? name : maildir_getmaildir());
#else
  if (*name == '/') {	
    strncpy (dst, name, MAILTMPLEN - 2);
    strncat (dst, "/cur", MAILTMPLEN - 2);
    dst[MAILTMPLEN - 1] = '\0';
  }
  else {
      char *maildir = maildir_getmaildir();
      if (*maildir == '/') {
          snprintf (dst,MAILTMPLEN,"%scur",maildir);
      } else {
          snprintf (dst,MAILTMPLEN,"%s/%s/cur",myhomedir (),
                   strcmp (ucase (strncpy (tmp, name, MAILTMPLEN)), "INBOX") ? name : maildir);
      }
  }

#endif 
  
  #ifdef MTA_DEBUG
  printf("maildir_file '%s'\n", dst);
  #endif
  return dst;
}

/* Maildir open
 */
 
MAILSTREAM *maildir_open (MAILSTREAM *stream)
{
 char tmp[MAILTMPLEN],tmp2[MAILTMPLEN];

 if (!stream) return &maildirproto;
 if (LOCAL) {		/* recycle stream */
	maildir_close (stream, 0);
	stream->dtb = &maildirdriver;
	mail_free_cache (stream);
	stream->uid_last = 0;       /* default UID validity */
	stream->uid_validity = time (0);
	}

	stream->uid_validity = 0; /* was time(0) */

  if (stream->uid_last < time(0))
  	stream->uid_last = time (0);  
  
    stream->local = fs_get (sizeof (MAILDIRLOCAL));
  LOCAL->inbox = !strcmp (ucase (strncpy (tmp,stream->mailbox,MAILTMPLEN)),"INBOX") ||
      !strcmp (stream->mailbox,maildir_file (tmp2,"INBOX"));
  LOCAL->dir = cpystr (maildir_file (tmp,stream->mailbox)); /* copy dir name */
                                /* make temporary buffer */
  LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE-1) + 1);
  LOCAL->scantime = 0;          /* not scanned yet */
  stream->sequence++;
  stream->nmsgs = stream->recent = 0;

  maildir_ping_core (stream);
  maildir_ping (stream);
/*  if (maildir_ping (stream) && !(stream->nmsgs || stream->silent))
    printf("Mailbox is empty\n");
*/
  return stream;

}

/* Maildir ping mailbox
 */

long maildir_ping_core (MAILSTREAM *stream)
{
  char tmp[MAILTMPLEN];
  MESSAGECACHE *elt;
  struct stat sbuf, sbuf2;
  DIR *dir;
  struct direct *d;
  int reloadall = NIL;
  int uidinvalid = NIL;
  unsigned long old;
  long i;
  long nmsgs = stream->nmsgs;
  long recent = stream->recent;
  long nfiles = stream->nmsgs;
  int silent = stream->silent;
  char *s, *s2;
  mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);

/*  maildir_copynew (LOCAL->dir);
 */
  
  if (stat (LOCAL->dir,&sbuf) < 0) {
    snprintf (tmp,MAILTMPLEN,"Unable to open maildir: %s",strerror (errno));
    mm_log (tmp,ERROR);
    return NIL;
  }

  /* okay, lets try to figure out the Maildir UID validity. This is done
     by checking the last modification time of the file .uidvalidity
     in the rootdir of the Maildir. Any program reordering the files
     in the directory have to touch this file */

  snprintf(tmp, MAILTMPLEN, "%s/../.uidvalidity", LOCAL->dir);
  
  if (stat (tmp,&sbuf2) < 0) {
	/* no uid validity file found, if uid_validity == 0, we have
	   to set it to something other than 0, and then create the
	   .uidvalidity file for future accesses */

	if (stream->uid_validity == 0) {
		FILE *fl;
		struct utimbuf tbuf;
		
		stream->uid_validity = time(0);
		tbuf.actime = stream->uid_validity;
		tbuf.modtime = stream->uid_validity;

		if ((fl = fopen(tmp, "w"))) {
			fclose(fl);
			chmod (tmp, S_IRUSR|S_IWUSR);
			utime(tmp, &tbuf);
			}
		}
		uidinvalid = T; /* UID's are invalid, update them */
	} else {
	/* valid file, lets set UID if uid_validity = 0 */
	if (stream->uid_validity == 0) {
	stream->uid_validity = sbuf2.st_mtime;
		}
	}

  #ifdef NO_UID_VALIDITY
  uidinvalid = T; /* force the UIDs to be invalid and reset every time,
  		     useful in an environment without imap servers and
  		     clients that screw up the UIDs.. i'd leave it to
  		     OFF until I really need it though... */
  #endif

  stream->silent = T;  
  if (sbuf.st_ctime != LOCAL->scantime) {
    /* update the message list */
    /* int maildir_namesort(const void *,const void *);
       int (*mn)(struct direct **, struct direct **) = maildir_namesort; */
    struct direct **names = NIL;
    nfiles = scandir (LOCAL->dir,&names,maildir_select,maildir_namesort);

    for (i = 0; i < nfiles; i++) {

	/* check if file has executable bit set */
	snprintf(tmp, MAILTMPLEN, "%s/%s", LOCAL->dir, names[i]->d_name);
  	stat (tmp,&sbuf2);
	if (sbuf2.st_mode & S_IXUSR) {
		/* executable bit set, modtime is uid */
		if (sbuf2.st_mtime > stream->uid_last)
			stream->uid_last = sbuf2.st_mtime+1;
		}
	/* this is kept for backwards compatibility */
        if ((s = strstr (names[i]->d_name,":3,")))
		s += 3;
        if (s && (s2 = strstr (s, ",U"))) {
      		s2 += 2;
		sscanf(s2, "%d", &old);
		if (old > stream->uid_last) {
			stream->uid_last = old+1;
			}
		}	
	
    }

    mm_critical (stream);	/* go critical */
    old = stream->uid_last;
    LOCAL->scantime = sbuf.st_ctime;

				/* check if old files same */
    for (i = 0; i < stream->nmsgs; i++) {

      if (strcmp ((char *) mail_elt (stream, i + 1)->maildirp,
		  names[i]->d_name)) {
	reloadall = T;
	break;
      }
    }

    if (reloadall) { /* files are out of order, rebuild cache */

      i = 1;
      while (i <= stream->nmsgs)
				/* clean out cache */
	if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT))) {
	  fs_give ((void **) &elt->maildirp);
	  mail_expunged (stream,i);
	}
	else
	  i++;
      
      mm_log ("Warning: Mailbox has changed in an unexpected way.  Reloading.",
	      WARN);
      stream->nmsgs = 0;
    }
    nmsgs = stream->nmsgs;
    
    stream->nmsgs = nfiles; /* hm? */    

    for (i = nmsgs; i < nfiles; i++) {

      mail_exists(stream, i+1);
				/* if newly seen, add to list */
      (elt = mail_elt (stream, i + 1))->maildirp = (long) cpystr (names[i]->d_name);
       elt->valid = T;

      /* grab the flags */
      if ((s = strstr (names[i]->d_name,":3,"))) {
	s += 3;
	if (strchr (s,'F'))
	  elt->flagged = T;
	if (strchr (s,'R'))
	  elt->answered = T;
	if (strchr (s,'S'))
	  elt->seen = T;
	if (strchr (s,'T'))
	  elt->deleted = T;
      } else if ((s = strstr (names[i]->d_name,":2,"))) {
	/* this is the :2, id where all files go nowadays */
	s += 3;
	if (strchr (s,'F'))
	  elt->flagged = T;
	if (strchr (s,'R'))
	  elt->answered = T;
	if (strchr (s,'S'))
	  elt->seen = T;
	if (strchr (s,'T'))
	  elt->deleted = T;
	snprintf(tmp, MAILTMPLEN, "%s/%s", LOCAL->dir, names[i]->d_name);
  	stat (tmp,&sbuf2);
	if (sbuf2.st_mode & S_IXUSR) {
		/* executable bit set, modtime is uid */
		elt->private.uid = sbuf2.st_mtime;
		}
	/* and if we could not retrieve UID from modtime, or if
	   UIDs are invalid, go here */
	if (elt->private.uid == 0 || uidinvalid) {
        	stream->uid_last = (elt = mail_elt (stream,i+1))->private.uid = stream->uid_last+1;
		maildir_flagmsg(stream, elt);
		}	
	s = 0; /* make sure next if statement does not trigger */      
      	}

      if (s)
      if ((s2 = strstr (s, ",U"))) {
      	s2 += 2;
	sscanf(s2, "%d", &elt->private.uid);
	if (elt->private.uid == 0 || uidinvalid) {
        	stream->uid_last = (elt = mail_elt (stream,i+1))->private.uid = stream->uid_last+1;
		maildir_flagmsg(stream, elt);
		}	
	
      	} else { /* assign new UID */
        stream->uid_last = (elt = mail_elt (stream,i+1))->private.uid = stream->uid_last+1;
	elt->recent = T;
	recent++;
	maildir_flagmsg(stream, elt); /* store the UID that we assigned to it */
	}	



    }

    mm_nocritical (stream);	/* release critical */
				/* free the names stuff */
    for (i = 0; i < nfiles; i++)
      fs_give ((void **) &names[i]);
    if (names)
      fs_give ((void **) &names);
  }
  stream->silent = silent;
  mail_exists(stream,nfiles);
/*  if (!reloadall)  */
  	mail_recent (stream,recent);

  return T;			/* return that we are alive */
}

long maildir_ping (MAILSTREAM *stream)
{
  maildir_copynew (LOCAL->dir);
  return maildir_ping_core (stream);
}

void maildir_copynew (const char *mailbox)
{
  char tmp[MAILTMPLEN],file[MAILTMPLEN],newfile[MAILTMPLEN];
  DIR *dir;
  struct dirent *d;
  struct stat sbuf;
  
  snprintf (tmp,MAILTMPLEN,"%s/../new",mailbox);
  if (!(dir = opendir (tmp)))
    return;

  while (d = readdir (dir)) {
    if (d->d_name[0] == '.')
      continue;			/* skip .files */

    snprintf (file,MAILTMPLEN,"%s/%s",tmp,d->d_name);
				/* make sure this is a normal file */
    if (stat (file,&sbuf) == 0 && S_ISREG (sbuf.st_mode)) {
      
      if (strstr (d->d_name,":3,")) /* this message already has flags */
	snprintf (newfile,MAILTMPLEN,"%s/%s",mailbox,d->d_name);
      else
	snprintf (newfile,MAILTMPLEN,"%s/%s:3,",mailbox,d->d_name);
      
				/* move the new mail to the cur dir */
      if (link (file,newfile) == -1)
	mm_log("Unable to read new mail!",WARN);
      else
	unlink (file);	
    }
  }
  closedir (dir);
}

int maildir_select (struct direct *name)
{
  if (name->d_name[0] != '.')
    return T;

  return NIL;
}

/* int maildir_namesort (struct direct **d1,struct direct **d2) */
int maildir_namesort (const void *d1, const void *d2)
{
  return strcmp ((*(struct direct **)d1)->d_name,
                 (*(struct direct **)d2)->d_name);
}


/* Maildir garbage collect stream
 */

void maildir_gc (MAILSTREAM *stream,long gcflags)
{
  unsigned long i;
  
  if (gcflags & GC_TEXTS) {	/* garbage collect texts? */
				/* flush texts from cache */
/*    if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr);
//    if (stream->text) fs_give ((void **) &stream->text);
//    stream->msgno = 0;		invalidate stream text
*/
  }
}

/* Maildir close
 */

void maildir_close (MAILSTREAM *stream, long options)
{
  MESSAGECACHE *elt;
  int i;
  mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
 
/*  CL_EXPUNGE OPTION SUPPORT HERE SOMEWHERE! */
				/* clean out the cached paths */
  for (i = 1; i <= stream->nmsgs; i++)
    if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) && elt->maildirp) {
      fs_give ((void **) &elt->maildirp);
      elt->maildirp = 0; /* otherwise pine coredumps */  
	}  
  
  if (LOCAL) {			/* only if a stream is open */
    if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
    maildir_gc (stream,GC_TEXTS); /* free local cache */
				/* free local scratch buffer */
    if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
				/* nuke the local data */
    fs_give ((void **) &stream->local);
    stream->dtb = NIL;		/* log out the DTB */
  }
}

void maildir_check (MAILSTREAM *stream)
{
  /* Perhaps in the future this will preserve flags */
  if (maildir_ping (stream)) mm_log ("Check completed",(long) NIL);   
}

long maildir_fetchtext (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags)
{
  unsigned long i;
  MESSAGECACHE *elt;
                                /* UID call "impossible" */
  if (flags & FT_UID) return NIL;
  elt = mail_elt (stream,msgno);/* get elt */
                                /* snarf message if don't have it yet */
  if (!elt->private.msg.text.text.data) {
    maildir_fetchheader (stream,msgno,&i,flags);
    if (!elt->private.msg.text.text.data) return NIL;
  }
  if (!(flags & FT_PEEK)) {     /* mark as seen */
    mail_elt (stream,msgno)->seen = T;
    maildir_flagmsg (stream, mail_elt(stream,msgno));
    mm_flags (stream,msgno);
  }
  if (!elt->private.msg.text.text.data) return NIL;
  INIT (bs,mail_string,elt->private.msg.text.text.data,
        elt->private.msg.text.text.size);
  return T;
}


/* Maildir fetch message header
 */

char *maildir_fetchheader (MAILSTREAM *stream,unsigned long msgno,
		unsigned long *length, long flags)
{
  unsigned long i,hdrsize;
  int fd;
  char *t;
  char tmp[MAILTMPLEN];
  char *s,*b;
  struct stat sbuf;
  struct tm *tm;
  MESSAGECACHE *elt;
  *length = 0;                  /* default to empty */
  if (flags & FT_UID) return "";/* UID call "impossible" */
  elt = mail_elt (stream,msgno);/* get elt */
  if (!elt->private.msg.header.text.data) {

/*    maildir_gc (stream,GC_TEXTS);  invalidate current cache */
				/* build message file name */
    snprintf (tmp,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp);
    if ((fd = open (tmp,O_RDONLY,NIL)) >= 0) {
      fstat (fd,&sbuf);		/* get size of message */
				/* make plausible IMAPish date string */
      tm = gmtime (&sbuf.st_mtime);
      elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
      elt->year = tm->tm_year + 1900 - BASEYEAR;
      elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
      elt->seconds = tm->tm_sec;
      elt->zhours = 0; elt->zminutes = 0;
				/* slurp message */
      read (fd,s = (char *) fs_get (sbuf.st_size + 1),sbuf.st_size);
      s[sbuf.st_size] = '\0';	/* tie off file */
      close (fd);		/* close file */

      for (i = 0,b = s; *b && !(i && (*b == '\n')); i = (*b++ == '\n'));
      hdrsize = (*b ? ++b:b)-s;	/* number of header bytes */

      elt->rfc822_size =          /* size of entire message in CRLF form */
       (elt->private.msg.header.text.size =
        strcrlfcpy ((char **) &elt->private.msg.header.text.data,&i,s,
                    hdrsize)) +
          (elt->private.msg.text.text.size =
           strcrlfcpy ((char **) &elt->private.msg.text.text.data,&i,b,
                       sbuf.st_size - hdrsize));
      fs_give ((void **) &s);
    } else return "";

  }

  *length = elt->private.msg.header.text.size;
  return (char *) elt->private.msg.header.text.data;
}

void maildir_fast (MAILSTREAM *stream,char *sequence,long flags)
{
  unsigned long i,j;
				/* ugly and slow */
  if (stream && LOCAL && ((flags & FT_UID) ?
			  mail_uid_sequence (stream,sequence) :
			  mail_sequence (stream,sequence)))
    for (i = 1; i <= stream->nmsgs; i++)
      if (mail_elt (stream,i)->sequence) maildir_fetchheader (stream,i,&j,NIL);
}

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

void maildir_list (MAILSTREAM *stream,char *ref, char *pat)
{
  DIR *dp;
  struct direct *d;
  struct stat sbuf;
  char *s,*s2,*cp,*np,curdir[MAILTMPLEN],name[MAILTMPLEN],subdir[MAILTMPLEN],rname[MAILTMPLEN];

  strncpy(subdir, pat, MAILTMPLEN);

  s = subdir;
  s2 = 0;
  while ((s = strstr(s, "/"))) {
  	s2 = s;
	s++;
	}

  if (s2) { /* pointer to last / */
  	*s2 = '\0'; /* snip off string */
	s2++;
	/*syslog(LOG_INFO, "dir: '%s' pat: '%s'", subdir, s2);*/
  	snprintf(name, MAILTMPLEN, "%s/%s", myhomedir(), subdir);
	} else {
	s2 = pat;
	snprintf(name, MAILTMPLEN, "%s", myhomedir());
	strcpy(subdir, "");
	}

 maildir_listwork(name, stream, s2, subdir, 1);

 if (pmatch ("INBOX", pat))
 	mm_list (stream,NIL,"INBOX",LATT_NOINFERIORS);

}

void maildir_listwork(char *name1, MAILSTREAM *stream, char *s2, char *subdir, int flag)
{
  DIR *dp;
  struct direct *d;
  struct stat sbuf;
  char *s,*cp,*np,curdir[MAILTMPLEN],rname[MAILTMPLEN],name[MAILTMPLEN];
  char tmpdir[MAILTMPLEN];
  
  strncpy(name, name1, MAILTMPLEN);
  
  /*syslog(LOG_INFO, "opendir: '%s'", name);
  //syslog(LOG_INFO, "subdir: '%s'", subdir);
   */ 
  if (dp = opendir (name)) {	/* open directory */
    while (d = readdir (dp))	/* scan directory, ignore all . names */
      if (d->d_name[0] != '.' &&
	  strcmp(d->d_name, "Maildir")) {/* build file name */
	/* we do not want to list Maildir to customers, because
	   that might give them weird ideas :) */

	if (subdir[0] != '\0' && subdir[0] != '%' && subdir[0] != '*')
		snprintf(name, MAILTMPLEN, "%s/%s", subdir, d->d_name);
	else
		snprintf(name, MAILTMPLEN, "%s", d->d_name);
	/*syslog(LOG_INFO, "ls: '%s'", name);*/

	/* this matching might not be correctly implemented	*/
	if (maildir_file(curdir, name) &&
      		stat (curdir,&sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
		/*syslog(LOG_INFO, "here for some stange reason");*/
		if (pmatch(d->d_name, s2)) {
			mm_list (stream,'/',name,NIL);
			/* call function recursive if last char % */
			/*syslog(LOG_INFO, "s2: '%s'",s2);*/
			if (s2[strlen(s2)-1] == '%'
			||  s2[strlen(s2)-1] == '*') {
				snprintf(rname, MAILTMPLEN, "%s/%s", name1, name);
				/*syslog(LOG_INFO, "rname: '%s'", rname);*/
				maildir_listwork(rname, stream, "%", name, 1);
				}			
			}
		} else
	if (strlen(curdir) > 4 && pmatch(d->d_name, s2)) { 
  		int fnlen = strlen (curdir);
  		curdir[fnlen - 4] = '\0';
  		/*syslog(LOG_INFO, "fname: '%s'", curdir);*/
	if (stat (curdir,&sbuf) == 0 && S_ISDIR (sbuf.st_mode)) {
		/* okay, do not list with NoSelect, since Netscape
		   does not f*cking honor this flag.. stupid netscape */
		/*mm_list (stream,'/',name,LATT_NOSELECT);*/

			/* call function recursive if last char % */
			/*syslog(LOG_INFO, "s2: '%s'",s2);*/
			if (s2[strlen(s2)-1] == '%' 
			||  s2[strlen(s2)-1] == '*') {
				snprintf(rname, MAILTMPLEN, "%s/%s", name1, name);
				/*syslog(LOG_INFO, "rname: '%s'", rname);*/
				maildir_listwork(rname, stream, "%", name, 1);
				}			

		}
	    }

	}
      closedir (dp);		/* all done, flush directory */
      }

}





void *maildir_parameters (long function,void *value)
{
  return NIL;
}

long maildir_create (MAILSTREAM *stream,char *mailbox)
{
  char tmp[MAILTMPLEN];
  char err[MAILTMPLEN];
  char *s, *s2;
  int fnlen, i;
  char *subdir_names[] = {"/cur","/new","/tmp",NULL};

				/* must not already exist */
  if (access (maildir_file (tmp,mailbox),F_OK) == 0) {
    snprintf (err,MAILTMPLEN,"Can't create mailbox %s: mailbox already exists",mailbox);
    mm_log (err,ERROR);
    return NIL;
  }
  
  maildir_file (tmp,mailbox);	/* get file name */
  fnlen = strlen (tmp);
  /*syslog(LOG_INFO, "fname: '%s'", tmp);*/
  tmp[fnlen - 4] = '\0';	/* making main directory's name */
  fnlen -= 4;

  /* okay, try to add support for adding hiearchys of directories, this
     is done by scanning for /'s.... */
     
  /*syslog(LOG_INFO, "tmp '%s'", tmp);*/
  s = tmp;

  while ((s = strstr(s, "/")) != 0) {
	/*syslog(LOG_INFO, "Before make: '%s'", s);*/
  	*s = '\0';
  	/*syslog(LOG_INFO, "Trying to make: '%s'", tmp);*/
	if (mkdir (tmp,0700) && *s != '\0') /* trying to make the dir */
	  if (errno != EEXIST) {
	    snprintf (err,MAILTMPLEN,"Can't create mailbox %s: %s %s",
		     mailbox,tmp,strerror (errno));
    	     mm_log (err,ERROR);
             return NIL;
	  }
  	*s = '/';
	s++;
  	} 

  if (mkdir (tmp,0700)) {	/* try to make new dir */
    snprintf (err,MAILTMPLEN,"Can't create mailbox %s: %s %s",
	     mailbox,tmp,strerror (errno));
    mm_log (err,ERROR);
    return NIL;
  }

  /*syslog(LOG_INFO, "create maildir");*/
  for (i = 0; subdir_names[i]; i++) {
    strncpy (tmp + fnlen,subdir_names[i],MAILTMPLEN);

    if (mkdir (tmp,0700)) {	/* try to make new dir */
      snprintf (err,MAILTMPLEN,"Can't create mailbox %s: %s %s",
	       mailbox,tmp,strerror (errno));
      mm_log (err,ERROR);
      return NIL;
    }
  }

  return T;			/* return success */
}

void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
{
  char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN];
  struct utimbuf tbuf;
  char *s;

                                /* build the new filename */
  snprintf (oldfile,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp);
  if ((s = strchr ((char *) elt->maildirp,':'))) *s = '\0';
  snprintf (fn,MAILTMPLEN,"%s:2,%s%s%s%s",(char *) elt->maildirp,elt->flagged ? "F" : "",
           elt->answered ? "R" : "",elt->seen ? "S" : "",
           elt->deleted ? "T" : "");
  snprintf (newfile,MAILTMPLEN,"%s/%s",LOCAL->dir,fn);
                                /* rename the file with new flags */
  if (rename (oldfile,newfile) < 0) {
    snprintf(oldfile,MAILTMPLEN,"Unable to write flags to disk: %s",strerror (errno));
    mm_log(oldfile,ERROR);
    return;
  }
                                /* update the file name in cache */
  fs_give ((void **) &elt->maildirp);
  elt->maildirp = (long) cpystr (fn);

  /* fix the UID on the file */
  tbuf.actime = elt->private.uid;
  tbuf.modtime = elt->private.uid;
  chmod (newfile, S_IRUSR|S_IWUSR|S_IXUSR);
  utime (newfile, &tbuf);

}

void maildir_expunge (MAILSTREAM *stream)
{
  MESSAGECACHE *elt;
  unsigned long i = 1;
  unsigned long n = 0;
  unsigned long recent = stream->recent;
  
  maildir_gc (stream,GC_TEXTS);	/* invalidate texts */
  mm_critical (stream);		/* go critical */
  while (i <= stream->nmsgs) {	/* for each message */
				/* if deleted, need to trash it */
    if ((elt = mail_elt (stream,i))->deleted) {
      snprintf (LOCAL->buf,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp);
      if (unlink (LOCAL->buf)) {/* try to delete the message */
	snprintf (LOCAL->buf,MAILTMPLEN,"Expunge of message %ld failed, aborted: %s",i,
		 strerror (errno));
	mm_log (LOCAL->buf,WARN);
	break;
      }
				/* free the cached filename */
      if (elt->maildirp) {
	      fs_give ((void **) &elt->maildirp);
	      elt->maildirp = 0; /* otherwise pine coredumps */
		}
      if (elt->recent) --recent;/* if recent, note one less recent message */
      mail_expunged (stream,i);	/* notify upper levels */
      n++;			/* count up one more expunged message */
    }
    else i++;			/* otherwise try next message */
  }
  if (n) {			/* output the news if any expunged */
    snprintf (LOCAL->buf,MAILTMPLEN,"Expunged %ld messages",n);
    mm_log (LOCAL->buf,(long) NIL);
  }
  else mm_log ("No messages deleted, so no update needed",(long) NIL);
  mm_nocritical (stream);	/* release critical */
				/* notify upper level of new mailbox size */
  mail_exists (stream,stream->nmsgs);
  mail_recent (stream,recent);
}

/* dont forget to process options in here */

/* modified by sakira@sundent.mine.nu (03.01.13) */
long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
{
  STRING st;
  MESSAGECACHE *elt;
  struct stat sbuf;
  int fd;
  unsigned long i;
  char flags[MAILTMPLEN], date[MAILTMPLEN];

				/* copy the messages */
  if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) : 
  	mail_sequence (stream,sequence)) 
  for (i = 1; i <= stream->nmsgs; i++)
    if ((elt = mail_elt (stream,i))->sequence) {
      sprintf (LOCAL->buf, "%s/%s", LOCAL->dir, (char *) elt->maildirp);
      /*      snprintf (LOCAL->buf,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp); */
      if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
      fstat (fd,&sbuf);		/* get size of message */

      if (!elt->day) {
	struct tm *tm = gmtime (&sbuf.st_mtime);
	elt->day = tm->tm_mday; elt->month = tm->tm_mon + 1;
	elt->year = tm->tm_year + 1900 - BASEYEAR;
	elt->hours = tm->tm_hour; elt->minutes = tm->tm_min;
	elt->seconds = tm->tm_sec;
	elt->zhours = 0; elt->zminutes = 0;
      }

      if (sbuf.st_size > LOCAL->buflen) {
	fs_give((void **) &LOCAL->buf);
	LOCAL->buf = (char *) fs_get ((LOCAL->buflen = sbuf.st_size) + 1);
      }

				/* slurp message */
      read(fd, LOCAL->buf, sbuf.st_size);
      LOCAL->buf[sbuf.st_size] = '\0';
      close(fd);
      /* read (fd, s = (char *) fs_get (sbuf.st_size +1),sbuf.st_size); 
      s[sbuf.st_size] = '\0';
      close (fd); */

      INIT (&st,mail_string,(void *) LOCAL->buf,sbuf.st_size);
      flags[0] = flags[1] = '\0';
      if (elt->seen) strcat(flags, " \\Seen");
      if (elt->deleted) strcat(flags, " \\Deleted");
      if (elt->answered) strcat(flags, " \\Answered");
      if (elt->draft) strcat(flags, " \\Draft");
      flags[0] = '(';
      strcat(flags, ")");
      mail_date(date, elt);
      /* snprintf (LOCAL->buf,MAILTMPLEN,"%s%s%s%s%s)",
	       elt->seen ? " \\Seen" : "",
	       elt->deleted ? " \\Deleted" : "",
	       elt->flagged ? " \\Flagged" : "",
	       elt->answered ? " \\Answered" : "",
	       (elt->seen || elt->deleted || elt->flagged || elt->answered) ?
	       "" : " ");
      LOCAL->buf[0] = '(';
      mail_date (tmp,elt); */

      if (!mail_append_full (NIL, mailbox, flags, date, &st)) return NIL;
      if (options & CP_MOVE) elt->deleted = T;

      /* if (!maildir_append (stream,mailbox,LOCAL->buf,tmp,&st)) {
	fs_give ((void **) &s);	
	return NIL; 
	} */
      /*      fs_give ((void **) &s); */	/* give back temporary space */
    }
  return T;			/* return success */
}

#if 0
long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
{
  STRING st;
  MESSAGECACHE *elt;
  struct stat sbuf;
  int fd;
  long i;
  char *s,tmp[MAILTMPLEN];
				/* copy the messages */
  if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) : 
  	mail_sequence (stream,sequence)) 
  for (i = 1; i <= stream->nmsgs; i++)
    if ((elt = mail_elt (stream,i))->sequence) {
      snprintf (LOCAL->buf,MAILTMPLEN,"%s/%s",LOCAL->dir,(char *) elt->maildirp);
      if ((fd = open (LOCAL->buf,O_RDONLY,NIL)) < 0) return NIL;
      fstat (fd,&sbuf);		/* get size of message */
				/* slurp message */
      read (fd,s = (char *) fs_get (sbuf.st_size +1),sbuf.st_size);
      s[sbuf.st_size] = '\0';	/* tie off file */
      close (fd);		/* flush message file */
      INIT (&st,mail_string,(void *) s,sbuf.st_size);
      snprintf (LOCAL->buf,MAILTMPLEN,"%s%s%s%s%s)",
	       elt->seen ? " \\Seen" : "",
	       elt->deleted ? " \\Deleted" : "",
	       elt->flagged ? " \\Flagged" : "",
	       elt->answered ? " \\Answered" : "",
	       (elt->seen || elt->deleted || elt->flagged || elt->answered) ?
	       "" : " ");
      LOCAL->buf[0] = '(';	/* open list */
      mail_date (tmp,elt);	/* generate internal date */
      if (!maildir_append (stream,mailbox,LOCAL->buf,tmp,&st)) {
	fs_give ((void **) &s);	/* give back temporary space */
	return NIL;
      }
      fs_give ((void **) &s);	/* give back temporary space */
    }
  return T;			/* return success */
}
#endif

long maildir_append (MAILSTREAM *stream, char *mailbox, append_t af, void *data)
{
  int fd;
  char c, *s;
  char *flags, *date;
  char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN];
  MESSAGECACHE elt;
  STRING *message;
  long i;
  long size = 0;
  long ret = LONGT;
  short uf = 0;
  
  /*  
     This is intentionaly made static.  Users can ask to save a LOT of messages
     at once and this program can do that within one second. Dan's assumption
     that time+pid+hostname always will be unique stops being true in this
     case. So we will add yet another number to host part of message file's
     name. Hostname is used only to make filename unique and Dan  explicitly
     says that "<...>  Other than this [skipping filenames starting at dot] ,
     readers should not attempt to parse filenames. <...>". Therefore this 
     addition should be no problem. Am I right, Dan?   --AK
  */ 
  
  static unsigned int transact = 0;

  if (!stream) stream = &maildirproto;

  if (flags) 			/* get flags if given */
    uf = maildir_getflags (user_flags (&maildirproto),flags);

				/* N.B.: can't use LOCAL->buf for tmp */
				/* make sure valid mailbox */
  if (!maildir_isvalid (mailbox, NIL)) {
    snprintf (tmp,MAILTMPLEN,"Not a valid Maildir mailbox: %s",mailbox);
    mm_log (tmp,ERROR);
    return NIL;
  }

  if (!(*af)(stream, data, &flags, &date, &message)) return NIL;

  mm_critical(stream);	/* go critical */
  do {
    if (!SIZE(message)) { /* guard againt zero-length */
      mm_log("Append of zero-length message", ERROR);
      ret = NIL;
      break;
    }

    /* build file name we will use */
    snprintf (file,MAILTMPLEN,"%u.%d.%09u.%s:3,%s%s%s%s",
	      time (0),getpid (),transact++,mylocalhost (),
	      uf&fFLAGGED ? "F" : "",uf&fANSWERED ? "R" : "",
	      uf&fSEEN ? "S" : "",uf&fDELETED ? "T" : "");
				/* build tmp file name */
    snprintf (path1,MAILTMPLEN,"%s/../tmp/%s",maildir_file (tmp,mailbox),file);
  
    if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) {
      snprintf (tmp,MAILTMPLEN,"Can't open append mailbox: %s",strerror (errno));
      mm_log (tmp,ERROR);
      return NIL;
    }

    i = SIZE (message);		/* get size of message */
    s = (char *) fs_get (i + 1);	/* get space for the data */
				/* copy the data w/o CR's */
    while (i--) if ((c = SNX (message)) != '\015') s[size++] = c;
				/* write the data */
    if ((write (fd,s,size) < 0) || fsync (fd)) {
      unlink (path1);		/* delete message */
      snprintf (tmp,MAILTMPLEN,"Message append failed: %s",strerror (errno));
      mm_log (tmp,ERROR);
      ret = NIL;
    }
				/* build final filename to use */
    snprintf (path2,MAILTMPLEN,"%s/../new/%s",maildir_file (tmp,mailbox),file);
    if (link (path1,path2) < 0) {
      snprintf (tmp,MAILTMPLEN,"Message append failed: %s",strerror (errno));
      mm_log (tmp,ERROR);
      ret = NIL;
    }
    unlink (path1);
    fs_give ((void **) &s);	/* flush the buffer */
    close (fd);			/* close the file */

    if (ret) {
      if (!(*af)(stream, data, &flags, &date, &message)) ret = NIL;
    }
  } while(ret && message);
  
  mm_nocritical (stream);	/* release critical */
  return ret;
}

#if 0
long maildir_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
		   STRING *message)
{
  int fd;
  char c,*s;
  char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN];
  MESSAGECACHE elt;
  long i;
  long size = 0;
  long ret = LONGT;
  short uf = 0;
  
  /*  
     This is intentionaly made static.  Users can ask to save a LOT of messages
     at once and this program can do that within one second. Dan's assumption
     that time+pid+hostname always will be unique stops being true in this
     case. So we will add yet another number to host part of message file's
     name. Hostname is used only to make filename unique and Dan  explicitly
     says that "<...>  Other than this [skipping filenames starting at dot] ,
     readers should not attempt to parse filenames. <...>". Therefore this 
     addition should be no problem. Am I right, Dan?   --AK
  */ 
  
  static unsigned int transact = 0;

  if (flags) 			/* get flags if given */
    uf = maildir_getflags (user_flags (&maildirproto),flags);

/*  if (date) {			want to preserve date?
    //syslog(LOG_INFO, "date: '%s'", date);
				// yes, parse date into an elt
    if (!mail_parse_date (&elt,date)) {
      snprintf (tmp,MAILTMPLEN,"Bad date in append: %s",date);
      mm_log (tmp,ERROR);
      return NIL;
    }
  } */
				/* N.B.: can't use LOCAL->buf for tmp */
				/* make sure valid mailbox */
  if (!maildir_isvalid (mailbox, NIL)) {
    snprintf (tmp,MAILTMPLEN,"Not a valid Maildir mailbox: %s",mailbox);
    mm_log (tmp,ERROR);
    return NIL;
  }
				/* build file name we will use */
  snprintf (file,MAILTMPLEN,"%u.%d.%09u.%s:3,%s%s%s%s",
	   time (0),getpid (),transact++,mylocalhost (),
	   uf&fFLAGGED ? "F" : "",uf&fANSWERED ? "R" : "",
	   uf&fSEEN ? "S" : "",uf&fDELETED ? "T" : "");
				/* build tmp file name */
  snprintf (path1,MAILTMPLEN,"%s/../tmp/%s",maildir_file (tmp,mailbox),file);
  
  if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) {
    snprintf (tmp,MAILTMPLEN,"Can't open append mailbox: %s",strerror (errno));
    mm_log (tmp,ERROR);
    return NIL;
  }
  i = SIZE (message);		/* get size of message */
  s = (char *) fs_get (i + 1);	/* get space for the data */
				/* copy the data w/o CR's */
  while (i--) if ((c = SNX (message)) != '\015') s[size++] = c;
  mm_critical (stream);		/* go critical */
				/* write the data */
  if ((write (fd,s,size) < 0) || fsync (fd)) {
    unlink (path1);		/* delete message */
    snprintf (tmp,MAILTMPLEN,"Message append failed: %s",strerror (errno));
    mm_log (tmp,ERROR);
    ret = NIL;
  }
				/* build final filename to use */
  snprintf (path2,MAILTMPLEN,"%s/../new/%s",maildir_file (tmp,mailbox),file);
  if (link (path1,path2) < 0) {
    snprintf (tmp,MAILTMPLEN,"Message append failed: %s",strerror (errno));
    mm_log (tmp,ERROR);
    ret = NIL;
  }
  unlink (path1);
  
  close (fd);			/* close the file */
  mm_nocritical (stream);	/* release critical */
  fs_give ((void **) &s);	/* flush the buffer */
  return ret;
}
#endif

short bezerk_getflags (MAILSTREAM *stream,char *flag)
{
  char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  short f = 0;
  short i,j;
  if (flag && *flag) {		/* no-op if no flag string */
				/* check if a list and make sure valid */
    if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
      mm_log ("Bad flag list",ERROR);
      return NIL;
    }
				/* copy the flag string w/o list construct */
    strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
    tmp[j] = '\0';
    t = ucase (tmp);		/* uppercase only from now on */

    while (t && *t) {		/* parse the flags */
      if (*t == '\\') {		/* system flag? */
	switch (*++t) {		/* dispatch based on first character */
	case 'S':		/* possible \Seen flag */
	  if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
	  t += 4;		/* skip past flag name */
	  break;
	case 'D':		/* possible \Deleted flag */
	  if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
	      t[5] == 'E' && t[6] == 'D') i = fDELETED;
	  t += 7;		/* skip past flag name */
	  break;
	case 'F':		/* possible \Flagged flag */
	  if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
	      t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
	  t += 7;		/* skip past flag name */
	  break;
	case 'A':		/* possible \Answered flag */
	  if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
	      t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
	  t += 8;		/* skip past flag name */
	  break;
	default:		/* unknown */
	  i = 0;
	  break;
	}
				/* add flag to flags list */
	if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
      }
      else {			/* no user flags yet */
	return f;
	t = strtok (t," ");	/* isolate flag name */
	/*	snprintf (err,MAILTMPLEN,"Unknown flag: %.80s",t); */
	t = strtok (NIL," ");	/* get next flag */
	/*	mm_log (err,ERROR); */
      }
    }
  }
  return f;
}

short maildir_getflags (MAILSTREAM *stream,char *flag)
{
  return bezerk_getflags (stream,flag); /* nothing exciting, reuse old code */
}

long maildir_delete (MAILSTREAM *stream,char *mailbox)
{
  DIR *dirp;
  struct direct *d;
  int i,j;
  char tmp[MAILTMPLEN],err[MAILTMPLEN];
  char *subdir_names[] = {"cur/","new/","tmp/",NULL};

				/* check if mailbox even exists */
  if (!maildir_isvalid (mailbox,NIL)) {
/*    sprintf (tmp,"Can't delete mailbox %s: no such mailbox",mailbox);
    mm_log (tmp,ERROR);
    return NIL; */
    /*syslog(LOG_INFO, "Invalid maildir in delete()"); */
    return T; /* well.. a stupid hack to get by a problem in netscape ..
    		it remembers folders locally, and if a folder is deleted on
    		another machine, you have no way removing it on any other
    		netscapes... */
  }

				/* get name of directory */
  i = strlen (maildir_file (tmp,mailbox)) + 1;
  for (j = 0; subdir_names[j]; j++) {
    strncpy (tmp + i - 4,subdir_names[j], MAILTMPLEN);
    if (dirp = opendir (tmp)) {	/* open directory */
      while (d = readdir (dirp))	/* empty the directory */
	if (strcmp (d->d_name,".") && strcmp (d->d_name,"..")) {
	  strncpy (tmp + i,d->d_name, MAILTMPLEN);
	  /*syslog(LOG_INFO, "unlink1: '%s'");*/
	  unlink (tmp);
	}
      closedir (dirp);		/* flush directory */
    }
				/* remove the subdir */
    tmp[i + 3] = '\0';
    /*syslog(LOG_INFO, "tmp: '%s'", tmp);*/
    if (rmdir (tmp)) {
    /*  sprintf (err,"Can't delete directory %s: %s",tmp,strerror (errno));
      mm_log (err,ERROR);*/
    }
  }

				/* try to remove the directory */
  *(tmp + i - 5) = '\0';
  /*syslog(LOG_INFO, "tmp2: '%s'", tmp);*/
  if (rmdir (tmp)) {
/*    sprintf (err,"Can't delete mailbox %s: %s",mailbox,strerror (errno));
    mm_log (err,ERROR);
    return NIL; */
  }
  return T;			/* return success */
}

long maildir_rename (MAILSTREAM *stream,char *old,char *new)
{
  char tmp[MAILTMPLEN],tmpnew[MAILTMPLEN];

				/* old mailbox name must be valid */
  if (!maildir_isvalid (old,NIL)) {
    snprintf (tmp,MAILTMPLEN,"Can't rename mailbox %s: no such mailbox",old);
    mm_log (tmp,ERROR);
    return NIL;
  }

				/* new mailbox name must not exist */
  if (access (maildir_file (tmp,new),F_OK) == 0) {
    snprintf (tmp,MAILTMPLEN,"Can't rename to mailbox %s: destination already exists",new);
    mm_log (tmp,ERROR);
    return NIL;
  }

				/* try to rename the directory */
  if (rename (maildir_file (tmp,old),maildir_file (tmpnew,new))) {
    snprintf (tmp,MAILTMPLEN,"Can't rename mailbox %s to %s: %s",old,new,strerror (errno));
    mm_log (tmp,ERROR);
    return NIL;
  }
  return T;			/* return success */
}

long maildir_sub (MAILSTREAM *stream,char *mailbox)
{
  char tmp[MAILTMPLEN];
  return sm_subscribe (mailbox);
}

long maildir_unsub (MAILSTREAM *stream,char *mailbox)
{
  char tmp[MAILTMPLEN];
  return sm_unsubscribe (mailbox);
}

void maildir_lsub (MAILSTREAM *stream,char *ref,char *pat)
{
 void *sdb = NIL;
  char *s;
                                /* get canonical form of name */
  if ((s = sm_read (&sdb))) {
    do if (pmatch_full (s,pat,'/')) mm_lsub (stream,'/',s,NIL);
    while (s = sm_read (&sdb)); /* until no more subscriptions */
  }

}