imapext-2007

diff src/mailutil/mailutil.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/mailutil/mailutil.c	Mon Sep 14 15:17:45 2009 +0900
     1.3 @@ -0,0 +1,942 @@
     1.4 +/* ========================================================================
     1.5 + * Copyright 1988-2008 University of Washington
     1.6 + *
     1.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.8 + * you may not use this file except in compliance with the License.
     1.9 + * You may obtain a copy of the License at
    1.10 + *
    1.11 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.12 + *
    1.13 + * 
    1.14 + * ========================================================================
    1.15 + */
    1.16 +
    1.17 +/*
    1.18 + * Program:	Mail utility
    1.19 + *
    1.20 + * Author:	Mark Crispin
    1.21 + *		UW Technology
    1.22 + *		University of Washington
    1.23 + *		Seattle, WA  98195
    1.24 + *		Internet: MRC@Washington.EDU
    1.25 + *
    1.26 + * Date:	2 February 1994
    1.27 + * Last Edited:	19 February 2008
    1.28 + */
    1.29 +
    1.30 +
    1.31 +#include <stdio.h>
    1.32 +#include <errno.h>
    1.33 +extern int errno;		/* just in case */
    1.34 +#include "c-client.h"
    1.35 +#ifdef SYSCONFIG		/* defined in env_unix.h */
    1.36 +#include <pwd.h>
    1.37 +#endif
    1.38 +
    1.39 +/* Globals */
    1.40 +
    1.41 +char *version = "13";		/* edit number */
    1.42 +int debugp = NIL;		/* flag saying debug */
    1.43 +int verbosep = NIL;		/* flag saying verbose */
    1.44 +int rwcopyp = NIL;		/* flag saying readwrite copy (for POP) */
    1.45 +int kwcopyp = NIL;		/* flag saying keyword copy */
    1.46 +int ignorep = NIL;		/* flag saying ignore keywords */
    1.47 +int critical = NIL;		/* flag saying in critical code */
    1.48 +int trycreate = NIL;		/* [TRYCREATE] seen */
    1.49 +char *suffix = NIL;		/* suffer merge mode suffix text */
    1.50 +int ddelim = -1;		/* destination delimiter */
    1.51 +FILE *f = NIL;
    1.52 +
    1.53 +/* Usage strings */
    1.54 +
    1.55 +char *usage2 = "usage: %s %s\n\n%s\n";
    1.56 +char *usage3 = "usage: %s %s %s\n\n%s\n";
    1.57 +char *usgchk = "check [MAILBOX]";
    1.58 +char *usgcre = "create MAILBOX";
    1.59 +char *usgdel = "delete MAILBOX";
    1.60 +char *usgren = "rename SOURCE DESTINATION";
    1.61 +char *usgcpymov = "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION";
    1.62 +char *usgappdel = "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION";
    1.63 +char *usgprn = "prune mailbox SEARCH_CRITERIA";
    1.64 +char *usgxfr = "transfer [-rw[copy]] [-kw[copy]] [-ig[nore]] [-m[erge] m] SOURCE DEST";
    1.65 +#ifdef SYSCONFIG
    1.66 +char *stdsw = "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]] [-u[ser] userid] [--]";
    1.67 +#else
    1.68 +char *stdsw = "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]]";
    1.69 +#endif
    1.70 +
    1.71 +/* Merge modes */
    1.72 +
    1.73 +#define mPROMPT 1
    1.74 +#define mAPPEND 2
    1.75 +#define mSUFFIX 3
    1.76 +
    1.77 +
    1.78 +/* Function prototypes */
    1.79 +
    1.80 +void ms_init (STRING *s,void *data,unsigned long size);
    1.81 +char ms_next (STRING *s);
    1.82 +void ms_setpos (STRING *s,unsigned long i);
    1.83 +int main (int argc,char *argv[]);
    1.84 +SEARCHPGM *prune_criteria (char *criteria);
    1.85 +int prune_criteria_number (unsigned long *number,char **r);
    1.86 +int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del,
    1.87 +	     int mode);
    1.88 +long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date,
    1.89 +		STRING **message);
    1.90 +
    1.91 +
    1.92 +/* Append package */
    1.93 +
    1.94 +typedef struct append_package {
    1.95 +  MAILSTREAM *stream;		/* source stream */
    1.96 +  unsigned long msgno;		/* current message number */
    1.97 +  unsigned long msgmax;		/* maximum message number */
    1.98 +  char *flags;			/* current flags */
    1.99 +  char *date;			/* message internal date */
   1.100 +  STRING *message;		/* stringstruct of message */
   1.101 +} APPENDPACKAGE;
   1.102 +
   1.103 +
   1.104 +/* Message string driver for message stringstructs */
   1.105 +
   1.106 +STRINGDRIVER mstring = {
   1.107 +  ms_init,			/* initialize string structure */
   1.108 +  ms_next,			/* get next byte in string structure */
   1.109 +  ms_setpos			/* set position in string structure */
   1.110 +};
   1.111 +
   1.112 +/* Initialize file string structure for file stringstruct
   1.113 + * Accepts: string structure
   1.114 + *	    pointer to message data structure
   1.115 + *	    size of string
   1.116 + */
   1.117 +
   1.118 +void ms_init (STRING *s,void *data,unsigned long size)
   1.119 +{
   1.120 +  APPENDPACKAGE *md = (APPENDPACKAGE *) data;
   1.121 +  s->data = data;		/* note stream/msgno and header length */
   1.122 +  mail_fetch_header (md->stream,md->msgno,NIL,NIL,&s->data1,
   1.123 +		     FT_PREFETCHTEXT|FT_PEEK);
   1.124 +#if 0
   1.125 +  s->size = size;		/* message size */
   1.126 +#else	/* This kludge is necessary because of broken IMAP servers (sigh!) */
   1.127 +  mail_fetch_text (md->stream,md->msgno,NIL,&s->size,FT_PEEK);
   1.128 +  s->size += s->data1;		/* header + body size */
   1.129 +#endif
   1.130 +  SETPOS (s,0);
   1.131 +}
   1.132 +
   1.133 +
   1.134 +/* Get next character from file stringstruct
   1.135 + * Accepts: string structure
   1.136 + * Returns: character, string structure chunk refreshed
   1.137 + */
   1.138 +
   1.139 +char ms_next (STRING *s)
   1.140 +{
   1.141 +  char c = *s->curpos++;	/* get next byte */
   1.142 +  SETPOS (s,GETPOS (s));	/* move to next chunk */
   1.143 +  return c;			/* return the byte */
   1.144 +}
   1.145 +
   1.146 +
   1.147 +/* Set string pointer position for file stringstruct
   1.148 + * Accepts: string structure
   1.149 + *	    new position
   1.150 + */
   1.151 +
   1.152 +void ms_setpos (STRING *s,unsigned long i)
   1.153 +{
   1.154 +  APPENDPACKAGE *md = (APPENDPACKAGE *) s->data;
   1.155 +  if (i < s->data1) {		/* want header? */
   1.156 +    s->chunk = mail_fetch_header (md->stream,md->msgno,NIL,NIL,NIL,FT_PEEK);
   1.157 +    s->chunksize = s->data1;	/* header length */
   1.158 +    s->offset = 0;		/* offset is start of message */
   1.159 +  }
   1.160 +  else if (i < s->size) {	/* want body */
   1.161 +    s->chunk = mail_fetch_text (md->stream,md->msgno,NIL,NIL,FT_PEEK);
   1.162 +    s->chunksize = s->size - s->data1;
   1.163 +    s->offset = s->data1;	/* offset is end of header */
   1.164 +  }
   1.165 +  else {			/* off end of message */
   1.166 +    s->chunk = NIL;		/* make sure that we crack on this then */
   1.167 +    s->chunksize = 1;		/* make sure SNX cracks the right way... */
   1.168 +    s->offset = i;
   1.169 +  }
   1.170 +				/* initial position and size */
   1.171 +  s->curpos = s->chunk + (i -= s->offset);
   1.172 +  s->cursize = s->chunksize - i;
   1.173 +}
   1.174 +
   1.175 +/* Main program */
   1.176 +
   1.177 +int main (int argc,char *argv[])
   1.178 +{
   1.179 +  MAILSTREAM *source = NIL;
   1.180 +  MAILSTREAM *dest = NIL;
   1.181 +  SEARCHPGM *criteria;
   1.182 +  char c,*s,*dp,*t,*t1,tmp[MAILTMPLEN],mbx[MAILTMPLEN];
   1.183 +  unsigned long m,len,curlen,start,last;
   1.184 +  int i;
   1.185 +  int merge = NIL;
   1.186 +  int retcode = 1;
   1.187 +  int moreswitchp = T;
   1.188 +  char *cmd = NIL;
   1.189 +  char *src = NIL;
   1.190 +  char *dst = NIL;
   1.191 +  char *pgm = argc ? argv[0] : "mailutil";
   1.192 +#include "linkage.c"
   1.193 +  for (i = 1; i < argc; i++) {
   1.194 +    s = argv[i];		/* pick up argument */
   1.195 +				/* parse switches */
   1.196 +    if (moreswitchp && (*s == '-')) {
   1.197 +      if (!strcmp (s,"-debug") || !strcmp (s,"-d")) debugp = T;
   1.198 +      else if (!strcmp (s,"-verbose") || !strcmp (s,"-v")) verbosep = T;
   1.199 +      else if (!strcmp (s,"-rwcopy") || !strcmp (s,"-rw")) rwcopyp = T;
   1.200 +      else if (!strcmp (s,"-kwcopy") || !strcmp (s,"-kw")) kwcopyp = T;
   1.201 +      else if (!strcmp (s,"-ignore") || !strcmp (s,"-ig")) ignorep = T;
   1.202 +      else if ((!strcmp (s,"-merge") || !strcmp (s,"-m")) && (++i < argc)) {
   1.203 +	if (!strcmp (s = argv[i],"prompt")) merge = mPROMPT;
   1.204 +	else if (!strcmp (s,"append")) merge = mAPPEND;
   1.205 +	else if (!strncmp (s,"suffix=",7) && s[7]) {
   1.206 +	  merge = mSUFFIX;
   1.207 +	  suffix = cpystr (s+7);
   1.208 +	}
   1.209 +	else {
   1.210 +	  printf ("unknown merge option: %s\n",s);
   1.211 +	  exit (retcode);
   1.212 +	}
   1.213 +      }
   1.214 +
   1.215 +#ifdef SYSCONFIG
   1.216 +      else if ((!strcmp (s,"-user") || !strcmp (s,"-u")) && (++i < argc)) {
   1.217 +	struct passwd *pw = getpwnam (s = argv[i]);
   1.218 +	if (!pw) {
   1.219 +	  printf ("unknown user id: %s\n",argv[i]);
   1.220 +	  exit (retcode);
   1.221 +	}
   1.222 +	else if (setuid (pw->pw_uid)) {
   1.223 +	  perror ("unable to change user id");
   1.224 +	  exit (retcode);
   1.225 +	}
   1.226 +      }
   1.227 +#endif
   1.228 +				/* -- means no more switches, so mailbox
   1.229 +				   name can start with "-" */
   1.230 +      else if ((s[1] == '-') && !s[2]) moreswitchp = NIL;
   1.231 +      else {
   1.232 +	printf ("unknown switch: %s\n",s);
   1.233 +	exit (retcode);
   1.234 +      }
   1.235 +    }
   1.236 +    else if (!cmd) cmd = s;	/* first non-switch is command */
   1.237 +    else if (!src) src = s;	/* second non-switch is source */
   1.238 +    else if (!dst) dst = s;	/* third non-switch is destination */
   1.239 +    else {
   1.240 +      printf ("unknown argument: %s\n",s);
   1.241 +      exit (retcode);
   1.242 +    }
   1.243 +  }
   1.244 +  if (kwcopyp && ignorep) {
   1.245 +    puts ("-kwcopy and -ignore are mutually exclusive");
   1.246 +    exit (retcode);
   1.247 +  }
   1.248 +  if (!cmd) cmd = "";		/* prevent SEGV */
   1.249 +
   1.250 +  if (!strcmp (cmd,"check")) {	/* check for new messages */
   1.251 +    if (!src) src = "INBOX";
   1.252 +    if (dst || merge || rwcopyp || kwcopyp || ignorep)
   1.253 +      printf (usage2,pgm,usgchk,stdsw);
   1.254 +    else if (mail_status (source = (*src == '{') ?
   1.255 +			  mail_open (NIL,src,OP_HALFOPEN |
   1.256 +				     (debugp ? OP_DEBUG : NIL)) : NIL,
   1.257 +			  src,SA_MESSAGES | SA_RECENT | SA_UNSEEN))
   1.258 +      retcode = 0;
   1.259 +  }
   1.260 +  else if (!strcmp (cmd,"create")) {
   1.261 +    if (!src || dst || merge || rwcopyp || kwcopyp || ignorep)
   1.262 +      printf (usage2,pgm,usgcre,stdsw);
   1.263 +    else if (mail_create (source = (*src == '{') ?
   1.264 +			  mail_open (NIL,src,OP_HALFOPEN |
   1.265 +				     (debugp ? OP_DEBUG : NIL)) : NIL,src))
   1.266 +      retcode = 0;
   1.267 +  }
   1.268 +  else if (!strcmp (cmd,"delete")) {
   1.269 +    if (!src || dst || merge || rwcopyp || kwcopyp || ignorep)
   1.270 +      printf (usage2,pgm,usgdel,stdsw);
   1.271 +    else if (mail_delete (source = (*src == '{') ?
   1.272 +			  mail_open (NIL,src,OP_HALFOPEN |
   1.273 +				     (debugp ? OP_DEBUG : NIL)) : NIL,src))
   1.274 +      retcode = 0;
   1.275 +  }
   1.276 +  else if (!strcmp (cmd,"rename")) {
   1.277 +    if (!src || !dst || merge || rwcopyp || kwcopyp || ignorep)
   1.278 +      printf (usage2,pgm,usgren,stdsw);
   1.279 +    else if (mail_rename (source = (*src == '{') ?
   1.280 +			  mail_open (NIL,src,OP_HALFOPEN |
   1.281 +				     (debugp ? OP_DEBUG : NIL)) : NIL,src,dst))
   1.282 +      retcode = 0;
   1.283 +  }
   1.284 +
   1.285 +  else if ((i = !strcmp (cmd,"move")) || !strcmp (cmd,"copy")) {
   1.286 +    if (!src || !dst || merge) printf (usage3,pgm,cmd,usgcpymov,stdsw);
   1.287 +    else if (source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) |
   1.288 +				 (debugp ? OP_DEBUG : NIL))) {
   1.289 +      dest = NIL;		/* open destination stream if network */
   1.290 +      if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN |
   1.291 +					      (debugp ? OP_DEBUG : NIL)))) {
   1.292 +	if (mbxcopy (source,dest,dst,T,i,merge)) retcode = 0;
   1.293 +      }
   1.294 +    }
   1.295 +  }
   1.296 +  else if ((i = !strcmp (cmd,"appenddelete")) || !strcmp (cmd,"append")) {
   1.297 +    if (!src || !dst || merge) printf (usage3,pgm,cmd,usgappdel,stdsw);
   1.298 +    else if (source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) |
   1.299 +				 (debugp ? OP_DEBUG : NIL))) {
   1.300 +      dest = NIL;		/* open destination stream if network */
   1.301 +      if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN |
   1.302 +					      (debugp ? OP_DEBUG : NIL)))) {
   1.303 +	if (mbxcopy (source,dest,dst,NIL,i,merge)) retcode = 0;
   1.304 +      }
   1.305 +    }
   1.306 +  }
   1.307 +
   1.308 +  else if (!strcmp (cmd,"prune")) {
   1.309 +    if (!src || !dst || merge || rwcopyp || kwcopyp || ignorep ||
   1.310 +	!(criteria = prune_criteria (dst))) printf (usage2,pgm,usgprn,stdsw);
   1.311 +    else if ((source = mail_open (NIL,src,(debugp ? OP_DEBUG : NIL))) &&
   1.312 +	     mail_search_full (source,NIL,criteria,SE_FREE)) {
   1.313 +      for (m = 1, s = t = NIL, len = start = last = 0; m <= source->nmsgs; m++)
   1.314 +	if (mail_elt (source,m)->searched) {
   1.315 +	  if (s) {		/* continuing a range? */
   1.316 +	    if (m == last + 1) last = m;
   1.317 +	    else {		/* no, end of previous range? */
   1.318 +	      if (last != start) sprintf (t,":%lu,%lu",last,m);
   1.319 +				/* no, just this message */
   1.320 +	      else sprintf (t,",%lu",m);
   1.321 +	      start = last = m;	/* either way, start new range */
   1.322 +				/* running out of space? */
   1.323 +	      if ((len - (curlen = (t += strlen (t)) - s)) < 20) {
   1.324 +		fs_resize ((void **) &s,len += MAILTMPLEN);
   1.325 +		t = s + curlen;	/* relocate current pointer */
   1.326 +	      }
   1.327 +	    }
   1.328 +	  }
   1.329 +	  else {		/* first time, start new buffer */
   1.330 +	    s = (char *) fs_get (len = MAILTMPLEN);
   1.331 +	    sprintf (s,"%lu",start = last = m);
   1.332 +	    t = s + strlen (s);	/* end of buffer */
   1.333 +	  }
   1.334 +	}
   1.335 +				/* finish last range if necessary */
   1.336 +      if (last != start) sprintf (t,":%lu",last);
   1.337 +      if (s) {			/* delete/expunge any matching messages */
   1.338 +	mail_flag (source,s,"\\Deleted",ST_SET);
   1.339 +	m = source->nmsgs;	/* get number of messages before purge */
   1.340 +	mail_expunge (source);
   1.341 +	printf ("%lu message(s) purged\n",m - source->nmsgs);
   1.342 +	fs_give ((void **) &s);	/* flush buffer */
   1.343 +      }
   1.344 +      else puts ("No matching messages, so nothing purged");
   1.345 +      source = mail_close (source);
   1.346 +    }
   1.347 +  }
   1.348 +
   1.349 +  else if (!strcmp (cmd,"transfer")) {
   1.350 +    if (!src || !dst) printf (usage2,pgm,usgxfr,stdsw);
   1.351 +    else if ((*src == '{') &&	/* open source mailbox */
   1.352 +	     !(source = mail_open (NIL,src,OP_HALFOPEN |
   1.353 +				   (debugp ? OP_DEBUG : NIL))));
   1.354 +    else if ((*dst == '{') &&	/* open destination server */
   1.355 +	     !(dest = mail_open (NIL,dst,OP_HALFOPEN |
   1.356 +				 (debugp ? OP_DEBUG : NIL))));
   1.357 +    else if (!(f = tmpfile ())) puts ("can't open temporary file");
   1.358 +    else {
   1.359 +      if (verbosep) puts ("Listing mailboxes...");
   1.360 +      if (dest) strcpy (strchr (strcpy (tmp,dest->mailbox),'}') + 1,
   1.361 +			dp = strchr (dst,'}') + 1);
   1.362 +      else {
   1.363 +	dp = dst;
   1.364 +	tmp[0] = '\0';
   1.365 +      }
   1.366 +      mail_list (dest,tmp,"");
   1.367 +      rewind (f);		/* list all mailboxes matching prefix */
   1.368 +      if (ddelim < 0) {		/* if server failed to give delimiter */
   1.369 +	puts ("warning: unable to get destination hierarchy delimiter!");
   1.370 +	ddelim = 0;		/* default to none */
   1.371 +      }
   1.372 +      if (source) strcpy (strchr (strcpy (tmp,source->mailbox),'}') + 1,
   1.373 +			  strchr (src,'}') + 1);
   1.374 +      else strcpy (tmp,src);
   1.375 +      mail_list (source,tmp,"*");
   1.376 +      rewind (f);
   1.377 +				/* read back mailbox names */
   1.378 +      for (retcode = 0; !retcode && (fgets (tmp,MAILTMPLEN-1,f)); ) {
   1.379 +	if (t = strchr (tmp+1,'\n')) *t = '\0';
   1.380 +	for (t = mbx,t1 = dest ? dest->mailbox : "",c = NIL; (c != '}') && *t1;
   1.381 +	     *t++ = c= *t1++);
   1.382 +	for (t1 = dp; *t1; *t++ = *t1++);
   1.383 +				/* point to name without delim or netspec */
   1.384 +	t1 = source ? (strchr (tmp+1,'}') + 1) : tmp + 1;
   1.385 +				/* src and mbx have different delimiters? */
   1.386 +	if (ddelim && (ddelim != tmp[0]))
   1.387 +	  while (c = *t1++) {	/* swap delimiters then */
   1.388 +	    if (c == ddelim) c = tmp[0] ? tmp[0] : 'x';
   1.389 +	    else if (c == tmp[0]) c = ddelim;
   1.390 +	    *t++ = c;
   1.391 +	  }
   1.392 +				/* easy case */
   1.393 +	else while (*t1) *t++ = *t1++;
   1.394 +	*t++ = '\0';
   1.395 +	if (verbosep) {
   1.396 +	  printf ("Copying %s\n  => %s\n",tmp+1,mbx);
   1.397 +	  fflush (stdout);
   1.398 +	}
   1.399 +	if (source = mail_open (source,tmp+1,(debugp ? OP_DEBUG : NIL) | 
   1.400 +				(rwcopyp ? NIL : OP_READONLY))) {
   1.401 +	  if (!mbxcopy (source,dest,mbx,T,NIL,merge)) retcode = 1;
   1.402 +	  if (source->dtb->flags & DR_LOCAL) source = mail_close (source);
   1.403 +	}
   1.404 +	else printf ("can't open source mailbox %s\n",tmp+1);
   1.405 +      }
   1.406 +    }
   1.407 +  }
   1.408 +
   1.409 +  else {
   1.410 +    printf ("%s version %s.%s\n\n",pgm,CCLIENTVERSION,version);
   1.411 +    printf (usage2,pgm,"command [switches] arguments",stdsw);
   1.412 +    printf ("\nCommands:\n %s\n",usgchk);
   1.413 +    puts   ("   ;; report number of messages and new messages");
   1.414 +    printf (" %s\n",usgcre);
   1.415 +    puts   ("   ;; create new mailbox");
   1.416 +    printf (" %s\n",usgdel);
   1.417 +    puts   ("   ;; delete existing mailbox");
   1.418 +    printf (" %s\n",usgren);
   1.419 +    puts   ("   ;; rename mailbox to a new name");
   1.420 +    printf (" copy %s\n",usgcpymov);
   1.421 +    printf (" move %s\n",usgcpymov);
   1.422 +    puts   ("   ;; create new mailbox and copy/move messages");
   1.423 +    printf (" append %s\n",usgappdel);
   1.424 +    printf (" appenddelete %s\n",usgappdel);
   1.425 +    puts   ("   ;; copy/move messages to existing mailbox");
   1.426 +    printf (" %s\n",usgprn);
   1.427 +    puts   ("   ;; prune mailbox of messages matching criteria");
   1.428 +    printf (" %s\n",usgxfr);
   1.429 +    puts   ("   ;; copy source hierarchy to destination");
   1.430 +    puts   ("   ;;  -merge modes are prompt, append, or suffix=xxxx");
   1.431 +  }
   1.432 +				/* close streams */
   1.433 +  if (source) mail_close (source);
   1.434 +  if (dest) mail_close (dest);
   1.435 +  exit (retcode);
   1.436 +  return retcode;		/* stupid compilers */
   1.437 +}
   1.438 +
   1.439 +/* Pruning criteria, somewhat extended from mail_criteria()
   1.440 + * Accepts: criteria
   1.441 + * Returns: search program if parse successful, else NIL
   1.442 + */
   1.443 +
   1.444 +SEARCHPGM *prune_criteria (char *criteria)
   1.445 +{
   1.446 +  SEARCHPGM *pgm = NIL;
   1.447 +  char *criterion,*r,tmp[MAILTMPLEN];
   1.448 +  int f;
   1.449 +  if (criteria) {		/* only if criteria defined */
   1.450 +				/* make writeable copy of criteria */
   1.451 +    criteria = cpystr (criteria);
   1.452 +				/* for each criterion */
   1.453 +    for (pgm = mail_newsearchpgm (), criterion = strtok_r (criteria," ",&r);
   1.454 +	 criterion; (criterion = strtok_r (NIL," ",&r))) {
   1.455 +      f = NIL;			/* init then scan the criterion */
   1.456 +      switch (*ucase (criterion)) {
   1.457 +      case 'A':			/* possible ALL, ANSWERED */
   1.458 +	if (!strcmp (criterion+1,"LL")) f = T;
   1.459 +	else if (!strcmp (criterion+1,"NSWERED")) f = pgm->answered = T;
   1.460 +	break;
   1.461 +      case 'B':			/* possible BCC, BEFORE, BODY */
   1.462 +	if (!strcmp (criterion+1,"CC"))
   1.463 +	  f = mail_criteria_string (&pgm->bcc,&r);
   1.464 +	else if (!strcmp (criterion+1,"EFORE"))
   1.465 +	  f = mail_criteria_date (&pgm->before,&r);
   1.466 +	else if (!strcmp (criterion+1,"ODY"))
   1.467 +	  f = mail_criteria_string (&pgm->body,&r);
   1.468 +	break;
   1.469 +      case 'C':			/* possible CC */
   1.470 +	if (!strcmp (criterion+1,"C")) f = mail_criteria_string (&pgm->cc,&r);
   1.471 +	break;
   1.472 +      case 'D':			/* possible DELETED, DRAFT */
   1.473 +	if (!strcmp (criterion+1,"ELETED")) f = pgm->deleted = T;
   1.474 +	else if (!strcmp (criterion+1,"RAFT")) f = pgm->draft = T;
   1.475 +	break;
   1.476 +      case 'F':			/* possible FLAGGED, FROM */
   1.477 +	if (!strcmp (criterion+1,"LAGGED")) f = pgm->flagged = T;
   1.478 +	else if (!strcmp (criterion+1,"ROM"))
   1.479 +	  f = mail_criteria_string (&pgm->from,&r);
   1.480 +	break;
   1.481 +      case 'K':			/* possible KEYWORD */
   1.482 +	if (!strcmp (criterion+1,"EYWORD"))
   1.483 +	  f = mail_criteria_string (&pgm->keyword,&r);
   1.484 +	break;
   1.485 +      case 'L':			/* possible LARGER */
   1.486 +	if (!strcmp (criterion+1,"ARGER"))
   1.487 +	  f = prune_criteria_number (&pgm->larger,&r);
   1.488 +
   1.489 +      case 'N':			/* possible NEW */
   1.490 +	if (!strcmp (criterion+1,"EW")) f = pgm->recent = pgm->unseen = T;
   1.491 +	break;
   1.492 +      case 'O':			/* possible OLD, ON */
   1.493 +	if (!strcmp (criterion+1,"LD")) f = pgm->old = T;
   1.494 +	else if (!strcmp (criterion+1,"N"))
   1.495 +	  f = mail_criteria_date (&pgm->on,&r);
   1.496 +	break;
   1.497 +      case 'R':			/* possible RECENT */
   1.498 +	if (!strcmp (criterion+1,"ECENT")) f = pgm->recent = T;
   1.499 +	break;
   1.500 +      case 'S':			/* possible SEEN, SENT*, SINCE, SMALLER,
   1.501 +				   SUBJECT */
   1.502 +	if (!strcmp (criterion+1,"EEN")) f = pgm->seen = T;
   1.503 +	else if (!strncmp (criterion+1,"ENT",3)) {
   1.504 +	  if (!strcmp (criterion+4,"BEFORE"))
   1.505 +	    f = mail_criteria_date (&pgm->sentbefore,&r);
   1.506 +	  else if (!strcmp (criterion+4,"ON"))
   1.507 +	    f = mail_criteria_date (&pgm->senton,&r);
   1.508 +	  else if (!strcmp (criterion+4,"SINCE"))
   1.509 +	    f = mail_criteria_date (&pgm->sentsince,&r);
   1.510 +	}
   1.511 +	else if (!strcmp (criterion+1,"INCE"))
   1.512 +	  f = mail_criteria_date (&pgm->since,&r);
   1.513 +	else if (!strcmp (criterion+1,"MALLER"))
   1.514 +	  f = prune_criteria_number (&pgm->smaller,&r);
   1.515 +	else if (!strcmp (criterion+1,"UBJECT"))
   1.516 +	  f = mail_criteria_string (&pgm->subject,&r);
   1.517 +	break;
   1.518 +      case 'T':			/* possible TEXT, TO */
   1.519 +	if (!strcmp (criterion+1,"EXT"))
   1.520 +	  f = mail_criteria_string (&pgm->text,&r);
   1.521 +	else if (!strcmp (criterion+1,"O"))
   1.522 +	  f = mail_criteria_string (&pgm->to,&r);
   1.523 +	break;
   1.524 +      case 'U':			/* possible UN* */
   1.525 +	if (criterion[1] == 'N') {
   1.526 +	  if (!strcmp (criterion+2,"ANSWERED")) f = pgm->unanswered = T;
   1.527 +	  else if (!strcmp (criterion+2,"DELETED")) f = pgm->undeleted = T;
   1.528 +	  else if (!strcmp (criterion+2,"DRAFT")) f = pgm->undraft = T;
   1.529 +	  else if (!strcmp (criterion+2,"FLAGGED")) f = pgm->unflagged = T;
   1.530 +	  else if (!strcmp (criterion+2,"KEYWORD"))
   1.531 +	    f = mail_criteria_string (&pgm->unkeyword,&r);
   1.532 +	  else if (!strcmp (criterion+2,"SEEN")) f = pgm->unseen = T;
   1.533 +	}
   1.534 +	break;
   1.535 +      default:			/* we will barf below */
   1.536 +	break;
   1.537 +      }
   1.538 +
   1.539 +      if (!f) {			/* if can't identify criterion */
   1.540 +	sprintf (tmp,"Unknown search criterion: %.30s",criterion);
   1.541 +	MM_LOG (tmp,ERROR);
   1.542 +	mail_free_searchpgm (&pgm);
   1.543 +	break;
   1.544 +      }
   1.545 +    }
   1.546 +				/* no longer need copy of criteria */
   1.547 +    fs_give ((void **) &criteria);
   1.548 +  }
   1.549 +  return pgm;
   1.550 +}
   1.551 +
   1.552 +
   1.553 +/* Parse a number
   1.554 + * Accepts: pointer to integer to return
   1.555 + *	    pointer to strtok state
   1.556 + * Returns: T if successful, else NIL
   1.557 + */
   1.558 +
   1.559 +int prune_criteria_number (unsigned long *number,char **r)
   1.560 +{
   1.561 +  char *t;
   1.562 +  STRINGLIST *s = NIL;
   1.563 +				/* parse the date and return fn if OK */
   1.564 +  int ret = (mail_criteria_string (&s,r) &&
   1.565 +	     (*number = strtoul ((char *) s->text.data,&t,10)) && !*t) ?
   1.566 +	       T : NIL;
   1.567 +  if (s) mail_free_stringlist (&s);
   1.568 +  return ret;
   1.569 +}
   1.570 +
   1.571 +/* Copy mailbox
   1.572 + * Accepts: stream open on source
   1.573 + *	    halfopen stream for destination or NIL
   1.574 + *	    destination mailbox name
   1.575 + *	    non-zero to create destination mailbox
   1.576 + *	    non-zero to delete messages from source after copying
   1.577 + *	    merge mode
   1.578 + * Returns: T if success, NIL if error
   1.579 + */
   1.580 +
   1.581 +int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del,
   1.582 +	     int mode)
   1.583 +{
   1.584 +  char *s,tmp[MAILTMPLEN];
   1.585 +  APPENDPACKAGE ap;
   1.586 +  STRING st;
   1.587 +  char *ndst = NIL;
   1.588 +  int ret = NIL;
   1.589 +  trycreate = NIL;		/* no TRYCREATE yet */
   1.590 +  if (create) while (!mail_create (dest,dst) && (mode != mAPPEND)) {
   1.591 +    switch (mode) {
   1.592 +    case mPROMPT:		/* prompt user for new name */
   1.593 +      tmp[0] = '\0';
   1.594 +      while (!tmp[0]) {		/* read name */
   1.595 +	fputs ("alternative name: ",stdout);
   1.596 +	fflush (stdout);
   1.597 +	fgets (tmp,MAILTMPLEN-1,stdin);
   1.598 +	if (s = strchr (tmp,'\n')) *s = '\0';
   1.599 +      }
   1.600 +      if (ndst) fs_give ((void **) &ndst);
   1.601 +      ndst = cpystr (tmp);
   1.602 +      break;
   1.603 +    case mSUFFIX:		/* try again with new suffix */
   1.604 +      if (ndst) fs_give ((void **) &ndst);
   1.605 +      sprintf (ndst = (char *) fs_get (strlen (dst) + strlen (suffix) + 1),
   1.606 +	       "%s%s",dst,suffix);
   1.607 +      printf ("retry to create %s\n",ndst);
   1.608 +      mode = mPROMPT;		/* switch to prompt mode if name fails */
   1.609 +      break;
   1.610 +    case NIL:			/* not merging */
   1.611 +      return NIL;
   1.612 +    }
   1.613 +    if (ndst) dst = ndst;	/* if alternative name given, use it */
   1.614 +  }
   1.615 +
   1.616 +  if (kwcopyp) {
   1.617 +    int i;
   1.618 +    size_t len;
   1.619 +    char *dummymsg = "Date: Thu, 18 May 2006 00:00 -0700\r\nFrom: dummy@example.com\r\nSubject: dummy\r\n\r\ndummy\r\n";
   1.620 +    for (i = 0,len = 0; i < NUSERFLAGS; ++i)
   1.621 +      if (source->user_flags[i]) len += strlen (source->user_flags[i]) + 1;
   1.622 +    if (len) {			/* easy if no user flags to copy... */
   1.623 +      char *t;
   1.624 +      char *tail = "\\Deleted)";
   1.625 +      char *flags = (char *) fs_get (1 + len + strlen (tail) + 1);
   1.626 +      s = flags; *s++ = '(';
   1.627 +      for (i = 0; i < NUSERFLAGS; ++i) if (t = source->user_flags[i]) {
   1.628 +	while (*t) *s++ = *t++;
   1.629 +	*s++ = ' ';
   1.630 +      }
   1.631 +      strcpy (s,tail);		/* terminate flags list */
   1.632 +      if ((dst[0] == '#') && ((dst[1] == 'D') || (dst[1] == 'd')) &&
   1.633 +	  ((dst[2] == 'R') || (dst[2] == 'r')) &&
   1.634 +	  ((dst[3] == 'I') || (dst[3] == 'i')) &&
   1.635 +	  ((dst[4] == 'V') || (dst[4] == 'v')) &&
   1.636 +	  ((dst[5] == 'E') || (dst[5] == 'e')) &&
   1.637 +	  ((dst[6] == 'R') || (dst[6] == 'r')) && (dst[7] == '.') &&
   1.638 +	  (t = strchr (dst+8,'/'))) ++t;
   1.639 +      else t = dst;
   1.640 +      INIT (&st,mail_string,dummymsg,strlen (dummymsg));
   1.641 +      if (!(mail_append (dest,dst,&st) &&
   1.642 +	    (dest = mail_open (dest,t,debugp ? OP_DEBUG : NIL)))) {
   1.643 +	fs_give ((void **) &flags);
   1.644 +	return NIL;
   1.645 +      }
   1.646 +      mail_setflag (dest,"*",flags);
   1.647 +      mail_expunge (dest);
   1.648 +      fs_give ((void **) &flags);
   1.649 +    }
   1.650 +  }
   1.651 +
   1.652 +  if (source->nmsgs) {		/* non-empty source */
   1.653 +    if (verbosep) printf ("%s [%lu message(s)] => %s\n",
   1.654 +			      source->mailbox,source->nmsgs,dst);
   1.655 +    ap.stream = source;		/* prepare append package */
   1.656 +    ap.msgno = 0;
   1.657 +    ap.msgmax = source->nmsgs;
   1.658 +    ap.flags = ap.date = NIL;
   1.659 +    ap.message = &st;
   1.660 +				/* make sure we have all messages */
   1.661 +    sprintf (tmp,"1:%lu",ap.msgmax);
   1.662 +    mail_fetchfast (source,tmp);
   1.663 +    if (mail_append_multiple (dest,dst,mm_append,(void *) &ap)) {
   1.664 +      --ap.msgno;		/* make sure user knows it won */
   1.665 +      if (verbosep) printf ("[Ok %lu messages(s)]\n",ap.msgno);
   1.666 +      if (del && ap.msgno) {	/* delete source messages */
   1.667 +	sprintf (tmp,"1:%lu",ap.msgno);
   1.668 +	mail_flag (source,tmp,"\\Deleted",ST_SET);
   1.669 +				/* flush moved messages */
   1.670 +	mail_expunge (source);
   1.671 +      }
   1.672 +      ret = T;
   1.673 +    }
   1.674 +    else if ((mode == mAPPEND) && trycreate)
   1.675 +      ret = mbxcopy (source,dest,dst,create,del,mPROMPT);
   1.676 +    else if (verbosep) puts ("[Failed]");
   1.677 +  }
   1.678 +  else {			/* empty source */
   1.679 +    if (verbosep) printf ("%s [empty] => %s\n",source->mailbox,dst);
   1.680 +    ret = T;
   1.681 +  }
   1.682 +  if (ndst) fs_give ((void **) &ndst);
   1.683 +  return ret;
   1.684 +}
   1.685 +
   1.686 +/* Append callback
   1.687 + * Accepts: mail stream
   1.688 + *	    append package
   1.689 + *	    pointer to return flags
   1.690 + *	    pointer to return date
   1.691 + *	    pointer to return message stringstruct
   1.692 + * Returns: T on success
   1.693 + */
   1.694 +
   1.695 +long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date,
   1.696 +		STRING **message)
   1.697 +{
   1.698 +  char *t,*t1,tmp[MAILTMPLEN];
   1.699 +  unsigned long u;
   1.700 +  MESSAGECACHE *elt;
   1.701 +  APPENDPACKAGE *ap = (APPENDPACKAGE *) data;
   1.702 +  *flags = *date = NIL;		/* assume no flags or date */
   1.703 +  if (ap->flags) fs_give ((void **) &ap->flags);
   1.704 +  if (ap->date) fs_give ((void **) &ap->date);
   1.705 +  mail_gc (ap->stream,GC_TEXTS);
   1.706 +  if (++ap->msgno <= ap->msgmax) {
   1.707 +				/* initialize flag string */
   1.708 +    memset (t = tmp,0,MAILTMPLEN);
   1.709 +				/* output system flags */
   1.710 +    if ((elt = mail_elt (ap->stream,ap->msgno))->seen) strcat (t," \\Seen");
   1.711 +    if (elt->deleted) strcat (t," \\Deleted");
   1.712 +    if (elt->flagged) strcat (t," \\Flagged");
   1.713 +    if (elt->answered) strcat (t," \\Answered");
   1.714 +    if (elt->draft) strcat (t," \\Draft");
   1.715 +				/* any user flags? */
   1.716 +    if (!ignorep && (u = elt->user_flags)) do
   1.717 +      if ((t1 = ap->stream->user_flags[find_rightmost_bit (&u)]) &&
   1.718 +	  (MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long) (2 + strlen (t1))){
   1.719 +	*t++ = ' ';		/* space delimiter */
   1.720 +	strcpy (t,t1);		/* copy the user flag */
   1.721 +      }
   1.722 +    while (u);			/* until no more user flags */
   1.723 +    *flags = ap->flags = cpystr (tmp + 1);
   1.724 +    *date = ap->date = cpystr (mail_date (tmp,elt));
   1.725 +    *message = ap->message;	/* message stringstruct */
   1.726 +    INIT (ap->message,mstring,(void *) ap,elt->rfc822_size);
   1.727 +  }
   1.728 +  else *message = NIL;		/* all done */
   1.729 +  return LONGT;
   1.730 +}
   1.731 +
   1.732 +/* Co-routines from MAIL library */
   1.733 +
   1.734 +
   1.735 +/* Message matches a search
   1.736 + * Accepts: MAIL stream
   1.737 + *	    message number
   1.738 + */
   1.739 +
   1.740 +void mm_searched (MAILSTREAM *stream,unsigned long msgno)
   1.741 +{
   1.742 +				/* dummy routine */
   1.743 +}
   1.744 +
   1.745 +
   1.746 +/* Message exists (i.e. there are that many messages in the mailbox)
   1.747 + * Accepts: MAIL stream
   1.748 + *	    message number
   1.749 + */
   1.750 +
   1.751 +void mm_exists (MAILSTREAM *stream,unsigned long number)
   1.752 +{
   1.753 +				/* dummy routine */
   1.754 +}
   1.755 +
   1.756 +
   1.757 +/* Message expunged
   1.758 + * Accepts: MAIL stream
   1.759 + *	    message number
   1.760 + */
   1.761 +
   1.762 +void mm_expunged (MAILSTREAM *stream,unsigned long number)
   1.763 +{
   1.764 +				/* dummy routine */
   1.765 +}
   1.766 +
   1.767 +
   1.768 +/* Message flags update seen
   1.769 + * Accepts: MAIL stream
   1.770 + *	    message number
   1.771 + */
   1.772 +
   1.773 +void mm_flags (MAILSTREAM *stream,unsigned long number)
   1.774 +{
   1.775 +				/* dummy routine */
   1.776 +}
   1.777 +
   1.778 +/* Mailbox found
   1.779 + * Accepts: MAIL stream
   1.780 + *	    hierarchy delimiter
   1.781 + *	    mailbox name
   1.782 + *	    mailbox attributes
   1.783 + */
   1.784 +
   1.785 +void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
   1.786 +{
   1.787 +				/* note destination delimiter */
   1.788 +  if (ddelim < 0) ddelim = delimiter;
   1.789 +				/* if got a selectable name */
   1.790 +  else if (!(attributes & LATT_NOSELECT) && *name)
   1.791 +    fprintf (f,"%c%s\n",delimiter,name);
   1.792 +}
   1.793 +
   1.794 +
   1.795 +/* Subscribe mailbox found
   1.796 + * Accepts: MAIL stream
   1.797 + *	    hierarchy delimiter
   1.798 + *	    mailbox name
   1.799 + *	    mailbox attributes
   1.800 + */
   1.801 +
   1.802 +void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
   1.803 +{
   1.804 +				/* dummy routine */
   1.805 +}
   1.806 +
   1.807 +
   1.808 +/* Mailbox status
   1.809 + * Accepts: MAIL stream
   1.810 + *	    mailbox name
   1.811 + *	    mailbox status
   1.812 + */
   1.813 +
   1.814 +void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
   1.815 +{
   1.816 +  if (status->recent || status->unseen)
   1.817 +    printf ("%lu new message(s) (%lu unseen),",status->recent,status->unseen);
   1.818 +  else fputs ("No new messages,",stdout);
   1.819 +  printf (" %lu total in %s\n",status->messages,mailbox);
   1.820 +}
   1.821 +
   1.822 +/* Notification event
   1.823 + * Accepts: MAIL stream
   1.824 + *	    string to log
   1.825 + *	    error flag
   1.826 + */
   1.827 +
   1.828 +void mm_notify (MAILSTREAM *stream,char *string,long errflg)
   1.829 +{
   1.830 +  if (!errflg && (string[0] == '[') &&
   1.831 +      ((string[1] == 'T') || (string[1] == 't')) &&
   1.832 +      ((string[2] == 'R') || (string[2] == 'r')) &&
   1.833 +      ((string[3] == 'Y') || (string[3] == 'y')) &&
   1.834 +      ((string[4] == 'C') || (string[4] == 'c')) &&
   1.835 +      ((string[5] == 'R') || (string[5] == 'r')) &&
   1.836 +      ((string[6] == 'E') || (string[6] == 'e')) &&
   1.837 +      ((string[7] == 'A') || (string[7] == 'a')) &&
   1.838 +      ((string[8] == 'T') || (string[8] == 't')) &&
   1.839 +      ((string[9] == 'E') || (string[9] == 'e')) &&
   1.840 +      (string[10] == ']'))
   1.841 +    trycreate = T;  
   1.842 +  mm_log (string,errflg);	/* just do mm_log action */
   1.843 +}
   1.844 +
   1.845 +
   1.846 +/* Log an event for the user to see
   1.847 + * Accepts: string to log
   1.848 + *	    error flag
   1.849 + */
   1.850 +
   1.851 +void mm_log (char *string,long errflg)
   1.852 +{
   1.853 +  switch (errflg) {  
   1.854 +  case BYE:
   1.855 +  case NIL:			/* no error */
   1.856 +    if (verbosep) fprintf (stderr,"[%s]\n",string);
   1.857 +    break;
   1.858 +  case PARSE:			/* parsing problem */
   1.859 +  case WARN:			/* warning */
   1.860 +    fprintf (stderr,"warning: %s\n",string);
   1.861 +    break;
   1.862 +  case ERROR:			/* error */
   1.863 +  default:
   1.864 +    fprintf (stderr,"%s\n",string);
   1.865 +    break;
   1.866 +  }
   1.867 +}
   1.868 +
   1.869 +
   1.870 +/* Log an event to debugging telemetry
   1.871 + * Accepts: string to log
   1.872 + */
   1.873 +
   1.874 +void mm_dlog (char *string)
   1.875 +{
   1.876 +  fprintf (stderr,"%s\n",string);
   1.877 +}
   1.878 +
   1.879 +/* Get user name and password for this host
   1.880 + * Accepts: parse of network mailbox name
   1.881 + *	    where to return user name
   1.882 + *	    where to return password
   1.883 + *	    trial count
   1.884 + */
   1.885 +
   1.886 +void mm_login (NETMBX *mb,char *username,char *password,long trial)
   1.887 +{
   1.888 +  char *s,tmp[MAILTMPLEN];
   1.889 +  sprintf (s = tmp,"{%s/%s",mb->host,mb->service);
   1.890 +  if (*mb->user) sprintf (tmp+strlen (tmp),"/user=%s",
   1.891 +			  strcpy (username,mb->user));
   1.892 +  if (*mb->authuser) sprintf (tmp+strlen (tmp),"/authuser=%s",mb->authuser);
   1.893 +  if (*mb->user) strcat (s = tmp,"} password:");
   1.894 +  else {
   1.895 +    printf ("%s} username: ",tmp);
   1.896 +    fgets (username,NETMAXUSER-1,stdin);
   1.897 +    username[NETMAXUSER-1] = '\0';
   1.898 +    if (s = strchr (username,'\n')) *s = '\0';
   1.899 +    s = "password: ";
   1.900 +  }
   1.901 +  strcpy (password,getpass (s));
   1.902 +}
   1.903 +
   1.904 +
   1.905 +/* About to enter critical code
   1.906 + * Accepts: stream
   1.907 + */
   1.908 +
   1.909 +void mm_critical (MAILSTREAM *stream)
   1.910 +{
   1.911 +  critical = T;			/* note in critical code */
   1.912 +}
   1.913 +
   1.914 +
   1.915 +/* About to exit critical code
   1.916 + * Accepts: stream
   1.917 + */
   1.918 +
   1.919 +void mm_nocritical (MAILSTREAM *stream)
   1.920 +{
   1.921 +  critical = NIL;		/* note not in critical code */
   1.922 +}
   1.923 +
   1.924 +
   1.925 +/* Disk error found
   1.926 + * Accepts: stream
   1.927 + *	    system error code
   1.928 + *	    flag indicating that mailbox may be clobbered
   1.929 + * Returns: T if user wants to abort
   1.930 + */
   1.931 +
   1.932 +long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
   1.933 +{
   1.934 +  return T;
   1.935 +}
   1.936 +
   1.937 +
   1.938 +/* Log a fatal error event
   1.939 + * Accepts: string to log
   1.940 + */
   1.941 +
   1.942 +void mm_fatal (char *string)
   1.943 +{
   1.944 +  fprintf (stderr,"FATAL: %s\n",string);
   1.945 +}

UW-IMAP'd extensions by yuuji