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