/*
 * Maildir driver for Alpine 2.20
 * 
 * Written by Eduardo Chappa <chappa@gmx.com>
 * Last Update: June 10, 2014
 *
 */

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

/* Driver dispatch used by MAIL */
DRIVER maildirdriver = {
  "md",				/* driver name, yes it's md, not maildir */
  DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT,		/* driver flags */
  (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 			*/
  mail_status_default,		/* 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 structure 		*/
  maildir_header,		/* fetch message header 		*/
  maildir_text,			/* 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 	*/
  NIL				/* garbage collect stream 		*/
};


DRIVER courierdriver = {
  "mc",	/* Why a separate driver? So that createproto will work		*/
  DR_MAIL|DR_LOCAL|DR_NAMESPACE|DR_DIRFMT,		/* driver flags */
  (DRIVER *) NIL,		/* next driver 				*/
  maildir_valid,		/* mailbox is valid for us 		*/
  maildir_parameters,		/* manipulate parameters		*/
  NIL,				/* scan mailboxes 			*/
  courier_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 			*/
  mail_status_default,		/* 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 structure 		*/
  maildir_header,		/* fetch message header 		*/
  maildir_text,			/* 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 	*/
  NIL				/* garbage collect stream 		*/
};

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

long maildir_dirfmttest (char *name)
{
  int i;
  for (i = 0; mdstruct[i] && strcmp(name, mdstruct[i]); i++);
  return (i < EndDir) || !strcmp(name, MDDIR) 
	|| !strncmp(name, MDUIDLAST, strlen(MDUIDLAST))
	|| !strncmp(name, MDUIDTEMP, strlen(MDUIDTEMP)) ? LONGT : NIL;
}

void 
md_domain_name(void)
{
   int i, j;

   strcpy(mdlocaldomain, mylocalhost ());
   for (i = 0; mdlocaldomain[i] != '\0' ;)
      if(mdlocaldomain[i] == '/' || mdlocaldomain[i] == ':'){
	 for(j = strlen(mdlocaldomain); j >= i; j--)
	    mdlocaldomain[j+4] = mdlocaldomain[j];
	 mdlocaldomain[i++] = '\\';
	 mdlocaldomain[i++] = '0';
	 if(mdlocaldomain[i] == '/'){
	    mdlocaldomain[i++] = '5';
	    mdlocaldomain[i++] = '7';
	 } else {
	    mdlocaldomain[i++] = '7';
	    mdlocaldomain[i++] = '2';
	 }
      }
      else
	i++;
}

char *
myrootdir(char *name)
{
return myhomedir();
}

char *
mdirpath(void)
{
  char *path = maildir_parameters(GET_MDINBOXPATH, NIL);
  return path ? (path[0] ? path : ".") : "Maildir";
}

/* remove the "#md/" or "#mc/" part from a folder name
 * memory freed by caller
 */
char *
maildir_remove_root (char *name)
{
  int courier = IS_COURIER(name), offset;
  char realname[MAILTMPLEN];

  offset = maildir_valid_name(name) ? (name[3] == '/' ? 4 : 3) : 0;
  if(courier)
     courier_realname(name+offset, realname);
  else
     strcpy(realname, name+offset);
  return cpystr(realname);
}


/* Check validity of the name, we accept:
 *	a) #md/directory/folder
 *	b) #md/inbox
 * A few considerations: We can only accept as valid
 *  a) names that start with #md/ and the directory exists or
 *  b) names that do not start with #md/ but are maildir directories (have
 *     the /cur, /tmp and /new structure)
 */
int maildir_valid_name (char *name)
{
  char tmpname[MAILTMPLEN] = {'\0'};

   if (mdfpath)
      fs_give((void **)&mdfpath);
   if (name && (name[0] != '#'))
	snprintf(tmpname, sizeof(tmpname), "%s%s",MDPREFIX(CCLIENT), name);
   mdfpath = cpystr(tmpname[0] ? tmpname : name);

  return IS_CCLIENT(name) || IS_COURIER(name);
}

/* Check if the directory whose path is given by name is a valid maildir
 *  directory (contains /cur, /tmp and /new)
 */
int maildir_valid_dir (char *name)
{
 int len;
 DirNamesType i;
 struct stat sbuf;
 char tmp[MAILTMPLEN];

   if(name[strlen(name) - 1] == '/')
      name[strlen(name) - 1] = '\0';
   len = strlen(name);
   for (i = Cur; i != EndDir; i++){
      MDFLD(tmp, name, i);
      if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode))
	  break;
   }
   name[len] = '\0';
   return (i == EndDir) ? T : NIL;
}

void courier_realname(char *name, char *realname)
{
  int i,j;

  if(!name)
    return;

  for (i = 0, j = 0; i < MAILTMPLEN && j < strlen(name); j++, i++){
      realname[i] = name[j];
      if(name[j] == '/' && name[j+1] != '.' && name[j+1] != '%' 
		&& name[j+1] != '*') 
	realname[++i] = '.';
  }
  if(realname[i-1] == '.')
    i--; 
  realname[i] = '\0';
}


/* given a maildir folder, return its path. Memory freed by caller. Directory
 * does not contain the trailing slash "/". On error NULL is returned.
 */
int maildir_file_path (char *name, char *tmp, size_t sizeoftmp)
{
   char *maildirpath = mdirpath(), *rname;
   int courier = IS_COURIER(name);

   /* There are several ways in which the path can come, so we will handle 
      them here. First we deal with #mc/ or #md/ prefix by removing the 
      prefix, if any */

    if(strlen(name) >= MAILTMPLEN)
      name[MAILTMPLEN] = '\0';
    strcpy(tmp, name);
    rname = maildir_remove_root(tmp);
    tmp[0] = '\0';	/* just in case something fails */

    if (strlen(myrootdir(rname)) + 
		max(strlen(rname), strlen(maildirpath)) > sizeoftmp){
	errno = ENAMETOOLONG;
	snprintf(tmp, sizeoftmp, "Error opening \"%s\": %s", rname, strerror (errno));
	mm_log(tmp,ERROR);
	if(rname) fs_give((void **)&rname);
	return NIL;
    }

    /* There are two ways in which the name can come here, either as a 
       full path or not. If it is not a full path it can come in two ways, 
       either as a file system path (Maildir/.Drafts) or as a maildir path 
       (INBOX.Drafts)
     */

     if(*rname == '/'){	/* full path */
	strncpy(tmp, rname, sizeoftmp); /* do nothing */
	tmp[sizeoftmp-1] = '\0';
     }
     else
	snprintf (tmp, sizeoftmp, "%s/%s%s%s", myrootdir (rname),
	    strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) 
		? rname : maildirpath,
	    strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) 
		? "" : (courier ? "/" : ""),
	    strncmp (ucase (strcpy (tmp, rname)), "INBOX", 5) 
		? "" : (*(rname+5) == MDSEPARATOR(courier) ? rname+5 : ""));
    if(rname) fs_give((void **)&rname);
    return tmp[0] ? T : NIL;
}

/* This function is given a full path for a mailbox and returns
 * if it is a valid maildir transformed to canonical notation
 */
int
is_valid_maildir (char **name)
{
  if (!strncmp(*name, myrootdir (*name), strlen(myrootdir(*name)))){
     (*name) += strlen(myrootdir(*name));
     if (**name == '/') (*name)++;
  }
  return maildir_valid(*name) ? T :  NIL;
}

/* Check validity of mailbox. This routine does not send errors to log, other
 *  routines calling this one may do so, though
 */ 

DRIVER *maildir_valid (char *name)
{
   char tmpname[MAILTMPLEN];

   maildir_file_path(name, tmpname, sizeof(tmpname));
   
   return maildir_valid_dir(tmpname) 
		? (IS_COURIER(name) ? &courierdriver : &maildirdriver) : NIL;
}

void maildir_fast (MAILSTREAM *stream,char *sequence,long flags)
{
  unsigned long i;
  MESSAGECACHE *elt;
                                /* get sequence */
  if (stream && LOCAL && ((flags & FT_UID) ?
                          mail_uid_sequence (stream,sequence) :
                          mail_sequence (stream,sequence)))
    for (i = 1L; i <= stream->nmsgs; i++) {
      if ((elt = mail_elt (stream,i))->sequence && (elt->valid = T) &&
          !(elt->day && elt->rfc822_size)) {
        ENVELOPE **env = NIL;
        ENVELOPE *e = NIL;
        if (!stream->scache) env = &elt->private.msg.env;
        else if (stream->msgno == i) env = &stream->env;
        else env = &e;
        if (!*env || !elt->rfc822_size) {
          STRING bs;
          unsigned long hs;
          char *ht = (*stream->dtb->header) (stream,i,&hs,NIL);

          if (!*env) rfc822_parse_msg (env,NIL,ht,hs,NIL,BADHOST,
                                       stream->dtb->flags);
          if (!elt->rfc822_size) {
            (*stream->dtb->text) (stream,i,&bs,FT_PEEK);
            elt->rfc822_size = hs + SIZE (&bs) - GETPOS (&bs);
          }
        }

        if (!elt->day && *env && (*env)->date)
          mail_parse_date (elt,(*env)->date);

        if (!elt->day) elt->day = elt->month = 1;
        mail_free_envelope (&e);
      }
    }
}

int
maildir_eliminate_duplicate (char *name, struct direct ***flist, unsigned long *nfiles)
{
   int i, j, k, error = 0, scanr;
   char new[MAILTMPLEN], old[MAILTMPLEN], tmp[MAILTMPLEN], *str;
   struct direct **names = NIL;

   if((scanr = maildir_doscandir(name, &names, CCLIENT)) < 0)
     return -1;

   if(nfiles) *nfiles = scanr;
   for(i = 0, j = 1, k = 0;  j < scanr; i++, j++){
      if(k)
	names[i] = names[i+k];
      if(same_maildir_file(names[i]->d_name, names[j]->d_name)){
	int d, f, r, s;
	maildir_getflag(names[i]->d_name, &d, &f, &r, &s, NIL);
	snprintf(old, sizeof(old), "%s/%s", name, names[i]->d_name);
	snprintf(new, sizeof(new), "%s/.%s", name, names[i]->d_name);
	if(rename(old, new) < 0 && errno != EEXIST)
	  error++;
	if(!error){
	  for(; j < scanr
		&& same_maildir_file(names[i]->d_name, names[j]->d_name)
	      ; j++, k++){
	      maildir_getflag(names[j]->d_name, (d ? NIL : &d), 
			(f ? NIL : &f), (r ? NIL : &r), (s ? NIL : &s), NIL);
	      snprintf(tmp, sizeof(tmp), "%s/%s", name, names[j]->d_name);
	      if(unlink(tmp) < 0){	/* Hmmm... a problem, let's see */
		struct stat sbuf;
		if (stat(tmp, &sbuf) == 0 && (sbuf.st_mode & S_IFMT) == S_IFREG)
		   error++;
	      }
	  }
	  if((str = strrchr(names[i]->d_name,FLAGSEP)) != NULL) *str = '\0';
	  snprintf (old, sizeof(old), "%s/%s%s%s%s%s%s", name, names[i]->d_name, MDSEP(2),
		MDFLAG(Draft, d), MDFLAG(Flagged, f), MDFLAG(Replied, r), 
		MDFLAG(Seen, s));
	  if(rename(new, old) < 0)
	     error++;
	}
      }

   }
   if(k > 0)
     fs_give((void **)&names);
   else
     *flist = names;
   return error ? -1 : k;
}

int
maildir_doscandir(char *name, struct direct ***flist, int flag)
{
return scandir(name, flist, 
		flag == CCLIENT ? maildir_select : courier_dir_select, 
		flag == CCLIENT ? maildir_namesort : courier_dir_sort);
}

/* 
 * return all files in a given directory. This is a separate call
 * so that if there are warnings during compilation this only appears once.
 */
unsigned long
maildir_scandir (char *name, struct direct ***flist, 
			unsigned long *nfiles, int *scand, int flag)
{
  struct stat sbuf;
  int rv = -2;	/* impossible value */

  if (scand)
     *scand = -1;	/* assume error for safety */
  *nfiles = 0;
  if((stat(name,&sbuf) < 0) 
	|| (flag == CCLIENT
	 && ((rv = maildir_eliminate_duplicate(name, flist, nfiles)) < 0)))
     return 0L;

  if (scand && (rv > 0 || rv == -2))
     *nfiles = maildir_doscandir(name, flist, flag);

  if(scand) *scand = *nfiles;

  return (unsigned long) sbuf.st_ctime;
}

/* Does a message with given name exists (or was it removed)?
 * Returns: 1 - yes, such message exist,
 *	    0 - No, that message does not exist anymore
 *
 * Parameters: stream, name of mailbox, new name if his message does not
 *		exist.
 */

int maildir_message_exists(MAILSTREAM *stream, char *name, char *newfile)
{
  char tmp[MAILTMPLEN];
  int gotit = NIL;
  DIR *dir;
  struct direct *d;
  struct stat sbuf;

  /* First check directly if it exists, if not there, look for it */
  snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->path[Cur], name);
  if ((stat(tmp, &sbuf) == 0) && ((sbuf.st_mode & S_IFMT) == S_IFREG))
    return T;

  if (!(dir = opendir (LOCAL->path[Cur])))
     return NIL;

  while ((d = readdir(dir)) && gotit == NIL){
    if (d->d_name[0] == '.')
      continue;
    if (same_maildir_file(d->d_name, name)){
	  gotit = T;
	  strcpy(newfile, d->d_name);
    }
  }
  closedir(dir);
  return gotit;
}

/* Maildir open */
 
MAILSTREAM *maildir_open (MAILSTREAM *stream)
{
  char tmp[MAILTMPLEN];
  struct stat sbuf;

  if (!stream) return &maildirproto;
  if (stream->local) fatal ("maildir recycle stream");
  md_domain_name();    /* get domain name for maildir files in mdlocaldomain */
  if(mypid == (pid_t) 0)
    mypid = getpid();
  if (!stream->rdonly){
     stream->perm_seen = stream->perm_deleted = stream->perm_flagged = 
	stream->perm_answered = stream->perm_draft = T;
  }
  stream->local = (MAILDIRLOCAL *) fs_get (sizeof (MAILDIRLOCAL));
  memset(LOCAL, 0, sizeof(MAILDIRLOCAL));
  LOCAL->fd = -1;

  LOCAL->courier = IS_COURIER(stream->mailbox);
  strcpy(tmp, stream->mailbox);
  if (maildir_file_path (stream->mailbox, tmp, sizeof(tmp)))
     LOCAL->dir = cpystr (tmp);
  LOCAL->candouid = maildir_can_assign_uid(stream);
  maildir_read_uid(stream, &stream->uid_last, &stream->uid_validity);
  if (LOCAL->dir){
     LOCAL->path = (char **) fs_get(EndDir*sizeof(char *));
     MDFLD(tmp, LOCAL->dir, Cur); LOCAL->path[Cur] = cpystr (tmp);
     MDFLD(tmp, LOCAL->dir, New); LOCAL->path[New] = cpystr (tmp);
     MDFLD(tmp, LOCAL->dir, Tmp); LOCAL->path[Tmp] = cpystr (tmp);
     if (stat (LOCAL->path[Cur],&sbuf) < 0) {
         snprintf (tmp, sizeof(tmp), "Can't open folder %s: %s",
				stream->mailbox,strerror (errno));
         mm_log (tmp,ERROR);
	 maildir_close(stream, 0);
        return NIL;
     }
  }

  if(maildir_file_path (stream->mailbox, tmp, sizeof(tmp))){
    fs_give ((void **) &stream->mailbox);
    stream->mailbox = cpystr(tmp);
  }

  LOCAL->buf = (char *) fs_get (CHUNKSIZE);
  LOCAL->buflen = CHUNKSIZE - 1;
  stream->sequence++;
  stream->nmsgs = stream->recent = 0L;

  maildir_parse_folder(stream, 1);

  return stream;
}

/* Maildir initial parsing of the folder */
void
maildir_parse_folder (MAILSTREAM *stream, int full)
{
   char tmp[MAILTMPLEN];
   struct direct **namescur = NIL, **namesnew = NIL;
   unsigned long i, nfilescur = 0L, nfilesnew = 0L, oldpos, newpos, total;
   int scan_err, rescan, loop = 0;

   if (!stream)		/* what??? */
      return;

   MM_CRITICAL(stream);

   maildir_scandir (LOCAL->path[New], &namesnew, &nfilesnew, &scan_err, CCLIENT);
   if (scan_err < 0)
      maildir_abort(stream);

   /* Scan old messages first, escoba! */
   if(stream->rdonly ||
      (LOCAL && ((maildir_initial_check(stream, Cur) == 0)
							|| nfilesnew > 0L))){
      LOCAL->scantime =  maildir_scandir (LOCAL->path[Cur], &namescur, &nfilescur, 
					&scan_err, CCLIENT);
      if (scan_err < 0){
	if(namesnew){
	  for(i = 0L; i < nfilesnew; i++)
	    fs_give((void **)&namesnew[i]);
	  fs_give((void **) &namesnew);
	}
	maildir_abort(stream);
      }
   }
   if(LOCAL && (maildir_initial_check(stream, New) == 0)
	&& (nfilescur > 0L)){
      while(LOCAL && loop < 10){
	 if(nfilesnew == 0L)
	   maildir_scandir (LOCAL->path[New], &namesnew, &nfilesnew, &scan_err, CCLIENT);
         if (scan_err < 0){
	    if(namesnew){
	      for(i = 0L; i < nfilesnew; i++)
		fs_give((void **)&namesnew[i]);
	      fs_give((void **) &namesnew);
	    }
	    maildir_abort(stream);
	    break;
	 }
	 for(i = 0L, rescan = 0, newpos = oldpos = 0L; 
		newpos < nfilescur && i < nfilesnew; i++){
	    if(maildir_message_in_list(namesnew[i]->d_name, namescur, oldpos, 
						nfilescur - 1L, &newpos)){
	       oldpos = newpos;
	       snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->path[New], namesnew[i]->d_name);
	       if(unlink(tmp) < 0)
		 scan_err = -1;
	       rescan++;
	    }
	    else
	      newpos = oldpos;
	 }
	 if(scan_err < 0)
	    maildir_abort(stream);
	 if(rescan == 0)
	   break;
	 else{ /* restart */
	   if(namesnew){
	     for(i = 0L; i < nfilesnew; i++)
		fs_give((void **)&namesnew[i]);
	     fs_give((void **) &namesnew);
	   }
	   nfilesnew = 0L;
	   loop++;
	 }
      }
   }
   if(loop == 10)
     maildir_abort(stream);
   if(LOCAL){
     if(stream->rdonly)
	stream->recent = 0L;
     total = namescur || stream->rdonly 
		? maildir_parse_dir(stream, 0L, Cur, namescur, 
					      nfilescur, full) : stream->nmsgs;
     stream->nmsgs = maildir_parse_dir(stream, total, New, namesnew, 
						nfilesnew, full);
   }
   if(namesnew){
     for(i = 0L; i < nfilesnew; i++)
	fs_give((void **)&namesnew[i]);
     fs_give((void **) &namesnew);
   }
   if(namescur){
     for(i = 0L; i < nfilescur; i++)
	fs_give((void **)&namescur[i]);
     fs_give((void **) &namescur);
   }
   MM_NOCRITICAL(stream);
}

int
maildir_initial_check (MAILSTREAM *stream, DirNamesType dirtype)
{
   char *tmp;
   struct stat sbuf;

   if (access (LOCAL->path[dirtype], R_OK|W_OK|X_OK) != 0){
      maildir_abort(stream);
      return -1;
   }

   if (dirtype != New && 
	(stat(LOCAL->path[Cur], &sbuf) < 0 || sbuf.st_ctime == LOCAL->scantime))
      return -1;
   return 0;
}


/* Return the number of messages in the directory, while filling the
 * elt structure.
 */

unsigned long
maildir_parse_dir(MAILSTREAM *stream, unsigned long nmsgs,
		  DirNamesType dirtype, struct direct **names, 
		  unsigned long nfiles, int full)
{
   char tmp[MAILTMPLEN], file[MAILTMPLEN], newfile[MAILTMPLEN], *mdstr;
   struct stat sbuf;
   unsigned long i, new = 0L, l, uid_last;
   unsigned long recent = stream ? stream->recent : 0L;
   int d = 0, f = 0, r = 0, s = 0, t = 0;
   int we_compute, in_list;
   int silent = stream ? stream->silent : NIL;
   MESSAGECACHE *elt;

   if (dirtype == Cur && !stream->rdonly)
      for (i = 1L; i <= stream->nmsgs;){
	elt = mail_elt(stream,  i);
	in_list = elt && elt->private.spare.ptr && nfiles > 0L
		  ? (MDPOS(elt) < nfiles 
		    ? same_maildir_file(MDFILE(elt), names[MDPOS(elt)]->d_name)
		    : NIL)
		    || maildir_message_in_list(MDFILE(elt), names, 0L, 
						nfiles - 1L, &MDPOS(elt))
		  : NIL;
	if (!in_list){
	   if (elt->private.spare.ptr)
	      maildir_free_file ((void **) &elt->private.spare.ptr);

	   if (elt->recent) --recent;
	   mail_expunged(stream,i);
	}
	else i++;
      }

   stream->silent = T;
   uid_last = 0L;
   for (we_compute = 0, i = l = 1L; l <= nfiles; l++){
      unsigned long pos, uid;
      if (dirtype == New && !stream->rdonly){ /* move new messages to cur */
	pos = l - 1L;
	snprintf (file, sizeof(file), "%s/%s", LOCAL->path[New], names[pos]->d_name);
	if(lstat(file,&sbuf) == 0)
	   switch(sbuf.st_mode & S_IFMT){
	    case S_IFREG:
		strcpy(tmp, names[pos]->d_name);
		if((mdstr = strstr(tmp,MDSEP(3))) 
		   || (mdstr = strstr(tmp,MDSEP(2))))
		   *(mdstr+1) = '2';
		else
		   strcat(tmp, MDSEP(2));
		snprintf(newfile, sizeof(newfile), "%s/%s", LOCAL->path[Cur], tmp);
		if(rename (file, newfile) != 0){
		   mm_log("Unable to read new mail!", WARN);
		   continue;
		}
		unlink (file);
		new++;
	        break;
	    case S_IFLNK:  /* clean up, clean up, everybody, everywhere */
		if(unlink(file) < 0){
		   if(LOCAL->link == NIL){ 
		      mm_log("Unable to remove symbolic link", WARN);
		      LOCAL->link = T;
		   }
		}
		continue;
		break;
	    default: 
		if(LOCAL && LOCAL->link == NIL){
		  mm_log("Unrecognized file or link in folder", WARN);
		  LOCAL->link = T;
		}
		continue;
		break;
	   }
      }
      mail_exists(stream, i + nmsgs);
      elt = mail_elt(stream, i + nmsgs);
      pos = (elt && elt->private.spare.ptr) ? MDPOS(elt) : l - 1L;
      if (dirtype == New) elt->recent = T;
      maildir_getflag(names[pos]->d_name, &d, &f, &r ,&s, &t);
      if (elt->private.spare.ptr)
	 maildir_free_file_only ((void **)&elt->private.spare.ptr);
      else{
	 maildir_get_file((MAILDIRFILE **)&elt->private.spare.ptr);
	 we_compute++;
      }
      MDFILE(elt) = cpystr(names[pos]->d_name);
      MDPOS(elt)  = pos;
      MDLOC(elt)  = dirtype;
      if (dirtype == Cur){	/* deal with UIDs */
	if(elt->private.uid == 0L)
	  elt->private.uid = maildir_get_uid(MDFILE(elt));
	if(elt->private.uid <= uid_last){
	  uid = (we_compute ? uid_last : stream->uid_last) + 1L;
	  if(LOCAL->candouid)
	    maildir_assign_uid(stream, i + nmsgs, uid);
	  else
	    elt->private.uid = uid;
	}
	else
	  uid = elt->private.uid;
	uid_last = uid;
	if(uid_last > stream->uid_last)
	  stream->uid_last = uid_last;
      }
      if(dirtype == New && !stream->rdonly){
	maildir_free_file_only((void **)&elt->private.spare.ptr);
	MDFILE(elt)  = cpystr(tmp);
	MDSIZE(elt)  = sbuf.st_size;
	MDMTIME(elt) = sbuf.st_mtime;
	MDLOC(elt)   = Cur;
      }
      if (elt->draft != d || elt->flagged != f || 
	elt->answered != r || elt->seen != s || elt->deleted != t){
	   elt->draft = d; elt->flagged = f; elt->answered = r;
	   elt->seen  = s; elt->deleted = t;
	   if (!we_compute && !stream->rdonly)
	      MM_FLAGS(stream, i+nmsgs);
      }
      maildir_get_date(stream, i+nmsgs);
      elt->valid = T;
      i++;
   }
   stream->silent = silent;
   if(LOCAL->candouid && dirtype == Cur)
      maildir_read_uid(stream, NULL, &stream->uid_validity);
   if (dirtype == New && stream->rdonly)
      new = nfiles;
   mail_exists(stream, nmsgs  + ((dirtype == New) ? new : nfiles));
   mail_recent(stream, recent + ((dirtype == New) ? new : 0L));

   return (nmsgs  + (dirtype == New ? new : nfiles));
}

long maildir_ping (MAILSTREAM *stream)
{
  maildir_parse_folder(stream, 0);
  if(stream && LOCAL){
     if(LOCAL->candouid < 0)
	LOCAL->candouid++;
     else if(LOCAL->candouid)
        maildir_uid_renew_tempfile(stream);
     else	 /* try again to get uids */
	LOCAL->candouid = maildir_can_assign_uid(stream);
  }
  return stream && LOCAL ? LONGT : NIL;
}

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

/*
 * Unfortunately, there is no way to sort by arrival in this driver, this
 * means that opening a folder in this driver using the scandir function
 * will always make this driver slower than any driver that has a natural
 * way of sorting by arrival (like a flat file format, "mbox", "mbx", etc).
 */
int maildir_namesort (const struct direct **d1, const struct direct **d2)
{
  const struct direct *e1 = *(const struct direct **) d1;
  const struct direct *e2 = *(const struct direct **) d2; 

  return comp_maildir_file((char *) e1->d_name, (char *) e2->d_name);
}

/* Maildir close */

void maildir_close (MAILSTREAM *stream, long options)
{
  MESSAGECACHE *elt;
  unsigned long i;
  int silent = stream ? stream->silent : 0;
  mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);

  if (!stream) return;

  for (i = 1L; i <= stream->nmsgs; i++)
    if((elt = (MESSAGECACHE *) (*mc)(stream,i,CH_ELT)) && elt->private.spare.ptr)
      maildir_free_file ((void **) &elt->private.spare.ptr);
  stream->silent = T;
  if (options & CL_EXPUNGE) maildir_expunge (stream, NIL, NIL);
  maildir_abort(stream);
  if (mdfpath) fs_give((void **)&mdfpath);
  if (mypid) mypid = (pid_t) 0;
  stream->silent = silent;
}

void maildir_check (MAILSTREAM *stream)
{
  if (maildir_ping (stream)) mm_log ("Check completed",(long) NIL);   
}

long maildir_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs, long flags)
{
  char tmp[MAILTMPLEN];
  unsigned long i;
  MESSAGECACHE *elt;
  char *s;
                                /* UID call "impossible" */
  if (flags & FT_UID || !LOCAL) return NIL;
  elt = mail_elt (stream, msgno);

  if (!(flags & FT_PEEK) && !elt->seen){
    elt->seen = T;
    maildir_flagmsg (stream, elt);
    MM_FLAGS(stream, elt->msgno);
  }

  MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt));
  if (LOCAL->fd < 0)	/* if file closed ? */
     LOCAL->fd = open(tmp,O_RDONLY,NIL);

  if (LOCAL->fd < 0 && (errno == EACCES || errno == ENOENT)){
     INIT (bs, mail_string, "", 0);
     elt->rfc822_size = 0L;
     return NIL;
  }

  s = maildir_text_work(stream, elt, &i, flags);
  INIT (bs, mail_string, s, i);
  return LONGT;
}

char *maildir_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
                      unsigned long *length,long flags)
{
  FDDATA d;
  STRING bs;
  char *s,tmp[CHUNK];
  unsigned long msgno = elt->msgno;
  static int try = 0;

  if (length)
     *length = 0L;
  LOCAL->buf[0] = '\0';

  MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt));
  if (LOCAL->fd < 0)	/* if file closed ? */
     LOCAL->fd = open(tmp,O_RDONLY,NIL);

  if (LOCAL->fd < 0){		/* flag change? */
      if (try < 5){
	try++;
	if (maildir_update_elt_maildirp(stream, msgno) > 0)
	  try = 0;
	return maildir_text_work(stream, mail_elt(stream, msgno),length, flags);
      }
      try = 0;
      return NULL;
  }

  lseek (LOCAL->fd, elt->private.msg.text.offset,L_SET);

  if (flags & FT_INTERNAL) {    /* initial data OK? */
    if (elt->private.msg.text.text.size > LOCAL->buflen) {
      fs_give ((void **) &LOCAL->buf);
      LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
                                     elt->private.msg.text.text.size) + 1);
    }
    read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
    LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
  }
  else {
    if (elt->rfc822_size > LOCAL->buflen) {
      fs_give ((void **) &LOCAL->buf);
      LOCAL->buf = (char *) fs_get ((LOCAL->buflen = elt->rfc822_size) + 1);
    }
    d.fd = LOCAL->fd;           /* yes, set up file descriptor */
    d.pos = elt->private.msg.text.offset;
    d.chunk = tmp;              /* initial buffer chunk */
    d.chunksize = CHUNK;
    INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
    for (s = LOCAL->buf; SIZE (&bs);) switch (CHR (&bs)) {
    case '\r':                  /* carriage return seen */
      *s++ = SNX (&bs);         /* copy it and any succeeding LF */
      if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
      break;
    case '\n':
      *s++ = '\r';              /* insert a CR */
    default:
      *s++ = SNX (&bs);         /* copy characters */
    }
    *s = '\0';                  /* tie off buffer */
    *length = s - (char *) LOCAL->buf;   /* calculate length */
  }
  close(LOCAL->fd); LOCAL->fd = -1;
  return LOCAL->buf;
}

/* maildir parse, fill the elt structure... well not all of it... */
unsigned long maildir_parse_message(MAILSTREAM *stream, unsigned long msgno,
				    DirNamesType dirtype)
{
  char *b, *s, *t, c;
  char tmp[MAILTMPLEN];
  struct stat sbuf;
  unsigned long i, len;
  int d, f, r, se, dt;
  MESSAGECACHE *elt;

  elt = mail_elt (stream,msgno);
  MSGPATH(tmp, LOCAL->dir, MDFILE(elt), dirtype);
  if(stat(tmp, &sbuf) == 0)
     MDSIZE(elt) = sbuf.st_size;

  maildir_get_date(stream, msgno);
  maildir_getflag(MDFILE(elt), &d, &f, &r ,&se, &dt);
  elt->draft = d; elt->flagged = f; elt->answered = r; elt->seen = se;
  elt->deleted = dt; elt->valid  = T;
  if (LOCAL->fd < 0)	/* if file closed ? */
     LOCAL->fd = open(tmp,O_RDONLY,NIL);

  if (LOCAL->fd >= 0){
	s = (char *) fs_get (MDSIZE(elt) + 1);
	read (LOCAL->fd,s,MDSIZE(elt));
	s[MDSIZE(elt)] = '\0';
	t = s + strlen(s);	/* make t point to the end of s */
	for (i = 0L, b = s; b < t && !(i && (*b == '\n')); i = (*b++ == '\n'));
	len = (*b ? ++b : b) - s;
	elt->private.msg.header.text.size = 
		elt->private.msg.text.offset = len;
	elt->private.msg.text.text.size = MDSIZE(elt) - len;
	for (i = 0L, b = s, c = *b; b &&
	    ((c < '\016' && ((c == '\012' && ++i) 
			 ||(c == '\015' && *(b+1) == '\012' && ++b && (i +=2))))
	    || b < t); i++, c= *++b);
	elt->rfc822_size = i;
	fs_give ((void **) &s);
	close(LOCAL->fd); LOCAL->fd = -1;
  }
  return elt->rfc822_size;
}

int
maildir_update_elt_maildirp(MAILSTREAM *stream, unsigned long msgno)
{
     struct direct **names = NIL;
     unsigned long i, nfiles, pos;
     int d = 0, f = 0 , r = 0, s = 0, t = 0, in_list, scan_err;
     MESSAGECACHE *elt;

     maildir_scandir (LOCAL->path[Cur], &names, &nfiles, &scan_err, CCLIENT);

     elt = mail_elt (stream,msgno);

     in_list = nfiles > 0L
	    ? maildir_message_in_list(MDFILE(elt), names, 0L, nfiles - 1L, &pos)
	    : NIL;

     if (in_list && pos >= 0L && pos < nfiles
	 && !strcmp(MDFILE(elt), names[pos]->d_name)){
	in_list = NIL;
	maildir_abort(stream);
     }

     if (in_list && pos >= 0L && pos < nfiles){
	maildir_free_file_only((void **)&elt->private.spare.ptr);
	MDFILE(elt) = cpystr(names[pos]->d_name);
	maildir_getflag(MDFILE(elt), &d, &f, &r ,&s, &t);
	if (elt->draft != d || elt->flagged != f || 
	   elt->answered != r || elt->seen != s || elt->deleted != t){
	   elt->draft = d; elt->flagged = f; elt->answered = r;
	   elt->seen  = s; elt->deleted = t;
	   MM_FLAGS(stream, msgno);
        }
     }
     for (i = 0L; i < nfiles; i++)
	fs_give((void **) &names[i]);
     if (names)
	fs_give((void **) &names);
     return in_list ? 1 : -1;
}

/* Maildir fetch message header */

char *maildir_header (MAILSTREAM *stream,unsigned long msgno,
		unsigned long *length, long flags)
{
  char tmp[MAILTMPLEN], *s;
  MESSAGECACHE *elt;
  static int try = 0;

  if (length) *length = 0;
  if (flags & FT_UID || !LOCAL) return "";	/* UID call "impossible" */
  elt = mail_elt (stream,msgno);
  if(elt->private.msg.header.text.size == 0)
     maildir_parse_message(stream, msgno, MDLOC(elt));

  MSGPATH(tmp, LOCAL->dir, MDFILE(elt), MDLOC(elt));
  if (LOCAL->fd < 0)
     LOCAL->fd = open (tmp,O_RDONLY,NIL);

  if (LOCAL->fd < 0 && errno == EACCES){
     mm_log ("Message exists but can not be read. Envelope and body lost!",ERROR);
     return NULL;
  }

  if (LOCAL->fd < 0){			/* flag change? */
      if (try < 5){
	try++;
	if (maildir_update_elt_maildirp(stream, msgno) > 0)
	  try = 0;
	return maildir_header(stream, msgno, length, flags);
      }
      try = 0;
      return NULL;
  }

  if (flags & FT_INTERNAL){
     if(elt->private.msg.header.text.size > LOCAL->buflen){
         fs_give ((void **) &LOCAL->buf);
         LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
                                 elt->private.msg.header.text.size) + 1);
     }
     read (LOCAL->fd, (void *)LOCAL->buf, elt->private.msg.header.text.size);
     LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
  }
  else{
      s = (char *) fs_get(elt->private.msg.header.text.size+1);
      read (LOCAL->fd, (void *)s, elt->private.msg.header.text.size);
      s[elt->private.msg.header.text.size] = '\0';
      *length = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
                       elt->private.msg.header.text.size);
      fs_give ((void **) &s);
  }
  elt->private.msg.text.offset = elt->private.msg.header.text.size;
  elt->private.msg.text.text.size = MDSIZE(elt) - elt->private.msg.text.offset;
  close(LOCAL->fd); LOCAL->fd = -1;
  return LOCAL->buf;
}

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

void maildir_list (MAILSTREAM *stream,char *ref, char *pat)
{
  char *s,test[MAILTMPLEN],file[MAILTMPLEN];
  long i = 0L;

  if((!pat || !*pat) && maildir_canonicalize (test,ref,"*")
	&& maildir_valid_name(test)){	/* there is a #md/ leading here */
    for (i = 3L; test[i] && test[i] != '/'; i++);
    if ((s = strchr (test+i+1,'/')) != NULL) *++s = '\0';
    else test[0] = '\0';
    mm_list (stream,'/',test, LATT_NOSELECT);
  }
  else if (maildir_canonicalize (test,ref,pat)) {
    if (test[3] == '/') {       /* looking down levels? */
                                /* yes, found any wildcards? */
      if ((s = strpbrk (test,"%*")) != NULL){
                                /* yes, copy name up to that point */
        strncpy (file,test+4,i = s - (test+4));
        file[i] = '\0';         /* tie off */
      }
      else strcpy (file,test+4);/* use just that name then */
                                /* find directory name */
      if ((s = strrchr (file, '/')) != NULL){
        *s = '\0';              /* found, tie off at that point */
        s = file;
      }
                                /* do the work */
      if(IS_COURIER(test))
	courier_list_work (stream,s,test,0);
      else
	maildir_list_work (stream,s,test,0);
    }
                                /* always an INBOX */
    if (!compare_cstring (test,"#MD/INBOX"))
      mm_list (stream,NIL,"#MD/INBOX",LATT_NOINFERIORS);
    if (!compare_cstring (test,"#MC/INBOX"))
      mm_list (stream,NIL,"#MC/INBOX",LATT_NOINFERIORS);
  }
}

void courier_list (MAILSTREAM *stream,char *ref, char *pat)
{
/* I am too lazy to do anything. Do you care to ask maildir list, please?
   The real reason why this is a dummy function is because we do not want to
   see the same folder listed twice. 
*/
}

/* For those that want to hide things, we give them a chance to do so */
void *maildir_parameters (long function, void *value)
{
  void *ret = NIL;
  switch ((int) function) {
  case SET_MDINBOXPATH:
    if(strlen((char *) value ) > 49)
       strcpy(myMdInboxDir, "Maildir");
    else
       strcpy(myMdInboxDir, (char *) value);
  case GET_MDINBOXPATH:
    if (myMdInboxDir[0] == '\0') strcpy(myMdInboxDir,"Maildir");
    ret = (void *) myMdInboxDir;
    break;
  case SET_COURIERSTYLE:
    CourierStyle = (long) value;
  case GET_COURIERSTYLE:
    ret = (void *) CourierStyle;
    break;
  case GET_DIRFMTTEST:
    ret = (void *) maildir_dirfmttest;
    break;
  default:
    break;
  }
  return ret;
}

int maildir_create_folder(char *mailbox)
{
  char tmp[MAILTMPLEN], err[MAILTMPLEN];
  DirNamesType i;

  for (i = Cur; i != EndDir; i++){
	MDFLD(tmp, mailbox, i);
	if (mkdir(tmp, 0700) && errno != EEXIST){ /* try to make new dir */
	    snprintf (err, sizeof(err), "Can't create %s: %s", tmp, strerror(errno));
	    mm_log (err,ERROR);
	    return NIL;
	}
  }
  return T;
}

int maildir_create_work(char *mailbox, int loop)
{
  char *s, c, err[MAILTMPLEN], tmp[MAILTMPLEN], tmp2[MAILTMPLEN], mbx[MAILTMPLEN];
  int fnlen, create_dir = 0, courier, mv;
  struct stat sbuf;
  long style = *(long *) maildir_parameters(GET_COURIERSTYLE, NIL);

  courier = IS_COURIER(mailbox);
  strcpy(mbx, mailbox);
  mv = maildir_valid(mbx) ? 1 : 0;
  maildir_file_path(mailbox, tmp, sizeof(tmp));
  if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){
      create_dir++;
      mailbox[strlen(mailbox) - 1] = '\0';
  }

  if(!loop && courier){
    if(mv){
       if(create_dir){
	  if(style == CCLIENT)
	   strcpy (err,"Can not create directory: folder exists. Create subfolder");
	  else
	   strcpy(err,"Folder and Directory already exist");
       }
       else
          strcpy (err, "Can't create mailbox: mailbox already exists");
    }
    else{
	if(create_dir)
	   strcpy(err, "Can not create directory. Cread folder instead");
	else
	  err[0] = '\0';
    }
    if(err[0]){
       mm_log (err,ERROR);
       return NIL;
    }
  }

  fnlen = strlen(tmp);
  if ((s = strrchr(mailbox,MDSEPARATOR(courier))) != NULL){
     c = *++s;
    *s = '\0';
    if ((stat(tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
        !maildir_create_work (mailbox, ++loop))
      return NIL;
    *s = c;
  }
  tmp[fnlen] = '\0';

  if (mkdir(tmp,0700) && errno != EEXIST)
     return NIL;

  if (create_dir)
     mailbox[fnlen] = '/';

  if (create_dir){
     if(style == CCLIENT){
	if(!courier){
	   FILE *fp = NULL;
	   snprintf(tmp2, sizeof(tmp2), "%s%s", tmp, MDDIR);
	   if ((fp = fopen(tmp2,"w")) == NULL){
	      snprintf (err, sizeof(err), "Problem creating %s: %s", tmp2, strerror(errno));
              mm_log (err,ERROR);
              return NIL;
	   }
	   fclose(fp);
	}
     }
     return T;
  }
  else
     return maildir_create_folder(tmp);
}

long maildir_create (MAILSTREAM *stream,char *mailbox)
{
  char tmp[MAILTMPLEN], err[MAILTMPLEN];
  int rv, create_dir;

  create_dir = mailbox ? 
		(mailbox[strlen(mailbox) - 1] == 
					MDSEPARATOR(IS_COURIER(mailbox))) : 0;
  maildir_file_path(mailbox, tmp, sizeof(tmp));
  strcpy(tmp, mailbox);
  rv = maildir_create_work(mailbox, 0);
  strcpy(mailbox, tmp);
  if (rv == 0){
     snprintf (err, sizeof(err), "Can't create %s %s",
		   (create_dir ? "directory" : "mailbox"), mailbox);
     mm_log (err,ERROR);
  }
  return rv ? LONGT : NIL;
}

#define MAXTRY 10000
void maildir_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
{
  char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN];
  char *s;
  int ren, try = 0;

  if (elt->valid){
     for (try = 1; try > 0 && try < MAXTRY; try++){
                                /* build the new filename */
	snprintf (oldfile, sizeof(oldfile), "%s/%s",LOCAL->path[Cur], MDFILE(elt));
	fn[0] = '\0';
	if ((ren = maildir_message_exists(stream, MDFILE(elt), fn)) == 0){
	    errno = ENOENT;
	    try = MAXTRY;
	}
	if (*fn)	/* new oldfile! */
	   snprintf (oldfile,sizeof(oldfile),"%s/%s", LOCAL->path[Cur], fn);
        if ((s = strrchr (MDFILE(elt), FLAGSEP))) *s = '\0';
	snprintf (fn, sizeof(fn), "%s%s%s%s%s%s%s", MDFILE(elt), MDSEP(2),
		MDFLAG(Draft, elt->draft), MDFLAG(Flagged, elt->flagged),
		MDFLAG(Replied, elt->answered), MDFLAG(Seen, elt->seen),
		MDFLAG(Trashed, elt->deleted));
	snprintf (newfile, sizeof(newfile), "%s/%s",LOCAL->path[Cur],fn);
        if (ren != 0 && rename (oldfile,newfile) >= 0)
	    try = -1;
     }

     if (try > 0){
       snprintf(oldfile, sizeof(oldfile), "Unable to write flags to disk: %s",
		(errno == ENOENT) ? "message is gone!" : strerror (errno));
       mm_log(oldfile,ERROR);
       return;
     }
#ifdef __CYGWIN__
     utime(LOCAL->path[Cur], NIL);	/* make sure next scan will catch the change */
#endif
     maildir_free_file_only ((void **) &elt->private.spare.ptr);
     MDFILE(elt) = cpystr (fn);
  }
}

long maildir_expunge (MAILSTREAM *stream, char *sequence, long options)
{
  long ret;
  MESSAGECACHE *elt;
  unsigned long i, n = 0L;
  unsigned long recent = stream->recent;
  char tmp[MAILTMPLEN];

  mm_critical (stream);               /* go critical */
  ret = sequence ? ((options & EX_UID) ?
                         mail_uid_sequence (stream,sequence) :
                         mail_sequence (stream,sequence)) : LONGT;
  if(ret == 0L)
     return 0L;
  for (i = 1L; i <= stream->nmsgs;){
    elt = mail_elt (stream,i);
    if (elt->deleted && (sequence ? elt->sequence : T)){
      snprintf (tmp, sizeof(tmp), "%s/%s", LOCAL->path[Cur], MDFILE(elt));
      if (unlink (tmp) < 0) {/* try to delete the message */
      snprintf (tmp, sizeof(tmp), "Expunge of message %ld failed, aborted: %s",i,
              strerror (errno));
      if (!stream->silent)
         mm_log (tmp,WARN);
      break;
      }
      if (elt->private.spare.ptr)
       maildir_free_file ((void **) &elt->private.spare.ptr);
      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++;
  }
  if(n){                      /* output the news if any expunged */
    snprintf (tmp, sizeof(tmp), "Expunged %ld messages", n);
    if (!stream->silent)
       mm_log (tmp,(long) NIL);
  }
  else
    if (!stream->silent)
      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);
  return ret;
}

long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
{
  STRING st;
  MESSAGECACHE *elt;
  unsigned long len;
  int fd;
  unsigned long i;
  struct stat sbuf;
  char tmp[MAILTMPLEN], flags[MAILTMPLEN], path[MAILTMPLEN], *s;
				/* copy the messages */
  if ((options & CP_UID) ? mail_uid_sequence (stream, sequence) : 
  	mail_sequence (stream,sequence)) 
  for (i = 1L; i <= stream->nmsgs; i++)
    if ((elt = mail_elt (stream,i))->sequence){
      MSGPATH(path, LOCAL->dir, MDFILE(elt), MDLOC(elt));
      if (((fd = open (path,O_RDONLY,NIL)) < 0)	 
	  ||((!elt->rfc822_size && 
		((stat(path, &sbuf) < 0) || !S_ISREG (sbuf.st_mode)))))
	return NIL;
	if(!elt->rfc822_size)
	  MDSIZE(elt) = sbuf.st_size;
        s = (char *) fs_get(MDSIZE(elt) + 1);
        read (fd,s,MDSIZE(elt));
        s[MDSIZE(elt)] = '\0';
        close (fd);
	len = strcrlfcpy (&LOCAL->buf,&LOCAL->buflen, s, MDSIZE(elt));
        INIT (&st,mail_string, LOCAL->buf, len);
	elt->rfc822_size = len;
	fs_give ((void **)&s);

      flags[0] = flags[1] = '\0';
      if (elt->seen) strcat (flags," \\Seen");
      if (elt->draft) strcat (flags," \\Draft");
      if (elt->deleted) strcat (flags," \\Deleted");
      if (elt->flagged) strcat (flags," \\Flagged");
      if (elt->answered) strcat (flags," \\Answered");
      flags[0] = '(';		/* open list */
      strcat (flags,")");	/* close list */
      mail_date (tmp,elt);	/* generate internal date */
      if (!mail_append_full (NIL, mailbox, flags, tmp, &st))
        return NIL;
      if (options & CP_MOVE) elt->deleted = T;
    }
  return LONGT;			/* return success */
}

long maildir_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
{
  int fd, k, done, fail;
  STRING *message;
  char c,*s, *flags, *date;
  char tmp[MAILTMPLEN],file[MAILTMPLEN],path1[MAILTMPLEN],path2[MAILTMPLEN];
  MESSAGECACHE elt;
  long i, size = 0L, ret = LONGT, f;
  unsigned long uf, ti;
  static unsigned int transact = 0;
  struct stat sbuf;

  if (!maildir_valid(mailbox)) {
    snprintf (tmp, sizeof(tmp), "Not a valid Maildir mailbox: %s", mailbox);
    mm_log (tmp,ERROR);
    return NIL;
  }

 if (!*mdlocaldomain)
     md_domain_name();    /* get domain name for maildir files in mdlocaldomain now! */

 if (mypid == (pid_t) 0)
    mypid = getpid();

 if (!stream){
    stream = &maildirproto;
  
    for (k = 0; k < NUSERFLAGS && stream->user_flags[k]; ++k)
       fs_give ((void **) &stream->user_flags[k]);
 }

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

  mm_critical (stream);		/* go critical */
  do {
    fail = done = 0;			/* we have not determined name of message file yet */
    if (!SIZE (message)) {      /* guard against zero-length */
      mm_log ("Append of zero-length message", ERROR);
      ret = NIL;
      break;
    }

    if (date && !mail_parse_date(&elt,date)){
       snprintf (tmp, sizeof(tmp), "Bad date in append: %.80s", date);
       mm_log (tmp, ERROR);
       ret = NIL;
       break;
    }

    if(date){
      struct tm tm;

      tm.tm_sec  = elt.seconds;
      tm.tm_min  = elt.minutes;
      tm.tm_hour = elt.hours;
      tm.tm_mday = elt.day;
      tm.tm_mon  = elt.month - 1;
      tm.tm_year = BASEYEAR + elt.year - 1900;

      ti = mktime(&tm);
    } else ti = time(0);

    f = mail_parse_flags (stream,flags,&uf);
    do {
				/* build file name we will use */
	snprintf (file, sizeof(file), "%lu.%d_%09u.%s%s%s%s%s%s",
		ti, mypid, transact++, mdlocaldomain, (f ? MDSEP(2) : ""),
		MDFLAG(Draft, f&fDRAFT), MDFLAG(Flagged, f&fFLAGGED),
		MDFLAG(Replied, f&fANSWERED), MDFLAG(Seen, f&fSEEN));
				/* build tmp file name */
	if (maildir_file_path(mailbox, tmp, sizeof(tmp)))	/* copy in TMP */
	   MSGPATH(path1, tmp, file, Tmp);
				/* build final filename to use */
        if (maildir_file_path(mailbox, tmp, sizeof(tmp)))
	   MSGPATH(path2, tmp, file, New);			/* copy in NEW */
        if(stat(path1, &sbuf) < 0 && errno == ENOENT 
	   &&  stat(path2, &sbuf) < 0 && errno == ENOENT)
	  done++;
	else
	  fail++;
	if(fail == 1000){
	   snprintf (tmp, sizeof(tmp), "Failure to create append message name");
	   mm_log (tmp, ERROR);
	   return NIL;
	}
    } while (done == 0);

    if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0) {
       snprintf (tmp, sizeof(tmp), "Can't open append mailbox: %s", strerror (errno));
       mm_log (tmp, ERROR);
       return NIL;
    }
    for (size = 0,i = SIZE (message),s = (char *) fs_get (i + 1); i; --i)
      if ((c = SNX (message)) != '\015') s[size++] = c;
    if ((write (fd, s, size) < 0) || fsync (fd)) {
	unlink (path1);		/* delete message */
	snprintf (tmp, sizeof(tmp), "Message append failed: %s", strerror (errno));
	mm_log (tmp, ERROR);
	ret = NIL;
    }
    fs_give ((void **) &s);	/* flush the buffer */
    close (fd);			/* close the file */

    if (rename (path1,path2) < 0) {
       snprintf (tmp, sizeof(tmp), "Message append failed: %s", strerror (errno));
       mm_log (tmp, ERROR);
       ret = NIL;
    }
    unlink (path1);
    if(date){
	time_t tp[2];
        tp[0] = tp[1] = ti;
        utime (path2,tp);
    }

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

  } while (ret && message);	/* write the data */
  mm_nocritical (stream);	/* release critical */
  return ret;
}

long maildir_delete (MAILSTREAM *stream,char *mailbox)
{
  DIR *dirp;
  struct direct *d;
  int i, remove_dir = 0, mddir = 0, rv, error = 0;
  char tmp[MAILTMPLEN],tmp2[MAILTMPLEN], realname[MAILTMPLEN];
  struct stat sbuf;
  int courier = IS_COURIER(mailbox);

  if (mailbox[strlen(mailbox) - 1] == MDSEPARATOR(courier)){
      remove_dir++;
      mailbox[strlen(mailbox) -1] = '\0';
  }

  if (!maildir_valid(mailbox)){
      maildir_file_path(mailbox, tmp, sizeof(tmp));
      if (stat(tmp, &sbuf) < 0 || !S_ISDIR(sbuf.st_mode)){
        snprintf(tmp, sizeof(tmp), "Can not remove %s", mailbox);
	error++;
      }
  }

  if (!error && remove_dir && !maildir_dir_is_empty(mailbox)){
     snprintf(tmp, sizeof(tmp), "Can not remove directory %s/: directory not empty", mailbox);
     error++;
  }

  if(error){
     mm_log (tmp,ERROR);
     return NIL;
  }

  maildir_close(stream,0);	/* even if stream was NULL */

  maildir_file_path(mailbox, realname, sizeof(realname));

  if (remove_dir){
     snprintf(tmp, sizeof(tmp), "%s/%s", realname, MDDIR);
     if ((rv = stat (tmp,&sbuf)) == 0 && S_ISREG(sbuf.st_mode))
	rv = unlink(tmp);
     else if (errno == ENOENT)
	rv = 0;
     if (rv != 0){
	snprintf(tmp, sizeof(tmp), "Can not remove %s/%s: %s", tmp2, MDDIR, strerror(errno));
	mm_log (tmp,ERROR);
	return NIL;
     }
     if (!maildir_valid(realname) && rmdir(realname) != 0){
	snprintf(tmp, sizeof(tmp), "Can not remove %s/: %s", mailbox, strerror(errno));
	mm_log (tmp, ERROR);
	return NIL;
     }
     return LONGT;
  }
  /* else remove just the folder. Remove all hidden files, except MDDIR */
  for (i = Cur; i != EndDir; i++){
      MDFLD(tmp, realname, i);

      if (!(dirp = opendir (tmp))){
	  snprintf(tmp, sizeof(tmp), "Can not read %s/: %s", mailbox, strerror(errno));
	  mm_log (tmp, ERROR);
	  return NIL;
      }

      while ((d = readdir(dirp)) != NULL){
	 if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..")){
	    snprintf(tmp2, sizeof(tmp2), "%s/%s", tmp, d->d_name);
	    if (unlink(tmp2) != 0){
	       snprintf(tmp2, sizeof(tmp2), "Can not remove %s: %s", mailbox, strerror(errno));
	       mm_log (tmp2, ERROR);
	       return NIL;
	    }
	 }
      }
      closedir(dirp);
      if (rmdir(tmp) != 0){
	 snprintf(tmp, sizeof(tmp), "Can not remove %s: %s", mailbox, strerror(errno));
	 mm_log (tmp, ERROR);
	 return NIL;
      }
  }
  /* 
   * ok we have removed all subdirectories of the folder mailbox, Remove the
   * hidden files.
   */

  if(!(dirp = opendir (realname))){
    snprintf(tmp, sizeof(tmp), "Can not read %s/: %s", realname, strerror(errno));
    mm_log (tmp, ERROR);
    return NIL;
  }

  while ((d = readdir(dirp)) != NULL){
	if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..")
		&& (!strcmp(d->d_name, MDDIR)
			|| !strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST))
			|| !strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)))){
	   if(strcmp(d->d_name, MDDIR) == 0)
	      mddir++;
	   snprintf(tmp, sizeof(tmp), "%s/%s", realname, d->d_name);
	   if (unlink(tmp) != 0)
	      error++;
	}
  }
  closedir(dirp);
  if (error || 
	 (maildir_dir_is_empty(mailbox) && mddir == 0 && rmdir(realname) < 0)){
        snprintf(tmp, sizeof(tmp), "Can not remove folder %s: %s", mailbox, strerror(errno));
        mm_log (tmp, ERROR);
        return NIL;
  }
  return LONGT;
}

long maildir_rename (MAILSTREAM *stream, char *old, char *new)
{
  char tmp[MAILTMPLEN], tmpnew[MAILTMPLEN], realold[MAILTMPLEN];
  char realnew[MAILTMPLEN];
  int courier = IS_COURIER(old) && IS_COURIER(new);
  int i;
  long rv = LONGT;
  COURIER_S *cdir;

  if((IS_COURIER(old) || IS_COURIER(new)) && !courier){
    snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s to %s", old, new);
    mm_log (tmp, ERROR);
    return NIL;
  }

  if (!maildir_valid(old)){
    snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s: folder not in maildir format",old);
    mm_log (tmp, ERROR);
    return NIL;
  }
  maildir_file_path(old, realold, sizeof(realold));
  if (!maildir_valid_name(new) && new[0] == '#'){
    snprintf (tmp, sizeof(tmp), "Cannot rename mailbox %s: folder not in maildir format", new);
    mm_log (tmp, ERROR);
    return NIL;
  }
  maildir_file_path(new, realnew, sizeof(realnew));
  if (access(tmpnew,F_OK) == 0){ 	/* new mailbox name must not exist */
    snprintf (tmp, sizeof(tmp), "Cannot rename to mailbox %s: destination already exists", new);
    mm_log (tmp, ERROR);
    return NIL;
  }

  if(!courier){
    if (rename(realold, realnew)){	/* try to rename the directory */
       snprintf(tmp, sizeof(tmp), "Can't rename mailbox %s to %s: %s", old, new,
							strerror(errno));
       mm_log(tmp,ERROR);
       return NIL;
    }
    return LONGT;	/* return success */
  }

  cdir = courier_list_dir(old);
  for (i = 0; cdir && i < cdir->total; i++){
      if(strstr(cdir->data[i]->name, old)){
	snprintf(tmp, sizeof(tmp), "%s%s", new, cdir->data[i]->name+strlen(old));
	maildir_file_path(cdir->data[i]->name, realold, sizeof(realold));
	maildir_file_path(tmp, realnew, sizeof(realnew));
	if (rename(realold, realnew)){
	   snprintf (tmp, sizeof(tmp), "Can't rename mailbox %s to %s: %s", old, new,
							strerror(errno));
	   mm_log(tmp,ERROR);
	   rv = NIL;
	}
    }
  }
  courier_free_cdir(&cdir);
  return rv;
}

long maildir_sub(MAILSTREAM *stream,char *mailbox)
{
  return sm_subscribe(mailbox);
}

long maildir_unsub(MAILSTREAM *stream,char *mailbox)
{
  return sm_unsubscribe(mailbox);
}

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

long maildir_canonicalize (char *pattern,char *ref,char *pat)
{
  if (ref && *ref) {            /* have a reference */
    strcpy (pattern,ref);       /* copy reference to pattern */
                                /* # overrides mailbox field in reference */
    if (*pat == '#') strcpy (pattern,pat);
                                /* pattern starts, reference ends, with / */
    else if ((*pat == '/') && (pattern[strlen (pattern) - 1] == '/'))
      strcat (pattern,pat + 1); /* append, omitting one of the period */
                                                                                
    else strcat (pattern,pat);  /* anything else is just appended */
  }
  else strcpy (pattern,pat);    /* just have basic name */
  return maildir_valid_name(pattern) ? LONGT : NIL;
}

void maildir_list_work (MAILSTREAM *stream,char *dir,char *pat,long level)
{
  DIR *dp;
  struct direct *d;
  struct stat sbuf;
  char curdir[MAILTMPLEN],name[MAILTMPLEN], tmp[MAILTMPLEN];
  char realpat[MAILTMPLEN];
  long i;
  char *maildirpath = mdirpath();

  snprintf(curdir, sizeof(curdir), "%s/%s/", myrootdir(pat), dir ? dir : maildirpath);
  if ((dp = opendir (curdir)) != NULL){ 
     if (dir) snprintf (name, sizeof(name), "%s%s/",MDPREFIX(CCLIENT),dir);
     else strcpy (name, pat);

     if (level == 0 && !strpbrk(pat,"%*")){
	if(maildir_valid(pat)){
	  i =  maildir_contains_folder(pat, NULL)
		? LATT_HASCHILDREN
		: (maildir_is_dir(pat, NULL)
			     ? LATT_HASNOCHILDREN : LATT_NOINFERIORS);
	  maildir_file_path(pat, realpat, sizeof(realpat));
	  i +=  maildir_any_new_msgs(realpat) 
			? LATT_MARKED : LATT_UNMARKED;
	  mm_list (stream,'/', pat, i);
	}
	else
	   if(pat[strlen(pat) - 1] == '/')
	     mm_list (stream,'/', pat, LATT_NOSELECT);
     }

     while ((d = readdir (dp)) != NULL)
	if(strcmp(d->d_name, ".") && strcmp(d->d_name,"..")
		&& strcmp(d->d_name, MDNAME(Cur)) 
		&& strcmp(d->d_name, MDNAME(Tmp)) 
		&& strcmp(d->d_name, MDNAME(New))){

	  if (dir) snprintf (tmp, sizeof(tmp), "%s%s", name,d->d_name);
	  else strcpy(tmp, d->d_name);

	  if(pmatch_full (tmp, pat,'/')){
	     snprintf(tmp, sizeof(tmp), "%s/%s/%s", myrootdir(d->d_name), 
				(dir ? dir : maildirpath), d->d_name);
	     if(stat (tmp,&sbuf) == 0 
		   && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
	       if (dir) snprintf (tmp, sizeof(tmp), "%s%s", name,d->d_name);
	       else strcpy(tmp, d->d_name);
               i = maildir_valid(tmp)
			? (maildir_contains_folder(dir, d->d_name)
			  ? LATT_HASCHILDREN
			  : (maildir_is_dir(dir, d->d_name)
			     ? LATT_HASNOCHILDREN : LATT_NOINFERIORS))
			: LATT_NOSELECT;
	       i +=  maildir_any_new_msgs(tmp)
			    ? LATT_MARKED : LATT_UNMARKED;
	       mm_list (stream,'/',tmp, i);
	       strcat (tmp, "/");
	       if(dmatch (tmp, pat,'/') &&
                 (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL))){
		   snprintf(tmp, sizeof(tmp), "%s/%s",dir,d->d_name);
 		   maildir_list_work (stream,tmp,pat,level+1);
	       }
	     }
	  }
       }
     closedir (dp);
  }
}

void courier_list_work (MAILSTREAM *stream, char *dir, char *pat, long level)
{
  char c, curdir[MAILTMPLEN], tmp[MAILTMPLEN];
  char realname[MAILTMPLEN], realpat[MAILTMPLEN] = {'\0'};
  int i, found;
  long style = *(long *) maildir_parameters(GET_COURIERSTYLE, NIL), j;
  char *maildirpath = mdirpath();
  COURIER_S *cdir;

  if(!strpbrk(pat,"%*")){	/* a mailbox */
     maildir_file_path(pat, curdir, sizeof(curdir));
     i = strlen(curdir) - 1;
     if(curdir[i] == '/')
       curdir[i] = '\0';
     cdir = courier_list_dir(curdir);
     if(cdir){
	found = 0; j = 0L;
	if(maildir_valid_name(pat)){
	  for(i = 0; !found && i < cdir->total; i++)
	     if(strstr(curdir, cdir->data[i]->name)){
		if(strlen(curdir) < strlen(cdir->data[i]->name))
		  found += 2;
		else if(strlen(curdir) == strlen(cdir->data[i]->name))
		  found -= 1;
	     }
	  if(found > 0)
            j = LATT_HASCHILDREN;
          else if(found == 0)
	    j = (style == COURIER) ? LATT_HASNOCHILDREN : LATT_NOINFERIORS;
	}
	else
	   j = LATT_NOSELECT;
        j += maildir_any_new_msgs(curdir) ? LATT_MARKED : LATT_UNMARKED;
	if (found)
	   mm_list (stream, '.', pat, j);
        courier_free_cdir(&cdir);
     }
     return;
  }

  strcpy(tmp,pat + 4);	/* a directory */
  j = strlen(pat) - 1;
  maildir_file_path(pat, realpat, sizeof(realpat));
  c = pat[j];
  pat[j] = '\0';
  realname[0] = '\0';
  if(dir)
    maildir_file_path(dir, realname, sizeof(realname));
  snprintf(curdir, sizeof(curdir), "%s%s%s/%s", (dir ? "" : myrootdir(pat)), (dir ? "" : "/"),
		(dir ? realname : maildirpath),	(dir ? "" : "."));
  snprintf(tmp, sizeof(tmp), "%s%s/.", MDPREFIX(COURIER), dir ? dir : maildirpath);
  if (level == 0 && tmp && pmatch_full (tmp, realpat, '.'))
     mm_list (stream,'.', tmp, LATT_NOSELECT);

  cdir = courier_list_dir(pat);
  pat[j] = c;
  for (i = 0; cdir && i < cdir->total; i++)
   if(pmatch_full (cdir->data[i]->name, pat, '.')){
      snprintf(tmp, sizeof(tmp), "%s.", cdir->data[i]->name);
      courier_list_info(&cdir, tmp, i);
      mm_list (stream,'.',cdir->data[i]->name, cdir->data[i]->attribute);
   }
  courier_free_cdir(&cdir);
}

int 
same_maildir_file(char *name1, char *name2)
{
 char tmp1[MAILTMPLEN], tmp2[MAILTMPLEN];
 char *s;

 strcpy(tmp1, name1 ? name1 : "");
 strcpy(tmp2, name2 ? name2 : "");
 if ((s = strrchr(tmp1, FLAGSEP)) != NULL)
   *s = '\0';
 if (((s = strrchr(tmp1, SIZESEP)) != NULL) && (strchr(s,'.') == NULL))
   *s = '\0';
 if ((s = strrchr(tmp2, FLAGSEP)) != NULL)
   *s = '\0';
 if (((s = strrchr(tmp2, SIZESEP)) != NULL) && (strchr(s,'.') == NULL))
   *s = '\0';

 return !strcmp(tmp1, tmp2);
}

unsigned long antoul(char *seed)
{
  int i, error = 0;
  unsigned long val = 0L, rv1 = 0L, t;
  char c, *p;
 if(!seed)
   return 0L;
 t = strtoul(seed, &p, 10);
 if(p && (*p == '.' || *p == '_'))
   return t;
 /* else */
 if((p = strchr(seed,'.')) != NULL)
   *p = '\0';
 error = (strlen(seed) > 6); /* too long */
 for(i= strlen(seed)-1; error == 0 && i >= 0; i--){
    c = seed[i];
    if (c >= 'A' && c <= 'Z') val = c - 'A';
    else if (c >= 'a' && c <= 'z') val = c - 'a' + 26;
    else if (c >= '0' && c <= '9') val = c - '0' + 26 + 26; 
    else if (c == '-') val = c - '-' + 26 + 26 + 10;
    else if (c == '_') val = c - '_' + 26 + 26 + 10 + 1;
    else error++;
    rv1 = val + (rv1 << 6);
 }
 if(p)
   *p = '.';
  return error ? 0L : rv1;
}

unsigned long mdfntoul (char *name)
{
  unsigned long t;
  char *r, last;

  if((*name == '_') && ((r = strpbrk(name,".,%+")) != NULL)){ /* Grrr!!! */
    last = *r;
    *r = '\0';
     t = antoul(r+1);
    *r = last;
  }
  else
    t = antoul(name);
  return t;
}

int comp_maildir_file(char *name1, char *name2)
{
  int uset1 = 1, uset2 = 1, i, j, cmp;
  unsigned long t1, t2;
  char *s1, *s2;

  if (!(name1 && *name1))
     return (name2 && *name2) ? (*name2 == FLAGSEP ? 0 : -1) : 0;

  if (!(name2 && *name2))
     return (name1 && *name1) ? (*name1 == FLAGSEP ? 0 : 1) : 0;

   if((cmp = strcmp(name1,name2)) == 0)
      return 0;

  t1 = strtoul(name1, &s1, 10);
  t2 = strtoul(name2, &s2, 10);

  if(!s1 || *s1 != '.')
    uset1 = 0;

  if(!s2 || *s2 != '.')
    uset2 = 0;

  if(uset1 && uset2)	/* normal sort order */
    return (t1 < t2) ? -1 : (t1 > t2 ? 1 : (cmp < 0 ? -1 : 1));

  /* If we make it here we say Grrrr.... first, then we try to figure out
   * how to sort this mess.
   * These are the rules.
   * If there is a number at the beginning it is bigger than anything else.
   * If there are digits, then the number of digits decides which one is bigger.
   */

  for(i = 0; isdigit(name1[i]); i++);
  for(j = 0; isdigit(name2[j]); j++);

  return(uset1 ? 1 
	       : (uset2 ? -1 
			: (i < j ? -1 : (i > j ? 1 : (cmp < 0 ? -1 : 1)))));
}

void
maildir_getflag(char *name, int *d, int *f, int *r ,int *s, int *t)
{
  char tmp[MAILTMPLEN], *b;
  int offset = 0;
  int tmpd, tmpf, tmpr, tmps, tmpt;

  if(d) *d = 0;
  if(f) *f = 0;
  if(r) *r = 0;
  if(s) *s = 0;
  if(t) *t = 0;

  tmpd = tmpf = tmpr = tmps = tmpt = NIL; /* no flags set by default */
  strcpy(tmp,name);
  while ((b = strrchr(tmp+offset, FLAGSEP)) != NULL){
    char flag,last;
    int  k;
    if (!++b) break;
    switch (*b){
	case '1':
	case '2':
	case '3': flag = *b; b += 2;
		  for (k = 0; b[k] && b[k] != FLAGSEP && b[k] != ','; k++);
		  last = b[k];
		  b[k] = '\0';
		  if (flag == '2' || flag == '3'){
		     tmpd = strchr (b, MDFLAGC(Draft))   ? T : NIL;
		     tmpf = strchr (b, MDFLAGC(Flagged)) ? T : NIL;
		     tmpr = strchr (b, MDFLAGC(Replied)) ? T : NIL;
		     tmps = strchr (b, MDFLAGC(Seen))    ? T : NIL;
		     tmpt = strchr (b, MDFLAGC(Trashed)) ? T : NIL;
		  }
		  b[k] = last;
		  b += k;
		  for (; tmp[offset] && tmp[offset] != FLAGSEP; offset++);
		  offset++;
		break;
	default: break;	/* Should we crash?... Nahhh */
    }
  }
  if(d) *d = tmpd;
  if(f) *f = tmpf;
  if(r) *r = tmpr;
  if(s) *s = tmps;
  if(t) *t = tmpt;
}

int
maildir_message_in_list(char *msgname, struct direct **names, 
		unsigned long bottom, unsigned long top, unsigned long *pos)
{
  unsigned long middle = (bottom + top)/2;
  int test;

  if (!msgname)
     return NIL;

  if (pos) *pos = middle;

  if (same_maildir_file(msgname, names[middle]->d_name))
     return T;

  if (middle == bottom){	 /* 0 <= 0 < 1 */
     int rv = NIL;
     if (same_maildir_file(msgname, names[middle]->d_name)){
	rv = T;
	if (pos) *pos = middle;
     }
     else
       if (same_maildir_file(msgname, names[top]->d_name)){
	rv = T;
	if (pos) *pos = top;
       }
     return rv;
  }

  test = comp_maildir_file(msgname, names[middle]->d_name);

  if (top <= bottom)
      return test ? NIL : T;

  if (test < 0 ) /* bottom <  msgname < middle */
     return maildir_message_in_list(msgname, names, bottom, middle, pos);
  else if (test > 0)  /* middle < msgname < top */
     return maildir_message_in_list(msgname, names, middle, top, pos);
  else return T;
}

void
maildir_abort(MAILSTREAM *stream)
{
  if (LOCAL){
    DirNamesType i;

    if(LOCAL->candouid)
      maildir_read_uid(stream, NULL, &stream->uid_validity);
    if (LOCAL->dir) fs_give ((void **) &LOCAL->dir);
    for (i = Cur; i < EndDir; i++)
      if(LOCAL->path[i]) fs_give ((void **) &LOCAL->path[i]);
    fs_give ((void **) &LOCAL->path);
    if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
    if(LOCAL->uidtempfile){
      unlink(LOCAL->uidtempfile);
      fs_give ((void **) &LOCAL->uidtempfile);
    }
    fs_give ((void **) &stream->local);
  }
  if (mdfpath) fs_give((void **)&mdfpath);
  stream->dtb = NIL;
}

int
maildir_contains_folder(char *dirname, char *name)
{
  char tmp[MAILTMPLEN], tmp2[MAILTMPLEN];
  int rv = 0;
  DIR *dir;
  struct direct *d;

  maildir_file_path(dirname, tmp2, sizeof(tmp2));
  if(name){
    strcat(tmp2,"/");
    strcat(tmp2, name);
  }

  if (!(dir = opendir (tmp2)))
     return NIL;

  while ((d = readdir(dir)) != NULL){
    if (strcmp(d->d_name, ".") && strcmp(d->d_name,"..")
	&& strcmp(d->d_name, MDNAME(Cur)) 
	&& strcmp(d->d_name, MDNAME(Tmp)) 
	&& strcmp(d->d_name, MDNAME(New))){

       snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name);
       if(maildir_valid(tmp)){
	  rv++;
	  break;
       }
    }
  }
  closedir(dir);
  return rv;
}

int
maildir_is_dir(char *dirname, char *name)
{
  char tmp[MAILTMPLEN];
  struct stat sbuf;

  maildir_file_path(dirname, tmp, sizeof(tmp));
  if(name){
    strcat(tmp, "/");
    strcat(tmp, name);
  }
  strcat(tmp, "/");
  strcat(tmp, MDDIR);

  return ((stat(tmp, &sbuf) == 0) && S_ISREG (sbuf.st_mode)) ? 1 : 0;
}

int
maildir_dir_is_empty(char *mailbox)
{
  char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], tmp3[MAILTMPLEN],*s;
  int rv = 1, courier = IS_COURIER(mailbox);
  DIR *dir;
  struct direct *d;
  struct stat sbuf;

  maildir_file_path(mailbox, tmp2, sizeof(tmp2));

  if(courier){
     strcpy(tmp3, tmp2);
     if(s = strrchr(tmp2, '/'))
	*s = '\0';
  }

  if (!(dir = opendir (tmp2)))
     return rv;

  if(courier){
     while((d = readdir(dir)) != NULL){
        snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name);
	if(!strncmp(tmp, tmp3, strlen(tmp3)) 
	   && tmp[strlen(tmp3)] == '.'){
	   rv = 0;
	   break;
	}
     }
  }
  else
    while ((d = readdir(dir)) != NULL){
      snprintf(tmp, sizeof(tmp), "%s/%s", tmp2, d->d_name);
      if (strcmp(d->d_name, ".") 
	&& strcmp(d->d_name,"..")
	&& strcmp(d->d_name, MDNAME(Cur)) 
	&& strcmp(d->d_name, MDNAME(Tmp)) 
	&& strcmp(d->d_name, MDNAME(New))
	&& strcmp(d->d_name, MDDIR)
	&& strcmp(d->d_name, MDUIDVALIDITY)
	&& !(d->d_name[0] == '.' 
		&& stat (tmp,&sbuf) == 0 
		&& S_ISREG(sbuf.st_mode))){
	   rv = 0;
	   break;
       }
    }
  closedir(dir);
  return rv;
}

void
maildir_get_file (MAILDIRFILE **mdfile)
{
  MAILDIRFILE *md;

  md = (MAILDIRFILE *) fs_get(sizeof(MAILDIRFILE));
  memset(md, 0, sizeof(MAILDIRFILE));
  *mdfile = md;
}

void
maildir_free_file (void **mdfile)
{
  MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL;

  if (md){
     if (md->name) fs_give((void **)&md->name);
     fs_give((void **)&md);
  }
}

void
maildir_free_file_only (void **mdfile)
{
  MAILDIRFILE *md = (mdfile && *mdfile) ? (MAILDIRFILE *) *mdfile : NULL;

  if (md && md->name) 
     fs_give((void **)&md->name);
}

int
maildir_any_new_msgs(char *mailbox)
{
  char tmp[MAILTMPLEN];
  int rv = NIL;
  DIR *dir;
  struct direct *d;

  MDFLD(tmp, mailbox, New);

  if (!(dir = opendir (tmp)))
     return rv;

  while ((d = readdir(dir)) != NULL){
    if (d->d_name[0] == '.')
	continue;
    rv = T;
    break;
  }
  closedir(dir);
  return rv;
}


void
maildir_get_date(MAILSTREAM *stream, unsigned long msgno)
{
  MESSAGECACHE *elt;
  struct tm *t;
  time_t ti;
  int i,k;

  elt = mail_elt (stream,msgno);
  if(elt && elt->year != 0)
    return;
  if ((ti = mdfntoul(MDFILE(elt))) > 0L && (t = gmtime(&ti))){
     i = t->tm_hour * 60 + t->tm_min;
     k = t->tm_yday;
     t = localtime(&ti);
     i = t->tm_hour * 60 + t->tm_min - i;
     if((k = t->tm_yday - k) != 0) 
	i += ((k < 0) == (abs (k) == 1)) ? -24*60 : 24*60;
     k = abs (i);
     elt->hours = t->tm_hour; 
     elt->minutes = t->tm_min; 
     elt->seconds = t->tm_sec;
     elt->day = t->tm_mday; elt->month = t->tm_mon + 1;
     elt->year = t->tm_year - (BASEYEAR - 1900);
     elt->zoccident = (k == i) ? 0 : 1;
     elt->zhours = k/60;
     elt->zminutes = k % 60;
  }
}

/* Support for Courier Style directories 
   When this code is complete there will be two types of support, which 
   will be configurable. The problem is the following: In Courier style 
   folder structure, a "folder" may have a subfolder called 
   "folder.subfolder", which is not natural in the file system in the 
   sense that I can not stat for "folder.subfolder" wihtout knowing what 
   "subfolder" is. It needs to be guessed. Because of this I need to look 
   in the list of folders if there is a folder with a name 
   "folder.subfolder", before I can say if the folder is dual or not. One 
   can avoid this annoyance if one ignores the problem by declaring that 
   every folder is dual. I will however code as the default the more 
   complicated idea of scaning the containing directory each time it is 
   modified and search for subfolders, and list the entries it found.
 */

int courier_dir_select (const struct direct *name)
{
 return name->d_name[0] == '.' && (strlen(name->d_name) > 2
	|| (strlen(name->d_name) == 2 &&  name->d_name[1] != '.'));
}

int courier_dir_sort (const struct direct **d1, const struct direct **d2)
{
  const struct direct *e1 = *(const struct direct **) d1;
  const struct direct *e2 = *(const struct direct **) d2;

  return strcmp((char *) e1->d_name, (char *) e2->d_name);
}

void courier_free_cdir (COURIER_S **cdir)
{
  int i;

  if (!*cdir)
     return;

  if ((*cdir)->path) fs_give((void **)&((*cdir)->path));
  for (i = 0; i < (*cdir)->total; i++)
    if((*cdir)->data[i]->name) fs_give((void **)&((*cdir)->data[i]->name));
  fs_give((void **)&((*cdir)->data));
  fs_give((void **)&(*cdir));
}

COURIER_S *courier_get_cdir (int total)
{
 COURIER_S *cdir;

 cdir = (COURIER_S *)fs_get(sizeof(COURIER_S));
 memset(cdir, 0, sizeof(COURIER_S));
 cdir->data = (COURIERLOCAL **) fs_get(total*sizeof(COURIERLOCAL *));
 memset(cdir->data, 0, sizeof(COURIERLOCAL *));
 cdir->total = total;
 return cdir;
}

int courier_search_list(COURIERLOCAL **data, char *name, int first, int last)
{
  int try = (first + last)/2;

  if(!strstr(data[try]->name, name)){
     if(first == try) /* first == last || first + 1 == last */
	return strstr(data[last]->name, name) ? 1 : 0;
     if(strcmp(data[try]->name, name) < 0) /*data[try] < name < data[end] */
	return courier_search_list(data, name, try, last);
     else	/* data[begin] < name < data[try] */
	return courier_search_list(data, name, first, try);
  }
  return 1;
}

/* Lists all directories that are subdirectories of a given directory */

COURIER_S *courier_list_dir(char *curdir)
{
  struct direct **names = NIL;
  struct stat sbuf;
  unsigned long ndir;
  COURIER_S *cdir = NULL;
  char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], pathname[MAILTMPLEN], 
	realname[MAILTMPLEN];
  int i, j, scand, td;

  /* There are two cases, either curdir is 
 	 #mc/INBOX.	 #mc/INBOX.foo
	or
	 #mc/Maildir/. 	 #mc/Maildir/.foo
   */
  strcpy(tmp,curdir + 4);
  if(!strncmp(ucase(tmp), "INBOX", 5))
    strcpy(tmp, "#mc/INBOX.");
  else{
   strcpy(tmp, curdir);
   for (i = strlen(tmp) - 1; tmp[i] && tmp[i] != '/'; i--);
   tmp[i+2] = '\0'; 	/* keep the last "." intact */
  }
  maildir_file_path(tmp, realname, sizeof(realname));
  maildir_scandir (realname, &names, &ndir, &scand, COURIER);

  if (scand > 0){
     cdir = courier_get_cdir(ndir);
     cdir->path = cpystr(realname);
     for(i = 0, j = 0; i < ndir; i++){
        td = realname[strlen(realname) - 1] == '.'
		&& *names[i]->d_name == '.';
	snprintf(tmp2, sizeof(tmp2), "%s%s", tmp, names[i]->d_name+1);
	snprintf(pathname, sizeof(pathname), "%s%s", realname, names[i]->d_name + td);
	if(stat(pathname, &sbuf) == 0 && S_ISDIR(sbuf.st_mode)){
	   cdir->data[j] = (COURIERLOCAL *) fs_get(sizeof(COURIERLOCAL));
	   cdir->data[j++]->name = cpystr(tmp2);
	}
	fs_give((void **)&names[i]);
     }
     cdir->total = j;
     if(cdir->total == 0)
        courier_free_cdir(&cdir);
  }
  if(names)
    fs_give((void **) &names);
  return cdir;
}

void
courier_list_info(COURIER_S **cdirp, char *data, int i)
{
   long style = *(long *) maildir_parameters(GET_COURIERSTYLE, NIL);
   COURIER_S *cdir = *cdirp;

   if(maildir_valid(cdir->data[i]->name)){
      if(courier_search_list(cdir->data, data, 0, cdir->total - 1))
	 cdir->data[i]->attribute = LATT_HASCHILDREN;
      else
	 cdir->data[i]->attribute = (style == COURIER)
				? LATT_HASNOCHILDREN : LATT_NOINFERIORS;
   }
   else
      cdir->data[i]->attribute = LATT_NOSELECT;
      cdir->data[i]->attribute += maildir_any_new_msgs(cdir->data[i]->name) 
					? LATT_MARKED : LATT_UNMARKED;
}

/* UID Support */
/* Yes, I know I procastinated a lot about this, but here it is finally */

/* return code:
   bigger than zero: this session can assign uids
   zero: this session will not assign uid
   smaller than zero: this session temporarily suspends assigning uids 
 */
int
maildir_can_assign_uid (MAILSTREAM *stream)
{
  unsigned int rv = 0;
  int ownuid, existuid;
  unsigned long t;
  char tmp[MAILTMPLEN], tmp2[MAILTMPLEN], *p, *s;
  DIR *dir;
  struct direct *d;

  if(!stream || stream->rdonly 
	|| !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir)))
    return 0;

  if(mypid == (pid_t) 0)
    mypid = getpid();

  snprintf(tmp, sizeof(tmp), "%s.%d", MDUIDTEMP, mypid);

  ownuid = existuid = 0;
  s = NULL;
  while ((d = readdir(dir)) != NULL){
    if(strncmp(d->d_name, tmp, strlen(tmp)) == 0){
	existuid++; ownuid++;
	if(ownuid > 1){
	  snprintf(tmp2, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name);
	  unlink(tmp2);
	  if(s){
	     snprintf(tmp2, sizeof(tmp2), "%s/%s", LOCAL->dir, s);
	     unlink(tmp2);
	     fs_give((void **)&s);
	  }
	}
	else
	  s = cpystr(d->d_name);
    }
    else if(strncmp(d->d_name, MDUIDTEMP, strlen(MDUIDTEMP)) == 0)
        existuid++;
  }

  closedir(dir);
  if(s)
    fs_give((void **)&s);

  if(ownuid == 1 && existuid == 1)
     rv = 1;

  if(ownuid == 0 && existuid == 0){ /* nobody owns the uid? */
    FILE *fp;
    snprintf(tmp, sizeof(tmp), "%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0));
    if(fp = fopen(tmp, "w")){
      fclose(fp);
      if(LOCAL->uidtempfile)
	 fs_give((void **)&LOCAL->uidtempfile);
      LOCAL->uidtempfile = cpystr(tmp);
    }
    rv = 1;
  }

  if(ownuid == 0 && existuid > 0) /* someone else owns uid assignment */
    return 0;

  /* if we own the uid, check that we do not own it more than once
   * or that we share ownership. If any of these situations happens,
   * give up the ownership until we can recover it
   */

  if(ownuid > 0){
    if(ownuid > 1)	/* impossible, two lock files for the same session */
       return (-1)*ownuid;

    if(ownuid != existuid){	/* lock files for different sessions */
      if(LOCAL->uidtempfile){
	 unlink(LOCAL->uidtempfile);
	 fs_give((void **)&LOCAL->uidtempfile);
      }
      return (-1)*ownuid;
    }
  }

  return rv;
}

void
maildir_read_uid(MAILSTREAM *stream, unsigned long *uid_last, 
			unsigned long *uid_validity)
{
  int createuid, deleteuid = 0;
  char tmp[MAILTMPLEN], *s = NULL;
  DIR *dir;
  struct direct *d;

  if(uid_last) *uid_last = 0L;
  if(uid_last && uid_validity) *uid_validity = time(0);
  if(!stream || !LOCAL || !LOCAL->dir || !(dir = opendir(LOCAL->dir)))
    return;

  while ((d = readdir(dir)) != NULL){
      if(!strncmp(d->d_name, MDUIDLAST, strlen(MDUIDLAST)))
       break;
  }
  createuid = d == NULL ? 1 : 0;
  if(uid_last == NULL)
    deleteuid++;
  if(d){
     if(uid_last){
	s = d->d_name + strlen(MDUIDLAST) + 1;
	*uid_last = strtoul(s, &s, 10);
	if(!s || *s != '.'){
	  deleteuid++;
	  createuid++;
	  *uid_last = 0L;
	}
     }
     if(s && *s == '.'){
        if(uid_validity){
	  s++;
	  *uid_validity = strtoul(s, &s, 10);
	  if(s && *s != '\0'){
	    *uid_validity = time(0);
	    deleteuid++;
	    createuid++;
	  }
	}
     }
     else{
	deleteuid++;
	createuid++;
     }
  }
  if(deleteuid){
     snprintf(tmp, sizeof(tmp), "%s/%s", LOCAL->dir, d->d_name);
     unlink(tmp);
  }
  if(createuid)
     maildir_write_uid(stream, (uid_last ? *uid_last : stream->uid_last), 
		uid_validity ? *uid_validity : time(0));
  closedir(dir);
}

void
maildir_write_uid(MAILSTREAM *stream, unsigned long uid_last, 
			unsigned long uid_validity)
{
  char tmp[MAILTMPLEN];
  FILE *fp;

  if(!stream || stream->rdonly || !LOCAL || !LOCAL->dir)
    return;

  snprintf(tmp, sizeof(tmp), "%s/%s.%010lu.%010lu", LOCAL->dir, MDUIDLAST, 
			uid_last, uid_validity);
  if(fp = fopen(tmp, "w"))
     fclose(fp);
}

unsigned long 
maildir_get_uid(char *name)
{
  char *s;
  unsigned long rv = 0L;

  if(!name || (s = strstr(name,MDUIDSEP)) == NULL)
    return rv;

  s += strlen(MDUIDSEP);
  rv = strtoul(s, NULL, 10);
  return rv;
}


void
maildir_delete_uid(MAILSTREAM *stream, unsigned long msgno)
{
  char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t;
  MESSAGECACHE *elt;

  elt = mail_elt(stream, msgno);
  if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir)
    return;

  snprintf(old, sizeof(old), "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt));
  t = MDFILE(elt);
  if(s = strstr(MDFILE(elt), MDUIDSEP)){
     *s = '\0';
     s += strlen(MDUIDSEP);
     strtoul(s, &s, 10);
     snprintf(new, sizeof(new), "%s/%s/%s%s", LOCAL->dir, MDNAME(Cur), t, s);
     if(rename(old, new) == 0){
	maildir_free_file_only ((void **)&elt->private.spare.ptr);
	s = strrchr(new, '/');
	MDFILE(elt) = cpystr(s+1);
     }
     elt->private.uid = 0L;
  }
}

void
maildir_assign_uid(MAILSTREAM *stream, unsigned long msgno, unsigned long uid)
{
  int createuid, deleteuid = 0;
  char old[MAILTMPLEN], new[MAILTMPLEN], *s, *t;
  MESSAGECACHE *elt;

  elt = mail_elt(stream, msgno);
  if(!stream || !elt || !elt->private.spare.ptr || !LOCAL || !LOCAL->dir)
    return;

  maildir_delete_uid(stream, msgno);
  snprintf(old, sizeof(old), "%s/%s/%s", LOCAL->dir, MDNAME(Cur), MDFILE(elt));
  t = MDFILE(elt);
  if((s = strrchr(MDFILE(elt),FLAGSEP)) != NULL){
     *s++ = '\0';
     snprintf(new, sizeof(new), "%s/%s/%s%s%lu%c%s", 
		LOCAL->dir, MDNAME(Cur), t, MDUIDSEP, uid, FLAGSEP, s);
     if(rename(old, new) == 0){
	maildir_free_file_only ((void **)&elt->private.spare.ptr);
	s = strrchr(new, '/');
	MDFILE(elt) = cpystr(s+1);
	stream->uid_validity = time(0);
     }
     elt->private.uid = uid;
  }
}

void
maildir_uid_renew_tempfile(MAILSTREAM *stream)
{
  char tmp[MAILTMPLEN];

  if(!stream || stream->rdonly 
	|| !LOCAL || !LOCAL->candouid || !LOCAL->dir || !LOCAL->uidtempfile)
    return;

  if(mypid == (pid_t) 0)
    mypid = getpid();

  snprintf(tmp, sizeof(tmp), "%s/%s.%d.%lu", LOCAL->dir, MDUIDTEMP, mypid, time(0));
  if(rename(LOCAL->uidtempfile, tmp) == 0){
      fs_give((void **)&LOCAL->uidtempfile);
      LOCAL->uidtempfile = cpystr(tmp);
  }
}
