imap4r1.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:129k
- * Returns: parsed reply
- */
- #define MAXSEQUENCE 1000
- IMAPPARSEDREPLY *imap_send (MAILSTREAM *stream,char *cmd,IMAPARG *args[])
- {
- IMAPPARSEDREPLY *reply;
- IMAPARG *arg,**arglst;
- SORTPGM *spg;
- STRINGLIST *list;
- SIZEDTEXT st;
- char c,*s,*t,tag[10];
- /* gensym a new tag */
- sprintf (tag,"%08lx",stream->gensym++);
- if (!LOCAL->netstream) return imap_fake (stream,tag,"No-op dead stream");
- mail_lock (stream); /* lock up the stream */
- /* ignored referral from previous command */
- if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
- sprintf (LOCAL->tmp,"%s ",tag);
- for (t = cmd, s = LOCAL->tmp + 9; *t; *s++ = *t++);
- if (arglst = args) while (arg = *arglst++) {
- *s++ = ' '; /* delimit argument with space */
- switch (arg->type) {
- case ATOM: /* atom */
- for (t = (char *) arg->text; *t; *s++ = *t++);
- break;
- case NUMBER: /* number */
- sprintf (s,"%lu",(unsigned long) arg->text);
- s += strlen (s);
- break;
- case FLAGS: /* flag list as a single string */
- if (*(t = (char *) arg->text) != '(') {
- *s++ = '('; /* wrap parens around string */
- while (*t) *s++ = *t++;
- *s++ = ')'; /* wrap parens around string */
- }
- else while (*t) *s++ = *t++;
- break;
- case ASTRING: /* atom or string, must be literal? */
- st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
- if (reply = imap_send_astring (stream,tag,&s,&st,NIL)) return reply;
- break;
- case LITERAL: /* literal, as a stringstruct */
- if (reply = imap_send_literal (stream,tag,&s,arg->text)) return reply;
- break;
- case LIST: /* list of strings */
- list = (STRINGLIST *) arg->text;
- c = '('; /* open paren */
- do { /* for each list item */
- *s++ = c; /* write prefix character */
- if (reply = imap_send_astring (stream,tag,&s,&list->text,NIL))
- return reply;
- c = ' '; /* prefix character for subsequent strings */
- }
- while (list = list->next);
- *s++ = ')'; /* close list */
- break;
- case SEARCHPROGRAM: /* search program */
- if (reply = imap_send_spgm (stream,tag,&s,arg->text)) return reply;
- break;
- case SORTPROGRAM: /* search program */
- c = '('; /* open paren */
- for (spg = (SORTPGM *) arg->text; spg; spg = spg->next) {
- *s++ = c; /* write prefix */
- if (spg->reverse) for (t = "REVERSE "; *t; *s++ = *t++);
- switch (spg->function) {
- case SORTDATE:
- for (t = "DATE"; *t; *s++ = *t++);
- break;
- case SORTARRIVAL:
- for (t = "ARRIVAL"; *t; *s++ = *t++);
- break;
- case SORTFROM:
- for (t = "FROM"; *t; *s++ = *t++);
- break;
- case SORTSUBJECT:
- for (t = "SUBJECT"; *t; *s++ = *t++);
- break;
- case SORTTO:
- for (t = "TO"; *t; *s++ = *t++);
- break;
- case SORTCC:
- for (t = "CC"; *t; *s++ = *t++);
- break;
- case SORTSIZE:
- for (t = "SIZE"; *t; *s++ = *t++);
- break;
- default:
- fatal ("Unknown sort program function in imap_send()!");
- }
- c = ' '; /* prefix character for subsequent items */
- }
- *s++ = ')'; /* close list */
- break;
- case BODYTEXT: /* body section */
- for (t = "BODY["; *t; *s++ = *t++);
- for (t = (char *) arg->text; *t; *s++ = *t++);
- break;
- case BODYPEEK: /* body section */
- for (t = "BODY.PEEK["; *t; *s++ = *t++);
- for (t = (char *) arg->text; *t; *s++ = *t++);
- break;
- case BODYCLOSE: /* close bracket and possible length */
- s[-1] = ']'; /* no leading space */
- for (t = (char *) arg->text; *t; *s++ = *t++);
- break;
- case SEQUENCE: /* sequence */
- while (t = (strlen ((char *) arg->text) > (size_t) MAXSEQUENCE) ?
- strchr (((char *) arg->text) + MAXSEQUENCE - 20,',') : NIL) {
- *t = ' '; /* tie off sequence */
- mail_unlock (stream); /* unlock stream, recurse to do part */
- imap_send (stream,cmd,args);
- mail_lock (stream); /* lock up stream again */
- /* rewrite the tag */
- memcpy (LOCAL->tmp,tag,8);
- *t++ = ','; /* restore the comma */
- arg->text = (void *) t; /* now do rest of data */
- }
- /* falls through */
- case LISTMAILBOX: /* astring with wildcards */
- st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
- if (reply = imap_send_astring (stream,tag,&s,&st,T)) return reply;
- break;
- default:
- fatal ("Unknown argument type in imap_send()!");
- }
- }
- /* send the command */
- reply = imap_sout (stream,tag,LOCAL->tmp,&s);
- mail_unlock (stream); /* unlock stream */
- return reply;
- }
- /* IMAP send atom-string
- * Accepts: MAIL stream
- * reply tag
- * pointer to current position pointer of output bigbuf
- * atom-string to output
- * flag if list_wildcards allowed
- * Returns: error reply or NIL if success
- */
- IMAPPARSEDREPLY *imap_send_astring (MAILSTREAM *stream,char *tag,char **s,
- SIZEDTEXT *as,long wildok)
- {
- unsigned long j;
- char c;
- STRING st;
- /* default to not quoted unless empty */
- int qflag = as->size ? NIL : T;
- for (j = 0; j < as->size; j++) switch (c = as->data[j]) {
- default: /* all other characters */
- if (!(c & 0x80)) { /* must not be 8bit */
- if (c <= ' ') qflag = T; /* must quote if a CTL */
- break;
- }
- case ' ': /* not a CHAR */
- case ' 12': case ' 15': /* not a TEXT-CHAR */
- case '"': case '\': /* quoted-specials (IMAP2 required this) */
- INIT (&st,mail_string,(void *) as->data,as->size);
- return imap_send_literal (stream,tag,s,&st);
- case '*': case '%': /* list_wildcards */
- if (wildok) break; /* allowed if doing the wild thing */
- /* atom_specials */
- case '(': case ')': case '{': case ' ': case 0x7f:
- #if 0
- case '"': case '\': /* quoted-specials (could work in IMAP4) */
- #endif
- qflag = T; /* must use quoted string format */
- break;
- }
- if (qflag) *(*s)++ = '"'; /* write open quote */
- for (j = 0; j < as->size; j++) *(*s)++ = as->data[j];
- if (qflag) *(*s)++ = '"'; /* write close quote */
- return NIL;
- }
- /* IMAP send literal
- * Accepts: MAIL stream
- * reply tag
- * pointer to current position pointer of output bigbuf
- * literal to output as stringstruct
- * Returns: error reply or NIL if success
- */
- IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
- STRING *st)
- {
- IMAPPARSEDREPLY *reply;
- unsigned long i = SIZE (st);
- sprintf (*s,"{%lu}",i); /* write literal count */
- *s += strlen (*s); /* size of literal count */
- /* send the command */
- reply = imap_sout (stream,tag,LOCAL->tmp,s);
- if (strcmp (reply->tag,"+")) {/* prompt for more data? */
- mail_unlock (stream); /* no, give up */
- return reply;
- }
- while (i) { /* dump the text */
- if (!net_sout (LOCAL->netstream,st->curpos,st->cursize)) {
- mail_unlock (stream);
- return imap_fake (stream,tag,"IMAP connection broken (data)");
- }
- i -= st->cursize; /* note that we wrote out this much */
- st->curpos += (st->cursize - 1);
- st->cursize = 0;
- (*st->dtb->next) (st); /* advance to next buffer's worth */
- }
- return NIL; /* success */
- }
- /* IMAP send search program
- * Accepts: MAIL stream
- * reply tag
- * pointer to current position pointer of output bigbuf
- * search program to output
- * Returns: error reply or NIL if success
- */
- IMAPPARSEDREPLY *imap_send_spgm (MAILSTREAM *stream,char *tag,char **s,
- SEARCHPGM *pgm)
- {
- IMAPPARSEDREPLY *reply;
- SEARCHHEADER *hdr;
- SEARCHOR *pgo;
- SEARCHPGMLIST *pgl;
- char *t = "ALL";
- while (*t) *(*s)++ = *t++; /* default initial text */
- /* message sequences */
- if (pgm->msgno) imap_send_sset (s,pgm->msgno);
- if (pgm->uid) { /* UID sequence */
- for (t = " UID"; *t; *(*s)++ = *t++);
- imap_send_sset (s,pgm->uid);
- }
- /* message sizes */
- if (pgm->larger) {
- sprintf (*s," LARGER %lu",pgm->larger);
- *s += strlen (*s);
- }
- if (pgm->smaller) {
- sprintf (*s," SMALLER %lu",pgm->smaller);
- *s += strlen (*s);
- }
- /* message flags */
- if (pgm->answered) for (t = " ANSWERED"; *t; *(*s)++ = *t++);
- if (pgm->unanswered) for (t =" UNANSWERED"; *t; *(*s)++ = *t++);
- if (pgm->deleted) for (t =" DELETED"; *t; *(*s)++ = *t++);
- if (pgm->undeleted) for (t =" UNDELETED"; *t; *(*s)++ = *t++);
- if (pgm->draft) for (t =" DRAFT"; *t; *(*s)++ = *t++);
- if (pgm->undraft) for (t =" UNDRAFT"; *t; *(*s)++ = *t++);
- if (pgm->flagged) for (t =" FLAGGED"; *t; *(*s)++ = *t++);
- if (pgm->unflagged) for (t =" UNFLAGGED"; *t; *(*s)++ = *t++);
- if (pgm->recent) for (t =" RECENT"; *t; *(*s)++ = *t++);
- if (pgm->old) for (t =" OLD"; *t; *(*s)++ = *t++);
- if (pgm->seen) for (t =" SEEN"; *t; *(*s)++ = *t++);
- if (pgm->unseen) for (t =" UNSEEN"; *t; *(*s)++ = *t++);
- if ((pgm->keyword && /* keywords */
- (reply = imap_send_slist (stream,tag,s,"KEYWORD",pgm->keyword))) ||
- (pgm->unkeyword &&
- (reply = imap_send_slist (stream,tag,s,"UNKEYWORD",pgm->unkeyword))))
- return reply;
- /* sent date ranges */
- if (pgm->sentbefore) imap_send_sdate (s,"SENTBEFORE",pgm->sentbefore);
- if (pgm->senton) imap_send_sdate (s,"SENTON",pgm->senton);
- if (pgm->sentsince) imap_send_sdate (s,"SENTSINCE",pgm->sentsince);
- /* internal date ranges */
- if (pgm->before) imap_send_sdate (s,"BEFORE",pgm->before);
- if (pgm->on) imap_send_sdate (s,"ON",pgm->on);
- if (pgm->since) imap_send_sdate (s,"SINCE",pgm->since);
- /* search texts */
- if ((pgm->bcc && (reply = imap_send_slist (stream,tag,s,"BCC",pgm->bcc))) ||
- (pgm->cc && (reply = imap_send_slist (stream,tag,s,"CC",pgm->cc))) ||
- (pgm->from && (reply = imap_send_slist(stream,tag,s,"FROM",pgm->from)))||
- (pgm->to && (reply = imap_send_slist (stream,tag,s,"TO",pgm->to))))
- return reply;
- if ((pgm->subject &&
- (reply = imap_send_slist (stream,tag,s,"SUBJECT",pgm->subject))) ||
- (pgm->body && (reply = imap_send_slist(stream,tag,s,"BODY",pgm->body)))||
- (pgm->text && (reply = imap_send_slist (stream,tag,s,"TEXT",pgm->text))))
- return reply;
- /* Note that these criteria are not supported by IMAP and have to be
- emulated */
- if ((pgm->return_path &&
- (reply = imap_send_slist (stream,tag,s,"HEADER Return-Path",
- pgm->return_path))) ||
- (pgm->sender &&
- (reply = imap_send_slist (stream,tag,s,"HEADER Sender",pgm->sender))) ||
- (pgm->reply_to &&
- (reply = imap_send_slist (stream,tag,s,"HEADER Reply-To",
- pgm->reply_to))) ||
- (pgm->in_reply_to &&
- (reply = imap_send_slist (stream,tag,s,"HEADER In-Reply-To",
- pgm->in_reply_to))) ||
- (pgm->message_id &&
- (reply = imap_send_slist (stream,tag,s,"HEADER Message-ID",
- pgm->message_id))) ||
- (pgm->newsgroups &&
- (reply = imap_send_slist (stream,tag,s,"HEADER Newsgroups",
- pgm->newsgroups))) ||
- (pgm->followup_to &&
- (reply = imap_send_slist (stream,tag,s,"HEADER Followup-To",
- pgm->followup_to))) ||
- (pgm->references &&
- (reply = imap_send_slist (stream,tag,s,"HEADER References",
- pgm->references)))) return reply;
- /* all other headers */
- if (hdr = pgm->header) do {
- for (t = " HEADER "; *t; *(*s)++ = *t++);
- if (reply = imap_send_astring (stream,tag,s,&hdr->line,NIL)) return reply;
- *(*s)++ = ' ';
- if (reply = imap_send_astring (stream,tag,s,&hdr->text,NIL)) return reply;
- }
- while (hdr = hdr->next);
- for (pgo = pgm->or; pgo; pgo = pgo->next) {
- for (t = " OR ("; *t; *(*s)++ = *t++);
- if (reply = imap_send_spgm (stream,tag,s,pgo->first)) return reply;
- for (t = ") ("; *t; *(*s)++ = *t++);
- if (reply = imap_send_spgm (stream,tag,s,pgo->second)) return reply;
- *(*s)++ = ')';
- }
- for (pgl = pgm->not; pgl; pgl = pgl->next) {
- for (t = " NOT ("; *t; *(*s)++ = *t++);
- if (reply = imap_send_spgm (stream,tag,s,pgl->pgm)) return reply;
- *(*s)++ = ')';
- }
- return NIL; /* search program written OK */
- }
- /* IMAP send search set
- * Accepts: pointer to current position pointer of output bigbuf
- * search set to output
- */
- void imap_send_sset (char **s,SEARCHSET *set)
- {
- char c = ' ';
- do { /* run down search set */
- sprintf (*s,set->last ? "%c%ld:%ld" : "%c%ld",c,set->first,set->last);
- *s += strlen (*s);
- c = ','; /* if there are any more */
- }
- while (set = set->next);
- }
- /* IMAP send search list
- * Accepts: MAIL stream
- * reply tag
- * pointer to current position pointer of output bigbuf
- * name of search list
- * search list to output
- * Returns: NIL if success, error reply if error
- */
- IMAPPARSEDREPLY *imap_send_slist (MAILSTREAM *stream,char *tag,char **s,
- char *name,STRINGLIST *list)
- {
- char *t;
- IMAPPARSEDREPLY *reply;
- do {
- *(*s)++ = ' '; /* output name of search list */
- for (t = name; *t; *(*s)++ = *t++);
- *(*s)++ = ' ';
- reply = imap_send_astring (stream,tag,s,&list->text,NIL);
- }
- while (!reply && (list = list->next));
- return reply;
- }
- /* IMAP send search date
- * Accepts: pointer to current position pointer of output bigbuf
- * field name
- * search date to output
- */
- void imap_send_sdate (char **s,char *name,unsigned short date)
- {
- sprintf (*s," %s %2d-%s-%d",name,date & 0x1f,
- months[((date >> 5) & 0xf) - 1],BASEYEAR + (date >> 9));
- *s += strlen (*s);
- }
- /* IMAP send buffered command to sender
- * Accepts: MAIL stream
- * reply tag
- * string
- * pointer to string tail pointer
- * Returns: reply
- */
- IMAPPARSEDREPLY *imap_sout (MAILSTREAM *stream,char *tag,char *base,char **s)
- {
- IMAPPARSEDREPLY *reply;
- if (stream->debug) { /* output debugging telemetry */
- **s = ' ';
- mm_dlog (base);
- }
- *(*s)++ = ' 15'; /* append CRLF */
- *(*s)++ = ' 12';
- **s = ' ';
- reply = net_sout (LOCAL->netstream,base,*s - base) ?
- imap_reply (stream,tag) :
- imap_fake (stream,tag,"IMAP connection broken (command)");
- *s = base; /* restart buffer */
- return reply;
- }
- /* IMAP send null-terminated string to sender
- * Accepts: MAIL stream
- * string
- * Returns: T if success, else NIL
- */
- long imap_soutr (MAILSTREAM *stream,char *string)
- {
- char tmp[MAILTMPLEN];
- if (stream->debug) mm_dlog (string);
- sprintf (tmp,"%s 15 12",string);
- return net_soutr (LOCAL->netstream,tmp);
- }
- /* IMAP get reply
- * Accepts: MAIL stream
- * tag to search or NIL if want a greeting
- * Returns: parsed reply, never NIL
- */
- IMAPPARSEDREPLY *imap_reply (MAILSTREAM *stream,char *tag)
- {
- IMAPPARSEDREPLY *reply;
- while (LOCAL->netstream) { /* parse reply from server */
- if (reply = imap_parse_reply (stream,net_getline (LOCAL->netstream))) {
- /* continuation ready? */
- if (!strcmp (reply->tag,"+")) return reply;
- /* untagged data? */
- else if (!strcmp (reply->tag,"*")) {
- imap_parse_unsolicited (stream,reply);
- if (!tag) return reply; /* return if just wanted greeting */
- }
- else { /* tagged data */
- if (tag && !strcmp (tag,reply->tag)) return reply;
- /* report bogon */
- sprintf (LOCAL->tmp,"Unexpected tagged response: %.80s %.80s %.80s",
- reply->tag,reply->key,reply->text);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- }
- return imap_fake (stream,tag,"IMAP connection broken (server response)");
- }
- /* IMAP parse reply
- * Accepts: MAIL stream
- * text of reply
- * Returns: parsed reply, or NIL if can't parse at least a tag and key
- */
- IMAPPARSEDREPLY *imap_parse_reply (MAILSTREAM *stream,char *text)
- {
- if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
- /* init fields in case error */
- LOCAL->reply.key = LOCAL->reply.text = LOCAL->reply.tag = NIL;
- if (!(LOCAL->reply.line = text)) {
- /* NIL text means the stream died */
- if (LOCAL->netstream) net_close (LOCAL->netstream);
- LOCAL->netstream = NIL;
- return NIL;
- }
- if (stream->debug) mm_dlog (LOCAL->reply.line);
- if (!(LOCAL->reply.tag = (char *) strtok (LOCAL->reply.line," "))) {
- mm_log ("IMAP server sent a blank line",WARN);
- return NIL;
- }
- /* non-continuation replies */
- if (strcmp (LOCAL->reply.tag,"+")) {
- /* parse key */
- if (!(LOCAL->reply.key = (char *) strtok (NIL," "))) {
- /* determine what is missing */
- sprintf (LOCAL->tmp,"Missing IMAP reply key: %.80s",LOCAL->reply.tag);
- mm_log (LOCAL->tmp,WARN); /* pass up the barfage */
- return NIL; /* can't parse this text */
- }
- ucase (LOCAL->reply.key); /* make sure key is upper case */
- /* get text as well, allow empty text */
- if (!(LOCAL->reply.text = (char *) strtok (NIL,"n")))
- LOCAL->reply.text = LOCAL->reply.key + strlen (LOCAL->reply.key);
- }
- else { /* special handling of continuation */
- LOCAL->reply.key = "BAD"; /* so it barfs if not expecting continuation */
- if (!(LOCAL->reply.text = (char *) strtok (NIL,"n")))
- LOCAL->reply.text = "";
- }
- return &LOCAL->reply; /* return parsed reply */
- }
- /* IMAP fake reply
- * Accepts: MAIL stream
- * tag
- * text of fake reply
- * Returns: parsed reply
- */
- IMAPPARSEDREPLY *imap_fake (MAILSTREAM *stream,char *tag,char *text)
- {
- mm_notify (stream,text,BYE); /* send bye alert */
- if (LOCAL->netstream) net_close (LOCAL->netstream);
- LOCAL->netstream = NIL; /* farewell, dear NET stream... */
- /* build fake reply string */
- sprintf (LOCAL->tmp,"%s NO [CLOSED] %s",tag ? tag : "*",text);
- /* parse and return it */
- return imap_parse_reply (stream,cpystr (LOCAL->tmp));
- }
- /* IMAP check for OK response in tagged reply
- * Accepts: MAIL stream
- * parsed reply
- * Returns: T if OK else NIL
- */
- long imap_OK (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
- {
- long ret = NIL;
- /* OK - operation succeeded */
- if (!strcmp (reply->key,"OK") ||
- (!strcmp (reply->tag,"*") && !strcmp (reply->key,"PREAUTH"))) {
- imap_parse_response (stream,reply->text,NIL,NIL);
- ret = T;
- }
- /* NO - operation failed */
- else if (!strcmp (reply->key,"NO"))
- imap_parse_response (stream,reply->text,WARN,NIL);
- else { /* BAD - operation rejected */
- if (!strcmp (reply->key,"BAD")) {
- imap_parse_response (stream,reply->text,ERROR,NIL);
- sprintf (LOCAL->tmp,"IMAP protocol error: %.80s",reply->text);
- }
- /* bad protocol received */
- else sprintf (LOCAL->tmp,"Unexpected IMAP response: %.80s %.80s",
- reply->key,reply->text);
- mm_log (LOCAL->tmp,ERROR); /* either way, this is not good */
- }
- return ret;
- }
- /* IMAP parse and act upon unsolicited reply
- * Accepts: MAIL stream
- * parsed reply
- */
- void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
- {
- unsigned long i = 0;
- unsigned long msgno;
- char *s,*t;
- if (isdigit (*reply->key)) { /* see if key is a number */
- msgno = strtoul (reply->key,&s,10);
- if (*s) { /* better be nothing after number */
- sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",reply->key);
- mm_log (LOCAL->tmp,WARN);
- return;
- }
- if (!reply->text) { /* better be some data */
- mm_log ("Missing message data",WARN);
- return;
- }
- /* get keyword */
- s = ucase ((char *) strtok (reply->text," "));
- /* and locate the text after it */
- t = (char *) strtok (NIL,"n");
- /* now take the action */
- /* change in size of mailbox */
- if (!strcmp (s,"EXISTS")) mail_exists (stream,msgno);
- else if (!strcmp (s,"RECENT")) mail_recent (stream,msgno);
- else if (!strcmp (s,"EXPUNGE") && msgno && (msgno <= stream->nmsgs)) {
- mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
- MESSAGECACHE *elt = (MESSAGECACHE *) (*mc) (stream,msgno,CH_ELT);
- if (elt) imap_gc_body (elt->private.msg.body);
- /* notify upper level */
- mail_expunged (stream,msgno);
- }
- else if ((!strcmp (s,"FETCH") || !strcmp (s,"STORE")) &&
- msgno && (msgno <= stream->nmsgs)) {
- char *prop;
- GETS_DATA md;
- ENVELOPE **e;
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- ENVELOPE *env = NIL;
- imapenvelope_t ie =
- (imapenvelope_t) mail_parameters (stream,GET_IMAPENVELOPE,NIL);
- ++t; /* skip past open parenthesis */
- /* parse Lisp-form property list */
- while (prop = ((char *) strtok (t," )"))) {
- t = (char *) strtok (NIL,"n");
- INIT_GETS (md,stream,elt->msgno,NIL,0,0);
- e = NIL; /* not pointing at any envelope yet */
- /* parse the property and its value */
- if (!strcmp (ucase (prop),"FLAGS")) imap_parse_flags (stream,elt,&t);
- else if (!strcmp (prop,"INTERNALDATE") &&
- (s = imap_parse_string (stream,&t,reply,NIL,NIL))) {
- if (!mail_parse_date (elt,s)) {
- sprintf (LOCAL->tmp,"Bogus date: %.80s",s);
- mm_log (LOCAL->tmp,WARN);
- }
- fs_give ((void **) &s);
- }
- else if (!strcmp (prop,"UID")) /* unique identifier */
- elt->private.uid = strtoul (t,&t,10);
- else if (!strcmp (prop,"ENVELOPE")) {
- if (stream->scache) { /* short cache, flush old stuff */
- mail_free_body (&stream->body);
- stream->msgno = elt->msgno;
- e = &stream->env; /* get pointer to envelope */
- }
- else e = &elt->private.msg.env;
- imap_parse_envelope (stream,e,&t,reply);
- }
- else if (!strncmp (prop,"BODY",4)) {
- if (!prop[4] || !strcmp (prop+4,"STRUCTURE")) {
- BODY **body;
- if (stream->scache){/* short cache, flush old stuff */
- if (stream->msgno != msgno) {
- mail_free_envelope (&stream->env);
- sprintf (LOCAL->tmp,"Body received for %lu but current is %lu",
- msgno,stream->msgno);
- stream->msgno = msgno;
- }
- /* get pointer to body */
- body = &stream->body;
- }
- else body = &elt->private.msg.body;
- /* flush any prior body */
- mail_free_body (body);
- /* instantiate and parse a new body */
- imap_parse_body_structure (stream,*body = mail_newbody(),&t,reply);
- }
- else if (prop[4] == '[') {
- STRINGLIST *stl = NIL;
- SIZEDTEXT text;
- /* will want to return envelope data */
- if (!strcmp (md.what = cpystr (prop + 5),"HEADER]") ||
- !strcmp (md.what,"0]"))
- e = stream->scache ? &stream->env : &elt->private.msg.env;
- LOCAL->tmp[0] =' ';/* no errors yet */
- /* found end of section? */
- if (!(s = strchr (md.what,']'))) {
- /* skip leading nesting */
- for (s = md.what; *s && (isdigit (*s) || (*s == '.')); s++);
- /* better be one of these */
- if (strncmp (s,"HEADER.FIELDS",13) &&
- (!s[13] || strcmp (s+13,".NOT")))
- sprintf (LOCAL->tmp,"Unterminated section: %.80s",md.what);
- /* get list of headers */
- else if (!(stl = imap_parse_stringlist (stream,&t,reply)))
- sprintf (LOCAL->tmp,"Bogus header field list: %.80s",t);
- else if (*t != ']')
- sprintf (LOCAL->tmp,"Unterminated header section: %.80s",t);
- /* point after the text */
- else if (t = strchr (s = t,' ')) *t++ = ' ';
- }
- if (s && !LOCAL->tmp[0]) {
- *s++ = ' '; /* tie off section specifier */
- if (*s == '<') { /* partial specifier? */
- md.first = strtoul (s+1,&s,10) + 1;
- if (*s++ != '>') /* make sure properly terminated */
- sprintf (LOCAL->tmp,"Unterminated partial data: %.80s",s-1);
- }
- if (!LOCAL->tmp[0] && *s)
- sprintf (LOCAL->tmp,"Junk after section: %.80s",s);
- }
- if (LOCAL->tmp[0]) { /* got any errors? */
- mm_log (LOCAL->tmp,WARN);
- mail_free_stringlist (&stl);
- }
- else { /* parse text from server */
- text.data = (unsigned char *)
- imap_parse_string (stream,&t,reply,
- ((md.what[0] && (md.what[0] != 'H')) ||
- md.first || md.last) ? &md : NIL,
- &text.size);
- /* all done if partial */
- if (md.first || md.last) mail_free_stringlist (&stl);
- /* otherwise register it in the cache */
- else imap_cache (stream,msgno,md.what,stl,&text);
- }
- fs_give ((void **) &md.what);
- }
- else {
- sprintf (LOCAL->tmp,"Unknown body message property: %.80s",prop);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- /* one of the RFC822 props? */
- else if (!strncmp (prop,"RFC822",6) && (!prop[6] || (prop[6] == '.'))){
- SIZEDTEXT text;
- if (!prop[6]) { /* cache full message */
- md.what = "";
- text.data = (unsigned char *)
- imap_parse_string (stream,&t,reply,&md,&text.size);
- imap_cache (stream,msgno,md.what,NIL,&text);
- }
- else if (!strcmp (prop+7,"SIZE"))
- elt->rfc822_size = strtoul (t,&t,10);
- /* legacy properties */
- else if (!strcmp (prop+7,"HEADER")) {
- text.data = (unsigned char *)
- imap_parse_string (stream,&t,reply,NIL,&text.size);
- imap_cache (stream,msgno,"HEADER",NIL,&text);
- e = stream->scache ? &stream->env : &elt->private.msg.env;
- }
- else if (!strcmp (prop+7,"TEXT")) {
- md.what = "TEXT";
- text.data = (unsigned char *)
- imap_parse_string (stream,&t,reply,&md,&text.size);
- imap_cache (stream,msgno,md.what,NIL,&text);
- }
- else {
- sprintf (LOCAL->tmp,"Unknown RFC822 message property: %.80s",prop);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- else {
- sprintf (LOCAL->tmp,"Unknown message property: %.80s",prop);
- mm_log (LOCAL->tmp,WARN);
- }
- if (e && *e) env = *e; /* note envelope if we got one */
- }
- /* do callback if requested */
- if (ie && env) (*ie) (stream,msgno,env);
- }
- /* obsolete response to COPY */
- else if (strcmp (s,"COPY")) {
- sprintf (LOCAL->tmp,"Unknown message data: %lu %.80s",msgno,s);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- else if (!strcmp (reply->key,"FLAGS")) {
- /* flush old user flags if any */
- while ((i < NUSERFLAGS) && stream->user_flags[i])
- fs_give ((void **) &stream->user_flags[i++]);
- i = 0; /* add flags */
- if (reply->text && (s = (char *) strtok (reply->text+1," )"))) do
- if (*s != '\') stream->user_flags[i++] = cpystr (s);
- while (s = (char *) strtok (NIL," )"));
- }
- else if (!strcmp (reply->key,"SEARCH")) {
- /* only do something if have text */
- if (reply->text && (t = (char *) strtok (reply->text," "))) do {
- /* UIDs always passed to main program */
- if (LOCAL->uidsearch) mm_searched (stream,atol (t));
- /* should be a msgno then */
- else if ((i = atol (t)) <= stream->nmsgs) {
- mail_elt (stream,i)->searched = T;
- if (!stream->silent) mm_searched (stream,i);
- }
- } while (t = (char *) strtok (NIL," "));
- }
- else if (!strcmp (reply->key,"NAMESPACE")) {
- if (LOCAL->namespace) {
- mail_free_namespace (&LOCAL->namespace[0]);
- mail_free_namespace (&LOCAL->namespace[1]);
- mail_free_namespace (&LOCAL->namespace[2]);
- }
- else LOCAL->namespace = (NAMESPACE **) fs_get (3 * sizeof (NAMESPACE *));
- if (s = reply->text) { /* parse namespace results */
- LOCAL->namespace[0] = imap_parse_namespace (stream,&s,reply);
- LOCAL->namespace[1] = imap_parse_namespace (stream,&s,reply);
- LOCAL->namespace[2] = imap_parse_namespace (stream,&s,reply);
- if (s && *s) {
- sprintf (LOCAL->tmp,"Junk after namespace list: %.80s",s);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- else mm_log ("Missing namespace list",WARN);
- }
- else if (!strcmp (reply->key,"SORT")) {
- sortresults_t sr = (sortresults_t)
- mail_parameters (NIL,GET_SORTRESULTS,NIL);
- LOCAL->sortsize = 0; /* initialize sort data */
- if (LOCAL->sortdata) fs_give ((void **) &LOCAL->sortdata);
- LOCAL->sortdata = (unsigned long *)
- fs_get ((stream->nmsgs + 1) * sizeof (unsigned long));
- /* only do something if have text */
- if (reply->text && (t = (char *) strtok (reply->text," "))) do
- LOCAL->sortdata[LOCAL->sortsize++] = atol (t);
- while ((t = (char *) strtok (NIL," ")) && (LOCAL->sortsize<stream->nmsgs));
- LOCAL->sortdata[LOCAL->sortsize] = 0;
- /* also return via callback if requested */
- if (sr) (*sr) (stream,LOCAL->sortdata,LOCAL->sortsize);
- }
- else if (!strcmp (reply->key,"THREAD")) {
- threadresults_t tr = (threadresults_t)
- mail_parameters (NIL,GET_THREADRESULTS,NIL);
- if (LOCAL->threaddata) mail_free_threadnode (&LOCAL->threaddata);
- if (s = reply->text) {
- LOCAL->threaddata = imap_parse_thread (&s);
- if (tr) (*tr) (stream,LOCAL->threaddata);
- if (s && *s) {
- sprintf (LOCAL->tmp,"Junk at end of thread: %.80s",s);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- }
- else if (!strcmp (reply->key,"STATUS")) {
- MAILSTATUS status;
- char *txt;
- if (!reply->text) {
- mm_log ("Missing STATUS data",WARN);
- return;
- }
- switch (*reply->text) { /* mailbox is an astring */
- case '"': /* quoted string? */
- case '{': /* literal? */
- txt = reply->text; /* status data is in reply */
- t = imap_parse_string (stream,&txt,reply,NIL,NIL);
- /* must be followed by space */
- if (!(txt && (*txt++ == ' '))) txt = NIL;
- break;
- default: /* must be atom */
- t = cpystr (reply->text);
- if (txt = strchr (t,' ')) *txt++ = ' ';
- break;
- }
- if (t && txt && (*txt++ == '(') && (s = strchr (txt,')')) && (s - txt) &&
- !s[1]) {
- *s = ' '; /* tie off status data */
- /* initialize data block */
- status.flags = status.messages = status.recent = status.unseen =
- status.uidnext = status.uidvalidity = 0;
- ucase (txt); /* do case-independent match */
- while (*txt && (s = strchr (txt,' '))) {
- *s++ = ' '; /* tie off status attribute name */
- i = strtoul (s,&s,10);/* get attribute value */
- if (!strcmp (txt,"MESSAGES")) {
- status.flags |= SA_MESSAGES;
- status.messages = i;
- }
- else if (!strcmp (txt,"RECENT")) {
- status.flags |= SA_RECENT;
- status.recent = i;
- }
- else if (!strcmp (txt,"UNSEEN")) {
- status.flags |= SA_UNSEEN;
- status.unseen = i;
- }
- else if (!strcmp (txt,"UIDNEXT") || !strcmp (txt,"UID-NEXT")) {
- status.flags |= SA_UIDNEXT;
- status.uidnext = i;
- }
- else if (!strcmp (txt,"UIDVALIDITY")|| !strcmp (txt,"UID-VALIDITY")){
- status.flags |= SA_UIDVALIDITY;
- status.uidvalidity = i;
- }
- /* next attribute */
- txt = (*s == ' ') ? s + 1 : s;
- }
- strcpy (strchr (strcpy (LOCAL->tmp,stream->mailbox),'}') + 1,t);
- /* pass status to main program */
- mm_status (stream,LOCAL->tmp,&status);
- }
- fs_give ((void **) &t);
- }
- else if ((!strcmp (reply->key,"LIST") || !strcmp (reply->key,"LSUB")) &&
- (*reply->text == '(') && (s = strchr (reply->text,')')) &&
- (s[1] == ' ')) {
- char delimiter = ' ';
- if (!reply->text) {
- mm_log ("Missing LIST/LSUB data",WARN);
- return;
- }
- *s++ = ' '; /* tie off attribute list */
- /* parse attribute list */
- if (t = (char *) strtok (reply->text+1," ")) do {
- if (!strcmp (ucase (t),"\NOINFERIORS")) i |= LATT_NOINFERIORS;
- else if (!strcmp (t,"\NOSELECT")) i |= LATT_NOSELECT;
- else if (!strcmp (t,"\MARKED")) i |= LATT_MARKED;
- else if (!strcmp (t,"\UNMARKED")) i |= LATT_UNMARKED;
- /* ignore extension flags */
- }
- while (t = (char *) strtok (NIL," "));
- switch (*++s) { /* process delimiter */
- case 'N': /* NIL */
- case 'n':
- s += 4; /* skip over NIL<space> */
- break;
- case '"': /* have a delimiter */
- delimiter = (*++s == '\') ? *++s : *s;
- s += 3; /* skip over <delimiter><quote><space> */
- }
- /* need to prepend a prefix? */
- if (LOCAL->prefix) strcpy (LOCAL->tmp,LOCAL->prefix);
- else LOCAL->tmp[0] = ' '; /* no prefix needed */
- /* need to do string parse? */
- if ((*s == '"') || (*s == '{')) {
- strcat (LOCAL->tmp,t = imap_parse_string (stream,&s,reply,NIL,NIL));
- fs_give ((void **) &t);
- }
- else strcat(LOCAL->tmp,s);/* atom is easy */
- if (reply->key[1] == 'S') mm_lsub (stream,delimiter,LOCAL->tmp,i);
- else mm_list (stream,delimiter,LOCAL->tmp,i);
- }
- else if (!strcmp (reply->key,"MAILBOX")) {
- if (!reply->text) {
- mm_log ("Missing MAILBOX data",WARN);
- return;
- }
- if (LOCAL->prefix)
- sprintf (t = LOCAL->tmp,"%s%s",LOCAL->prefix,reply->text);
- else t = reply->text;
- mm_list (stream,NIL,t,NIL);
- }
- else if (!strcmp (reply->key,"OK") || !strcmp (reply->key,"PREAUTH"))
- imap_parse_response (stream,reply->text,NIL,T);
- else if (!strcmp (reply->key,"NO"))
- imap_parse_response (stream,reply->text,WARN,T);
- else if (!strcmp (reply->key,"BAD"))
- imap_parse_response (stream,reply->text,ERROR,T);
- else if (!strcmp (reply->key,"BYE")) {
- LOCAL->byeseen = T; /* note that a BYE seen */
- imap_parse_response (stream,reply->text,BYE,T);
- }
- else if (!strcmp (reply->key,"CAPABILITY")) {
- /* only do something if have text */
- if (reply->text && (t = (char *) strtok (ucase (reply->text)," "))) do {
- if (!strcmp (t,"IMAP4")) LOCAL->imap4 = T;
- else if (!strcmp (t,"IMAP4REV1")) LOCAL->imap4rev1 = T;
- else if (!strcmp (t,"NAMESPACE")) LOCAL->use_namespace = T;
- else if (!strcmp (t,"MAILBOX-REFERRALS")) LOCAL->use_mbx_ref = T;
- else if (!strcmp (t,"LOGIN-REFERRALS")) LOCAL->use_log_ref = T;
- else if (!strcmp (t,"SCAN")) LOCAL->use_scan = T;
- else if (!strncmp (t,"SORT",4)) LOCAL->use_sort = T;
- else if (!strncmp (t,"THREAD=",7)) {
- THREADER *thread = (THREADER *) fs_get (sizeof (THREADER));
- thread->name = cpystr (t+7);
- thread->dispatch = NIL;
- thread->next = LOCAL->threader;
- LOCAL->threader = thread;
- }
- else if (!strncmp (t,"AUTH",4) && ((t[4] == '=') || (t[4] == '-'))) {
- if ((i = mail_lookup_auth_name (t+5,stream->secure)) &&
- (--i < MAXAUTHENTICATORS)) LOCAL->use_auth |= (1 << i);
- else if (!strcmp (t+5,"ANONYMOUS")) LOCAL->use_authanon = T;
- }
- /* unsupported IMAP4 extension */
- else if (!strcmp (t,"STATUS")) LOCAL->use_status = T;
- /* ignore other capabilities */
- }
- while (t = (char *) strtok (NIL," "));
- }
- else {
- sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",reply->key);
- mm_log (LOCAL->tmp,WARN);
- }
- }
- /* Parse human-readable response text
- * Accepts: mail stream
- * text
- * error level for mm_notify()
- * non-NIL if want mm_notify() event even if no response code
- */
- void imap_parse_response (MAILSTREAM *stream,char *text,long errflg,long ntfy)
- {
- char *s,*t;
- size_t i;
- if (text && (*text == '[') && (t = strchr (s = text + 1,']')) &&
- ((i = t - s) < IMAPTMPLEN)) {
- LOCAL->tmp[i] = ' '; /* make mungable copy of text code */
- if (s = strchr (strncpy (LOCAL->tmp,s,i),' ')) *s++ = ' ';
- ucase (LOCAL->tmp); /* make code uppercase */
- if (s) { /* have argument? */
- ntfy = NIL; /* suppress mm_notify if normal SELECT data */
- if (!strcmp (LOCAL->tmp,"UIDVALIDITY"))
- stream->uid_validity = strtoul (s,NIL,10);
- else if (!strcmp (LOCAL->tmp,"UIDNEXT"))
- stream->uid_last = strtoul (s,NIL,10) - 1;
- else if (!strcmp (LOCAL->tmp,"PERMANENTFLAGS") && (*s == '(') &&
- (LOCAL->tmp[i-1] == ')')) {
- LOCAL->tmp[i-1] = ' '; /* tie off flags */
- stream->perm_seen = stream->perm_deleted = stream->perm_answered =
- stream->perm_draft = stream->kwd_create = NIL;
- stream->perm_user_flags = NIL;
- if (s = strtok (s+1," ")) do {
- if (*ucase (s) == '\') { /* system flags */
- if (!strcmp (s,"\SEEN")) stream->perm_seen = T;
- else if (!strcmp (s,"\DELETED")) stream->perm_deleted = T;
- else if (!strcmp (s,"\FLAGGED")) stream->perm_flagged = T;
- else if (!strcmp (s,"\ANSWERED")) stream->perm_answered = T;
- else if (!strcmp (s,"\DRAFT")) stream->perm_draft = T;
- else if (!strcmp (s,"\*")) stream->kwd_create = T;
- }
- else stream->perm_user_flags |= imap_parse_user_flag (stream,s);
- }
- while (s = strtok (NIL," "));
- }
- else { /* all other response code events */
- ntfy = T; /* must mm_notify() */
- if (!strcmp (LOCAL->tmp,"REFERRAL"))
- LOCAL->referral = cpystr (LOCAL->tmp + 9);
- }
- }
- else { /* no arguments */
- if (!strcmp (LOCAL->tmp,"UIDNOTSTICKY")) {
- ntfy = NIL;
- stream->uid_nosticky = T;
- }
- else if (!strcmp (LOCAL->tmp,"READ-ONLY")) stream->rdonly = T;
- else if (!strcmp (LOCAL->tmp,"READ-WRITE")) stream->rdonly = NIL;
- }
- }
- /* give event to main program */
- if (ntfy && !stream->silent) mm_notify (stream,text ? text : "",errflg);
- }
- /* Parse a namespace
- * Accepts: mail stream
- * current text pointer
- * parsed reply
- * Returns: namespace list, text pointer updated
- */
- NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- NAMESPACE *ret = NIL;
- NAMESPACE *nam = NIL;
- NAMESPACE *prev = NIL;
- PARAMETER *par = NIL;
- if (*txtptr) { /* only if argument given */
- /* ignore leading space */
- while (**txtptr == ' ') ++*txtptr;
- switch (**txtptr) {
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "N" */
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- case '(':
- ++*txtptr; /* skip past open paren */
- while (**txtptr == '(') {
- ++*txtptr; /* skip past open paren */
- prev = nam; /* note previous if any */
- nam = (NAMESPACE *) memset (fs_get (sizeof (NAMESPACE)),0,
- sizeof (NAMESPACE));
- if (!ret) ret = nam; /* if first time note first namespace */
- /* if previous link new block to it */
- if (prev) prev->next = nam;
- nam->name = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- /* ignore whitespace */
- while (**txtptr == ' ') ++*txtptr;
- switch (**txtptr) { /* parse delimiter */
- case 'N':
- case 'n':
- *txtptr += 3; /* bump past "NIL" */
- break;
- case '"':
- if (*++*txtptr == '\') nam->delimiter = *++*txtptr;
- else nam->delimiter = **txtptr;
- *txtptr += 2; /* bump past character and closing quote */
- break;
- default:
- sprintf (LOCAL->tmp,"Missing delimiter in namespace: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- *txtptr = NIL; /* stop parse */
- return ret;
- }
- while (**txtptr == ' '){/* append new parameter to tail */
- if (nam->param) par = par->next = mail_newbody_parameter ();
- else nam->param = par = mail_newbody_parameter ();
- if (!(par->attribute = imap_parse_string (stream,txtptr,reply,NIL,
- NIL))) {
- mm_log ("Missing namespace extension attribute",WARN);
- par->attribute = cpystr ("UNKNOWN");
- }
- if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,NIL))){
- sprintf (LOCAL->tmp,"Missing value for namespace attribute %.80s",
- par->attribute);
- mm_log (LOCAL->tmp,WARN);
- par->value = cpystr ("UNKNOWN");
- }
- }
- if (**txtptr == ')') ++*txtptr;
- else { /* missing trailing paren */
- sprintf (LOCAL->tmp,"Junk at end of namespace: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- return ret;
- }
- }
- if (**txtptr == ')') { /* expected trailing paren? */
- ++*txtptr; /* got it! */
- break;
- }
- default:
- sprintf (LOCAL->tmp,"Not a namespace: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- *txtptr = NIL; /* stop parse now */
- break;
- }
- }
- return ret;
- }
- /* Parse a thread node list
- * Accepts: current text pointer
- * Returns: thread node list, text pointer updated
- */
- THREADNODE *imap_parse_thread (char **txtptr)
- {
- THREADNODE *ret = NIL;
- THREADNODE *last = NIL;
- THREADNODE *cur = NIL;
- THREADNODE *n;
- while (**txtptr == '(') { /* see a thread? */
- ++*txtptr; /* skip past open paren */
- while (**txtptr && (isdigit (**txtptr) || (**txtptr == '('))) {
- /* thread branch */
- if (**txtptr == '(') n = imap_parse_thread (txtptr);
- /* threaded message number */
- else (n = mail_newthreadnode (NIL))->num = strtoul (*txtptr,txtptr,10);
- /* new current node */
- if (cur) cur = cur->next = n;
- else { /* entirely new thread */
- /* plop at end of tree */
- if (ret) last = last->branch = cur = n;
- /* entirely new tree */
- else ret = last = cur = n;
- }
- /* skip past any space */
- if (**txtptr == ' ') ++*txtptr;
- }
- if (**txtptr == ')') { /* end of thread? */
- ++*txtptr; /* skip past end of thread */
- cur = NIL; /* close current thread */
- }
- else break;
- }
- return ret;
- }
- /* Parse RFC822 message header
- * Accepts: MAIL stream
- * envelope to parse into
- * header as sized text
- * stringlist if partial header
- */
- void imap_parse_header (MAILSTREAM *stream,ENVELOPE **env,SIZEDTEXT *hdr,
- STRINGLIST *stl)
- {
- ENVELOPE *nenv;
- /* parse what we can from this header */
- rfc822_parse_msg (&nenv,NIL,(char *) hdr->data,hdr->size,NIL,
- imap_host (stream),stream->dtb->flags);
- if (*env) { /* need to merge this header into envelope? */
- if (!(*env)->newsgroups) { /* need Newsgroups? */
- (*env)->newsgroups = nenv->newsgroups;
- (*env)->ngbogus = nenv->ngbogus;
- nenv->newsgroups = NIL;
- }
- if (!(*env)->followup_to) { /* need Followup-To? */
- (*env)->followup_to = nenv->followup_to;
- nenv->followup_to = NIL;
- }
- if (!(*env)->references) { /* need References? */
- (*env)->references = nenv->references;
- nenv->references = NIL;
- }
- mail_free_envelope (&nenv);
- }
- /* otherwise set it to this envelope */
- else (*env = nenv)->incomplete = stl ? T : NIL;
- }
- /* IMAP parse envelope
- * Accepts: MAIL stream
- * pointer to envelope pointer
- * current text pointer
- * parsed reply
- *
- * Updates text pointer
- */
- void imap_parse_envelope (MAILSTREAM *stream,ENVELOPE **env,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- ENVELOPE *oenv = *env;
- char c = *((*txtptr)++); /* grab first character */
- /* ignore leading spaces */
- while (c == ' ') c = *((*txtptr)++);
- switch (c) { /* dispatch on first character */
- case '(': /* if envelope S-expression */
- *env = mail_newenvelope (); /* parse the new envelope */
- (*env)->date = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- (*env)->subject = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- (*env)->from = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->sender = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->reply_to = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->to = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->cc = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->bcc = imap_parse_adrlist (stream,txtptr,reply);
- (*env)->in_reply_to = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- (*env)->message_id = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- if (oenv) { /* need to merge old envelope? */
- (*env)->newsgroups = oenv->newsgroups;
- oenv->newsgroups = NIL;
- (*env)->ngbogus = oenv->ngbogus;
- (*env)->followup_to = oenv->followup_to;
- oenv->followup_to = NIL;
- (*env)->references = oenv->references;
- oenv->references = NIL;
- mail_free_envelope(&oenv);/* free old envelope */
- }
- if (**txtptr != ')') {
- sprintf (LOCAL->tmp,"Junk at end of envelope: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- default:
- sprintf (LOCAL->tmp,"Not an envelope: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- }
- /* IMAP parse address list
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * Returns: address list, NIL on failure
- *
- * Updates text pointer
- */
- ADDRESS *imap_parse_adrlist (MAILSTREAM *stream,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- ADDRESS *adr = NIL;
- char c = **txtptr; /* sniff at first character */
- /* ignore leading spaces */
- while (c == ' ') c = *++*txtptr;
- ++*txtptr; /* skip past open paren */
- switch (c) {
- case '(': /* if envelope S-expression */
- adr = imap_parse_address (stream,txtptr,reply);
- if (**txtptr != ')') {
- sprintf (LOCAL->tmp,"Junk at end of address list: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- default:
- sprintf (LOCAL->tmp,"Not an address: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- return adr;
- }
- /* IMAP parse address
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * Returns: address, NIL on failure
- *
- * Updates text pointer
- */
- ADDRESS *imap_parse_address (MAILSTREAM *stream,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- ADDRESS *adr = NIL;
- ADDRESS *ret = NIL;
- ADDRESS *prev = NIL;
- char c = **txtptr; /* sniff at first address character */
- switch (c) {
- case '(': /* if envelope S-expression */
- while (c == '(') { /* recursion dies on small stack machines */
- ++*txtptr; /* skip past open paren */
- if (adr) prev = adr; /* note previous if any */
- adr = mail_newaddr (); /* instantiate address and parse its fields */
- adr->personal = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- adr->adl = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- adr->mailbox = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- adr->host = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- if (**txtptr != ')') { /* handle trailing paren */
- sprintf (LOCAL->tmp,"Junk at end of address: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past close paren */
- c = **txtptr; /* set up for while test */
- /* ignore leading spaces in front of next */
- while (c == ' ') c = *++*txtptr;
- if (!ret) ret = adr; /* if first time note first adr */
- /* if previous link new block to it */
- if (prev) prev->next = adr;
- }
- break;
- case 'N': /* if NIL */
- case 'n':
- *txtptr += 3; /* bump past NIL */
- break;
- default:
- sprintf (LOCAL->tmp,"Not an address: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- return ret;
- }
- /* IMAP parse flags
- * Accepts: current message cache
- * current text pointer
- *
- * Updates text pointer
- */
- void imap_parse_flags (MAILSTREAM *stream,MESSAGECACHE *elt,char **txtptr)
- {
- char *flag;
- char c = ' ';
- struct { /* old flags */
- unsigned int valid : 1;
- unsigned int seen : 1;
- unsigned int deleted : 1;
- unsigned int flagged : 1;
- unsigned int answered : 1;
- unsigned int draft : 1;
- unsigned long user_flags;
- } old;
- old.valid = elt->valid; old.seen = elt->seen; old.deleted = elt->deleted;
- old.flagged = elt->flagged; old.answered = elt->answered;
- old.draft = elt->draft; old.user_flags = elt->user_flags;
- elt->valid = T; /* mark have valid flags now */
- elt->user_flags = NIL; /* zap old flag values */
- elt->seen = elt->deleted = elt->flagged = elt->answered = elt->draft =
- elt->recent = NIL;
- while (c != ')') { /* parse list of flags */
- /* point at a flag */
- while (*(flag = ++*txtptr) == ' ');
- /* scan for end of flag */
- while (**txtptr != ' ' && **txtptr != ')') ++*txtptr;
- c = **txtptr; /* save delimiter */
- **txtptr = ' '; /* tie off flag */
- if (!*flag) break; /* null flag */
- /* if starts with must be sys flag */
- else if (*ucase (flag) == '\') {
- if (!strcmp (flag,"\SEEN")) elt->seen = T;
- else if (!strcmp (flag,"\DELETED")) elt->deleted = T;
- else if (!strcmp (flag,"\FLAGGED")) elt->flagged = T;
- else if (!strcmp (flag,"\ANSWERED")) elt->answered = T;
- else if (!strcmp (flag,"\RECENT")) elt->recent = T;
- else if (!strcmp (flag,"\DRAFT")) elt->draft = T;
- }
- /* otherwise user flag */
- else elt->user_flags |= imap_parse_user_flag (stream,flag);
- }
- ++*txtptr; /* bump past delimiter */
- if (!old.valid || (old.seen != elt->seen) ||
- (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
- (old.answered != elt->answered) || (old.draft != elt->draft) ||
- (old.user_flags != elt->user_flags)) mm_flags (stream,elt->msgno);
- }
- /* IMAP parse user flag
- * Accepts: MAIL stream
- * flag name
- * Returns: flag bit position
- */
- unsigned long imap_parse_user_flag (MAILSTREAM *stream,char *flag)
- {
- char tmp[MAILTMPLEN];
- long i;
- /* sniff through all user flags */
- for (i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i]) {
- sprintf (tmp,"%.1000s",stream->user_flags[i]);
- if (!strcmp (flag,ucase (tmp))) return (1 << i);
- }
- return (unsigned long) 0; /* not found */
- }
- /* IMAP parse string
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * mailgets data
- * returned string length
- * Returns: string
- *
- * Updates text pointer
- */
- char *imap_parse_string (MAILSTREAM *stream,char **txtptr,
- IMAPPARSEDREPLY *reply,GETS_DATA *md,
- unsigned long *len)
- {
- char *st;
- char *string = NIL;
- unsigned long i,j,k;
- char c = **txtptr; /* sniff at first character */
- mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
- readprogress_t rp =
- (readprogress_t) mail_parameters (NIL,GET_READPROGRESS,NIL);
- /* ignore leading spaces */
- while (c == ' ') c = *++*txtptr;
- st = ++*txtptr; /* remember start of string */
- switch (c) {
- case '"': /* if quoted string */
- i = 0; /* initial byte count */
- while (**txtptr != '"') { /* search for end of string */
- if (**txtptr == '\') ++*txtptr;
- ++i; /* bump count */
- ++*txtptr; /* bump pointer */
- }
- ++*txtptr; /* bump past delimiter */
- string = (char *) fs_get ((size_t) i + 1);
- for (j = 0; j < i; j++) { /* copy the string */
- if (*st == '\') ++st; /* quoted character */
- string[j] = *st++;
- }
- string[j] = ' '; /* tie off string */
- if (len) *len = i; /* set return value too */
- if (md && mg) { /* have special routine to slurp string? */
- STRING bs;
- if (md->first) { /* partial fetch? */
- md->first--; /* restore origin octet */
- md->last = i; /* number of octets that we got */
- }
- INIT (&bs,mail_string,string,i);
- (*mg) (mail_read,&bs,i,md);
- }
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- if (len) *len = 0;
- break;
- case '{': /* if literal string */
- /* get size of string */
- i = strtoul (*txtptr,txtptr,10);
- if (len) *len = i; /* set return value */
- if (md && mg) { /* have special routine to slurp string? */
- if (md->first) { /* partial fetch? */
- md->first--; /* restore origin octet */
- md->last = i; /* number of octets that we got */
- }
- else md->flags |= MG_COPY;/* otherwise flag need to copy */
- string = (*mg) (net_getbuffer,LOCAL->netstream,i,md);
- }
- else { /* must slurp into free storage */
- string = (char *) fs_get ((size_t) i + 1);
- *string = ' '; /* init in case getbuffer fails */
- /* get the literal */
- if (rp) for (k = 0; j = min ((long) MAILTMPLEN,(long) i); i -= j) {
- net_getbuffer (LOCAL->netstream,j,string + k);
- (*rp) (md,k += j);
- }
- else net_getbuffer (LOCAL->netstream,i,string);
- }
- fs_give ((void **) &reply->line);
- /* get new reply text line */
- reply->line = net_getline (LOCAL->netstream);
- if (stream->debug) mm_dlog (reply->line);
- *txtptr = reply->line; /* set text pointer to point at it */
- break;
- default:
- sprintf (LOCAL->tmp,"Not a string: %c%.80s",c,*txtptr);
- mm_log (LOCAL->tmp,WARN);
- if (len) *len = 0;
- break;
- }
- return string;
- }
- /* Register text in IMAP cache
- * Accepts: MAIL stream
- * message number
- * IMAP segment specifier
- * header string list (if a HEADER section specifier)
- * sized text to register
- * Returns: non-zero if cache non-empty
- */
- long imap_cache (MAILSTREAM *stream,unsigned long msgno,char *seg,
- STRINGLIST *stl,SIZEDTEXT *text)
- {
- char *t,tmp[MAILTMPLEN];
- unsigned long i;
- BODY *b;
- SIZEDTEXT *ret;
- STRINGLIST *stc;
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- /* top-level header never does mailgets */
- if (!strcmp (seg,"HEADER") || !strcmp (seg,"0") ||
- !strcmp (seg,"HEADER.FIELDS") || !strcmp (seg,"HEADER.FIELDS.NOT")) {
- ret = &elt->private.msg.header.text;
- if (text) { /* don't do this if no text */
- if (ret->data) fs_give ((void **) &ret->data);
- mail_free_stringlist (&elt->private.msg.lines);
- elt->private.msg.lines = stl;
- /* prevent cache reuse of .NOT */
- if ((seg[0] == 'H') && (seg[6] == '.') && (seg[13] == '.'))
- for (stc = stl; stc; stc = stc->next) stc->text.size = 0;
- if (stream->scache) { /* short caching puts it in the stream */
- if (stream->msgno != msgno) {
- /* flush old stuff */
- mail_free_envelope (&stream->env);
- mail_free_body (&stream->body);
- stream->msgno = msgno;
- }
- imap_parse_header (stream,&stream->env,text,stl);
- }
- /* regular caching */
- else imap_parse_header (stream,&elt->private.msg.env,text,stl);
- }
- }
- /* top level text */
- else if (!strcmp (seg,"TEXT")) {
- ret = &elt->private.msg.text.text;
- if (text && ret->data) fs_give ((void **) &ret->data);
- }
- else if (!*seg) { /* full message */
- ret = &elt->private.msg.full.text;
- if (text && ret->data) fs_give ((void **) &ret->data);
- }
- else { /* nested, find non-contents specifier */
- for (t = seg; *t && !((*t == '.') && (isalpha(t[1]) || !atol (t+1))); t++);
- if (*t) *t++ = ' '; /* tie off section from data specifier */
- if (!(b = mail_body (stream,msgno,seg))) {
- sprintf (tmp,"Unknown section number: %.80s",seg);
- mm_log (tmp,WARN);
- return NIL;
- }
- if (*t) { /* if a non-numberic subpart */
- if ((i = (b->type == TYPEMESSAGE) && (!strcmp (b->subtype,"RFC822"))) &&
- (!strcmp (t,"HEADER") || !strcmp (t,"0") ||
- !strcmp (t,"HEADER.FIELDS") || !strcmp (t,"HEADER.FIELDS.NOT"))) {
- ret = &b->nested.msg->header.text;
- if (text) {
- if (ret->data) fs_give ((void **) &ret->data);
- mail_free_stringlist (&b->nested.msg->lines);
- b->nested.msg->lines = stl;
- /* prevent cache reuse of .NOT */
- if ((t[0] == 'H') && (t[6] == '.') && (t[13] == '.'))
- for (stc = stl; stc; stc = stc->next) stc->text.size = 0;
- imap_parse_header (stream,&b->nested.msg->env,text,stl);
- }
- }
- else if (i && !strcmp (t,"TEXT")) {
- ret = &b->nested.msg->text.text;
- if (text && ret->data) fs_give ((void **) &ret->data);
- }
- /* otherwise it must be MIME */
- else if (!strcmp (t,"MIME")) {
- ret = &b->mime.text;
- if (text && ret->data) fs_give ((void **) &ret->data);
- }
- else {
- sprintf (tmp,"Unknown section specifier: %.80s.%.80s",seg,t);
- mm_log (tmp,WARN);
- return NIL;
- }
- }
- else { /* ordinary contents */
- ret = &b->contents.text;
- if (text && ret->data) fs_give ((void **) &ret->data);
- }
- }
- if (text) { /* update cache if requested */
- ret->data = text->data;
- ret->size = text->size;
- }
- return ret->data ? LONGT : NIL;
- }
- /* IMAP parse body structure
- * Accepts: MAIL stream
- * body structure to write into
- * current text pointer
- * parsed reply
- *
- * Updates text pointer
- */
- void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- int i;
- char *s;
- PART *part = NIL;
- char c = *((*txtptr)++); /* grab first character */
- /* ignore leading spaces */
- while (c == ' ') c = *((*txtptr)++);
- switch (c) { /* dispatch on first character */
- case '(': /* body structure list */
- if (**txtptr == '(') { /* multipart body? */
- body->type= TYPEMULTIPART;/* yes, set its type */
- do { /* instantiate new body part */
- if (part) part = part->next = mail_newbody_part ();
- else body->nested.part = part = mail_newbody_part ();
- /* parse it */
- imap_parse_body_structure (stream,&part->body,txtptr,reply);
- } while (**txtptr == '(');/* for each body part */
- if (body->subtype = imap_parse_string (stream,txtptr,reply,NIL,NIL))
- ucase (body->subtype);
- else {
- mm_log ("Missing multipart subtype",WARN);
- body->subtype = cpystr (rfc822_default_subtype (body->type));
- }
- if (**txtptr == ' ') /* multipart parameters */
- body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
- if (**txtptr == ' ') /* disposition */
- imap_parse_disposition (stream,body,txtptr,reply);
- if (**txtptr == ' ') /* language */
- body->language = imap_parse_language (stream,txtptr,reply);
- while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
- if (**txtptr != ')') { /* validate ending */
- sprintf (LOCAL->tmp,"Junk at end of multipart body: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- }
- else { /* not multipart, parse type name */
- if (**txtptr == ')') { /* empty body? */
- ++*txtptr; /* bump past it */
- break; /* and punt */
- }
- body->type = TYPEOTHER; /* assume unknown type */
- body->encoding = ENCOTHER;/* and unknown encoding */
- /* parse type */
- if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL)) {
- ucase (s);
- for (i=0;(i<=TYPEMAX) && body_types[i] && strcmp(s,body_types[i]);i++);
- if (i <= TYPEMAX) { /* only if found a slot */
- body->type = i; /* set body type */
- if (body_types[i]) fs_give ((void **) &s);
- else body_types[i]=s; /* assign empty slot */
- }
- }
- /* parse subtype */
- if (body->subtype = imap_parse_string (stream,txtptr,reply,NIL,NIL))
- ucase (body->subtype);
- else {
- mm_log ("Missing body subtype",WARN);
- body->subtype = cpystr (rfc822_default_subtype (body->type));
- }
- body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
- body->id = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- body->description = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL)) {
- ucase (s); /* search for body encoding */
- for (i = 0; (i <= ENCMAX) && body_encodings[i] &&
- strcmp (s,body_encodings[i]); i++);
- if (i > ENCMAX) body->type = ENCOTHER;
- else { /* only if found a slot */
- body->encoding = i; /* set body encoding */
- if (body_encodings[i]) fs_give ((void **) &s);
- /* assign empty slot */
- else body_encodings[i] = s;
- }
- }
- /* parse size of contents in bytes */
- body->size.bytes = strtoul (*txtptr,txtptr,10);
- switch (body->type) { /* possible extra stuff */
- case TYPEMESSAGE: /* message envelope and body */
- if (strcmp (body->subtype,"RFC822")) break;
- body->nested.msg = mail_newmsg ();
- imap_parse_envelope (stream,&body->nested.msg->env,txtptr,reply);
- body->nested.msg->body = mail_newbody ();
- imap_parse_body_structure (stream,body->nested.msg->body,txtptr,reply);
- /* drop into text case */
- case TYPETEXT: /* size in lines */
- body->size.lines = strtoul (*txtptr,txtptr,10);
- break;
- default: /* otherwise nothing special */
- break;
- }
- if (**txtptr == ' ') /* if extension data */
- body->md5 = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- if (**txtptr == ' ') /* disposition */
- imap_parse_disposition (stream,body,txtptr,reply);
- if (**txtptr == ' ') /* language */
- body->language = imap_parse_language (stream,txtptr,reply);
- while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
- if (**txtptr != ')') { /* validate ending */
- sprintf (LOCAL->tmp,"Junk at end of body part: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- }
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- default: /* otherwise quite bogus */
- sprintf (LOCAL->tmp,"Bogus body structure: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- }
- /* IMAP parse body parameter
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * Returns: body parameter
- * Updates text pointer
- */
- PARAMETER *imap_parse_body_parameter (MAILSTREAM *stream,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- PARAMETER *ret = NIL;
- PARAMETER *par = NIL;
- char c,*s;
- /* ignore leading spaces */
- while ((c = *(*txtptr)++) == ' ');
- /* parse parameter list */
- if (c == '(') while (c != ')') {
- /* append new parameter to tail */
- if (ret) par = par->next = mail_newbody_parameter ();
- else ret = par = mail_newbody_parameter ();
- if(!(par->attribute=imap_parse_string (stream,txtptr,reply,NIL,NIL))) {
- mm_log ("Missing parameter attribute",WARN);
- par->attribute = cpystr ("UNKNOWN");
- }
- if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,NIL))) {
- sprintf (LOCAL->tmp,"Missing value for parameter %.80s",par->attribute);
- mm_log (LOCAL->tmp,WARN);
- par->value = cpystr ("UNKNOWN");
- }
- switch (c = **txtptr) { /* see what comes after */
- case ' ': /* flush whitespace */
- while ((c = *++*txtptr) == ' ');
- break;
- case ')': /* end of attribute/value pairs */
- ++*txtptr; /* skip past closing paren */
- break;
- default:
- sprintf (LOCAL->tmp,"Junk at end of parameter: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- break;
- }
- }
- /* empty parameter, must be NIL */
- else if (((c == 'N') || (c == 'n')) &&
- ((*(s = *txtptr) == 'I') || (*s == 'i')) &&
- ((s[1] == 'L') || (s[1] == 'l'))) *txtptr += 2;
- else {
- sprintf (LOCAL->tmp,"Bogus body parameter: %c%.80s",c,(*txtptr) - 1);
- mm_log (LOCAL->tmp,WARN);
- }
- return ret;
- }
- /* IMAP parse body disposition
- * Accepts: MAIL stream
- * body structure to write into
- * current text pointer
- * parsed reply
- */
- void imap_parse_disposition (MAILSTREAM *stream,BODY *body,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- switch (*++*txtptr) {
- case '(':
- ++*txtptr; /* skip open paren */
- body->disposition.type = imap_parse_string (stream,txtptr,reply,NIL,NIL);
- body->disposition.parameter =
- imap_parse_body_parameter (stream,txtptr,reply);
- if (**txtptr != ')') { /* validate ending */
- sprintf (LOCAL->tmp,"Junk at end of disposition: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- }
- else ++*txtptr; /* skip past delimiter */
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "N" */
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- default:
- sprintf (LOCAL->tmp,"Unknown body disposition: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- /* try to skip to next space */
- while ((*++*txtptr != ' ') && (**txtptr != ')') && **txtptr);
- break;
- }
- }
- /* IMAP parse body language
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * Returns: string list or NIL if empty or error
- */
- STRINGLIST *imap_parse_language (MAILSTREAM *stream,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- unsigned long i;
- char *s;
- STRINGLIST *ret = NIL;
- /* language is a list */
- if (*++*txtptr == '(') ret = imap_parse_stringlist (stream,txtptr,reply);
- else if (s = imap_parse_string (stream,txtptr,reply,NIL,&i)) {
- (ret = mail_newstringlist ())->text.data = (unsigned char *) s;
- ret->text.size = i;
- }
- return ret;
- }
- /* IMAP parse string list
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- * Returns: string list or NIL if empty or error
- */
- STRINGLIST *imap_parse_stringlist (MAILSTREAM *stream,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- STRINGLIST *stl = NIL;
- STRINGLIST *stc = NIL;
- char c,*s;
- char *t = *txtptr;
- /* parse the list */
- if (*t++ == '(') while (*t != ')') {
- if (stl) stc = stc->next = mail_newstringlist ();
- else stc = stl = mail_newstringlist ();
- /* atom */
- if ((*t != '{') && (*t != '"') && (s = strpbrk (t," )"))) {
- c = *s; /* note delimiter */
- *s = ' '; /* tie off atom and copy it*/
- stc->text.size = strlen ((char *) (stc->text.data =
- (unsigned char *) cpystr (t)));
- if (c == ' ') t = ++s; /* another token follows */
- else *(t = s) = c; /* restore delimiter */
- }
- /* string */
- else if (!(stc->text.data = (unsigned char *)
- imap_parse_string (stream,&t,reply,NIL,&stc->text.size))) {
- sprintf (LOCAL->tmp,"Bogus string list member: %.80s",t);
- mm_log (LOCAL->tmp,WARN);
- mail_free_stringlist (&stl);
- break;
- }
- else if (*t == ' ') ++t; /* another token follows */
- }
- if (stl) *txtptr = ++t; /* update return string */
- return stl;
- }
- /* IMAP parse unknown body extension data
- * Accepts: MAIL stream
- * current text pointer
- * parsed reply
- *
- * Updates text pointer
- */
- void imap_parse_extension (MAILSTREAM *stream,char **txtptr,
- IMAPPARSEDREPLY *reply)
- {
- unsigned long i,j;
- switch (*++*txtptr) { /* action depends upon first character */
- case '(':
- while (**txtptr != ')') imap_parse_extension (stream,txtptr,reply);
- ++*txtptr; /* bump past closing parenthesis */
- break;
- case '"': /* if quoted string */
- while (*++*txtptr != '"') if (**txtptr == '\') ++*txtptr;
- ++*txtptr; /* bump past closing quote */
- break;
- case 'N': /* if NIL */
- case 'n':
- ++*txtptr; /* bump past "N" */
- ++*txtptr; /* bump past "I" */
- ++*txtptr; /* bump past "L" */
- break;
- case '{': /* get size of literal */
- ++*txtptr; /* bump past open squiggle */
- if (i = strtoul (*txtptr,txtptr,10)) do
- net_getbuffer (LOCAL->netstream,j = max (i,(long)IMAPTMPLEN),LOCAL->tmp);
- while (i -= j);
- /* get new reply text line */
- reply->line = net_getline (LOCAL->netstream);
- if (stream->debug) mm_dlog (reply->line);
- *txtptr = reply->line; /* set text pointer to point at it */
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- strtoul (*txtptr,txtptr,10);
- break;
- default:
- sprintf (LOCAL->tmp,"Unknown extension token: %.80s",*txtptr);
- mm_log (LOCAL->tmp,WARN);
- /* try to skip to next space */
- while ((*++*txtptr != ' ') && (**txtptr != ')') && **txtptr);
- break;
- }
- }
- /* IMAP return host name
- * Accepts: MAIL stream
- * Returns: host name
- */
- char *imap_host (MAILSTREAM *stream)
- {
- /* return host name on stream if open */
- return (LOCAL && LOCAL->netstream) ? net_host (LOCAL->netstream) :
- ".NO-IMAP-CONNECTION.";
- }