/* * Copyright 2016 Eduardo Chappa */ /* ======================================================================== * Copyright 2009 Mark Crispin * ======================================================================== */ /* * Program: Mail utility * * Author: Mark Crispin * * Date: 2 February 1994 * Last Edited: 14 May 2009 * * Previous versions of this file were * * Copyright 1988-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * */ #include #include extern int errno; /* just in case */ #include "c-client.h" #ifdef SYSCONFIG /* defined in env_unix.h */ #include #endif /* Globals */ char *version = "17"; /* edit number */ int debugp = NIL; /* flag saying debug */ int verbosep = NIL; /* flag saying verbose */ int rwcopyp = NIL; /* flag saying readwrite copy (for POP) */ int kwcopyp = NIL; /* flag saying keyword copy */ int ignorep = NIL; /* flag saying ignore keywords */ int critical = NIL; /* flag saying in critical code */ int trycreate = NIL; /* [TRYCREATE] seen */ char *suffix = NIL; /* suffer merge mode suffix text */ int ddelim = -1; /* destination delimiter */ FILE *f = NIL; /* Usage strings */ char *usage2 = "usage: %s %s\n\n%s\n"; char *usage3 = "usage: %s %s %s\n\n%s\n"; char *usgchk = "check [MAILBOX]"; char *usgcre = "create MAILBOX"; char *usgdel = "delete MAILBOX"; char *usgren = "rename SOURCE DESTINATION"; char *usgdup = "dedup [MAILBOX]"; char *usgcpymov = "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION"; char *usgappdel = "[-rw[copy]] [-kw[copy]] [-ig[nore]] SOURCE DESTINATION"; char *usgprn = "prune mailbox SEARCH_CRITERIA"; char *usgxfr = "transfer [-rw[copy]] [-kw[copy]] [-ig[nore]] [-m[erge] m] SOURCE DEST"; #ifdef SYSCONFIG char *stdsw = "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]] [-u[ser] userid] [--]"; #else char *stdsw = "Standard switches valid with any command:\n\t[-d[ebug]] [-v[erbose]]"; #endif /* Merge modes */ #define mPROMPT 1 #define mAPPEND 2 #define mSUFFIX 3 /* Function prototypes */ void mailutil_add_sequence(char **sequence, size_t *len, unsigned long i, unsigned long j, unsigned long nmsgs); char *mailutil_string_sequence(MAILSTREAM *m); int mailutil_compare_message_id (const void *mcp1, const void *mcp2); int mailutil_dedup (char *mailbox, long options); void ms_init (STRING *s,void *data,unsigned long size); char ms_next (STRING *s); void ms_setpos (STRING *s,unsigned long i); int main (int argc,char *argv[]); SEARCHPGM *prune_criteria (char *criteria); int criteria_number (unsigned long *number,char **r); int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del, int mode); long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date, STRING **message); /* Append package */ typedef struct append_package { MAILSTREAM *stream; /* source stream */ unsigned long msgno; /* current message number */ unsigned long msgmax; /* maximum message number */ char *flags; /* current flags */ char *date; /* message internal date */ STRING *message; /* stringstruct of message */ } APPENDPACKAGE; /* Message string driver for message stringstructs */ STRINGDRIVER mstring = { ms_init, /* initialize string structure */ ms_next, /* get next byte in string structure */ ms_setpos /* set position in string structure */ }; /* Initialize file string structure for file stringstruct * Accepts: string structure * pointer to message data structure * size of string */ void ms_init (STRING *s,void *data,unsigned long size) { APPENDPACKAGE *md = (APPENDPACKAGE *) data; s->data = data; /* note stream/msgno and header length */ mail_fetch_header (md->stream,md->msgno,NIL,NIL,&s->data1, FT_PREFETCHTEXT|FT_PEEK); #if 0 s->size = size; /* message size */ #else /* This kludge is necessary because of broken IMAP servers (sigh!) */ mail_fetch_text (md->stream,md->msgno,NIL,&s->size,FT_PEEK); s->size += s->data1; /* header + body size */ #endif SETPOS (s,0); } /* Get next character from file stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */ char ms_next (STRING *s) { char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */ } /* Set string pointer position for file stringstruct * Accepts: string structure * new position */ void ms_setpos (STRING *s,unsigned long i) { APPENDPACKAGE *md = (APPENDPACKAGE *) s->data; if (i < s->data1) { /* want header? */ s->chunk = mail_fetch_header (md->stream,md->msgno,NIL,NIL,NIL,FT_PEEK); s->chunksize = s->data1; /* header length */ s->offset = 0; /* offset is start of message */ } else if (i < s->size) { /* want body */ s->chunk = mail_fetch_text (md->stream,md->msgno,NIL,NIL,FT_PEEK); s->chunksize = s->size - s->data1; s->offset = s->data1; /* offset is end of header */ } else { /* off end of message */ s->chunk = NIL; /* make sure that we crack on this then */ s->chunksize = 1; /* make sure SNX cracks the right way... */ s->offset = i; } /* initial position and size */ s->curpos = s->chunk + (i -= s->offset); s->cursize = s->chunksize - i; } /* Main program */ int main (int argc,char *argv[]) { MAILSTREAM *source = NIL; MAILSTREAM *dest = NIL; SEARCHPGM *criteria; char c,*s,*dp,*t,*t1,tmp[MAILTMPLEN],mbx[MAILTMPLEN]; unsigned long m,len,curlen,start,last; int i; int merge = NIL; int retcode = 1; int moreswitchp = T; char *cmd = NIL; char *src = NIL; char *dst = NIL; char *pgm = argc ? argv[0] : "mailutil"; #include "linkage.c" for (i = 1; i < argc; i++) { s = argv[i]; /* pick up argument */ /* parse switches */ if (moreswitchp && (*s == '-')) { if (!strcmp (s,"-debug") || !strcmp (s,"-d")) debugp = T; else if (!strcmp (s,"-verbose") || !strcmp (s,"-v")) verbosep = T; else if (!strcmp (s,"-rwcopy") || !strcmp (s,"-rw")) rwcopyp = T; else if (!strcmp (s,"-kwcopy") || !strcmp (s,"-kw")) kwcopyp = T; else if (!strcmp (s,"-ignore") || !strcmp (s,"-ig")) ignorep = T; else if ((!strcmp (s,"-merge") || !strcmp (s,"-m")) && (++i < argc)) { if (!strcmp (s = argv[i],"prompt")) merge = mPROMPT; else if (!strcmp (s,"append")) merge = mAPPEND; else if (!strncmp (s,"suffix=",7) && s[7]) { merge = mSUFFIX; suffix = cpystr (s+7); } else { printf ("unknown merge option: %s\n",s); exit (retcode); } } #ifdef SYSCONFIG else if ((!strcmp (s,"-user") || !strcmp (s,"-u")) && (++i < argc)) { struct passwd *pw = getpwnam (s = argv[i]); if (!pw) { printf ("unknown user id: %s\n",argv[i]); exit (retcode); } else if (setuid (pw->pw_uid)) { perror ("unable to change user id"); exit (retcode); } /* become victim in environment */ env_init (argv[1], pw->pw_dir); /* cancel restrictions since root call */ mail_parameters (NIL,SET_RESTRICTIONS,NIL); } #endif /* -- means no more switches, so mailbox name can start with "-" */ else if ((s[1] == '-') && !s[2]) moreswitchp = NIL; else { printf ("unknown switch: %s\n",s); exit (retcode); } } else if (!cmd) cmd = s; /* first non-switch is command */ else if (!src) src = s; /* second non-switch is source */ else if (!dst) dst = s; /* third non-switch is destination */ else { printf ("unknown argument: %s\n",s); exit (retcode); } } if (kwcopyp && ignorep) { puts ("-kwcopy and -ignore are mutually exclusive"); exit (retcode); } if (!cmd) cmd = ""; /* prevent SEGV */ if(!strcmp(cmd, "dedup")){ if (!src) src = "INBOX"; if (dst || merge || rwcopyp || kwcopyp || ignorep) fprintf (stdout, "%s", usgdup); else if(mailutil_dedup(src, debugp ? OP_DEBUG : NIL)) retcode = 0; } else if (!strcmp (cmd,"check")) { /* check for new messages */ if (!src) src = "INBOX"; if (dst || merge || rwcopyp || kwcopyp || ignorep) printf (usage2,pgm,usgchk,stdsw); else if (mail_status (source = (*src == '{') ? mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)) : NIL, src,SA_MESSAGES | SA_RECENT | SA_UNSEEN)) retcode = 0; } else if (!strcmp (cmd,"create")) { if (!src || dst || merge || rwcopyp || kwcopyp || ignorep) printf (usage2,pgm,usgcre,stdsw); else if (mail_create (source = (*src == '{') ? mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)) : NIL,src)) retcode = 0; } else if (!strcmp (cmd,"delete")) { if (!src || dst || merge || rwcopyp || kwcopyp || ignorep) printf (usage2,pgm,usgdel,stdsw); else if (mail_delete (source = (*src == '{') ? mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)) : NIL,src)) retcode = 0; } else if (!strcmp (cmd,"rename")) { if (!src || !dst || merge || rwcopyp || kwcopyp || ignorep) printf (usage2,pgm,usgren,stdsw); else if (mail_rename (source = (*src == '{') ? mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)) : NIL,src,dst)) retcode = 0; } else if ((i = !strcmp (cmd,"move")) || !strcmp (cmd,"copy")) { if (!src || !dst || merge) printf (usage3,pgm,cmd,usgcpymov,stdsw); else if ((source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) | (debugp ? OP_DEBUG : NIL))) != NULL) { dest = NIL; /* open destination stream if network */ if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)))) { if (mbxcopy (source,dest,dst,T,i,merge)) retcode = 0; } } } else if ((i = !strcmp (cmd,"appenddelete")) || !strcmp (cmd,"append")) { if (!src || !dst || merge) printf (usage3,pgm,cmd,usgappdel,stdsw); else if ((source = mail_open (NIL,src,((i || rwcopyp) ? NIL : OP_READONLY) | (debugp ? OP_DEBUG : NIL))) != NULL) { dest = NIL; /* open destination stream if network */ if ((*dst != '{') || (dest = mail_open (NIL,dst,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)))) { if (mbxcopy (source,dest,dst,NIL,i,merge)) retcode = 0; } } } else if (!strcmp (cmd,"prune")) { if (!src || !dst || merge || rwcopyp || kwcopyp || ignorep || !(criteria = prune_criteria (dst))) printf (usage2,pgm,usgprn,stdsw); else if ((source = mail_open (NIL,src,(debugp ? OP_DEBUG : NIL))) && mail_search_full (source,NIL,criteria,SE_FREE)) { for (m = 1, s = t = NIL, len = start = last = 0; m <= source->nmsgs; m++) if (mail_elt (source,m)->searched) { if (s) { /* continuing a range? */ if (m == last + 1) last = m; else { /* no, end of previous range? */ if (last != start) sprintf (t,":%lu,%lu",last,m); /* no, just this message */ else sprintf (t,",%lu",m); start = last = m; /* either way, start new range */ /* running out of space? */ if ((len - (curlen = (t += strlen (t)) - s)) < 20) { fs_resize ((void **) &s,len += MAILTMPLEN); t = s + curlen; /* relocate current pointer */ } } } else { /* first time, start new buffer */ s = (char *) fs_get (len = MAILTMPLEN); sprintf (s,"%lu",start = last = m); t = s + strlen (s); /* end of buffer */ } } /* finish last range if necessary */ if (last != start) sprintf (t,":%lu",last); if (s) { /* delete/expunge any matching messages */ mail_flag (source,s,"\\Deleted",ST_SET); m = source->nmsgs; /* get number of messages before purge */ mail_expunge (source); printf ("%lu message(s) purged\n",m - source->nmsgs); fs_give ((void **) &s); /* flush buffer */ } else puts ("No matching messages, so nothing purged"); source = mail_close (source); } } else if (!strcmp (cmd,"transfer")) { if (!src || !dst) printf (usage2,pgm,usgxfr,stdsw); else if ((*src == '{') && /* open source mailbox */ !(source = mail_open (NIL,src,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)))); else if ((*dst == '{') && /* open destination server */ !(dest = mail_open (NIL,dst,OP_HALFOPEN | (debugp ? OP_DEBUG : NIL)))); else if (!(f = tmpfile ())) puts ("can't open temporary file"); else { if (verbosep) puts ("Listing mailboxes..."); if (dest) strcpy (strchr (strcpy (tmp,dest->mailbox),'}') + 1, dp = strchr (dst,'}') + 1); else { dp = dst; tmp[0] = '\0'; } mail_list (dest,tmp,""); rewind (f); /* list all mailboxes matching prefix */ if (ddelim < 0) { /* if server failed to give delimiter */ puts ("warning: unable to get destination hierarchy delimiter!"); ddelim = 0; /* default to none */ } if (source) strcpy (strchr (strcpy (tmp,source->mailbox),'}') + 1, strchr (src,'}') + 1); else strcpy (tmp,src); mail_list (source,tmp,"*"); rewind (f); /* read back mailbox names */ for (retcode = 0; !retcode && (fgets (tmp,MAILTMPLEN-1,f)); ) { if ((t = strchr (tmp+1,'\n')) != NULL) *t = '\0'; for (t = mbx,t1 = dest ? dest->mailbox : "",c = NIL; (c != '}') && *t1; *t++ = c= *t1++); for (t1 = dp; *t1; *t++ = *t1++); /* point to name without delim or netspec */ t1 = source ? (strchr (tmp+1,'}') + 1) : tmp + 1; /* src and mbx have different delimiters? */ if (ddelim && (ddelim != tmp[0])) while ((c = *t1++) != '\0') { /* swap delimiters then */ if (c == ddelim) c = tmp[0] ? tmp[0] : 'x'; else if (c == tmp[0]) c = ddelim; *t++ = c; } /* easy case */ else while (*t1) *t++ = *t1++; *t++ = '\0'; if (verbosep) { printf ("Copying %s\n => %s\n",tmp+1,mbx); fflush (stdout); } if ((source = mail_open (source,tmp+1,(debugp ? OP_DEBUG : NIL) | (rwcopyp ? NIL : OP_READONLY))) != NULL) { if (!mbxcopy (source,dest,mbx,T,NIL,merge)) retcode = 1; if (source->dtb->flags & DR_LOCAL) source = mail_close (source); } else printf ("can't open source mailbox %s\n",tmp+1); } } } else { printf ("%s version %s.%s\n\n",pgm,CCLIENTVERSION,version); printf (usage2,pgm,"command [switches] arguments",stdsw); printf ("\nCommands:\n %s\n",usgchk); puts (" ;; report number of messages and new messages"); printf (" %s\n",usgdup); puts (" ;; removes duplicate messages from a mailbox"); printf (" %s\n",usgcre); puts (" ;; create new mailbox"); printf (" %s\n",usgdel); puts (" ;; delete existing mailbox"); printf (" %s\n",usgren); puts (" ;; rename mailbox to a new name"); printf (" copy %s\n",usgcpymov); printf (" move %s\n",usgcpymov); puts (" ;; create new mailbox and copy/move messages"); printf (" append %s\n",usgappdel); printf (" appenddelete %s\n",usgappdel); puts (" ;; copy/move messages to existing mailbox"); printf (" %s\n",usgprn); puts (" ;; prune mailbox of messages matching criteria"); printf (" %s\n",usgxfr); puts (" ;; copy source hierarchy to destination"); puts (" ;; -merge modes are prompt, append, or suffix=xxxx"); } /* close streams */ if (source) mail_close (source); if (dest) mail_close (dest); exit (retcode); return retcode; /* stupid compilers */ } char *mailutil_string_sequence(MAILSTREAM *m) { char *rv = NULL; unsigned long i, j; size_t len = 0; if(m == NULL || m->nmsgs == 0L) return NULL; for(i = 1L; i <= m->nmsgs;){ if(mail_elt(m, i)->sequence){ for(j = i+1; j <= m->nmsgs && mail_elt(m, j)->sequence; j++) ; mailutil_add_sequence(&rv, &len, i, j-1, m->nmsgs); i = j; } else i++; } return rv; } void mailutil_add_sequence(char **sequence, size_t *len, unsigned long i, unsigned long j, unsigned long nmsgs) { #define SEQ_BUF_LEN 256 char tmp[MAILTMPLEN]; size_t needed; if(sequence == NULL) return; if(i == j) sprintf(tmp, "%s%lu", *len == 0L ? "" : ",", i); else if(j == nmsgs) sprintf(tmp, "%s%lu:*", *len == 0L ? "" : ",", i); else sprintf(tmp, "%s%lu:%lu", *len == 0L ? "" : ",", i, j); tmp[sizeof(tmp)-1]='\0'; needed = strlen(*sequence ? *sequence : "") + strlen(tmp) + 1; if(needed > *len){ fs_resize((void **) sequence, (needed + SEQ_BUF_LEN)*sizeof(char)); if(*len == 0L) (*sequence)[0] = '\0'; *len = needed + SEQ_BUF_LEN; } strcat(*sequence + strlen(*sequence), tmp); } int mailutil_dedup (char *mailbox, long options) { int rv = 0; MAILSTREAM *m; if((m = mail_open(NIL, mailbox, options)) == NULL) rv = -1; else if(m->nmsgs > 1L){ unsigned long i, count = 0L; char *sequence; MESSAGECACHE **mc; ENVELOPE *env1, *env2; if(verbosep) fprintf(stdout, "Opened folder \"%s\" with %lu messages\n", mailbox, m->nmsgs); mail_fetch_overview_sequence(m, "1:*", NIL); for(i = 1L; i <= m->nmsgs; i++) mail_elt(m, i)->sequence = 0; mc = fs_get(m->nmsgs*sizeof(MESSAGECACHE *)); for(i = 1L; i <= m->nmsgs; i++) mc[i-1] = mail_elt(m, i); qsort((void *)mc, (size_t) m->nmsgs, sizeof(MESSAGECACHE *), mailutil_compare_message_id); for(i = 1L; i < m->nmsgs;){ unsigned long j, k; int done; done = 0; env1 = mail_fetch_structure(m, mc[i-1]->msgno, NIL, NIL); if(env1->message_id == NULL){ i++; continue; } for(j = i+1; j <= m->nmsgs; j++){ env2 = mail_fetch_structure(m, mc[j-1]->msgno, NIL, NIL); if(env2->message_id == NULL){ j--; break; } if(strcmp(env1->message_id, env2->message_id)) break; else if(verbosep) fprintf(stdout, "Message %lu and %lu are duplicates\n", mc[i-1]->msgno, mc[j-1]->msgno); } for(k = i+1; k <= j - 1; k++){ mc[k-1]->sequence = T; count++; } i = j; } if(verbosep) fprintf(stdout, "Found %lu extra duplicate messages\n", count); if((sequence = mailutil_string_sequence(m)) != NULL){ mail_flag(m, sequence, "\\DELETED", ST_SET); if(count && mail_expunge_full(m, "1:*", NIL)) fprintf (stdout, "Expunged %lu duplicate messages\n", count); else rv = -1; fs_give((void *)&sequence); } else rv = -1; } return rv; } int mailutil_compare_message_id (const void *mcp1, const void *mcp2) { MESSAGECACHE *mc1, *mc2; ENVELOPE *env1, *env2; mc1 = *(MESSAGECACHE **) mcp1; mc2 = *(MESSAGECACHE **) mcp2; env1 = mc1->private.msg.env; /* aarrggh direct access inside message cache */ env2 = mc2->private.msg.env; if(env1 == NULL) return env2 == NULL ? 0 : -1; else if (env2 == NULL) return 1; else if(env1->message_id == NULL) return env2->message_id == NULL ? 0 : -1; else if(env2->message_id == NULL) return 1; return strcmp(env1->message_id, env2->message_id); } /* Pruning criteria, somewhat extended from mail_criteria() * Accepts: criteria * Returns: search program if parse successful, else NIL */ SEARCHPGM *prune_criteria (char *criteria) { SEARCHPGM *pgm = NIL; char *criterion,*r,tmp[MAILTMPLEN]; int f; if (criteria) { /* only if criteria defined */ /* make writeable copy of criteria */ criteria = cpystr (criteria); /* for each criterion */ for (pgm = mail_newsearchpgm (), criterion = strtok_r (criteria," ",&r); criterion; (criterion = strtok_r (NIL," ",&r))) { f = NIL; /* init then scan the criterion */ switch (*ucase (criterion)) { case 'A': /* possible ALL, ANSWERED */ if (!strcmp (criterion+1,"LL")) f = T; else if (!strcmp (criterion+1,"NSWERED")) f = pgm->answered = T; break; case 'B': /* possible BCC, BEFORE, BODY */ if (!strcmp (criterion+1,"CC")) f = mail_criteria_string (&pgm->bcc,&r); else if (!strcmp (criterion+1,"EFORE")) f = mail_criteria_date (&pgm->before,&r); else if (!strcmp (criterion+1,"ODY")) f = mail_criteria_string (&pgm->body,&r); break; case 'C': /* possible CC */ if (!strcmp (criterion+1,"C")) f = mail_criteria_string (&pgm->cc,&r); break; case 'D': /* possible DELETED, DRAFT */ if (!strcmp (criterion+1,"ELETED")) f = pgm->deleted = T; else if (!strcmp (criterion+1,"RAFT")) f = pgm->draft = T; break; case 'F': /* possible FLAGGED, FROM */ if (!strcmp (criterion+1,"LAGGED")) f = pgm->flagged = T; else if (!strcmp (criterion+1,"ROM")) f = mail_criteria_string (&pgm->from,&r); break; case 'K': /* possible KEYWORD */ if (!strcmp (criterion+1,"EYWORD")) f = mail_criteria_string (&pgm->keyword,&r); break; case 'L': /* possible LARGER */ if (!strcmp (criterion+1,"ARGER")) f = criteria_number (&pgm->larger,&r); case 'N': /* possible NEW */ if (!strcmp (criterion+1,"EW")) f = pgm->recent = pgm->unseen = T; break; case 'O': /* possible OLD, ON */ if (!strcmp (criterion+1,"LD")) f = pgm->old = T; else if (!strcmp (criterion+1,"N")) f = mail_criteria_date (&pgm->on,&r); break; case 'R': /* possible RECENT */ if (!strcmp (criterion+1,"ECENT")) f = pgm->recent = T; break; case 'S': /* possible SEEN, SENT*, SINCE, SMALLER, SUBJECT */ if (!strcmp (criterion+1,"EEN")) f = pgm->seen = T; else if (!strncmp (criterion+1,"ENT",3)) { if (!strcmp (criterion+4,"BEFORE")) f = mail_criteria_date (&pgm->sentbefore,&r); else if (!strcmp (criterion+4,"ON")) f = mail_criteria_date (&pgm->senton,&r); else if (!strcmp (criterion+4,"SINCE")) f = mail_criteria_date (&pgm->sentsince,&r); } else if (!strcmp (criterion+1,"INCE")) f = mail_criteria_date (&pgm->since,&r); else if (!strcmp (criterion+1,"MALLER")) f = criteria_number (&pgm->smaller,&r); else if (!strcmp (criterion+1,"UBJECT")) f = mail_criteria_string (&pgm->subject,&r); break; case 'T': /* possible TEXT, TO */ if (!strcmp (criterion+1,"EXT")) f = mail_criteria_string (&pgm->text,&r); else if (!strcmp (criterion+1,"O")) f = mail_criteria_string (&pgm->to,&r); break; case 'U': /* possible UN* */ if (criterion[1] == 'N') { if (!strcmp (criterion+2,"ANSWERED")) f = pgm->unanswered = T; else if (!strcmp (criterion+2,"DELETED")) f = pgm->undeleted = T; else if (!strcmp (criterion+2,"DRAFT")) f = pgm->undraft = T; else if (!strcmp (criterion+2,"FLAGGED")) f = pgm->unflagged = T; else if (!strcmp (criterion+2,"KEYWORD")) f = mail_criteria_string (&pgm->unkeyword,&r); else if (!strcmp (criterion+2,"SEEN")) f = pgm->unseen = T; } break; default: /* we will barf below */ break; } if (!f) { /* if can't identify criterion */ sprintf (tmp,"Unknown search criterion: %.30s",criterion); MM_LOG (tmp,ERROR); mail_free_searchpgm (&pgm); break; } } /* no longer need copy of criteria */ fs_give ((void **) &criteria); } return pgm; } /* Parse a number * Accepts: pointer to integer to return * pointer to strtok state * Returns: T if successful, else NIL */ int criteria_number (unsigned long *number,char **r) { char *t; STRINGLIST *s = NIL; /* parse the date and return fn if OK */ int ret = (mail_criteria_string (&s,r) && (*number = strtoul ((char *) s->text.data,&t,10)) && !*t) ? T : NIL; if (s) mail_free_stringlist (&s); return ret; } /* Copy mailbox * Accepts: stream open on source * halfopen stream for destination or NIL * destination mailbox name * non-zero to create destination mailbox * non-zero to delete messages from source after copying * merge mode * Returns: T if success, NIL if error */ int mbxcopy (MAILSTREAM *source,MAILSTREAM *dest,char *dst,int create,int del, int mode) { char *s,tmp[MAILTMPLEN]; APPENDPACKAGE ap; STRING st; char *ndst = NIL; int ret = NIL; trycreate = NIL; /* no TRYCREATE yet */ if (create) while (!mail_create (dest,dst) && (mode != mAPPEND)) { switch (mode) { case mPROMPT: /* prompt user for new name */ tmp[0] = '\0'; while (!tmp[0]) { /* read name */ fputs ("alternative name: ",stdout); fflush (stdout); fgets (tmp,MAILTMPLEN-1,stdin); if ((s = strchr (tmp,'\n')) != NULL) *s = '\0'; } if (ndst) fs_give ((void **) &ndst); ndst = cpystr (tmp); break; case mSUFFIX: /* try again with new suffix */ if (ndst) fs_give ((void **) &ndst); sprintf (ndst = (char *) fs_get (strlen (dst) + strlen (suffix) + 1), "%s%s",dst,suffix); printf ("retry to create %s\n",ndst); mode = mPROMPT; /* switch to prompt mode if name fails */ break; case NIL: /* not merging */ return NIL; } if (ndst) dst = ndst; /* if alternative name given, use it */ } if (kwcopyp) { int i; size_t len; char *dummymsg = "Date: Thu, 18 May 2006 00:00 -0700\r\nFrom: dummy@example.com\r\nSubject: dummy\r\n\r\ndummy\r\n"; for (i = 0,len = 0; i < NUSERFLAGS; ++i) if (source->user_flags[i]) len += strlen (source->user_flags[i]) + 1; if (len) { /* easy if no user flags to copy... */ char *t; char *tail = "\\Deleted)"; char *flags = (char *) fs_get (1 + len + strlen (tail) + 1); s = flags; *s++ = '('; for (i = 0; i < NUSERFLAGS; ++i) if ((t = source->user_flags[i]) != NULL) { while (*t) *s++ = *t++; *s++ = ' '; } strcpy (s,tail); /* terminate flags list */ if ((dst[0] == '#') && ((dst[1] == 'D') || (dst[1] == 'd')) && ((dst[2] == 'R') || (dst[2] == 'r')) && ((dst[3] == 'I') || (dst[3] == 'i')) && ((dst[4] == 'V') || (dst[4] == 'v')) && ((dst[5] == 'E') || (dst[5] == 'e')) && ((dst[6] == 'R') || (dst[6] == 'r')) && (dst[7] == '.') && (t = strchr (dst+8,'/'))) ++t; else t = dst; INIT (&st,mail_string,dummymsg,strlen (dummymsg)); if (!(mail_append (dest,dst,&st) && (dest = mail_open (dest,t,debugp ? OP_DEBUG : NIL)))) { fs_give ((void **) &flags); return NIL; } mail_setflag (dest,"*",flags); mail_expunge (dest); fs_give ((void **) &flags); } } if (source->nmsgs) { /* non-empty source */ if (verbosep) printf ("%s [%lu message(s)] => %s\n", source->mailbox,source->nmsgs,dst); ap.stream = source; /* prepare append package */ ap.msgno = 0; ap.msgmax = source->nmsgs; ap.flags = ap.date = NIL; ap.message = &st; /* make sure we have all messages */ sprintf (tmp,"1:%lu",ap.msgmax); mail_fetchfast (source,tmp); if (mail_append_multiple (dest,dst,mm_append,(void *) &ap)) { --ap.msgno; /* make sure user knows it won */ if (verbosep) printf ("[Ok %lu messages(s)]\n",ap.msgno); if (del && ap.msgno) { /* delete source messages */ sprintf (tmp,"1:%lu",ap.msgno); mail_flag (source,tmp,"\\Deleted",ST_SET); /* flush moved messages */ mail_expunge (source); } ret = T; } else if ((mode == mAPPEND) && trycreate) ret = mbxcopy (source,dest,dst,create,del,mPROMPT); else if (verbosep) puts ("[Failed]"); } else { /* empty source */ if (verbosep) printf ("%s [empty] => %s\n",source->mailbox,dst); ret = T; } if (ndst) fs_give ((void **) &ndst); return ret; } /* Append callback * Accepts: mail stream * append package * pointer to return flags * pointer to return date * pointer to return message stringstruct * Returns: T on success */ long mm_append (MAILSTREAM *stream,void *data,char **flags,char **date, STRING **message) { char *t,*t1,tmp[MAILTMPLEN]; unsigned long u; MESSAGECACHE *elt; APPENDPACKAGE *ap = (APPENDPACKAGE *) data; *flags = *date = NIL; /* assume no flags or date */ if (ap->flags) fs_give ((void **) &ap->flags); if (ap->date) fs_give ((void **) &ap->date); mail_gc (ap->stream,GC_TEXTS); if (++ap->msgno <= ap->msgmax) { /* initialize flag string */ memset (t = tmp,0,MAILTMPLEN); /* output system flags */ if ((elt = mail_elt (ap->stream,ap->msgno))->seen) strcat (t," \\Seen"); if (elt->deleted) strcat (t," \\Deleted"); if (elt->flagged) strcat (t," \\Flagged"); if (elt->answered) strcat (t," \\Answered"); if (elt->draft) strcat (t," \\Draft"); /* any user flags? */ if (!ignorep && (u = elt->user_flags)) do if ((t1 = ap->stream->user_flags[find_rightmost_bit (&u)]) && (MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long) (2 + strlen (t1))){ *t++ = ' '; /* space delimiter */ strcpy (t,t1); /* copy the user flag */ } while (u); /* until no more user flags */ *flags = ap->flags = cpystr (tmp + 1); *date = ap->date = cpystr (mail_date (tmp,elt)); *message = ap->message; /* message stringstruct */ INIT (ap->message,mstring,(void *) ap,elt->rfc822_size); } else *message = NIL; /* all done */ return LONGT; } /* Co-routines from MAIL library */ /* Message matches a search * Accepts: MAIL stream * message number */ void mm_searched (MAILSTREAM *stream,unsigned long msgno) { /* dummy routine */ } /* Message exists (i.e. there are that many messages in the mailbox) * Accepts: MAIL stream * message number */ void mm_exists (MAILSTREAM *stream,unsigned long number) { /* dummy routine */ } /* Message expunged * Accepts: MAIL stream * message number */ void mm_expunged (MAILSTREAM *stream,unsigned long number) { /* dummy routine */ } /* Message flags update seen * Accepts: MAIL stream * message number */ void mm_flags (MAILSTREAM *stream,unsigned long number) { /* dummy routine */ } /* Mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * mailbox attributes */ void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes) { /* note destination delimiter */ if (ddelim < 0) ddelim = delimiter; /* if got a selectable name */ else if (!(attributes & LATT_NOSELECT) && *name) fprintf (f,"%c%s\n",delimiter,name); } /* Subscribe mailbox found * Accepts: MAIL stream * hierarchy delimiter * mailbox name * mailbox attributes */ void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes) { /* dummy routine */ } /* Mailbox status * Accepts: MAIL stream * mailbox name * mailbox status */ void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) { if (status->recent || status->unseen) printf ("%lu new message(s) (%lu unseen),",status->recent,status->unseen); else fputs ("No new messages,",stdout); printf (" %lu total in %s\n",status->messages,mailbox); } /* Notification event * Accepts: MAIL stream * string to log * error flag */ void mm_notify (MAILSTREAM *stream,char *string,long errflg) { if (!errflg && (string[0] == '[') && ((string[1] == 'T') || (string[1] == 't')) && ((string[2] == 'R') || (string[2] == 'r')) && ((string[3] == 'Y') || (string[3] == 'y')) && ((string[4] == 'C') || (string[4] == 'c')) && ((string[5] == 'R') || (string[5] == 'r')) && ((string[6] == 'E') || (string[6] == 'e')) && ((string[7] == 'A') || (string[7] == 'a')) && ((string[8] == 'T') || (string[8] == 't')) && ((string[9] == 'E') || (string[9] == 'e')) && (string[10] == ']')) trycreate = T; mm_log (string,errflg); /* just do mm_log action */ } /* Log an event for the user to see * Accepts: string to log * error flag */ void mm_log (char *string,long errflg) { switch (errflg) { case BYE: case NIL: /* no error */ if (verbosep) fprintf (stderr,"[%s]\n",string); break; case PARSE: /* parsing problem */ case WARN: /* warning */ fprintf (stderr,"warning: %s\n",string); break; case ERROR: /* error */ default: fprintf (stderr,"%s\n",string); break; } } /* Log an event to debugging telemetry * Accepts: string to log */ void mm_dlog (char *string) { fprintf (stderr,"%s\n",string); } /* Get user name and password for this host * Accepts: parse of network mailbox name * where to return user name * where to return password * trial count */ void mm_login (NETMBX *mb,char *username,char *password,long trial) { char *s,tmp[MAILTMPLEN]; sprintf (s = tmp,"{%s/%s",mb->host,mb->service); if (*mb->user) sprintf (tmp+strlen (tmp),"/user=%s", strcpy (username,mb->user)); if (*mb->authuser) sprintf (tmp+strlen (tmp),"/authuser=%s",mb->authuser); if (*mb->user) strcat (s = tmp,"} password:"); else { printf ("%s} username: ",tmp); fgets (username,NETMAXUSER-1,stdin); username[NETMAXUSER-1] = '\0'; if ((s = strchr (username,'\n')) != NULL) *s = '\0'; s = "password: "; } if(strlen (s = getpass (s)) < MAILTMPLEN) strcpy (password,s); } /* About to enter critical code * Accepts: stream */ void mm_critical (MAILSTREAM *stream) { critical = T; /* note in critical code */ } /* About to exit critical code * Accepts: stream */ void mm_nocritical (MAILSTREAM *stream) { critical = NIL; /* note not in critical code */ } /* Disk error found * Accepts: stream * system error code * flag indicating that mailbox may be clobbered * Returns: T if user wants to abort */ long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) { return T; } /* Log a fatal error event * Accepts: string to log */ void mm_fatal (char *string) { fprintf (stderr,"FATAL: %s\n",string); }