imap4r1.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:129k
- /*
- * Program: Interactive Message Access Protocol 4rev1 (IMAP4R1) routines
- *
- * Author: Mark Crispin
- * Networks and Distributed Computing
- * Computing & Communications
- * University of Washington
- * Administration Building, AG-44
- * Seattle, WA 98195
- * Internet: MRC@CAC.Washington.EDU
- *
- * Date: 15 June 1988
- * Last Edited: 28 October 1999
- *
- * Sponsorship: The original version of this work was developed in the
- * Symbolic Systems Resources Group of the Knowledge Systems
- * Laboratory at Stanford University in 1987-88, and was funded
- * by the Biomedical Research Technology Program of the National
- * Institutes of Health under grant number RR-00785.
- *
- * Original version Copyright 1988 by The Leland Stanford Junior University
- * Copyright 1999 by the University of Washington
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose and without fee is hereby granted, provided
- * that the above copyright notices appear in all copies and that both the
- * above copyright notices and this permission notice appear in supporting
- * documentation, and that the name of the University of Washington or The
- * Leland Stanford Junior University not be used in advertising or publicity
- * pertaining to distribution of the software without specific, written prior
- * permission. This software is made available "as is", and
- * THE UNIVERSITY OF WASHINGTON AND THE LELAND STANFORD JUNIOR UNIVERSITY
- * DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO THIS SOFTWARE,
- * INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
- * WASHINGTON OR THE LELAND STANFORD JUNIOR UNIVERSITY BE LIABLE FOR ANY
- * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
- * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
- * CONTRACT, TORT (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
- #include <ctype.h>
- #include <stdio.h>
- #include <time.h>
- #include "mail.h"
- #include "osdep.h"
- #include "imap4r1.h"
- #include "rfc822.h"
- #include "misc.h"
- static char *hdrheader = "BODY.PEEK[HEADER.FIELDS (Path Message-ID";
- static char *hdrtrailer ="Newsgroups Followup-To References)]";
- static char *allheader = "(UID ENVELOPE";
- static char *fasttrailer = "INTERNALDATE RFC822.SIZE FLAGS)";
- /* Driver dispatch used by MAIL */
- DRIVER imapdriver = {
- "imap", /* driver name */
- /* driver flags */
- DR_MAIL|DR_NEWS|DR_NAMESPACE|DR_CRLF|DR_RECYCLE,
- (DRIVER *) NIL, /* next driver */
- imap_valid, /* mailbox is valid for us */
- imap_parameters, /* manipulate parameters */
- imap_scan, /* scan mailboxes */
- imap_list, /* find mailboxes */
- imap_lsub, /* find subscribed mailboxes */
- imap_subscribe, /* subscribe to mailbox */
- imap_unsubscribe, /* unsubscribe from mailbox */
- imap_create, /* create mailbox */
- imap_delete, /* delete mailbox */
- imap_rename, /* rename mailbox */
- imap_status, /* status of mailbox */
- imap_open, /* open mailbox */
- imap_close, /* close mailbox */
- imap_fast, /* fetch message "fast" attributes */
- imap_flags, /* fetch message flags */
- imap_overview, /* fetch overview */
- imap_structure, /* fetch message envelopes */
- NIL, /* fetch message header */
- NIL, /* fetch message body */
- imap_msgdata, /* fetch partial message */
- imap_uid, /* unique identifier */
- imap_msgno, /* message number */
- imap_flag, /* modify flags */
- NIL, /* per-message modify flags */
- imap_search, /* search for message based on criteria */
- imap_sort, /* sort messages */
- imap_thread, /* thread messages */
- imap_ping, /* ping mailbox to see if still alive */
- imap_check, /* check for new messages */
- imap_expunge, /* expunge deleted messages */
- imap_copy, /* copy messages to another mailbox */
- imap_append, /* append string message to mailbox */
- imap_gc /* garbage collect stream */
- };
- /* prototype stream */
- MAILSTREAM imapproto = {&imapdriver};
- /* driver parameters */
- static unsigned long imap_maxlogintrials = MAXLOGINTRIALS;
- static long imap_lookahead = IMAPLOOKAHEAD;
- static long imap_uidlookahead = IMAPUIDLOOKAHEAD;
- static long imap_defaultport = 0;
- static long imap_altport = 0;
- static char *imap_altname = NIL;
- static long imap_prefetch = IMAPLOOKAHEAD;
- static long imap_closeonerror = NIL;
- static imapenvelope_t imap_envelope = NIL;
- static imapreferral_t imap_referral = NIL;
- static char *imap_extrahdrs = NIL;
- /* IMAP validate mailbox
- * Accepts: mailbox name
- * Returns: our driver if name is valid, NIL otherwise
- */
- DRIVER *imap_valid (char *name)
- {
- return mail_valid_net (name,&imapdriver,NIL,NIL);
- }
- /* IMAP manipulate driver parameters
- * Accepts: function code
- * function-dependent value
- * Returns: function-dependent return value
- */
- void *imap_parameters (long function,void *value)
- {
- switch ((int) function) {
- case SET_NAMESPACE:
- fatal ("SET_NAMESPACE not permitted");
- case GET_NAMESPACE:
- if (((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->use_namespace &&
- !((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->namespace)
- imap_send (((MAILSTREAM *) value),"NAMESPACE",NIL);
- value = (void *) &((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->namespace;
- break;
- case GET_THREADERS:
- value = (void *) ((IMAPLOCAL *) ((MAILSTREAM *) value)->local)->threader;
- break;
- case SET_MAXLOGINTRIALS:
- imap_maxlogintrials = (long) value;
- break;
- case GET_MAXLOGINTRIALS:
- value = (void *) imap_maxlogintrials;
- break;
- case SET_LOOKAHEAD:
- imap_lookahead = (long) value;
- break;
- case GET_LOOKAHEAD:
- value = (void *) imap_lookahead;
- break;
- case SET_UIDLOOKAHEAD:
- imap_uidlookahead = (long) value;
- break;
- case GET_UIDLOOKAHEAD:
- value = (void *) imap_uidlookahead;
- break;
- case SET_IMAPPORT:
- imap_defaultport = (long) value;
- break;
- case GET_IMAPPORT:
- value = (void *) imap_defaultport;
- break;
- case SET_ALTIMAPPORT:
- imap_altport = (long) value;
- break;
- case GET_ALTIMAPPORT:
- value = (void *) imap_altport;
- break;
- case SET_ALTIMAPNAME:
- imap_altname = (char *) value;
- break;
- case GET_ALTIMAPNAME:
- value = (void *) imap_altname;
- break;
- case SET_PREFETCH:
- imap_prefetch = (long) value;
- break;
- case GET_PREFETCH:
- value = (void *) imap_prefetch;
- break;
- case SET_CLOSEONERROR:
- imap_closeonerror = (long) value;
- break;
- case GET_CLOSEONERROR:
- value = (void *) imap_closeonerror;
- break;
- case SET_IMAPENVELOPE:
- imap_envelope = (imapenvelope_t) value;
- break;
- case GET_IMAPENVELOPE:
- value = (void *) imap_envelope;
- break;
- case SET_IMAPREFERRAL:
- imap_referral = (imapreferral_t) value;
- break;
- case GET_IMAPREFERRAL:
- value = (void *) imap_referral;
- break;
- case SET_IMAPEXTRAHEADERS:
- imap_extrahdrs = (char *) value;
- break;
- case GET_IMAPEXTRAHEADERS:
- value = (void *) imap_extrahdrs;
- break;
- default:
- value = NIL; /* error case */
- break;
- }
- return value;
- }
- /* IMAP scan mailboxes
- * Accepts: mail stream
- * reference
- * pattern to search
- * string to scan
- */
- void imap_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
- {
- imap_list_work (stream,"SCAN",ref,pat,contents);
- }
- /* IMAP list mailboxes
- * Accepts: mail stream
- * reference
- * pattern to search
- */
- void imap_list (MAILSTREAM *stream,char *ref,char *pat)
- {
- imap_list_work (stream,"LIST",ref,pat,NIL);
- }
- /* IMAP list subscribed mailboxes
- * Accepts: mail stream
- * reference
- * pattern to search
- */
- void imap_lsub (MAILSTREAM *stream,char *ref,char *pat)
- {
- void *sdb = NIL;
- char *s,mbx[MAILTMPLEN];
- /* do it on the server */
- imap_list_work (stream,"LSUB",ref,pat,NIL);
- if (*pat == '{') { /* if remote pattern, must be IMAP */
- if (!imap_valid (pat)) return;
- ref = NIL; /* good IMAP pattern, punt reference */
- }
- /* if remote reference, must be valid IMAP */
- if (ref && (*ref == '{') && !imap_valid (ref)) return;
- /* kludgy application of reference */
- if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
- else strcpy (mbx,pat);
- if (s = sm_read (&sdb)) do if (imap_valid (s) && pmatch (s,mbx))
- mm_lsub (stream,NIL,s,NIL);
- while (s = sm_read (&sdb)); /* until no more subscriptions */
- }
- /* IMAP find list of mailboxes
- * Accepts: mail stream
- * list command
- * reference
- * pattern to search
- * string to scan
- */
- void imap_list_work (MAILSTREAM *stream,char *cmd,char *ref,char *pat,
- char *contents)
- {
- MAILSTREAM *st = stream;
- int pl;
- char *s,prefix[MAILTMPLEN],mbx[MAILTMPLEN];
- IMAPARG *args[4],aref,apat,acont;
- if (ref && *ref) { /* have a reference? */
- if (!(imap_valid (ref) && /* make sure valid IMAP name and open stream */
- ((stream && LOCAL && LOCAL->netstream) ||
- (stream = mail_open (NIL,ref,OP_HALFOPEN|OP_SILENT))))) return;
- /* calculate prefix length */
- pl = strchr (ref,'}') + 1 - ref;
- strncpy (prefix,ref,pl); /* build prefix */
- prefix[pl] = ' '; /* tie off prefix */
- ref += pl; /* update reference */
- }
- else {
- if (!(imap_valid (pat) && /* make sure valid IMAP name and open stream */
- ((stream && LOCAL && LOCAL->netstream) ||
- (stream = mail_open (NIL,pat,OP_HALFOPEN|OP_SILENT))))) return;
- /* calculate prefix length */
- pl = strchr (pat,'}') + 1 - pat;
- strncpy (prefix,pat,pl); /* build prefix */
- prefix[pl] = ' '; /* tie off prefix */
- pat += pl; /* update reference */
- }
- LOCAL->prefix = prefix; /* note prefix */
- if (contents) { /* want to do a scan? */
- if (LOCAL->use_scan) {
- args[0] = &aref; args[1] = &apat; args[2] = &acont; args[3] = NIL;
- aref.type = ASTRING; aref.text = (void *) (ref ? ref : "");
- apat.type = LISTMAILBOX; apat.text = (void *) pat;
- acont.type = ASTRING; acont.text = (void *) contents;
- imap_send (stream,cmd,args);
- }
- else mm_log ("Scan not valid on this IMAP server",ERROR);
- }
- else if (LEVELIMAP4 (stream)){/* easy if IMAP4 */
- args[0] = &aref; args[1] = &apat; args[2] = NIL;
- aref.type = ASTRING; aref.text = (void *) (ref ? ref : "");
- apat.type = LISTMAILBOX; apat.text = (void *) pat;
- /* referrals armed? */
- if (LOCAL->use_mbx_ref && mail_parameters (stream,GET_IMAPREFERRAL,NIL) &&
- ((cmd[0] == 'L') || (cmd[0] == 'l')) && !cmd[4]) {
- /* yes, convert LIST -> RLIST */
- if (((cmd[1] == 'I') || (cmd[1] == 'i')) &&
- ((cmd[2] == 'S') || (cmd[1] == 's')) &&
- ((cmd[3] == 'T') || (cmd[3] == 't'))) cmd = "RLIST";
- /* and convert LSUB -> RLSUB */
- else if (((cmd[1] == 'S') || (cmd[1] == 's')) &&
- ((cmd[2] == 'U') || (cmd[1] == 'u')) &&
- ((cmd[3] == 'B') || (cmd[3] == 'b'))) cmd = "RLSUB";
- }
- imap_send (stream,cmd,args);
- }
- else if (LEVEL1176 (stream)) {/* convert to IMAP2 format wildcard */
- /* kludgy application of reference */
- if (ref && *ref) sprintf (mbx,"%s%s",ref,pat);
- else strcpy (mbx,pat);
- for (s = mbx; *s; s++) if (*s == '%') *s = '*';
- args[0] = &apat; args[1] = NIL;
- apat.type = LISTMAILBOX; apat.text = (void *) mbx;
- if (!(strstr (cmd,"LIST") &&/* if list, try IMAP2bis, then RFC-1176 */
- strcmp (imap_send (stream,"FIND ALL.MAILBOXES",args)->key,"BAD")) &&
- !strcmp (imap_send (stream,"FIND MAILBOXES",args)->key,"BAD"))
- LOCAL->rfc1176 = NIL; /* must be RFC-1064 */
- }
- LOCAL->prefix = NIL; /* no more prefix */
- /* close temporary stream if we made one */
- if (stream != st) mail_close (stream);
- }
- /* IMAP subscribe to mailbox
- * Accepts: mail stream
- * mailbox to add to subscription list
- * Returns: T on success, NIL on failure
- */
- long imap_subscribe (MAILSTREAM *stream,char *mailbox)
- {
- MAILSTREAM *st = stream;
- long ret = ((stream && LOCAL && LOCAL->netstream) ||
- (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT))) ?
- imap_manage (stream,mailbox,LEVELIMAP4 (stream) ?
- "Subscribe" : "Subscribe Mailbox",NIL) : NIL;
- /* toss out temporary stream */
- if (st != stream) mail_close (stream);
- return ret;
- }
- /* IMAP unsubscribe to mailbox
- * Accepts: mail stream
- * mailbox to delete from manage list
- * Returns: T on success, NIL on failure
- */
- long imap_unsubscribe (MAILSTREAM *stream,char *mailbox)
- {
- MAILSTREAM *st = stream;
- long ret = ((stream && LOCAL && LOCAL->netstream) ||
- (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT))) ?
- imap_manage (stream,mailbox,LEVELIMAP4 (stream) ?
- "Unsubscribe" : "Unsubscribe Mailbox",NIL) : NIL;
- /* toss out temporary stream */
- if (st != stream) mail_close (stream);
- return ret;
- }
- /* IMAP create mailbox
- * Accepts: mail stream
- * mailbox name to create
- * Returns: T on success, NIL on failure
- */
- long imap_create (MAILSTREAM *stream,char *mailbox)
- {
- return imap_manage (stream,mailbox,"Create",NIL);
- }
- /* IMAP delete mailbox
- * Accepts: mail stream
- * mailbox name to delete
- * Returns: T on success, NIL on failure
- */
- long imap_delete (MAILSTREAM *stream,char *mailbox)
- {
- return imap_manage (stream,mailbox,"Delete",NIL);
- }
- /* IMAP rename mailbox
- * Accepts: mail stream
- * old mailbox name
- * new mailbox name
- * Returns: T on success, NIL on failure
- */
- long imap_rename (MAILSTREAM *stream,char *old,char *newname)
- {
- return imap_manage (stream,old,"Rename",newname);
- }
- /* IMAP manage a mailbox
- * Accepts: mail stream
- * mailbox to manipulate
- * command to execute
- * optional second argument
- * Returns: T on success, NIL on failure
- */
- long imap_manage (MAILSTREAM *stream,char *mailbox,char *command,char *arg2)
- {
- MAILSTREAM *st = stream;
- IMAPPARSEDREPLY *reply;
- long ret = NIL;
- char mbx[MAILTMPLEN],mbx2[MAILTMPLEN];
- IMAPARG *args[3],ambx,amb2;
- imapreferral_t ir =
- (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
- ambx.type = amb2.type = ASTRING; ambx.text = (void *) mbx;
- amb2.text = (void *) mbx2;
- args[0] = &ambx; args[1] = args[2] = NIL;
- /* require valid names and open stream */
- if (mail_valid_net (mailbox,&imapdriver,NIL,mbx) &&
- (arg2 ? mail_valid_net (arg2,&imapdriver,NIL,mbx2) : &imapdriver) &&
- ((stream && LOCAL && LOCAL->netstream) ||
- (stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT)))) {
- if (arg2) args[1] = &amb2; /* second arg present? */
- if (!(ret = (imap_OK (stream,reply = imap_send (stream,command,args)))) &&
- ir && LOCAL->referral) {
- long code = -1;
- switch (*command) { /* which command was it? */
- case 'S': code = REFSUBSCRIBE; break;
- case 'U': code = REFUNSUBSCRIBE; break;
- case 'C': code = REFCREATE; break;
- case 'D': code = REFDELETE; break;
- case 'R': code = REFRENAME; break;
- default:
- fatal ("impossible referral command");
- }
- if ((code >= 0) && (mailbox = (*ir) (stream,LOCAL->referral,code)))
- ret = imap_manage (NIL,mailbox,command,(*command == 'R') ?
- (mailbox + strlen (mailbox) + 1) : NIL);
- }
- mm_log (reply->text,ret ? NIL : ERROR);
- /* toss out temporary stream */
- if (st != stream) mail_close (stream);
- }
- return ret;
- }
- /* IMAP status
- * Accepts: mail stream
- * mailbox name
- * status flags
- * Returns: T on success, NIL on failure
- */
- long imap_status (MAILSTREAM *stream,char *mbx,long flags)
- {
- IMAPARG *args[3],ambx,aflg;
- char tmp[MAILTMPLEN];
- NETMBX mb;
- unsigned long i;
- long ret = NIL;
- MAILSTREAM *tstream = stream;
- imapreferral_t ir =
- (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
- mail_valid_net_parse (mbx,&mb);
- /* can't use stream if not IMAP4rev1, STATUS,
- or halfopen and right host */
- if (stream && (!(LEVELSTATUS (stream) || stream->halfopen)
- || strcmp (ucase (strcpy (tmp,imap_host (stream))),
- ucase (mb.host))))
- return imap_status (NIL,mbx,flags);
- /* make stream if don't have one */
- if (!(stream || (stream = mail_open (NIL,mbx,OP_HALFOPEN|OP_SILENT))))
- return NIL;
- args[0] = &ambx;args[1] = NIL;/* set up first argument as mailbox */
- ambx.type = ASTRING; ambx.text = (void *) mb.mailbox;
- if (LEVELSTATUS (stream)) { /* have STATUS command? */
- aflg.type = FLAGS; aflg.text = (void *) tmp;
- args[1] = &aflg; args[2] = NIL;
- tmp[0] = tmp[1] = ' '; /* build flag list */
- if (flags & SA_MESSAGES) strcat (tmp," MESSAGES");
- if (flags & SA_RECENT) strcat (tmp," RECENT");
- if (flags & SA_UNSEEN) strcat (tmp," UNSEEN");
- if (flags & SA_UIDNEXT) strcat (tmp,LEVELIMAP4rev1 (stream) ?
- " UIDNEXT" : " UID-NEXT");
- if (flags & SA_UIDVALIDITY) strcat (tmp,LEVELIMAP4rev1 (stream) ?
- " UIDVALIDITY" : " UID-VALIDITY");
- tmp[0] = '(';
- strcat (tmp,")");
- /* send "STATUS mailbox flag" */
- if (imap_OK (stream,imap_send (stream,"STATUS",args))) ret = T;
- else if (ir && LOCAL->referral &&
- (mbx = (*ir) (stream,LOCAL->referral,REFSTATUS)))
- ret = imap_status (NIL,mbx,flags);
- }
- /* IMAP2 way */
- else if (imap_OK (stream,imap_send (stream,"EXAMINE",args))) {
- MAILSTATUS status;
- status.flags = flags & ~ (SA_UIDNEXT | SA_UIDVALIDITY);
- status.messages = stream->nmsgs;
- status.recent = stream->recent;
- status.unseen = 0;
- if (flags & SA_UNSEEN) { /* must search to get unseen messages */
- /* clear search vector */
- for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = NIL;
- if (imap_OK (stream,imap_send (stream,"SEARCH UNSEEN",NIL)))
- for (i = 1,status.unseen = 0; i <= stream->nmsgs; i++)
- if (mail_elt (stream,i)->searched) status.unseen++;
- }
- strcpy (strchr (strcpy (tmp,stream->mailbox),'}') + 1,mb.mailbox);
- /* pass status to main program */
- mm_status (stream,tmp,&status);
- ret = T; /* note success */
- }
- if (stream != tstream) mail_close (stream);
- return ret; /* success */
- }
- /* IMAP open
- * Accepts: stream to open
- * Returns: stream to use on success, NIL on failure
- */
- MAILSTREAM *imap_open (MAILSTREAM *stream)
- {
- unsigned long i,j;
- char *s,tmp[MAILTMPLEN],usr[MAILTMPLEN];
- NETMBX mb;
- IMAPPARSEDREPLY *reply = NIL;
- imapreferral_t ir =
- (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
- /* return prototype for OP_PROTOTYPE call */
- if (!stream) return &imapproto;
- mail_valid_net_parse (stream->mailbox,&mb);
- usr[0] = ' '; /* initially no user name */
- if (LOCAL) { /* if stream opened earlier by us */
- if (LOCAL->netstream) { /* recycle if still alive */
- i = stream->silent; /* temporarily mark silent */
- stream->silent = T; /* don't give mm_exists() events */
- j = imap_ping (stream); /* learn if stream still alive */
- stream->silent = i; /* restore prior state */
- if (j) { /* was stream still alive? */
- sprintf (tmp,"Reusing connection to %s",imap_host (stream));
- if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user="%s"",
- LOCAL->user);
- if (!stream->silent) mm_log (tmp,(long) NIL);
- }
- else imap_close (stream,NIL);
- }
- else imap_close (stream,NIL);
- }
- /* copy flags from name */
- if (mb.dbgflag) stream->debug = T;
- if (mb.anoflag) stream->anonymous = T;
- if (mb.secflag) stream->secure = T;
- mb.tryaltflag = stream->tryalt;
- if (!LOCAL) { /* open new connection if no recycle */
- NETDRIVER *altd = (NETDRIVER *) mail_parameters (NIL,GET_ALTDRIVER,NIL);
- char * alts = (char *) mail_parameters(NIL,GET_ALTIMAPNAME,NIL);
- unsigned long altp =
- (unsigned long) mail_parameters (NIL,GET_ALTIMAPPORT,NIL);
- unsigned long defprt = imap_defaultport ? imap_defaultport : IMAPTCPPORT;
- stream->local = /* instantiate localdata */
- (void *) memset (fs_get (sizeof (IMAPLOCAL)),0,sizeof (IMAPLOCAL));
- /* assume IMAP2bis server */
- LOCAL->imap2bis = LOCAL->rfc1176 = T;
- /* just do this if rimap not allowed */
- if (stream->anonymous || mb.port || mb.altflag)
- reply = imap_tcp (stream,&mb,NIL,defprt,altd,alts,altp);
- /* try secure rimap */
- else if (!(reply = imap_rimap (stream,"*imap",&mb,usr,tmp))) {
- /* no secure rimap, need to do tryalt? */
- if ((mb.tryaltflag || mail_parameters (NIL,GET_TRYALTFIRST,NIL)) &&
- altd && (reply = imap_tcp (stream,&mb,NIL,0,altd,alts,altp)))
- mb.altflag = T; /* tryalt succeeded, light altflag */
- /* no alt, try rimap first, then TCP */
- else if (!(reply = imap_rimap (stream,"imap",&mb,usr,tmp)))
- reply = imap_tcp (stream,&mb,NIL,defprt,NIL,NIL,NIL);
- }
- /* if have a connection */
- if (LOCAL->netstream && reply && imap_OK (stream,reply)) {
- /* if not preauthenticated */
- if (strcmp (reply->key,"PREAUTH")) {
- /* get server capabilities */
- imap_send (stream,"CAPABILITY",NIL);
- /* remote name for authentication */
- strncpy (mb.host,net_remotehost (LOCAL->netstream),NETMAXHOST-1);
- mb.host[NETMAXHOST-1] = ' ';
- if (!(stream->anonymous ? imap_anon (stream,tmp) :
- (LOCAL->use_auth ? imap_auth (stream,&mb,tmp,usr) :
- imap_login (stream,&mb,tmp,usr)))) {
- /* failed, is there a referral? */
- if (ir && LOCAL->referral &&
- (s = (*ir) (stream,LOCAL->referral,REFAUTHFAILED))) {
- imap_close (stream,NIL);
- fs_give ((void **) &stream->mailbox);
- stream->mailbox = s;/* set as new mailbox name to open */
- return imap_open (stream);
- }
- return NIL; /* authentication failed */
- }
- }
- }
- else { /* log error if there was one */
- if (reply) mm_log (reply->text,ERROR);
- return NIL; /* lost during greeting */
- }
- /* get server capabilities again */
- imap_send (stream,"CAPABILITY",NIL);
- }
- if (LOCAL->netstream) { /* still have a connection? */
- if (ir && LOCAL->referral &&
- (s = (*ir) (stream,LOCAL->referral,REFAUTH))) {
- imap_close (stream,NIL);
- fs_give ((void **) &stream->mailbox);
- stream->mailbox = s; /* set as new mailbox name to open */
- return imap_open (stream);/* recurse to log in on real site */
- }
- stream->perm_seen = stream->perm_deleted = stream->perm_answered =
- stream->perm_draft = LEVELIMAP4 (stream) ? NIL : T;
- stream->perm_user_flags = LEVELIMAP4 (stream) ? NIL : 0xffffffff;
- stream->sequence++; /* bump sequence number */
- sprintf (tmp,"{%s",net_host (LOCAL->netstream));
- if (!((i = net_port (LOCAL->netstream)) & 0xffff0000))
- sprintf (tmp + strlen (tmp),":%lu",i);
- strcat (tmp,"/imap");
- if (mb.altflag) sprintf (tmp + strlen (tmp),"/%s",(char *)
- mail_parameters (NIL,GET_ALTDRIVERNAME,NIL));
- if (mb.secflag) strcat (tmp,"/secure");
- if (stream->anonymous) strcat (tmp,"/anonymous}");
- else { /* record user name */
- if (!LOCAL->user && usr[0]) LOCAL->user = cpystr (usr);
- if (LOCAL->user) sprintf (tmp + strlen (tmp),"/user="%s"}",
- LOCAL->user);
- }
- if (!stream->halfopen) { /* wants to open a mailbox? */
- IMAPARG *args[2];
- IMAPARG ambx;
- ambx.type = ASTRING;
- ambx.text = (void *) mb.mailbox;
- args[0] = &ambx; args[1] = NIL;
- if (imap_OK (stream,reply = imap_send (stream,stream->rdonly ?
- "EXAMINE": "SELECT",args))) {
- strcat (tmp,mb.mailbox);/* mailbox name */
- if (!stream->nmsgs && !stream->silent)
- mm_log ("Mailbox is empty",(long) NIL);
- /* note if an INBOX or not */
- stream->inbox = !strcmp (ucase (mb.mailbox),"INBOX");
- }
- else if (ir && LOCAL->referral &&
- (s = (*ir) (stream,LOCAL->referral,REFSELECT))) {
- imap_close (stream,NIL);
- fs_give ((void **) &stream->mailbox);
- stream->mailbox = s; /* set as new mailbox name to open */
- return imap_open (stream);
- }
- else {
- mm_log (reply->text,ERROR);
- if (imap_closeonerror) return NIL;
- stream->halfopen = T; /* let him keep it half-open */
- }
- }
- if (stream->halfopen) { /* half-open connection? */
- strcat (tmp,"<no_mailbox>");
- /* make sure dummy message counts */
- mail_exists (stream,(long) 0);
- mail_recent (stream,(long) 0);
- }
- fs_give ((void **) &stream->mailbox);
- stream->mailbox = cpystr (tmp);
- }
- /* success if stream open */
- return LOCAL->netstream ? stream : NIL;
- }
- /* IMAP TCP connect
- * Accepts: MAIL stream
- * NETMBX specification
- * default network driver
- * default port (zero means do tryalt behavior)
- * alternate network driver
- * alternate driver service name
- * alternate driver port
- * Returns: parsed reply if success, else NIL
- */
- IMAPPARSEDREPLY *imap_tcp (MAILSTREAM *stream,NETMBX *mb,NETDRIVER *dv,
- unsigned long port,
- NETDRIVER *altd,char *alts,unsigned long altp)
- {
- return (LOCAL->netstream =
- (port ? net_open (mb,dv,port,altd,alts,altp) :
- net_open_work (altd,mb->host,alts,altp,mb->port,T))) ?
- imap_reply (stream,NIL) : NIL;
- }
- /* IMAP rimap connect
- * Accepts: MAIL stream
- * NETMBX specification
- * service to use
- * user name
- * scratch buffer
- * Returns: parsed reply if success, else NIL
- */
- IMAPPARSEDREPLY *imap_rimap (MAILSTREAM *stream,char *service,NETMBX *mb,
- char *usr,char *tmp)
- {
- unsigned long i;
- char c[2];
- NETSTREAM *tstream;
- IMAPPARSEDREPLY *reply = NIL;
- /* try rimap open */
- if (tstream = net_aopen (NIL,mb,service,usr)) {
- /* if success, see if reasonable banner */
- if (net_getbuffer (tstream,(long) 1,c) && (*c == '*')) {
- i = 0; /* copy to buffer */
- do tmp[i++] = *c;
- while (net_getbuffer (tstream,(long) 1,c) && (*c != ' 15') &&
- (*c != ' 12') && (i < (MAILTMPLEN-1)));
- tmp[i] = ' '; /* tie off */
- /* snarfed a valid greeting? */
- if ((*c == ' 15') && net_getbuffer (tstream,(long) 1,c) &&
- (*c == ' 12') &&
- !strcmp ((reply = imap_parse_reply (stream,cpystr (tmp)))->tag,"*")){
- /* parse line as IMAP */
- imap_parse_unsolicited (stream,reply);
- /* make sure it looked like good stuff */
- if (imap_OK (stream,reply)) {
- LOCAL->netstream = tstream;
- return reply; /* return success */
- }
- }
- }
- net_close (tstream); /* failed, punt the temporary netstream */
- }
- return NIL;
- }
- /* IMAP log in as anonymous
- * Accepts: stream to authenticate
- * scratch buffer
- * Returns: T on success, NIL on failure
- */
- long imap_anon (MAILSTREAM *stream,char *tmp)
- {
- IMAPPARSEDREPLY *reply;
- char *s = net_localhost (LOCAL->netstream);
- if (LOCAL->use_authanon) {
- char tag[16];
- unsigned long i;
- char *broken = "IMAP connection broken (anonymous auth)";
- sprintf (tag,"%08lx",stream->gensym++);
- /* build command */
- sprintf (tmp,"%s AUTHENTICATE ANONYMOUS",tag);
- if (!imap_soutr (stream,tmp)) {
- mm_log (broken,ERROR);
- return NIL;
- }
- if (imap_challenge (stream,&i)) imap_response (stream,s,strlen (s));
- /* get response */
- if (!(reply = &LOCAL->reply)->tag)
- reply = imap_fake (stream,tag,broken);
- /* what we wanted? */
- if (strcmp (reply->tag,tag)) {
- /* abort if don't have tagged response */
- while (strcmp ((reply = imap_reply (stream,tag))->tag,tag))
- imap_soutr (stream,"*");
- }
- }
- else {
- IMAPARG *args[2];
- IMAPARG ausr;
- ausr.type = ASTRING;
- ausr.text = (void *) s;
- args[0] = &ausr; args[1] = NIL;
- /* send "LOGIN anonymous <host>" */
- reply = imap_send (stream,"LOGIN ANONYMOUS",args);
- }
- /* success if reply OK */
- if (imap_OK (stream,reply)) return T;
- mm_log (reply->text,ERROR);
- return NIL;
- }
- /* IMAP authenticate
- * Accepts: stream to authenticate
- * parsed network mailbox structure
- * scratch buffer
- * place to return user name
- * Returns: T on success, NIL on failure
- */
- long imap_auth (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
- {
- unsigned long trial,ua;
- int ok;
- char tag[16];
- char *lsterr = NIL;
- AUTHENTICATOR *at;
- IMAPPARSEDREPLY *reply;
- for (ua = LOCAL->use_auth; LOCAL->netstream && ua &&
- (at = mail_lookup_auth (find_rightmost_bit (&ua) + 1));) {
- if (lsterr) { /* previous authenticator failed? */
- sprintf(tmp,"Retrying using %s authentication after %s",at->name,lsterr);
- mm_log (tmp,NIL);
- fs_give ((void **) &lsterr);
- }
- trial = 0; /* initial trial count */
- tmp[0] = ' '; /* no error */
- do { /* gensym a new tag */
- if (tmp[0]) mm_log (tmp,WARN);
- sprintf (tag,"%08lx",stream->gensym++);
- /* build command */
- sprintf (tmp,"%s AUTHENTICATE %s",tag,at->name);
- if (imap_soutr (stream,tmp)) {
- ok = (*at->client) (imap_challenge,imap_response,mb,stream,&trial,usr);
- /* get response */
- if (!(reply = &LOCAL->reply)->tag)
- reply=imap_fake (stream,tag,"IMAP connection broken (authenticate)");
- /* what we wanted? */
- if (strcmp (reply->tag,tag)) {
- /* abort if don't have tagged response */
- while (strcmp ((reply = imap_reply (stream,tag))->tag,tag))
- imap_soutr (stream,"*");
- }
- /* cancel any last error */
- if (lsterr) fs_give ((void **) &lsterr);
- /* done if got success response */
- if (ok && imap_OK (stream,reply)) return T;
- lsterr = cpystr (reply->text);
- sprintf (tmp,"Retrying %s authentication after %s",at->name,lsterr);
- }
- }
- while (LOCAL->netstream && !LOCAL->byeseen && trial &&
- (trial < imap_maxlogintrials));
- }
- if (lsterr) { /* previous authenticator failed? */
- sprintf (tmp,"Can not authenticate to IMAP server: %s",lsterr);
- mm_log (tmp,ERROR);
- fs_give ((void **) &lsterr);
- }
- return NIL; /* ran out of authenticators */
- }
- /* IMAP login
- * Accepts: stream to login
- * parsed network mailbox structure
- * scratch buffer
- * place to return user name
- * Returns: T on success, NIL on failure
- */
- long imap_login (MAILSTREAM *stream,NETMBX *mb,char *tmp,char *usr)
- {
- unsigned long trial = 0;
- IMAPPARSEDREPLY *reply;
- IMAPARG *args[3];
- IMAPARG ausr,apwd;
- if (stream->secure) { /* never do LOGIN if want security */
- mm_log ("Can't do secure authentication with this server",ERROR);
- return NIL;
- }
- ausr.type = apwd.type = ASTRING;
- ausr.text = (void *) usr;
- apwd.text = (void *) tmp;
- args[0] = &ausr; args[1] = &apwd; args[2] = NIL;
- while (LOCAL->netstream && !LOCAL->byeseen && !LOCAL->referral &&
- (trial < imap_maxlogintrials)) {
- tmp[0] = 0; /* prompt user for password */
- mm_login (mb,usr,tmp,trial++);
- if (!tmp[0]) { /* user refused to give a password */
- mm_log ("Login aborted",ERROR);
- return NIL;
- }
- /* send "LOGIN usr tmp" */
- if (imap_OK (stream,reply = imap_send (stream,"LOGIN",args))) return T;
- mm_log (reply->text,WARN);
- }
- if (!LOCAL->referral) mm_log ("Too many login failures",ERROR);
- return NIL;
- }
- /* Get challenge to authenticator in binary
- * Accepts: stream
- * pointer to returned size
- * Returns: challenge or NIL if not challenge
- */
- void *imap_challenge (void *s,unsigned long *len)
- {
- MAILSTREAM *stream = (MAILSTREAM *) s;
- IMAPPARSEDREPLY *reply;
- while (LOCAL->netstream) { /* parse reply from server */
- if (reply = imap_parse_reply (stream,net_getline (LOCAL->netstream))) {
- /* received challenge? */
- if (!strcmp (reply->tag,"+"))
- return rfc822_base64 ((unsigned char *) reply->text,
- strlen (reply->text),len);
- /* untagged data? */
- else if (!strcmp (reply->tag,"*")) imap_parse_unsolicited (stream,reply);
- else break; /* tagged response */
- }
- }
- return NIL; /* tagged response, bogon, or lost stream */
- }
- /* Send authenticator response in BASE64
- * Accepts: MAIL stream
- * string to send
- * length of string
- * Returns: T if successful, else NIL
- */
- long imap_response (void *s,char *response,unsigned long size)
- {
- MAILSTREAM *stream = (MAILSTREAM *) s;
- unsigned long i,j,ret;
- char *t,*u;
- if (response) { /* make CRLFless BASE64 string */
- if (size) {
- for (t = (char *) rfc822_binary ((void *) response,size,&i),u = t,j = 0;
- j < i; j++) if (t[j] > ' ') *u++ = t[j];
- *u = ' '; /* tie off string for mm_dlog() */
- if (stream->debug) mm_dlog (t);
- /* append CRLF */
- *u++ = ' 15'; *u++ = ' 12';
- ret = net_sout (LOCAL->netstream,t,u - t);
- fs_give ((void **) &t);
- }
- else ret = imap_soutr (stream,"");
- }
- /* abort requested */
- else ret = imap_soutr (stream,"*");
- return ret;
- }
- /* IMAP close
- * Accepts: MAIL stream
- * option flags
- */
- void imap_close (MAILSTREAM *stream,long options)
- {
- THREADER *thr,*t;
- IMAPPARSEDREPLY *reply;
- if (stream && LOCAL) { /* send "LOGOUT" */
- if (!LOCAL->byeseen) { /* don't even think of doing it if saw a BYE */
- /* expunge silently if requested */
- if (options & CL_EXPUNGE) imap_send (stream,"EXPUNGE",NIL);
- if (LOCAL->netstream &&
- !imap_OK (stream,reply = imap_send (stream,"LOGOUT",NIL)))
- mm_log (reply->text,WARN);
- }
- /* close NET connection if still open */
- if (LOCAL->netstream) net_close (LOCAL->netstream);
- LOCAL->netstream = NIL;
- /* free up memory */
- if (LOCAL->sortdata) fs_give ((void **) &LOCAL->sortdata);
- if (LOCAL->namespace) {
- mail_free_namespace (&LOCAL->namespace[0]);
- mail_free_namespace (&LOCAL->namespace[1]);
- mail_free_namespace (&LOCAL->namespace[2]);
- fs_give ((void **) &LOCAL->namespace);
- }
- if (LOCAL->threaddata) mail_free_threadnode (&LOCAL->threaddata);
- if (thr = LOCAL->threader) {/* flush threaders */
- while (t = thr) {
- fs_give ((void **) &t->name);
- thr = t->next;
- fs_give ((void **) &t);
- }
- }
- if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
- if (LOCAL->user) fs_give ((void **) &LOCAL->user);
- if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
- /* nuke the local data */
- fs_give ((void **) &stream->local);
- }
- }
- /* IMAP fetch fast information
- * Accepts: MAIL stream
- * sequence
- * option flags
- *
- * Generally, imap_fetchstructure is preferred
- */
- void imap_fast (MAILSTREAM *stream,char *sequence,long flags)
- { /* send "FETCH sequence FAST" */
- char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ? "UID FETCH":"FETCH";
- IMAPPARSEDREPLY *reply;
- IMAPARG *args[4],aseq,aatt[2];
- aseq.type = SEQUENCE; aseq.text = (void *) sequence;
- args[0] = &aseq; args[1] = &aatt[0];
- if (LEVELIMAP4 (stream)) { /* send the hairier form if IMAP4 */
- aatt[0].type = aatt[1].type = ATOM;
- aatt[0].text = (void *) "(UID";
- aatt[1].text = (void *) fasttrailer;
- args[2] = &aatt[1];
- args[3] = NIL;
- }
- else { /* just do FETCH FAST */
- aatt[0].type = ATOM;
- aatt[0].text = (void *) "FAST";
- args[2] = NIL;
- }
- if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
- mm_log (reply->text,ERROR);
- }
- /* IMAP fetch flags
- * Accepts: MAIL stream
- * sequence
- * option flags
- */
- void imap_flags (MAILSTREAM *stream,char *sequence,long flags)
- { /* send "FETCH sequence FLAGS" */
- char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ? "UID FETCH":"FETCH";
- IMAPPARSEDREPLY *reply;
- IMAPARG *args[3],aseq,aatt;
- aseq.type = SEQUENCE; aseq.text = (void *) sequence;
- aatt.type = ATOM; aatt.text = (void *) "FLAGS";
- args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
- if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
- mm_log (reply->text,ERROR);
- }
- /* IMAP fetch message overview
- * Accepts: mail stream
- * UID sequence to fetch
- * pointer to overview return function
- * Returns: T if successful, NIL otherwise
- */
- long imap_overview (MAILSTREAM *stream,char *sequence,overview_t ofn)
- {
- MESSAGECACHE *elt;
- ENVELOPE *env;
- OVERVIEW ov;
- char *s,*t;
- unsigned long i,j,start,last,len;
- if (!mail_uid_sequence (stream,sequence) || !LOCAL->netstream) return NIL;
- /* build overview sequence */
- for (i = 1,len = start = last = 0,s = t = NIL; i <= stream->nmsgs; ++i)
- if ((elt = mail_elt (stream,i))->sequence) {
- if (!elt->private.msg.env) {
- if (s) { /* continuing a sequence */
- if (i == last + 1) last = i;
- else { /* end of range */
- if (last != start) sprintf (t,":%lu,%lu",last,i);
- else sprintf (t,",%lu",i);
- start = last = i; /* begin a new range */
- if ((j = ((t += strlen (t)) - s)) > (MAILTMPLEN - 20)) {
- fs_resize ((void **) s,len += MAILTMPLEN);
- t = s + j; /* relocate current pointer */
- }
- }
- }
- else { /* first time, start new buffer */
- s = (char *) fs_get (len = MAILTMPLEN);
- sprintf (s,"%lu",start = last = i);
- t = s + strlen (s); /* end of buffer */
- }
- }
- }
- /* last sequence */
- if (last != start) sprintf (t,":%lu",last);
- if (s) { /* prefetch as needed */
- IMAPARG *args[7],aseq,aatt[5];
- args[0] = &aseq; args[1] = &aatt[0];
- aseq.type = SEQUENCE; aseq.text = (void *) s;
- /* send the hairier form if IMAP4rev1 */
- if (LEVELIMAP4rev1 (stream)) {
- aatt[0].type = aatt[1].type = aatt[2].type = aatt[3].type = aatt[4].type
- = ATOM;
- aatt[0].text = (void *) allheader;
- aatt[1].text = (void *) hdrheader;
- aatt[3].text = (void *) hdrtrailer;
- aatt[4].text = (void *) fasttrailer;
- args[i = 2] = &aatt[1];
- if (aatt[2].text = (void *) imap_extrahdrs) args[++i] = &aatt[2];
- args[++i] = &aatt[3];
- args[++i] = &aatt[4];
- args[++i] = NIL;
- }
- else { /* just do FETCH ALL */
- aatt[0].type = ATOM;
- aatt[0].text = (void *) "ALL";
- args[2] = NIL;
- }
- imap_send (stream,"FETCH",args);
- fs_give ((void **) &s);
- }
- ov.optional.lines = 0; /* now overview each message */
- ov.optional.xref = NIL;
- if (ofn) for (i = 1; i <= stream->nmsgs; i++)
- if (((elt = mail_elt (stream,i))->sequence) &&
- (env = mail_fetch_structure (stream,i,NIL,NIL)) && ofn) {
- ov.subject = env->subject;
- ov.from = env->from;
- ov.date = env->date;
- ov.message_id = env->message_id;
- ov.references = env->references;
- ov.optional.octets = elt->rfc822_size;
- (*ofn) (stream,mail_uid (stream,i),&ov);
- }
- return LONGT;
- }
- /* IMAP fetch structure
- * Accepts: MAIL stream
- * message # to fetch
- * pointer to return body
- * option flags
- * Returns: envelope of this message, body returned in body value
- *
- * Fetches the "fast" information as well
- */
- ENVELOPE *imap_structure (MAILSTREAM *stream,unsigned long msgno,BODY **body,
- long flags)
- {
- unsigned long i,j,k;
- char *s,seq[128],tmp[MAILTMPLEN];
- MESSAGECACHE *elt;
- ENVELOPE **env;
- BODY **b;
- IMAPPARSEDREPLY *reply = NIL;
- IMAPARG *args[3],aseq,aatt;
- args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
- aseq.type = SEQUENCE; aseq.text = (void *) seq;
- aatt.type = ATOM; aatt.text = NIL;
- if (flags & FT_UID) /* see if can find msgno from UID */
- for (i = 1; i <= stream->nmsgs; i++)
- if ((elt = mail_elt (stream,i))->private.uid == msgno) {
- msgno = i; /* found msgno, use it from now on */
- flags &= ~FT_UID; /* no longer a UID fetch */
- }
- sprintf (seq,"%lu",msgno); /* initial sequence */
- /* IMAP UID fetching is a special case */
- if (LEVELIMAP4 (stream) && (flags & FT_UID)) {
- strcpy (tmp,allheader);
- if (LEVELIMAP4rev1(stream)){/* get extra headers if IMAP4rev1 */
- if (imap_extrahdrs) sprintf (tmp + strlen (tmp)," %s %s %s",
- hdrheader,imap_extrahdrs,hdrtrailer);
- else sprintf (tmp + strlen (tmp)," %s %s",hdrheader,hdrtrailer);
- }
- if (body) strcat (tmp," BODYSTRUCTURE");
- sprintf (tmp + strlen (tmp)," %s",fasttrailer);
- aatt.text = (void *) tmp; /* do the built command */
- if (!imap_OK (stream,reply = imap_send (stream,"UID FETCH",args))) {
- mm_log (reply->text,ERROR);
- }
- /* now hunt for this UID */
- for (i = 1; i <= stream->nmsgs; i++)
- if ((elt = mail_elt (stream,i))->private.uid == msgno) {
- if (body) *body = elt->private.msg.body;
- return elt->private.msg.env;
- }
- if (body) *body = NIL; /* can't find the UID */
- return NIL;
- }
- elt = mail_elt (stream,msgno);/* get cache pointer */
- if (stream->scache) { /* short caching? */
- env = &stream->env; /* use temporaries on the stream */
- b = &stream->body;
- if (msgno != stream->msgno){/* flush old poop if a different message */
- mail_free_envelope (env);
- mail_free_body (b);
- stream->msgno = msgno; /* this is now the current short cache msg */
- }
- }
- else { /* normal cache */
- env = &elt->private.msg.env;/* get envelope and body pointers */
- b = &elt->private.msg.body;
- /* prefetch if don't have envelope */
- if ((k = imap_lookahead) && (!*env || (*env)->incomplete))
- /* build message number list */
- for (i = msgno + 1, s = seq; k && (i <= stream->nmsgs); i++)
- if (!mail_elt (stream,i)->private.msg.env) {
- s += strlen (s); /* find string end, see if nearing end */
- if ((s - seq) > (MAILTMPLEN - 20)) break;
- sprintf (s,",%lu",i); /* append message */
- for (j = i + 1, k--; /* hunt for last message without an envelope */
- k && (j <= stream->nmsgs) &&
- !mail_elt (stream,j)->private.msg.env; j++, k--);
- /* if different, make a range */
- if (i != --j) sprintf (s + strlen (s),":%lu",i = j);
- }
- }
- if (LEVELIMAP4 (stream)) { /* has extensible body structure and UIDs */
- tmp[0] = ' '; /* initialize command */
- /* need envelope? */
- if (!*env || (*env)->incomplete) {
- strcat (tmp," ENVELOPE"); /* yes, get it and possible extra poop */
- if (LEVELIMAP4rev1 (stream)) {
- if (imap_extrahdrs) sprintf (tmp + strlen (tmp)," %s %s %s",
- hdrheader,imap_extrahdrs,hdrtrailer);
- else sprintf (tmp + strlen (tmp)," %s %s",hdrheader,hdrtrailer);
- }
- }
- /* need anything else? */
- if (body && !*b) strcat (tmp," BODYSTRUCTURE");
- if (!elt->private.uid) strcat (tmp," UID");
- if (!elt->day) strcat (tmp," INTERNALDATE");
- if (!elt->rfc822_size) strcat (tmp," RFC822.SIZE");
- if (tmp[0]) { /* anything to do? */
- strcat (tmp," FLAGS)"); /* always get current flags */
- tmp[0] = '('; /* make into a list */
- aatt.text = (void *) tmp; /* do the built command */
- }
- }
- /* has non-extensive body */
- else if (LEVELIMAP2bis (stream)) {
- if (!*env || (*env)->incomplete)
- aatt.text = (body && !*b) ? (void *) "FULL" : (void *) "ALL";
- else if (body && !*b) aatt.text = (void *) "BODY";
- else if (!(elt->rfc822_size && elt->day)) aatt.text = (void *) "FAST";
- }
- else if (!*env || (*env)->incomplete) aatt.text = (void *) "ALL";
- else if (!(elt->rfc822_size && elt->day)) aatt.text = (void *) "FAST";
- if (aatt.text) { /* need to fetch anything? */
- if (!imap_OK (stream,reply = imap_send (stream,"FETCH",args))) {
- /* failed, probably RFC-1176 server */
- if (!LEVELIMAP4 (stream) && LEVELIMAP2bis (stream) && body && !*b){
- aatt.text = (void *) "ALL";
- if (imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
- LOCAL->imap2bis = NIL;/* doesn't have body capabilities */
- else mm_log (reply->text,ERROR);
- }
- else mm_log (reply->text,ERROR);
- }
- }
- if (body) *body = *b; /* return the body */
- return *env; /* return the envelope */
- }
- /* IMAP fetch message data
- * Accepts: MAIL stream
- * message number
- * section specifier
- * offset of first designated byte or 0 to start at beginning
- * maximum number of bytes or 0 for all bytes
- * lines to fetch if header
- * flags
- * Returns: T on success, NIL on failure
- */
- long imap_msgdata (MAILSTREAM *stream,unsigned long msgno,char *section,
- unsigned long first,unsigned long last,STRINGLIST *lines,
- long flags)
- {
- char *t,tmp[MAILTMPLEN],part[40];
- char *cmd = (LEVELIMAP4 (stream) && (flags & FT_UID)) ? "UID FETCH":"FETCH";
- IMAPPARSEDREPLY *reply;
- IMAPARG *args[5],aseq,aatt,alns,acls;
- aseq.type = NUMBER; aseq.text = (void *) msgno;
- aatt.type = ATOM; /* assume atomic attribute */
- alns.type = LIST; alns.text = (void *) lines;
- acls.type = BODYCLOSE; acls.text = (void *) part;
- args[0] = &aseq; args[1] = &aatt; args[2] = args[3] = args[4] = NIL;
- part[0] = ' '; /* initially no partial specifier */
- if (!(flags & FT_PREFETCHTEXT) &&
- LEVELIMAP4rev1 (stream)) {/* easy if IMAP4rev1 server and no prefetch */
- aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
- if (lines) { /* want specific header lines? */
- sprintf (tmp,"%s.FIELDS%s",section,(flags & FT_NOT) ? ".NOT" : "");
- aatt.text = (void *) tmp;
- args[2] = &alns; args[3] = &acls;
- }
- else {
- aatt.text = (void *) section;
- args[2] = &acls;
- }
- if (first || last) sprintf (part,"<%lu.%lu>",first,last ? last:-1);
- }
- #if 0
- /* I don't think that this is a good idea. If partial fetch isn't available,
- * the application is going to have to do a full fetch anyway if it wants to
- * get the data. The mailgets call will indicate what happened.
- */
- else if (first || last) { /* partial fetching is only on IMAP4rev1 */
- mm_notify (stream,"[NOTIMAP4REV1] Can't do partial fetch",WARN);
- return NIL;
- }
- #endif
- /* BODY.PEEK[HEADER] becomes RFC822.HEADER */
- else if (!strcmp (section,"HEADER")) {
- if (flags & FT_PEEK) aatt.text = (void *)
- ((flags & FT_PREFETCHTEXT) ? "(RFC822.HEADER RFC822.TEXT)" :
- "RFC822.HEADER");
- else {
- mm_notify (stream,"[NOTIMAP4] Can't do non-peeking header fetch",WARN);
- return NIL;
- }
- }
- /* other peeking was introduced in RFC-1730 */
- else if ((flags & FT_PEEK) && !LEVEL1730 (stream)) {
- mm_notify (stream,"[NOTIMAP4] Can't do peeking fetch",WARN);
- return NIL;
- }
- /* BODY[TEXT] becomes RFC822.TEXT */
- else if (!strcmp (section,"TEXT")) aatt.text = (void *)
- ((flags & FT_PEEK) ? "RFC822.TEXT.PEEK" : "RFC822.TEXT");
- /* BODY[] becomes RFC822 */
- else if (!section[0]) aatt.text = (void *)
- ((flags & FT_PEEK) ? "RFC822.PEEK" : "RFC822");
- /* nested header */
- else if (t = strstr (section,".HEADER")) {
- if (!LEVEL1730 (stream)) { /* this was introduced in RFC-1730 */
- mm_notify (stream,"[NOTIMAP4] Can't do nested header fetch",WARN);
- return NIL;
- }
- aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
- args[2] = &acls; /* will need to close section */
- aatt.text = (void *) tmp; /* convert .HEADER to .0 for RFC-1730 server */
- strncpy (tmp,section,t-section);
- strcpy (tmp+(t-section),".0");
- }
- /* extended nested text */
- else if (strstr (section,".MIME") || strstr (section,".TEXT")) {
- mm_notify (stream,"[NOTIMAP4REV1] Can't do extended body part fetch",WARN);
- return NIL;
- }
- /* nested message */
- else if (LEVELIMAP2bis (stream)) {
- aatt.type = (flags & FT_PEEK) ? BODYPEEK : BODYTEXT;
- args[2] = &acls; /* will need to close section */
- aatt.text = (void *) section;
- }
- else { /* ancient server */
- mm_notify (stream,"[NOTIMAP2BIS] Can't do body part fetch",WARN);
- return NIL;
- }
- /* send the fetch command */
- if (!imap_OK (stream,reply = imap_send (stream,cmd,args))) {
- mm_log (reply->text,ERROR);
- return NIL; /* failure */
- }
- return T;
- }
- /* IMAP fetch UID
- * Accepts: MAIL stream
- * message number
- * Returns: UID
- */
- unsigned long imap_uid (MAILSTREAM *stream,unsigned long msgno)
- {
- MESSAGECACHE *elt;
- IMAPPARSEDREPLY *reply;
- IMAPARG *args[3],aseq,aatt;
- char *s,seq[MAILTMPLEN];
- unsigned long i,j,k;
- /* IMAP2 didn't have UIDs */
- if (!LEVELIMAP4 (stream)) return msgno;
- /* do we know its UID yet? */
- if (!(elt = mail_elt (stream,msgno))->private.uid) {
- aseq.type = SEQUENCE; aseq.text = (void *) seq;
- aatt.type = ATOM; aatt.text = (void *) "UID";
- args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
- sprintf (seq,"%lu",msgno);
- if (k = imap_uidlookahead) {/* build UID list */
- for (i = msgno + 1, s = seq; k && (i <= stream->nmsgs); i++)
- if (!mail_elt (stream,i)->private.uid) {
- s += strlen (s); /* find string end, see if nearing end */
- if ((s - seq) > (MAILTMPLEN - 20)) break;
- sprintf (s,",%lu",i); /* append message */
- for (j = i + 1, k--; /* hunt for last message without a UID */
- k && (j <= stream->nmsgs) && !mail_elt (stream,j)->private.uid;
- j++, k--);
- /* if different, make a range */
- if (i != --j) sprintf (s + strlen (s),":%lu",i = j);
- }
- }
- /* send "FETCH msgno UID" */
- if (!imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
- mm_log (reply->text,ERROR);
- }
- return elt->private.uid; /* return our UID now */
- }
- /* IMAP fetch message number from UID
- * Accepts: MAIL stream
- * UID
- * Returns: message number
- */
- unsigned long imap_msgno (MAILSTREAM *stream,unsigned long uid)
- {
- IMAPPARSEDREPLY *reply;
- IMAPARG *args[3],aseq,aatt;
- char seq[MAILTMPLEN];
- unsigned long msgno;
- /* IMAP2 didn't have UIDs */
- if (!LEVELIMAP4 (stream)) return uid;
- /* have server hunt for UID */
- aseq.type = SEQUENCE; aseq.text = (void *) seq;
- aatt.type = ATOM; aatt.text = (void *) "UID";
- args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
- sprintf (seq,"%lu",uid);
- /* send "UID FETCH uid UID" */
- if (!imap_OK (stream,reply = imap_send (stream,"UID FETCH",args)))
- mm_log (reply->text,ERROR);
- for (msgno = 1; msgno <= stream->nmsgs; msgno++)
- if (mail_elt (stream,msgno)->private.uid == uid) return msgno;
- return 0; /* didn't find the UID anywhere */
- }
- /* IMAP modify flags
- * Accepts: MAIL stream
- * sequence
- * flag(s)
- * option flags
- */
- void imap_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
- {
- char *cmd = (LEVELIMAP4 (stream) && (flags & ST_UID)) ? "UID STORE":"STORE";
- IMAPPARSEDREPLY *reply;
- IMAPARG *args[4],aseq,ascm,aflg;
- aseq.type = SEQUENCE; aseq.text = (void *) sequence;
- ascm.type = ATOM; ascm.text = (void *)
- ((flags & ST_SET) ?
- ((LEVELIMAP4 (stream) && (flags & ST_SILENT)) ?
- "+Flags.silent" : "+Flags") :
- ((LEVELIMAP4 (stream) && (flags & ST_SILENT)) ?
- "-Flags.silent" : "-Flags"));
- aflg.type = FLAGS; aflg.text = (void *) flag;
- args[0] = &aseq; args[1] = &ascm; args[2] = &aflg; args[3] = NIL;
- /* send "STORE sequence +Flags flag" */
- if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
- mm_log (reply->text,ERROR);
- }
- /* IMAP search for messages
- * Accepts: MAIL stream
- * character set
- * search program
- * option flags
- */
- void imap_search (MAILSTREAM *stream,char *charset,SEARCHPGM *pgm,long flags)
- {
- char *cmd = (LEVELIMAP4 (stream) && (flags & SE_UID)) ?"UID SEARCH":"SEARCH";
- unsigned long i,j,k;
- char *s,tmp[MAILTMPLEN];
- IMAPPARSEDREPLY *reply;
- MESSAGECACHE *elt;
- IMAPARG *args[4],apgm,aseq,aatt,achs;
- args[1] = args[2] = args[3] = NIL;
- apgm.type = SEARCHPROGRAM; apgm.text = (void *) pgm;
- aseq.type = SEQUENCE;
- aatt.type = ATOM;
- achs.type = ASTRING;
- if (charset) { /* optional charset argument requested */
- args[0] = &aatt; args[1] = &achs; args[2] = &apgm;
- aatt.text = (void *) "CHARSET";
- achs.text = (void *) charset;
- }
- else args[0] = &apgm;
- /* be sure that receiver understands */
- LOCAL->uidsearch = (flags & SE_UID) ? T : NIL;
- if (!LEVELIMAP4 (stream) && /* if old server but new functions... */
- (charset || LOCAL->uidsearch || pgm->msgno || pgm->uid || pgm->or ||
- pgm->not || pgm->header || pgm->larger || pgm->smaller ||
- pgm->sentbefore || pgm->senton || pgm->sentsince || pgm->draft ||
- pgm->undraft || pgm->return_path || pgm->sender || pgm->reply_to ||
- pgm->message_id || pgm->in_reply_to || pgm->newsgroups ||
- pgm->followup_to || pgm->references))
- mail_search_default (stream,charset,pgm,flags);
- /* do the SEARCH */
- else if (!imap_OK (stream,reply = imap_send (stream,cmd,args)))
- mm_log (reply->text,ERROR);
- /* can never pre-fetch with a short cache */
- else if ((k = imap_prefetch) && !(flags & (SE_NOPREFETCH|SE_UID)) &&
- !stream->scache) { /* only if prefetching permitted */
- s = LOCAL->tmp; /* build sequence in temporary buffer */
- *s = ' '; /* initially nothing */
- /* search through mailbox */
- for (i = 1; k && (i <= stream->nmsgs); ++i)
- /* for searched messages with no envelope */
- if ((elt = mail_elt (stream,i)) && elt->searched &&
- !mail_elt (stream,i)->private.msg.env) {
- /* prepend with comma if not first time */
- if (LOCAL->tmp[0]) *s++ = ',';
- sprintf (s,"%lu",j = i);/* output message number */
- s += strlen (s); /* point at end of string */
- k--; /* count one up */
- /* search for possible end of range */
- while (k && (i < stream->nmsgs) &&
- (elt = mail_elt (stream,i+1))->searched &&
- !elt->private.msg.env) i++,k--;
- if (i != j) { /* if a range */
- sprintf (s,":%lu",i); /* output delimiter and end of range */
- s += strlen (s); /* point at end of string */
- }
- }
- if (LOCAL->tmp[0]) { /* anything to pre-fetch? */
- args[0] = &aseq; args[1] = &aatt; args[2] = NIL;
- aseq.text = (void *) cpystr (LOCAL->tmp);
- if (LEVELIMAP4 (stream)) {/* IMAP4 fetching does more */
- strcpy (tmp,allheader);
- if (LEVELIMAP4rev1 (stream)) {
- if (imap_extrahdrs) sprintf (tmp + strlen (tmp)," %s %s %s",
- hdrheader,imap_extrahdrs,hdrtrailer);
- else sprintf (tmp + strlen (tmp)," %s %s",hdrheader,hdrtrailer);
- }
- sprintf (tmp + strlen (tmp)," %s",fasttrailer);
- aatt.text = (void *) tmp;
- }
- else aatt.text = (void *) "ALL";
- if (!imap_OK (stream,reply = imap_send (stream,"FETCH",args)))
- mm_log (reply->text,ERROR);
- /* flush copy of sequence */
- fs_give ((void **) &aseq.text);
- }
- }
- }
- /* IMAP sort messages
- * Accepts: mail stream
- * character set
- * search program
- * sort program
- * option flags
- * Returns: vector of sorted message sequences or NIL if error
- */
- unsigned long *imap_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
- SORTPGM *pgm,long flags)
- {
- unsigned long i,j,start,last;
- unsigned long *ret = NIL;
- pgm->nmsgs = 0; /* start off with no messages */
- /* can use server-based sort? */
- if (LOCAL->use_sort && !(flags & SO_NOSERVER)) {
- char *cmd = (flags & SE_UID) ? "UID SORT" : "SORT";
- IMAPARG *args[4],apgm,achs,aspg;
- IMAPPARSEDREPLY *reply;
- SEARCHSET *ss = NIL;
- apgm.type = SORTPROGRAM; apgm.text = (void *) pgm;
- achs.type = ASTRING; achs.text = (void *) (charset ? charset : "US-ASCII");
- aspg.type = SEARCHPROGRAM;
- /* did he provide a searchpgm? */
- if (!(aspg.text = (void *) spg)) {
- for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
- if (mail_elt (stream,i)->searched) {
- if (ss) { /* continuing a sequence */
- if (i == last + 1) last = i;
- else { /* end of range */
- if (last != start) ss->last = last;
- (ss = ss->next = mail_newsearchset ())->first = i;
- start = last = i; /* begin a new range */
- }
- }
- else { /* first time, start new searchpgm */
- (spg = mail_newsearchpgm ())->msgno = ss = mail_newsearchset ();
- ss->first = start = last = i;
- }
- }
- /* nothing to sort if no messages */
- if (!(aspg.text = (void *) spg)) return NIL;
- /* else install last sequence */
- if (last != start) ss->last = last;
- }
- args[0] = &apgm; args[1] = &achs; args[2] = &aspg; args[3] = NIL;
- if (imap_OK (stream,reply = imap_send (stream,cmd,args))) {
- pgm->nmsgs = LOCAL->sortsize;
- ret = LOCAL->sortdata;
- LOCAL->sortdata = NIL; /* mail program is responsible for flushing */
- }
- else mm_log (reply->text,ERROR);
- /* free any temporary searchpgm */
- if (ss) mail_free_searchpgm (&spg);
- }
- /* not much can do if short caching */
- else if (stream->scache) ret = mail_sort_msgs (stream,charset,spg,pgm,flags);
- else { /* try to be a bit more clever */
- char *s,*t;
- unsigned long len;
- MESSAGECACHE *elt;
- SORTCACHE **sc;
- SORTPGM *sp;
- int needenvs = 0;
- /* see if need envelopes */
- for (sp = pgm; sp && !needenvs; sp = sp->next) switch (sp->function) {
- case SORTDATE: case SORTFROM: case SORTSUBJECT: case SORTTO: case SORTCC:
- needenvs = T;
- }
- if (spg) { /* only if a search needs to be done */
- int silent = stream->silent;
- stream->silent = T; /* don't pass up mm_searched() events */
- /* search for messages */
- mail_search_full (stream,charset,spg,NIL);
- stream->silent = silent; /* restore silence state */
- }
- /* initialize progress counters */
- pgm->nmsgs = pgm->progress.cached = 0;
- /* pass 1: count messages to sort */
- for (i = 1,len = start = last = 0,s = t = NIL; i <= stream->nmsgs; ++i)
- if ((elt = mail_elt (stream,i))->searched) {
- pgm->nmsgs++;
- if (needenvs ? !elt->private.msg.env : !elt->day) {
- if (s) { /* continuing a sequence */
- if (i == last + 1) last = i;
- else { /* end of range */
- if (last != start) sprintf (t,":%lu,%lu",last,i);
- else sprintf (t,",%lu",i);
- start = last = i; /* begin a new range */
- if ((j = ((t += strlen (t)) - s)) > (MAILTMPLEN - 20)) {
- fs_resize ((void **) s,len += MAILTMPLEN);
- t = s + j; /* relocate current pointer */
- }
- }
- }
- else { /* first time, start new buffer */
- s = (char *) fs_get (len = MAILTMPLEN);
- sprintf (s,"%lu",start = last = i);
- t = s + strlen (s); /* end of buffer */
- }
- }
- }
- /* last sequence */
- if (last != start) sprintf (t,":%lu",last);
- if (s) { /* prefetch needed data */
- IMAPARG *args[7],aseq,aatt[5];
- args[0] = &aseq; args[1] = &aatt[0];
- aseq.type = SEQUENCE; aseq.text = (void *) s;
- /* send the hairier form if IMAP4rev1 */
- if (needenvs && LEVELIMAP4rev1 (stream)) {
- aatt[0].type = aatt[1].type = aatt[2].type = aatt[3].type =
- aatt[4].type = ATOM;
- aatt[0].text = (void *) allheader;
- aatt[1].text = (void *) hdrheader;
- aatt[3].text = (void *) hdrtrailer;
- aatt[4].text = (void *) fasttrailer;
- args[i = 2] = &aatt[1];
- if (aatt[2].text = (void *) imap_extrahdrs) args[++i] = &aatt[2];
- args[++i] = &aatt[3];
- args[++i] = &aatt[4];
- args[++i] = NIL;
- }
- else { /* just do FETCH ALL */
- aatt[0].type = ATOM;
- aatt[0].text = (void *) (needenvs ? "ALL" : "FAST");
- args[2] = NIL;
- }
- imap_send (stream,"FETCH",args);
- fs_give ((void **) &s);
- }
- if (pgm->nmsgs) { /* pass 2: sort cache */
- sortresults_t sr = (sortresults_t)
- mail_parameters (NIL,GET_SORTRESULTS,NIL);
- sc = mail_sort_loadcache (stream,pgm);
- /* pass 3: sort messages */
- if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
- fs_give ((void **) &sc); /* don't need sort vector any more */
- /* also return via callback if requested */
- if (sr) (*sr) (stream,ret,pgm->nmsgs);
- }
- }
- return ret;
- }
- /* IMAP thread messages
- * Accepts: mail stream
- * thread type
- * character set
- * search program
- * option flags
- * Returns: thread node tree
- */
- THREADNODE *imap_thread (MAILSTREAM *stream,char *type,char *charset,
- SEARCHPGM *spg,long flags)
- {
- THREADNODE *ret = NIL;
- if (LOCAL->threader) {
- unsigned long i,start,last;
- char *cmd = (flags & SE_UID) ? "UID THREAD" : "THREAD";
- IMAPARG *args[4],apgm,achs,aspg;
- IMAPPARSEDREPLY *reply;
- SEARCHSET *ss = NIL;
- apgm.type = ATOM; apgm.text = (void *) type;
- achs.type = ASTRING; achs.text = (void *) (charset ? charset : "US-ASCII");
- aspg.type = SEARCHPROGRAM;
- /* did he provide a searchpgm? */
- if (!(aspg.text = (void *) spg)) {
- for (i = 1,start = last = 0; i <= stream->nmsgs; ++i)
- if (mail_elt (stream,i)->searched) {
- if (ss) { /* continuing a sequence */
- if (i == last + 1) last = i;
- else { /* end of range */
- if (last != start) ss->last = last;
- (ss = ss->next = mail_newsearchset ())->first = i;
- start = last = i; /* begin a new range */
- }
- }
- else { /* first time, start new searchpgm */
- (spg = mail_newsearchpgm ())->msgno = ss = mail_newsearchset ();
- ss->first = start = last = i;
- }
- }
- /* nothing to sort if no messages */
- if (!(aspg.text = (void *) spg)) return NIL;
- /* else install last sequence */
- if (last != start) ss->last = last;
- }
- args[0] = &apgm; args[1] = &achs; args[2] = &aspg; args[3] = NIL;
- if (imap_OK (stream,reply = imap_send (stream,cmd,args))) {
- ret = LOCAL->threaddata;
- LOCAL->threaddata = NIL; /* mail program is responsible for flushing */
- }
- else mm_log (reply->text,ERROR);
- /* free any temporary searchpgm */
- if (ss) mail_free_searchpgm (&spg);
- }
- else ret = mail_thread_msgs (stream,type,charset,spg,flags | SO_NOSERVER,
- imap_sort);
- return ret;
- }
- /* IMAP ping mailbox
- * Accepts: MAIL stream
- * Returns: T if stream still alive, else NIL
- */
- long imap_ping (MAILSTREAM *stream)
- {
- return (LOCAL->netstream && /* send "NOOP" */
- imap_OK (stream,imap_send (stream,"NOOP",NIL))) ? T : NIL;
- }
- /* IMAP check mailbox
- * Accepts: MAIL stream
- */
- void imap_check (MAILSTREAM *stream)
- {
- /* send "CHECK" */
- IMAPPARSEDREPLY *reply = imap_send (stream,"CHECK",NIL);
- mm_log (reply->text,imap_OK (stream,reply) ? (long) NIL : ERROR);
- }
- /* IMAP expunge mailbox
- * Accepts: MAIL stream
- */
- void imap_expunge (MAILSTREAM *stream)
- {
- /* send "EXPUNGE" */
- IMAPPARSEDREPLY *reply = imap_send (stream,"EXPUNGE",NIL);
- mm_log (reply->text,imap_OK (stream,reply) ? (long) NIL : ERROR);
- }
- /* IMAP copy message(s)
- * Accepts: MAIL stream
- * sequence
- * destination mailbox
- * option flags
- * Returns: T if successful else NIL
- */
- long imap_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long flags)
- {
- char *cmd = (LEVELIMAP4 (stream) && (flags & CP_UID)) ? "UID COPY" : "COPY";
- char *s;
- IMAPPARSEDREPLY *reply;
- IMAPARG *args[3],aseq,ambx;
- imapreferral_t ir =
- (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
- mailproxycopy_t pc =
- (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
- aseq.type = SEQUENCE; aseq.text = (void *) sequence;
- ambx.type = ASTRING; ambx.text = (void *) mailbox;
- args[0] = &aseq; args[1] = &ambx; args[2] = NIL;
- /* send "COPY sequence mailbox" */
- if (!imap_OK (stream,reply = imap_send (stream,cmd,args))) {
- if (ir && pc && LOCAL->referral && mail_sequence (stream,sequence) &&
- (s = (*ir) (stream,LOCAL->referral,REFCOPY)))
- return (*pc) (stream,sequence,s,flags);
- mm_log (reply->text,ERROR);
- return NIL;
- }
- /* delete the messages if the user said to */
- if (flags & CP_MOVE) imap_flag (stream,sequence,"\Deleted",
- ST_SET + ((flags & CP_UID) ? ST_UID : NIL));
- return T;
- }
- /* IMAP append message string
- * Accepts: mail stream
- * destination mailbox
- * stringstruct of message to append
- * Returns: T on success, NIL on failure
- */
- long imap_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
- STRING *msg)
- {
- char tmp[MAILTMPLEN];
- long ret = NIL;
- if (mail_valid_net (mailbox,&imapdriver,NIL,tmp)) {
- MAILSTREAM *st = stream;
- /* default stream */
- if (!(stream && LOCAL && LOCAL->netstream))
- stream = mail_open (NIL,mailbox,OP_HALFOPEN|OP_SILENT);
- if (stream) { /* do the operation if valid stream */
- IMAPPARSEDREPLY *reply = NIL;
- IMAPARG *args[5],ambx,aflg,adat,amsg;
- imapreferral_t ir =
- (imapreferral_t) mail_parameters (stream,GET_IMAPREFERRAL,NIL);
- int i = 0;
- ambx.type = ASTRING; ambx.text = (void *) tmp;
- aflg.type = FLAGS; aflg.text = (void *) flags;
- adat.type = ASTRING; adat.text = (void *) date;
- amsg.type = LITERAL; amsg.text = (void *) msg;
- args[i++] = &ambx;
- if (flags) args[i++] = &aflg;
- if (date) args[i++] = &adat;
- args[i++] = &amsg;
- args[i++] = NIL;
- if (!strcmp ((reply = imap_send (stream,"APPEND",args))->key,"BAD") &&
- (flags || date)) { /* full form and got a BAD? */
- /* yes, retry with old IMAP2bis form */
- args[1] = &amsg; args[2] = NIL;
- reply = imap_send (stream,"APPEND",args);
- }
- if (imap_OK (stream,reply)) ret = T;
- else if (ir && LOCAL->referral &&
- (mailbox = (*ir) (stream,LOCAL->referral,REFAPPEND)))
- ret = imap_append (NIL,mailbox,flags,date,msg);
- /* no referral, babble the error */
- else mm_log (reply->text,ERROR);
- /* toss out temporary stream */
- if (st != stream) mail_close (stream);
- }
- else mm_log ("Can't access server for append",ERROR);
- }
- return ret; /* return */
- }
- /* IMAP garbage collect stream
- * Accepts: Mail stream
- * garbage collection flags
- */
- void imap_gc (MAILSTREAM *stream,long gcflags)
- {
- unsigned long i;
- MESSAGECACHE *elt;
- mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
- /* make sure the cache is large enough */
- (*mc) (stream,stream->nmsgs,CH_SIZE);
- if (gcflags & GC_TEXTS) { /* garbage collect texts? */
- if (!stream->scache) for (i = 1; i <= stream->nmsgs; ++i)
- if (elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT))
- imap_gc_body (elt->private.msg.body);
- imap_gc_body (stream->body);
- }
- /* gc cache if requested and unlocked */
- if (gcflags & GC_ELT) for (i = 1; i <= stream->nmsgs; ++i)
- if ((elt = (MESSAGECACHE *) (*mc) (stream,i,CH_ELT)) &&
- (elt->lockcount == 1)) (*mc) (stream,i,CH_FREE);
- }
- /* IMAP garbage collect body texts
- * Accepts: body to GC
- */
- void imap_gc_body (BODY *body)
- {
- PART *part;
- if (body) { /* have a body? */
- if (body->mime.text.data) /* flush MIME data */
- fs_give ((void **) &body->mime.text.data);
- /* flush text contents */
- if (body->contents.text.data)
- fs_give ((void **) &body->contents.text.data);
- body->mime.text.size = body->contents.text.size = 0;
- /* multipart? */
- if (body->type == TYPEMULTIPART)
- for (part = body->nested.part; part; part = part->next)
- imap_gc_body (&part->body);
- /* MESSAGE/RFC822? */
- else if ((body->type == TYPEMESSAGE) && !strcmp (body->subtype,"RFC822")) {
- imap_gc_body (body->nested.msg->body);
- if (body->nested.msg->full.text.data)
- fs_give ((void **) &body->nested.msg->full.text.data);
- if (body->nested.msg->header.text.data)
- fs_give ((void **) &body->nested.msg->header.text.data);
- if (body->nested.msg->text.text.data)
- fs_give ((void **) &body->nested.msg->text.text.data);
- body->nested.msg->full.text.size = body->nested.msg->header.text.size =
- body->nested.msg->text.text.size = 0;
- }
- }
- }
- /* Internal routines */
- /* IMAP send command
- * Accepts: MAIL stream
- * command
- * argument list