Re: [EXIM] Bug#23763: exim SMTP handling is strange

Page principale
Supprimer ce message
Répondre à ce message
Auteur: Simon King
Date:  
À: 23763, exim-users
CC: Avery Pennarun
Sujet: Re: [EXIM] Bug#23763: exim SMTP handling is strange
>
> [this is a debian bug report; I'm cc-ing it to the exim-users list]
>

    .....    ......   ......

>
> And that's what you sent as the DATA part of the SMTP session, unaltered.
>
> As far as I can see, exim is doing the best it can with the clearly broken
> input it's receiving.
>


This seems like a good place to mention our recent experiences with
dodgy headers in mail messages:

About a month ago I posted a problem we were having receiving mail with
attachments from our ISP. We have an ISDN connection to our ISP and they
deliver incoming mail for us in a default POP3 account which we then
access via pullmail. The problem was eventually resolved as follows:

-----------------------------------------------------------------------------------

hi,
the problem is caused by the way the maildir format stores messages to
maintain backward compatibility with the berkley format, for example the
berkley mailbox uses an initial From line to separate individual emails,
for example...

>From owen@??? Fri Jun 19 13:03:45 1998

X-UIDL: f7731238b0cf0767b4e0d1db55502f39
Return-path: <owen@???>
etc etc

the Maildir format preserves this part of the header, but as each mail
is
now an individual file it doesnt seem to be needed.

We tested delivering to different smtp apps with and without this
extra header line, Sendmail and Agent didnt seem to worry about it and
mails worked ok, Exim seems to translate this extra line as being the
start of the message body, and so it sends the actual header as part of
the message body, the general opinion was that Exim was interpreting the
rfc's more strictly.

Apparently this line can throw off some pop3 mail programs aswell. There
is currently a pop3 daemon on port 110 and 112 or mail.u-net.com they
both now filter out this problematic line.

As you are now using maildir format aswell attached is a copy of the
maildir.c that we modified to get around the problem.

Paul
____________________________________________________
Paul Kelly              Email:paul@??? (Work)
Network operations      Email:pkelly@??? 
U-NET Internet          http://www.virtual.org.uk/


On Fri, 19 Jun 1998, Simon King wrote:

> nice one.
>
> our incoming mail appears to be ok now. Thanks very much!
>
> did you post this problem, along with your solution, on the exim-users
> mailling list? If not, could you please send me a short note detailing
> how the problem arose and what you had to do to get round it? We may
> need to use a similar sort of setup to forward mail to some of our
> smaller sites and we are keen to avoid running into this problem again!
>
> Once again, many thanks for your help
>
>
> --
> Simon King
> Senior Communications Analyst
> Co-operative Insurance Society
>





/*
 * Program:     Maildir routines
 *
 * Author:      Eric Green
 *              Bloodhounds International Inc.
 *              thrytis@???
 *
 * Additional contributions from:
 *              Aidas Kasparas (kaspar@???)
 *
 * 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@???
 *
 * 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.
 *
 */



#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 <unistd.h>
#include "maildir.h"
#include "rfc822.h"
#include "misc.h"


/* Maildir routines */


/* Driver dispatch used by MAIL */

DRIVER maildirdriver = {
  "maildir",                    /* driver name */
  (DRIVER *) NIL,               /* next driver */
  maildir_valid,                /* mailbox is valid for us */
  maildir_parameters,           /* manipulate parameters */
  maildir_find,                 /* find mailboxes */
  maildir_find_bboards,         /* find bboards */
  maildir_find_all,             /* find all mailboxes */
  maildir_find_all_bboards,     /* find all bboards */
  maildir_subscribe,            /* subscribe to mailbox */
  maildir_unsubscribe,          /* unsubscribe from mailbox */
  maildir_subscribe_bboard,     /* subscribe to bboard */
  maildir_unsubscribe_bboard,   /* unsubscribe from bboard */
  maildir_create,               /* create mailbox */
  maildir_delete,               /* delete mailbox */
  maildir_rename,               /* rename mailbox */
  maildir_open,                 /* open mailbox */
  maildir_close,                /* close mailbox */
  maildir_fetchfast,            /* fetch message "fast" attributes */
  maildir_fetchflags,           /* fetch message flags */
  maildir_fetchstructure,       /* fetch message structure */
  maildir_fetchheader,          /* fetch message header only */
  maildir_fetchtext,            /* fetch message body only */
  maildir_fetchbody,            /* fetch message body section */
  maildir_setflag,              /* set message flag */
  maildir_clearflag,            /* clear message flag */
  maildir_search,               /* search for message based on criteria
*/
  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_move,                 /* move messages to another mailbox */
  maildir_append,               /* append string message to mailbox */
  maildir_gc                    /* garbage collect stream */
};


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


/* Maildir validate mailbox
* Accepts: mailbox name
* Returns: our driver if name is valid, NIL otherwise
*/

DRIVER *maildir_valid (char *name)
{
return maildir_isvalid(name,T) ? &maildirdriver : NIL;
}

/* Maildir test for valid mailbox
 * Accepts: mailbox name
 *          name only test flag
 * Returns: T if valid, NIL otherwise
 */


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) != '/')))
    return NIL;


                                /* 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 (strcpy (tmp,name)), "INBOX") &&
      (stat (maildir_file (tmp,name),&sbuf) == 0) &&
      S_ISDIR (sbuf.st_mode))
    return T;


return NIL;
}


/* Maildir manipulate driver parameters
 * Accepts: function code
 *          function-dependent value
 * Returns: function-dependent return value
 */


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

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


void maildir_find (MAILSTREAM *stream,char *pat)
{
void *s = NIL;
char *t;

                                /* read subscription database */
  if (stream) while (t = sm_read (&s))
    if (pmatch (t,pat) && maildir_isvalid (t,NIL)) mm_mailbox (t);
}


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


void maildir_find_bboards (MAILSTREAM *stream,char *pat)
{
/* always a no-op */
}

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


void maildir_find_all (MAILSTREAM *stream,char *pat)
{
  DIR *dirp;
  struct direct *d;
  char file[MAILTMPLEN];
  char *s,*t;
  int i = 0;
  if (*pat == '{') return;      /* local only */
  if (s = strrchr (pat,'/')) {  /* directory specified in pattern? */
    strncpy (file,pat,i = (++s) - pat);
    file[i] = '\0';             /* tie off prefix */
    t = file;
  }
  else t = myhomedir ();        /* use home directory to search */
  if (dirp = opendir (t)) {     /* now open that directory */
    while (d = readdir (dirp))  /* for each directory entry */
      if ((d->d_name[0] != '.') ||
          (d->d_name[1] && ((d->d_name[1] != '.') || d->d_name[2]))) {
        strcpy (file + i,d->d_name);
        if (pmatch (file,pat)) mm_mailbox (file);
      }
    closedir (dirp);            /* flush directory */
  }
                                /* always an INBOX */
  if (pmatch ("INBOX",pat)) mm_mailbox ("INBOX");
}


/* Maildir find list of all bboards
 * Accepts: mail stream
 *          pattern to search
 */


void maildir_find_all_bboards (MAILSTREAM *stream,char *pat)
{
/* Always a no-op */
}

/* Maildir subscribe to mailbox
 * Accepts: mail stream
 *          mailbox to add to subscription list
 * Returns: T on success, NIL on failure
 */


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


/* Maildir unsubscribe to mailbox
 * Accepts: mail stream
 *          mailbox to delete from subscription list
 * Returns: T on success, NIL on failure
 */


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


/* Maildir subscribe to bboard
 * Accepts: mail stream
 *          bboard to add to subscription list
 * Returns: T on success, NIL on failure
 */


long maildir_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
{
  return NIL;                   /* always fails */
}



/* Maildir unsubscribe to bboard
 * Accepts: mail stream
 *          bboard to delete from subscription list
 * Returns: T on success, NIL on failure
 */


long maildir_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
{
  return NIL;                   /* always fails */
}


/* Maildir create mailbox
 * Accepts: mail stream
 *          mailbox name to create
 *          driver type to use
 * Returns: T on success, NIL on failure
 */


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

                                /* must not already exist */
  if (access (maildir_file (tmp,mailbox),F_OK) == 0) {
    sprintf (err,"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);
  tmp[fnlen - 4] = '\0';        /* making main directory's name */
  fnlen -= 4;


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


  for (i = 0; subdir_names[i]; i++) {
    strcpy (tmp + fnlen,subdir_names[i]);


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


  return T;                     /* return success */
}



/* Maildir delete mailbox
 * Accepts: mail stream
 *          mailbox name to delete
 * Returns: T on success, NIL on failure
 */


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;
  }


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



/* Mail rename mailbox
 * Accepts: mail stream
 *          old mailbox name
 *          new mailbox name
 * Returns: T on success, NIL on failure
 */


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)) {
    sprintf (tmp,"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) {
    sprintf (tmp,"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))) {
    sprintf (tmp,"Can't rename mailbox %s to %s: %s",old,new,strerror
(errno));
    mm_log (tmp,ERROR);
    return NIL;
  }
  return T;                     /* return success */
}


/* Maildir open
* Accepts: stream to open
* Returns: stream on success, NIL on failure
*/

MAILSTREAM *maildir_open (MAILSTREAM *stream)
{
  int fd;
  char tmp[MAILTMPLEN],tmp2[MAILTMPLEN];
  struct stat sbuf;
                                /* OP_PROTOTYPE call */
  if (!stream) return &maildirproto;
  if (LOCAL) {                  /* close old file if stream being
recycled */
    maildir_close (stream);     /* dump and save the changes */
    stream->dtb = &maildirdriver; /* reattach this driver */
    mail_free_cache (stream);   /* clean up cache */
  }


  stream->local = fs_get (sizeof (MAILDIRLOCAL));
                                /* note if an INBOX or not */
  LOCAL->inbox = !strcmp (ucase (strcpy (tmp,stream->mailbox)),"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 = MAXMESSAGESIZE) + 1);
  LOCAL->scantime = 0;          /* not scanned yet */
  LOCAL->hdr = NIL;
  stream->sequence++;           /* bump sequence number */
  stream->nmsgs = stream->recent = 0;


  if (maildir_ping (stream) && !(stream->nmsgs || stream->silent))
    mm_log ("Mailbox is empty",(long) NIL);


  maildir_clean (LOCAL->dir);   /* clean out tmp qmail dir */
  return stream;                /* return stream to caller */
}



/* Maildir close
* Accepts: MAIL stream
*/

void maildir_close (MAILSTREAM *stream)
{
MESSAGECACHE *elt;
int i;
mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);

                                /* clean out the cached paths */
  for (i = 1; i <= stream->nmsgs; i++)
    if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) && elt->data1)
      fs_give ((void **) &elt->data1);


  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 */
  }
}


/* Maildir fetch fast information
 * Accepts: MAIL stream
 *          sequence
 */


void maildir_fetchfast (MAILSTREAM *stream,char *sequence)
{
  long i;
                                /* ugly and slow */
  if (stream && LOCAL && mail_sequence (stream,sequence))
    for (i = 1; i <= stream->nmsgs; i++)
      if (mail_elt (stream,i)->sequence)
        maildir_fetchheader (stream,i);
}



/* Maildir fetch flags
 * Accepts: MAIL stream
 *          sequence
 */


void maildir_fetchflags (MAILSTREAM *stream,char *sequence)
{
  return;                       /* no-op for local mail */
}



/* Maildir fetch envelope
 * Accepts: MAIL stream
 *          message # to fetch
 *          pointer to return body
 * Returns: envelope of this message, body returned in body value
 */


ENVELOPE *maildir_fetchstructure (MAILSTREAM *stream,long msgno,BODY
**body)
{
  char *h,*t;
  LONGCACHE *lelt;
  ENVELOPE **env;
  STRING bs;
  BODY **b;
  if (stream->scache) {         /* short cache */
    if (msgno != stream->msgno){/* flush old poop if a different message
*/
      mail_free_envelope (&stream->env);
      mail_free_body (&stream->body);
    }
    stream->msgno = msgno;
    env = &stream->env;         /* get pointers to envelope and body */
    b = &stream->body;
  }
  else {                        /* long cache */
    lelt = mail_lelt (stream,msgno);
    env = &lelt->env;           /* get pointers to envelope and body */
    b = &lelt->body;
  }
  if ((body && !*b) || !*env) { /* have the poop we need? */
    mail_free_envelope (env);   /* flush old envelope and body */
    mail_free_body (b);
    h = maildir_fetchheader (stream,msgno);
                                /* can't use fetchtext since it'll set
seen */
    t = stream->text ? stream->text : "";
    INIT (&bs,mail_string,(void *) t,strlen (t));
                                /* parse envelope and body */
    rfc822_parse_msg (env,body ? b : NIL,h,strlen (h),&bs,mylocalhost
(),
                      LOCAL->buf);
  }
  if (body) *body = *b;         /* return the body */
  return *env;                  /* return the envelope */
}



/* Maildir fetch message header
 * Accepts: MAIL stream
 *          message # to fetch
 * Returns: message header in RFC822 format
 */


char *maildir_fetchheader (MAILSTREAM *stream,long msgno)
{
unsigned long i,hdrsize;
int fd,strsize=0,hdr_size=0;
char *s,*b,dodgy_line;
char tmp[MAILTMPLEN];
struct stat sbuf;
struct tm *tm;
MESSAGECACHE *elt = mail_elt (stream,msgno);

  if (stream->msgno != msgno)  
  {
    maildir_gc (stream,GC_TEXTS); /* invalidate current cache */
                                /* build message file name */
    sprintf (tmp,"%s/%s",LOCAL->dir,(char *) elt->data1);
    if ((fd = open (tmp,O_RDONLY,NIL)) >= 0) 
    {
      dodgy_line='\0';
      hdr_size=0;
      while(dodgy_line!='\n')
      {
        read(fd,&dodgy_line,1);
        hdr_size++;
      }


      fstat (fd,&sbuf);         /* get size of message */
                                /* make plausible IMAPish date string */
      sbuf.st_size=sbuf.st_size-hdr_size;       


      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
*/
        strcrlfcpy (&LOCAL->hdr,&i,s,hdrsize) +
        strcrlfcpy (&stream->text,&i,b,sbuf.st_size - hdrsize);
      fs_give ((void **) &s);
    }
  }


return LOCAL->hdr ? LOCAL->hdr : "";
}

/* Maildir fetch message text (body only)
 * Accepts: MAIL stream
 *          message # to fetch
 * Returns: message text in RFC822 format
 */


char *maildir_fetchtext (MAILSTREAM *stream,long msgno)
{
  MESSAGECACHE *elt = mail_elt (stream,msgno);
                                /* snarf message if don't have it yet */
  if (stream->msgno != msgno) maildir_fetchheader (stream,msgno);
  if (!elt->seen) maildir_elt_setflag(stream,elt,fSEEN); /* mark as seen
*/
  return stream->text ? stream->text : "";
}



/* Maildir fetch message body as a structure
 * Accepts: Mail stream
 *          message # to fetch
 *          section specifier
 * Returns: pointer to section of message body
 */


char *maildir_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long
*len)
{
  BODY *b;
  PART *pt;
  unsigned long i;
  char *base;
  unsigned long offset = 0;
  MESSAGECACHE *elt = mail_elt (stream,m);
                                /* make sure have a body */
  if (!(maildir_fetchstructure (stream,m,&b) && b && s && *s &&
        ((i = strtol (s,&s,10)) > 0) && (base = maildir_fetchtext
(stream,m))))
    return NIL;
  do {                          /* until find desired body part */
                                /* multipart content? */
    if (b->type == TYPEMULTIPART) {
      pt = b->contents.part;    /* yes, find desired part */
      while (--i && (pt = pt->next));
      if (!pt) return NIL;      /* bad specifier */
                                /* note new body, check valid nesting */
      if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
      offset = pt->offset;      /* get new offset */
    }
    else if (i != 1) return NIL;/* otherwise must be section 1 */
                                /* need to go down further? */
    if (i = *s) switch (b->type) {
    case TYPEMESSAGE:           /* embedded message */
      offset = b->contents.msg.offset;
      b = b->contents.msg.body; /* get its body, drop into multipart
case */
    case TYPEMULTIPART:         /* multipart, get next section */
      if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
    default:                    /* bogus subpart specification */
      return NIL;
    }
  } while (i);
                                /* lose if body bogus */
  if ((!b) || b->type == TYPEMULTIPART) return NIL;
  if (!elt->seen) maildir_elt_setflag(stream,elt,fSEEN); /* mark as seen
*/
  return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
                          b->size.ibytes,b->encoding);
}



/* Maildir set flag
 * Accepts: MAIL stream
 *          sequence
 *          flag(s)
 */


void maildir_setflag (MAILSTREAM *stream,char *sequence,char *flag)
{
  MESSAGECACHE *elt;
  long i;
  short f = maildir_getflags (stream,flag);
  if (!f) return;               /* no-op if no flags to modify */
                                /* get sequence and loop on it */


  if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs;
i++)
    if ((elt = mail_elt (stream,i))->sequence)
                                /* set all requested flags */
      maildir_elt_setflag (stream,elt,f);
}



/* Maildir clear flag
 * Accepts: MAIL stream
 *          sequence
 *          flag(s)
 */


void maildir_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
{
  MESSAGECACHE *elt;
  long i = stream->nmsgs;
  int changed = NIL;
  short f = maildir_getflags (stream,flag);
  if (!f) return;               /* no-op if no flags to modify */
                                /* get sequence and loop on it */
  if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs;
i++)
    if ((elt = mail_elt (stream,i))->sequence) {
                                /* clear all requested flags */
      if (f&fSEEN && elt->seen) {
        elt->seen = NIL;
        changed = T;
      }
      if (f&fDELETED && elt->deleted) {
        elt->deleted = NIL;
        changed = T;
      }
      if (f&fFLAGGED && elt->flagged) {
        elt->flagged = NIL;
        changed = T;
      }
      if (f&fANSWERED && elt->answered) {
        elt->answered = NIL;
        changed = T;
      }


      if (changed)              /* write new flags to disk */
        maildir_write_flags (stream,elt);
    }
}


/* Maildir search for messages
 * Accepts: MAIL stream
 *          search criteria
 */


void maildir_search (MAILSTREAM *stream,char *criteria)
{
  long i,n;
  char *d;
  search_t f;
                                /* initially all searched */
  for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched =
T;
                                /* get first criterion */
  if (criteria && (criteria = strtok (criteria," "))) {
                                /* for each criterion */
    for (; criteria; (criteria = strtok (NIL," "))) {
      f = NIL; d = NIL; n = 0;  /* init then scan the criterion */
      switch (*ucase (criteria)) {
      case 'A':                 /* possible ALL, ANSWERED */
        if (!strcmp (criteria+1,"LL")) f = maildir_search_all;
        else if (!strcmp (criteria+1,"NSWERED")) f =
maildir_search_answered;
        break;
      case 'B':                 /* possible BCC, BEFORE, BODY */
        if (!strcmp (criteria+1,"CC"))
          f = maildir_search_string (maildir_search_bcc,&d,&n);
        else if (!strcmp (criteria+1,"EFORE"))
          f = maildir_search_date (maildir_search_before,&n);
        else if (!strcmp (criteria+1,"ODY"))
          f = maildir_search_string (maildir_search_body,&d,&n);
        break;
      case 'C':                 /* possible CC */
        if (!strcmp (criteria+1,"C")) 
          f = maildir_search_string (maildir_search_cc,&d,&n);
        break;
      case 'D':                 /* possible DELETED */
        if (!strcmp (criteria+1,"ELETED")) f = maildir_search_deleted;
        break;
      case 'F':                 /* possible FLAGGED, FROM */
        if (!strcmp (criteria+1,"LAGGED")) f = maildir_search_flagged;
        else if (!strcmp (criteria+1,"ROM"))
          f = maildir_search_string (maildir_search_from,&d,&n);
        break;
      case 'K':                 /* possible KEYWORD */
        if (!strcmp (criteria+1,"EYWORD"))
          f = maildir_search_flag (maildir_search_keyword,&d);
        break;
      case 'N':                 /* possible NEW */
        if (!strcmp (criteria+1,"EW")) f = maildir_search_new;
        break;


      case 'O':                 /* possible OLD, ON */
        if (!strcmp (criteria+1,"LD")) f = maildir_search_old;
        else if (!strcmp (criteria+1,"N"))
          f = maildir_search_date (maildir_search_on,&n);
        break;
      case 'R':                 /* possible RECENT */
        if (!strcmp (criteria+1,"ECENT")) f = maildir_search_recent;
        break;
      case 'S':                 /* possible SEEN, SINCE, SUBJECT */
        if (!strcmp (criteria+1,"EEN")) f = maildir_search_seen;
        else if (!strcmp (criteria+1,"INCE"))
          f = maildir_search_date (maildir_search_since,&n);
        else if (!strcmp (criteria+1,"UBJECT"))
          f = maildir_search_string (maildir_search_subject,&d,&n);
        break;
      case 'T':                 /* possible TEXT, TO */
        if (!strcmp (criteria+1,"EXT"))
          f = maildir_search_string (maildir_search_text,&d,&n);
        else if (!strcmp (criteria+1,"O"))
          f = maildir_search_string (maildir_search_to,&d,&n);
        break;
      case 'U':                 /* possible UN* */
        if (criteria[1] == 'N') {
          if (!strcmp (criteria+2,"ANSWERED")) f =
maildir_search_unanswered;
          else if (!strcmp (criteria+2,"DELETED")) f =
maildir_search_undeleted;
          else if (!strcmp (criteria+2,"FLAGGED")) f =
maildir_search_unflagged;
          else if (!strcmp (criteria+2,"KEYWORD"))
            f = maildir_search_flag (maildir_search_unkeyword,&d);
          else if (!strcmp (criteria+2,"SEEN")) f =
maildir_search_unseen;
        }
        break;
      default:                  /* we will barf below */
        break;
      }
      if (!f) {                 /* if can't determine any criteria */
        sprintf (LOCAL->buf,"Unknown search criterion: %s",criteria);
        mm_log (LOCAL->buf,ERROR);
        return;
      }
                                /* run the search criterion */
      for (i = 1; i <= stream->nmsgs; ++i)
        if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
          mail_elt (stream,i)->searched = NIL;
    }
                                /* report search results to main program
*/
    for (i = 1; i <= stream->nmsgs; ++i)
      if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  }
}



/* Maildir ping mailbox
* Accepts: MAIL stream
* Returns: T if stream alive, else NIL
* No-op for readonly files, since read/writer can expunge it from under
us!
*
* Note: this depends on qmail naming of messages
*/

long maildir_ping (MAILSTREAM *stream)
{
char tmp[MAILTMPLEN];
MESSAGECACHE *elt;
struct stat sbuf;
DIR *dir;
struct direct *d;
int reloadall = NIL;
long i;
long nmsgs = stream->nmsgs;
long recent = 0;
char *s;
mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);

maildir_copynew (LOCAL->dir);

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


  if (sbuf.st_ctime != LOCAL->scantime) {
    /* update the message list */
    struct direct **names = NIL;
    nmsgs = scandir (LOCAL->dir,&names,maildir_select,maildir_namesort);


    mm_critical (stream);       /* go critical */
    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)->data1,
                  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->data1);
          mail_expunged (stream,i);
        }
        else
          i++;


      mm_log ("Warning: Mailbox has changed in an unexpected way. 
Reloading.",
              WARN);
      stream->nmsgs = 0;
    }


    for (i = stream->nmsgs; i < nmsgs; i++) {
                                /* if newly seen, add to list */
      (elt = mail_elt (stream, i + 1))->data1 = (long) cpystr
(names[i]->d_name);
      elt->valid = T;
      elt->recent = T;
      recent++;


      /* grab the flags */
      if ((s = strstr (names[i]->d_name,":2,"))) {
        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;
      }
    }
    mm_nocritical (stream);     /* release critical */
                                /* free the names stuff */
    for (i = 0; i < nmsgs; i++)
      fs_give ((void **) &names[i]);
    if (names)
      fs_give ((void **) &names);
  }
  mail_exists (stream,nmsgs);   /* notify upper level of mailbox size */
  if (!reloadall) mail_recent (stream,recent);
  return T;                     /* return that we are alive */
}



/* Maildir check mailbox
* Accepts: MAIL stream
* No-op for readonly files, since read/writer can expunge it from under
us!
*/

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


/* Maildir expunge mailbox
* Accepts: MAIL stream
*/

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) {
      sprintf (LOCAL->buf,"%s/%s",LOCAL->dir,(char *) elt->data1);
      if (unlink (LOCAL->buf)) {/* try to delete the message */
        sprintf (LOCAL->buf,"Expunge of message %ld failed, aborted:
%s",i,
                 strerror (errno));
        mm_log (LOCAL->buf,WARN);
        break;
      }
                                /* free the cached filename */
      if (elt->data1) fs_give ((void **) &elt->data1);
      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 */
    sprintf (LOCAL->buf,"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);
}


/* Maildir copy message(s)
 * Accepts: MAIL stream
 *          sequence
 *          destination mailbox
 * Returns: T if copy successful, else NIL
 */


long maildir_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
{
  STRING st;
  MESSAGECACHE *elt;
  struct stat sbuf;
  int fd;
  long i;
  char lptr;
  char *s,tmp[MAILTMPLEN];
                                /* copy the messages */
  if (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->data1);
      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);
      sprintf (LOCAL->buf,"%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 */
}



/* Maildir move message(s)
 * Accepts: MAIL stream
 *          sequence
 *          destination mailbox
 * Returns: T if move successful, else NIL
 */


long maildir_move (MAILSTREAM *stream,char *sequence,char *mailbox)
{
  MESSAGECACHE *elt;
  if (maildir_copy (stream,sequence,mailbox)) return NIL;
                                /* delete all requested messages */
  maildir_setflag (stream,sequence,"\\Deleted");
  return T;
}


/* Maildir append message string
 * Accepts: mail stream
 *          destination mailbox
 *          optional flags
 *          optional date
 *          stringstruct of message to append
 * Returns: T on success, NIL on failure
 */


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? */
                                /* yes, parse date into an elt */
    if (!mail_parse_date (&elt,date)) {
      sprintf (tmp,"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)) {
    sprintf (tmp,"Not a valid Maildir mailbox: %s",mailbox);
    mm_log (tmp,ERROR);
    return NIL;
  }
                                /* build file name we will use */
  sprintf (file,"%u.%d.%09u.%s:2,%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 */
  sprintf (path1,"%s/../tmp/%s",maildir_file (tmp,mailbox),file);


  if ((fd = open (path1,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) < 0)
{
    sprintf (tmp,"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 */
    sprintf (tmp,"Message append failed: %s",strerror (errno));
    mm_log (tmp,ERROR);
    ret = NIL;
  }
                                /* build final filename to use */
  sprintf (path2,"%s/../new/%s",maildir_file (tmp,mailbox),file);
  if (link (path1,path2) < 0) {
    sprintf (tmp,"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;
}



/* Maildir garbage collect stream
 * Accepts: mail stream
 *          garbage collection flags
 */


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 */
  }
}


/* Internal routines */


/* Maildir file name selection test
* Accepts: candidate directory entry
* Returns: T to use file name, NIL to skip it
*/

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


return NIL;
}


/* Maildir file name comparision
 * Accepts: first candidate directory entry
 *          second candidate directory entry
 * Returns: negative if d1 < d2, 0 if d1 == d2, postive if d1 > d2
 */


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


/* Maildir mail generate file string
 * Accepts: temporary buffer to write into (at least MAILTMPLEN long)
 *          mailbox name string
 * Returns: local 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;


  if (*name == '/') {           /* if absolute path given, just append
/cur */
    strncpy (dst, name, MAILTMPLEN - 2);
    strncat (dst, "/cur", MAILTMPLEN - 2);
    dst[MAILTMPLEN - 1] = '\0';
  }
  else
    sprintf (dst,"%s/%s/cur",myhomedir (),
            strcmp (ucase (strcpy (tmp, name)), "INBOX") ? name :
MAILDIRPATH);


return dst;
}


/* Parse flag list
 * Accepts: MAIL stream
 *          flag list as a character string
 * Returns: flag command list
 */


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

/* Maildir clean out tmpdir
* Accepts: the INBOX mailbox
* Returns: nothing
*/

void maildir_clean (const char *mailbox)
{
DIR *dir;
struct direct *d;
time_t t;
struct stat sbuf;
char tmpname[MAILTMPLEN],file[MAILTMPLEN];

t = time (0);

  sprintf (tmpname,"%s/../tmp",mailbox);
  if (!(dir = opendir (tmpname)))
    return;


  while (d = readdir (dir)) {
    if (!strcmp (d->d_name,".")) continue;
    if (!strcmp (d->d_name,"..")) continue;
    sprintf (file,"%s/%s",tmpname,d->d_name);
    if (stat (file,&sbuf) == 0)
      if (t > sbuf.st_atime + 129600)
        unlink (file);          /* delete the file */
  }
  closedir (dir);
}



/* Maildir copy in new mail
* Accepts: the INBOX mailbox
* Returns: nothing
*/

void maildir_copynew (const char *mailbox)
{
char tmp[MAILTMPLEN],file[MAILTMPLEN],newfile[MAILTMPLEN];
DIR *dir;
struct dirent *d;
struct stat sbuf;

  sprintf (tmp,"%s/../new",mailbox);
  if (!(dir = opendir (tmp)))
    return;


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


    sprintf (file,"%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,":2,")) /* this message already has flags */
        sprintf (newfile,"%s/%s",mailbox,d->d_name);
      else
        sprintf (newfile,"%s/%s:2,",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);
}



/* Maildir set flags on message
 * Accepts: MAIL stream
 *          message cache
 *          bitvector of flags to set
 * Returns: nothing
 */


void maildir_elt_setflag (MAILSTREAM *stream,MESSAGECACHE *elt,char
flags)
{
int changed = NIL;

  if (!flags) return;           /* no-op if no flags to modify */


                                /* set all requested flags */
  if (flags&fSEEN && !elt->seen) {
    elt->seen = T;
    changed = T;
  }
  if (flags&fDELETED && !elt->deleted) {
    elt->deleted = T;
    changed = T;
  }
  if (flags&fFLAGGED && !elt->flagged) {
    elt->flagged = T;
    changed = T;
  }
  if (flags&fANSWERED && !elt->answered) {
    elt->answered = T;
    changed = T;
  }


  if (changed)
    maildir_write_flags (stream,elt);
}



/* Maildir write flags on message to disk
 * Accepts: MAIL stream
 *          message cache
 * Returns: nothing
 */


void maildir_write_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
{
char oldfile[MAILTMPLEN],newfile[MAILTMPLEN],fn[MAILTMPLEN];
char *s;

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



/* Search support routines
 * Accepts: MAIL stream
 *          message number
 *          pointer to additional data
 *          pointer to temporary buffer
 * Returns: T if search matches, else NIL
 */


char maildir_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
{
  return T;                     /* ALL always succeeds */
}



char maildir_search_answered (MAILSTREAM *stream,long msgno,char *d,long
n)
{
return mail_elt (stream,msgno)->answered ? T : NIL;
}


char maildir_search_deleted (MAILSTREAM *stream,long msgno,char *d,long
n)
{
return mail_elt (stream,msgno)->deleted ? T : NIL;
}


char maildir_search_flagged (MAILSTREAM *stream,long msgno,char *d,long
n)
{
return mail_elt (stream,msgno)->flagged ? T : NIL;
}


char maildir_search_keyword (MAILSTREAM *stream,long msgno,char *d,long
n)
{
  return NIL;                   /* keywords not supported yet */
}



char maildir_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
{
MESSAGECACHE *elt = mail_elt (stream,msgno);
return (elt->recent && !elt->seen) ? T : NIL;
}

char maildir_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
{
return mail_elt (stream,msgno)->recent ? NIL : T;
}


char maildir_search_recent (MAILSTREAM *stream,long msgno,char *d,long
n)
{
return mail_elt (stream,msgno)->recent ? T : NIL;
}


char maildir_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
{
return mail_elt (stream,msgno)->seen ? T : NIL;
}


char maildir_search_unanswered (MAILSTREAM *stream,long msgno,char
*d,long n)
{
return mail_elt (stream,msgno)->answered ? NIL : T;
}


char maildir_search_undeleted (MAILSTREAM *stream,long msgno,char
*d,long n)
{
return mail_elt (stream,msgno)->deleted ? NIL : T;
}


char maildir_search_unflagged (MAILSTREAM *stream,long msgno,char
*d,long n)
{
return mail_elt (stream,msgno)->flagged ? NIL : T;
}


char maildir_search_unkeyword (MAILSTREAM *stream,long msgno,char
*d,long n)
{
  return T;                     /* keywords not supported yet */
}



char maildir_search_unseen (MAILSTREAM *stream,long msgno,char *d,long
n)
{
return mail_elt (stream,msgno)->seen ? NIL : T;
}

char maildir_search_before (MAILSTREAM *stream,long msgno,char *d,long
n)
{
return (char) (maildir_msgdate (stream,msgno) < n);
}


char maildir_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
{
return (char) (maildir_msgdate (stream,msgno) == n);
}


char maildir_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
{
                                /* everybody interprets "since" as .GE.
*/
  return (char) (maildir_msgdate (stream,msgno) >= n);
}



unsigned long maildir_msgdate (MAILSTREAM *stream,long msgno)
{
  struct stat sbuf;
  struct tm *tm;
  MESSAGECACHE *elt = mail_elt (stream,msgno);
  if (!elt->day) {              /* get date if don't have it yet */
                                /* build message file name */
    sprintf (LOCAL->buf,"%s/%s",LOCAL->dir,(char *) elt->data1);
    stat (LOCAL->buf,&sbuf);    /* get message date */
    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;
  }
  return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
}


char maildir_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
{
maildir_fetchheader (stream,msgno);
return stream->text ? search (stream->text,strlen (stream->text),d,n)
: NIL;
}


char maildir_search_subject (MAILSTREAM *stream,long msgno,char *d,long
n)
{
char *t = maildir_fetchstructure (stream,msgno,NIL)->subject;
return t ? search (t,strlen (t),d,n) : NIL;
}


char maildir_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
{
  char *t = maildir_fetchheader (stream,msgno);
  return (t && search (t,strlen (t),d,n)) ||
    maildir_search_body (stream,msgno,d,n);
}


char maildir_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
{
  ADDRESS *a = maildir_fetchstructure (stream,msgno,NIL)->bcc;
  LOCAL->buf[0] = '\0';         /* initially empty string */
                                /* get text for address */
  rfc822_write_address (LOCAL->buf,a);
  return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
}



char maildir_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
{
  ADDRESS *a = maildir_fetchstructure (stream,msgno,NIL)->cc;
  LOCAL->buf[0] = '\0';         /* initially empty string */
                                /* get text for address */
  rfc822_write_address (LOCAL->buf,a);
  return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
}



char maildir_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
{
  ADDRESS *a = maildir_fetchstructure (stream,msgno,NIL)->from;
  LOCAL->buf[0] = '\0';         /* initially empty string */
                                /* get text for address */
  rfc822_write_address (LOCAL->buf,a);
  return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
}



char maildir_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
{
  ADDRESS *a = maildir_fetchstructure (stream,msgno,NIL)->to;
  LOCAL->buf[0] = '\0';         /* initially empty string */
                                /* get text for address */
  rfc822_write_address (LOCAL->buf,a);
  return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
}


/* Search parsers */


/* Parse a date
 * Accepts: function to return
 *          pointer to date integer to return
 * Returns: function to return
 */


search_t maildir_search_date (search_t f,long *n)
{
  long i;
  char *s;
  MESSAGECACHE elt;
                                /* parse the date and return fn if OK */
  return (maildir_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
          (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f :
NIL;
}


/* Parse a flag
 * Accepts: function to return
 *          pointer to string to return
 * Returns: function to return
 */


search_t maildir_search_flag (search_t f,char **d)
{
                                /* get a keyword, return if OK */
  return (*d = strtok (NIL," ")) ? f : NIL;
}



/* Parse a string
 * Accepts: function to return
 *          pointer to string to return
 *          pointer to string length to return
 * Returns: function to return
 */


search_t maildir_search_string (search_t f,char **d,long *n)
{
  char *end = " ";
  char *c = strtok (NIL,"");    /* remainder of criteria */
  if (!c) return NIL;           /* missing argument */
  switch (*c) {                 /* see what the argument is */
  case '{':                     /* literal string */
    *n = strtol (c+1,d,10);     /* get its length */
    if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012')
&&
        (!(*(c = *d + *n)) || (*c == ' '))) {
      char e = *--c;
      *c = DELIM;               /* make sure not a space */
      strtok (c," ");           /* reset the strtok mechanism */
      *c = e;                   /* put character back */
      break;
    }
  case '\0':                    /* catch bogons */
  case ' ':
    return NIL;
  case '"':                     /* quoted string */
    if (strchr (c+1,'"')) end = "\"";
    else return NIL;
  default:                      /* atomic string */
    if (*d = strtok (c,end)) *n = strlen (*d);
    else return NIL;
    break;
  }
  return f;
}


###################################################################################


--
Simon King
Senior Communications Analyst
Co-operative Insurance Society

--
*** Exim information can be found at http://www.exim.org/ ***