imap4r1.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:129k
源码类别:

网络编程

开发平台:

Unix_Linux

  1.  * Returns: parsed reply
  2.  */
  3. #define MAXSEQUENCE 1000
  4. IMAPPARSEDREPLY *imap_send (MAILSTREAM *stream,char *cmd,IMAPARG *args[])
  5. {
  6.   IMAPPARSEDREPLY *reply;
  7.   IMAPARG *arg,**arglst;
  8.   SORTPGM *spg;
  9.   STRINGLIST *list;
  10.   SIZEDTEXT st;
  11.   char c,*s,*t,tag[10];
  12.    /* gensym a new tag */
  13.   sprintf (tag,"%08lx",stream->gensym++);
  14.   if (!LOCAL->netstream) return imap_fake (stream,tag,"No-op dead stream");
  15.   mail_lock (stream); /* lock up the stream */
  16. /* ignored referral from previous command */
  17.   if (LOCAL->referral) fs_give ((void **) &LOCAL->referral);
  18.   sprintf (LOCAL->tmp,"%s ",tag);
  19.   for (t = cmd, s = LOCAL->tmp + 9; *t; *s++ = *t++);
  20.   if (arglst = args) while (arg = *arglst++) {
  21.     *s++ = ' '; /* delimit argument with space */
  22.     switch (arg->type) {
  23.     case ATOM: /* atom */
  24.       for (t = (char *) arg->text; *t; *s++ = *t++);
  25.       break;
  26.     case NUMBER: /* number */
  27.       sprintf (s,"%lu",(unsigned long) arg->text);
  28.       s += strlen (s);
  29.       break;
  30.     case FLAGS: /* flag list as a single string */
  31.       if (*(t = (char *) arg->text) != '(') {
  32. *s++ = '('; /* wrap parens around string */
  33. while (*t) *s++ = *t++;
  34. *s++ = ')'; /* wrap parens around string */
  35.       }
  36.       else while (*t) *s++ = *t++;
  37.       break;
  38.     case ASTRING: /* atom or string, must be literal? */
  39.       st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
  40.       if (reply = imap_send_astring (stream,tag,&s,&st,NIL)) return reply;
  41.       break;
  42.     case LITERAL: /* literal, as a stringstruct */
  43.       if (reply = imap_send_literal (stream,tag,&s,arg->text)) return reply;
  44.       break;
  45.     case LIST: /* list of strings */
  46.       list = (STRINGLIST *) arg->text;
  47.       c = '('; /* open paren */
  48.       do { /* for each list item */
  49. *s++ = c; /* write prefix character */
  50. if (reply = imap_send_astring (stream,tag,&s,&list->text,NIL))
  51.   return reply;
  52. c = ' '; /* prefix character for subsequent strings */
  53.       }
  54.       while (list = list->next);
  55.       *s++ = ')'; /* close list */
  56.       break;
  57.     case SEARCHPROGRAM: /* search program */
  58.       if (reply = imap_send_spgm (stream,tag,&s,arg->text)) return reply;
  59.       break;
  60.     case SORTPROGRAM: /* search program */
  61.       c = '('; /* open paren */
  62.       for (spg = (SORTPGM *) arg->text; spg; spg = spg->next) {
  63. *s++ = c; /* write prefix */
  64. if (spg->reverse) for (t = "REVERSE "; *t; *s++ = *t++);
  65. switch (spg->function) {
  66. case SORTDATE:
  67.   for (t = "DATE"; *t; *s++ = *t++);
  68.   break;
  69. case SORTARRIVAL:
  70.   for (t = "ARRIVAL"; *t; *s++ = *t++);
  71.   break;
  72. case SORTFROM:
  73.   for (t = "FROM"; *t; *s++ = *t++);
  74.   break;
  75. case SORTSUBJECT:
  76.   for (t = "SUBJECT"; *t; *s++ = *t++);
  77.   break;
  78. case SORTTO:
  79.   for (t = "TO"; *t; *s++ = *t++);
  80.   break;
  81. case SORTCC:
  82.   for (t = "CC"; *t; *s++ = *t++);
  83.   break;
  84. case SORTSIZE:
  85.   for (t = "SIZE"; *t; *s++ = *t++);
  86.   break;
  87. default:
  88.   fatal ("Unknown sort program function in imap_send()!");
  89. }
  90. c = ' '; /* prefix character for subsequent items */
  91.       }
  92.       *s++ = ')'; /* close list */
  93.       break;
  94.     case BODYTEXT: /* body section */
  95.       for (t = "BODY["; *t; *s++ = *t++);
  96.       for (t = (char *) arg->text; *t; *s++ = *t++);
  97.       break;
  98.     case BODYPEEK: /* body section */
  99.       for (t = "BODY.PEEK["; *t; *s++ = *t++);
  100.       for (t = (char *) arg->text; *t; *s++ = *t++);
  101.       break;
  102.     case BODYCLOSE: /* close bracket and possible length */
  103.       s[-1] = ']'; /* no leading space */
  104.       for (t = (char *) arg->text; *t; *s++ = *t++);
  105.       break;
  106.     case SEQUENCE: /* sequence */
  107.       while (t = (strlen ((char *) arg->text) > (size_t) MAXSEQUENCE) ?
  108.      strchr (((char *) arg->text) + MAXSEQUENCE - 20,',') : NIL) {
  109. *t = ''; /* tie off sequence */
  110. mail_unlock (stream); /* unlock stream, recurse to do part */
  111. imap_send (stream,cmd,args);
  112. mail_lock (stream); /* lock up stream again */
  113. /* rewrite the tag */
  114. memcpy (LOCAL->tmp,tag,8);
  115. *t++ = ','; /* restore the comma */
  116. arg->text = (void *) t; /* now do rest of data */
  117.       }
  118. /* falls through */
  119.     case LISTMAILBOX: /* astring with wildcards */
  120.       st.size = strlen ((char *) (st.data = (unsigned char *) arg->text));
  121.       if (reply = imap_send_astring (stream,tag,&s,&st,T)) return reply;
  122.       break;
  123.     default:
  124.       fatal ("Unknown argument type in imap_send()!");
  125.     }
  126.   }
  127. /* send the command */
  128.   reply = imap_sout (stream,tag,LOCAL->tmp,&s);
  129.   mail_unlock (stream); /* unlock stream */
  130.   return reply;
  131. }
  132. /* IMAP send atom-string
  133.  * Accepts: MAIL stream
  134.  *     reply tag
  135.  *     pointer to current position pointer of output bigbuf
  136.  *     atom-string to output
  137.  *     flag if list_wildcards allowed
  138.  * Returns: error reply or NIL if success
  139.  */
  140. IMAPPARSEDREPLY *imap_send_astring (MAILSTREAM *stream,char *tag,char **s,
  141.     SIZEDTEXT *as,long wildok)
  142. {
  143.   unsigned long j;
  144.   char c;
  145.   STRING st;
  146. /* default to not quoted unless empty */
  147.   int qflag = as->size ? NIL : T;
  148.   for (j = 0; j < as->size; j++) switch (c = as->data[j]) {
  149.   default: /* all other characters */
  150.     if (!(c & 0x80)) { /* must not be 8bit */
  151.       if (c <= ' ') qflag = T; /* must quote if a CTL */
  152.       break;
  153.     }
  154.   case '': /* not a CHAR */
  155.   case '12': case '15': /* not a TEXT-CHAR */
  156.   case '"': case '\': /* quoted-specials (IMAP2 required this) */
  157.     INIT (&st,mail_string,(void *) as->data,as->size);
  158.     return imap_send_literal (stream,tag,s,&st);
  159.   case '*': case '%': /* list_wildcards */
  160.     if (wildok) break; /* allowed if doing the wild thing */
  161. /* atom_specials */
  162.   case '(': case ')': case '{': case ' ': case 0x7f:
  163. #if 0
  164.   case '"': case '\': /* quoted-specials (could work in IMAP4) */
  165. #endif
  166.     qflag = T; /* must use quoted string format */
  167.     break;
  168.   }
  169.   if (qflag) *(*s)++ = '"'; /* write open quote */
  170.   for (j = 0; j < as->size; j++) *(*s)++ = as->data[j];
  171.   if (qflag) *(*s)++ = '"'; /* write close quote */
  172.   return NIL;
  173. }
  174. /* IMAP send literal
  175.  * Accepts: MAIL stream
  176.  *     reply tag
  177.  *     pointer to current position pointer of output bigbuf
  178.  *     literal to output as stringstruct
  179.  * Returns: error reply or NIL if success
  180.  */
  181. IMAPPARSEDREPLY *imap_send_literal (MAILSTREAM *stream,char *tag,char **s,
  182.     STRING *st)
  183. {
  184.   IMAPPARSEDREPLY *reply;
  185.   unsigned long i = SIZE (st);
  186.   sprintf (*s,"{%lu}",i); /* write literal count */
  187.   *s += strlen (*s); /* size of literal count */
  188. /* send the command */
  189.   reply = imap_sout (stream,tag,LOCAL->tmp,s);
  190.   if (strcmp (reply->tag,"+")) {/* prompt for more data? */
  191.     mail_unlock (stream); /* no, give up */
  192.     return reply;
  193.   }
  194.   while (i) { /* dump the text */
  195.     if (!net_sout (LOCAL->netstream,st->curpos,st->cursize)) {
  196.       mail_unlock (stream);
  197.       return imap_fake (stream,tag,"IMAP connection broken (data)");
  198.     }
  199.     i -= st->cursize; /* note that we wrote out this much */
  200.     st->curpos += (st->cursize - 1);
  201.     st->cursize = 0;
  202.     (*st->dtb->next) (st); /* advance to next buffer's worth */
  203.   }
  204.   return NIL; /* success */
  205. }
  206. /* IMAP send search program
  207.  * Accepts: MAIL stream
  208.  *     reply tag
  209.  *     pointer to current position pointer of output bigbuf
  210.  *     search program to output
  211.  * Returns: error reply or NIL if success
  212.  */
  213. IMAPPARSEDREPLY *imap_send_spgm (MAILSTREAM *stream,char *tag,char **s,
  214.  SEARCHPGM *pgm)
  215. {
  216.   IMAPPARSEDREPLY *reply;
  217.   SEARCHHEADER *hdr;
  218.   SEARCHOR *pgo;
  219.   SEARCHPGMLIST *pgl;
  220.   char *t = "ALL";
  221.   while (*t) *(*s)++ = *t++; /* default initial text */
  222. /* message sequences */
  223.   if (pgm->msgno) imap_send_sset (s,pgm->msgno);
  224.   if (pgm->uid) { /* UID sequence */
  225.     for (t = " UID"; *t; *(*s)++ = *t++);
  226.     imap_send_sset (s,pgm->uid);
  227.   }
  228. /* message sizes */
  229.   if (pgm->larger) {
  230.     sprintf (*s," LARGER %lu",pgm->larger);
  231.     *s += strlen (*s);
  232.   }
  233.   if (pgm->smaller) {
  234.     sprintf (*s," SMALLER %lu",pgm->smaller);
  235.     *s += strlen (*s);
  236.   }
  237. /* message flags */
  238.   if (pgm->answered) for (t = " ANSWERED"; *t; *(*s)++ = *t++);
  239.   if (pgm->unanswered) for (t =" UNANSWERED"; *t; *(*s)++ = *t++);
  240.   if (pgm->deleted) for (t =" DELETED"; *t; *(*s)++ = *t++);
  241.   if (pgm->undeleted) for (t =" UNDELETED"; *t; *(*s)++ = *t++);
  242.   if (pgm->draft) for (t =" DRAFT"; *t; *(*s)++ = *t++);
  243.   if (pgm->undraft) for (t =" UNDRAFT"; *t; *(*s)++ = *t++);
  244.   if (pgm->flagged) for (t =" FLAGGED"; *t; *(*s)++ = *t++);
  245.   if (pgm->unflagged) for (t =" UNFLAGGED"; *t; *(*s)++ = *t++);
  246.   if (pgm->recent) for (t =" RECENT"; *t; *(*s)++ = *t++);
  247.   if (pgm->old) for (t =" OLD"; *t; *(*s)++ = *t++);
  248.   if (pgm->seen) for (t =" SEEN"; *t; *(*s)++ = *t++);
  249.   if (pgm->unseen) for (t =" UNSEEN"; *t; *(*s)++ = *t++);
  250.   if ((pgm->keyword && /* keywords */
  251.        (reply = imap_send_slist (stream,tag,s,"KEYWORD",pgm->keyword))) ||
  252.       (pgm->unkeyword &&
  253.        (reply = imap_send_slist (stream,tag,s,"UNKEYWORD",pgm->unkeyword))))
  254.     return reply;
  255. /* sent date ranges */
  256.   if (pgm->sentbefore) imap_send_sdate (s,"SENTBEFORE",pgm->sentbefore);
  257.   if (pgm->senton) imap_send_sdate (s,"SENTON",pgm->senton);
  258.   if (pgm->sentsince) imap_send_sdate (s,"SENTSINCE",pgm->sentsince);
  259. /* internal date ranges */
  260.   if (pgm->before) imap_send_sdate (s,"BEFORE",pgm->before);
  261.   if (pgm->on) imap_send_sdate (s,"ON",pgm->on);
  262.   if (pgm->since) imap_send_sdate (s,"SINCE",pgm->since);
  263. /* search texts */
  264.   if ((pgm->bcc && (reply = imap_send_slist (stream,tag,s,"BCC",pgm->bcc))) ||
  265.       (pgm->cc && (reply = imap_send_slist (stream,tag,s,"CC",pgm->cc))) ||
  266.       (pgm->from && (reply = imap_send_slist(stream,tag,s,"FROM",pgm->from)))||
  267.       (pgm->to && (reply = imap_send_slist (stream,tag,s,"TO",pgm->to))))
  268.     return reply;
  269.   if ((pgm->subject &&
  270.        (reply = imap_send_slist (stream,tag,s,"SUBJECT",pgm->subject))) ||
  271.       (pgm->body && (reply = imap_send_slist(stream,tag,s,"BODY",pgm->body)))||
  272.       (pgm->text && (reply = imap_send_slist (stream,tag,s,"TEXT",pgm->text))))
  273.     return reply;
  274.   /* Note that these criteria are not supported by IMAP and have to be
  275.      emulated */
  276.   if ((pgm->return_path &&
  277.        (reply = imap_send_slist (stream,tag,s,"HEADER Return-Path",
  278.  pgm->return_path))) ||
  279.       (pgm->sender &&
  280.        (reply = imap_send_slist (stream,tag,s,"HEADER Sender",pgm->sender))) ||
  281.       (pgm->reply_to &&
  282.        (reply = imap_send_slist (stream,tag,s,"HEADER Reply-To",
  283.  pgm->reply_to))) ||
  284.       (pgm->in_reply_to &&
  285.        (reply = imap_send_slist (stream,tag,s,"HEADER In-Reply-To",
  286.  pgm->in_reply_to))) ||
  287.       (pgm->message_id &&
  288.        (reply = imap_send_slist (stream,tag,s,"HEADER Message-ID",
  289.  pgm->message_id))) ||
  290.       (pgm->newsgroups &&
  291.        (reply = imap_send_slist (stream,tag,s,"HEADER Newsgroups",
  292.  pgm->newsgroups))) ||
  293.       (pgm->followup_to &&
  294.        (reply = imap_send_slist (stream,tag,s,"HEADER Followup-To",
  295.  pgm->followup_to))) ||
  296.       (pgm->references &&
  297.        (reply = imap_send_slist (stream,tag,s,"HEADER References",
  298.  pgm->references)))) return reply;
  299. /* all other headers */
  300.   if (hdr = pgm->header) do {
  301.     for (t = " HEADER "; *t; *(*s)++ = *t++);
  302.     if (reply = imap_send_astring (stream,tag,s,&hdr->line,NIL)) return reply;
  303.     *(*s)++ = ' ';
  304.     if (reply = imap_send_astring (stream,tag,s,&hdr->text,NIL)) return reply;
  305.   }
  306.   while (hdr = hdr->next);
  307.   for (pgo = pgm->or; pgo; pgo = pgo->next) {
  308.     for (t = " OR ("; *t; *(*s)++ = *t++);
  309.     if (reply = imap_send_spgm (stream,tag,s,pgo->first)) return reply;
  310.     for (t = ") ("; *t; *(*s)++ = *t++);
  311.     if (reply = imap_send_spgm (stream,tag,s,pgo->second)) return reply;
  312.     *(*s)++ = ')';
  313.   }
  314.   for (pgl = pgm->not; pgl; pgl = pgl->next) {
  315.     for (t = " NOT ("; *t; *(*s)++ = *t++);
  316.     if (reply = imap_send_spgm (stream,tag,s,pgl->pgm)) return reply;
  317.     *(*s)++ = ')';
  318.   }
  319.   return NIL; /* search program written OK */
  320. }
  321. /* IMAP send search set
  322.  * Accepts: pointer to current position pointer of output bigbuf
  323.  *     search set to output
  324.  */
  325. void imap_send_sset (char **s,SEARCHSET *set)
  326. {
  327.   char c = ' ';
  328.   do { /* run down search set */
  329.     sprintf (*s,set->last ? "%c%ld:%ld" : "%c%ld",c,set->first,set->last);
  330.     *s += strlen (*s);
  331.     c = ','; /* if there are any more */
  332.   }
  333.   while (set = set->next);
  334. }
  335. /* IMAP send search list
  336.  * Accepts: MAIL stream
  337.  *     reply tag
  338.  *     pointer to current position pointer of output bigbuf
  339.  *     name of search list
  340.  *     search list to output
  341.  * Returns: NIL if success, error reply if error
  342.  */
  343. IMAPPARSEDREPLY *imap_send_slist (MAILSTREAM *stream,char *tag,char **s,
  344.   char *name,STRINGLIST *list)
  345. {
  346.   char *t;
  347.   IMAPPARSEDREPLY *reply;
  348.   do {
  349.     *(*s)++ = ' '; /* output name of search list */
  350.     for (t = name; *t; *(*s)++ = *t++);
  351.     *(*s)++ = ' ';
  352.     reply = imap_send_astring (stream,tag,s,&list->text,NIL);
  353.   }
  354.   while (!reply && (list = list->next));
  355.   return reply;
  356. }
  357. /* IMAP send search date
  358.  * Accepts: pointer to current position pointer of output bigbuf
  359.  *     field name
  360.  *     search date to output
  361.  */
  362. void imap_send_sdate (char **s,char *name,unsigned short date)
  363. {
  364.   sprintf (*s," %s %2d-%s-%d",name,date & 0x1f,
  365.    months[((date >> 5) & 0xf) - 1],BASEYEAR + (date >> 9));
  366.   *s += strlen (*s);
  367. }
  368. /* IMAP send buffered command to sender
  369.  * Accepts: MAIL stream
  370.  *     reply tag
  371.  *     string
  372.  *     pointer to string tail pointer
  373.  * Returns: reply
  374.  */
  375. IMAPPARSEDREPLY *imap_sout (MAILSTREAM *stream,char *tag,char *base,char **s)
  376. {
  377.   IMAPPARSEDREPLY *reply;
  378.   if (stream->debug) { /* output debugging telemetry */
  379.     **s = '';
  380.     mm_dlog (base);
  381.   }
  382.   *(*s)++ = '15'; /* append CRLF */
  383.   *(*s)++ = '12';
  384.   **s = '';
  385.   reply = net_sout (LOCAL->netstream,base,*s - base) ?
  386.     imap_reply (stream,tag) :
  387.       imap_fake (stream,tag,"IMAP connection broken (command)");
  388.   *s = base; /* restart buffer */
  389.   return reply;
  390. }
  391. /* IMAP send null-terminated string to sender
  392.  * Accepts: MAIL stream
  393.  *     string
  394.  * Returns: T if success, else NIL
  395.  */
  396. long imap_soutr (MAILSTREAM *stream,char *string)
  397. {
  398.   char tmp[MAILTMPLEN];
  399.   if (stream->debug) mm_dlog (string);
  400.   sprintf (tmp,"%s1512",string);
  401.   return net_soutr (LOCAL->netstream,tmp);
  402. }
  403. /* IMAP get reply
  404.  * Accepts: MAIL stream
  405.  *     tag to search or NIL if want a greeting
  406.  * Returns: parsed reply, never NIL
  407.  */
  408. IMAPPARSEDREPLY *imap_reply (MAILSTREAM *stream,char *tag)
  409. {
  410.   IMAPPARSEDREPLY *reply;
  411.   while (LOCAL->netstream) { /* parse reply from server */
  412.     if (reply = imap_parse_reply (stream,net_getline (LOCAL->netstream))) {
  413. /* continuation ready? */
  414.       if (!strcmp (reply->tag,"+")) return reply;
  415. /* untagged data? */
  416.       else if (!strcmp (reply->tag,"*")) {
  417. imap_parse_unsolicited (stream,reply);
  418. if (!tag) return reply; /* return if just wanted greeting */
  419.       }
  420.       else { /* tagged data */
  421. if (tag && !strcmp (tag,reply->tag)) return reply;
  422. /* report bogon */
  423. sprintf (LOCAL->tmp,"Unexpected tagged response: %.80s %.80s %.80s",
  424.  reply->tag,reply->key,reply->text);
  425. mm_log (LOCAL->tmp,WARN);
  426.       }
  427.     }
  428.   }
  429.   return imap_fake (stream,tag,"IMAP connection broken (server response)");
  430. }
  431. /* IMAP parse reply
  432.  * Accepts: MAIL stream
  433.  *     text of reply
  434.  * Returns: parsed reply, or NIL if can't parse at least a tag and key
  435.  */
  436. IMAPPARSEDREPLY *imap_parse_reply (MAILSTREAM *stream,char *text)
  437. {
  438.   if (LOCAL->reply.line) fs_give ((void **) &LOCAL->reply.line);
  439. /* init fields in case error */
  440.   LOCAL->reply.key = LOCAL->reply.text = LOCAL->reply.tag = NIL;
  441.   if (!(LOCAL->reply.line = text)) {
  442. /* NIL text means the stream died */
  443.     if (LOCAL->netstream) net_close (LOCAL->netstream);
  444.     LOCAL->netstream = NIL;
  445.     return NIL;
  446.   }
  447.   if (stream->debug) mm_dlog (LOCAL->reply.line);
  448.   if (!(LOCAL->reply.tag = (char *) strtok (LOCAL->reply.line," "))) {
  449.     mm_log ("IMAP server sent a blank line",WARN);
  450.     return NIL;
  451.   }
  452. /* non-continuation replies */
  453.   if (strcmp (LOCAL->reply.tag,"+")) {
  454. /* parse key */
  455.     if (!(LOCAL->reply.key = (char *) strtok (NIL," "))) {
  456. /* determine what is missing */
  457.       sprintf (LOCAL->tmp,"Missing IMAP reply key: %.80s",LOCAL->reply.tag);
  458.       mm_log (LOCAL->tmp,WARN); /* pass up the barfage */
  459.       return NIL; /* can't parse this text */
  460.     }
  461.     ucase (LOCAL->reply.key); /* make sure key is upper case */
  462. /* get text as well, allow empty text */
  463.     if (!(LOCAL->reply.text = (char *) strtok (NIL,"n")))
  464.       LOCAL->reply.text = LOCAL->reply.key + strlen (LOCAL->reply.key);
  465.   }
  466.   else { /* special handling of continuation */
  467.     LOCAL->reply.key = "BAD"; /* so it barfs if not expecting continuation */
  468.     if (!(LOCAL->reply.text = (char *) strtok (NIL,"n")))
  469.       LOCAL->reply.text = "";
  470.   }
  471.   return &LOCAL->reply; /* return parsed reply */
  472. }
  473. /* IMAP fake reply
  474.  * Accepts: MAIL stream
  475.  *     tag
  476.  *     text of fake reply
  477.  * Returns: parsed reply
  478.  */
  479. IMAPPARSEDREPLY *imap_fake (MAILSTREAM *stream,char *tag,char *text)
  480. {
  481.   mm_notify (stream,text,BYE); /* send bye alert */
  482.   if (LOCAL->netstream) net_close (LOCAL->netstream);
  483.   LOCAL->netstream = NIL; /* farewell, dear NET stream... */
  484. /* build fake reply string */
  485.   sprintf (LOCAL->tmp,"%s NO [CLOSED] %s",tag ? tag : "*",text);
  486. /* parse and return it */
  487.   return imap_parse_reply (stream,cpystr (LOCAL->tmp));
  488. }
  489. /* IMAP check for OK response in tagged reply
  490.  * Accepts: MAIL stream
  491.  *     parsed reply
  492.  * Returns: T if OK else NIL
  493.  */
  494. long imap_OK (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
  495. {
  496.   long ret = NIL;
  497. /* OK - operation succeeded */
  498.   if (!strcmp (reply->key,"OK") ||
  499.       (!strcmp (reply->tag,"*") && !strcmp (reply->key,"PREAUTH"))) {
  500.     imap_parse_response (stream,reply->text,NIL,NIL);
  501.     ret = T;
  502.   }
  503. /* NO - operation failed */
  504.   else if (!strcmp (reply->key,"NO"))
  505.     imap_parse_response (stream,reply->text,WARN,NIL);
  506.   else { /* BAD - operation rejected */
  507.     if (!strcmp (reply->key,"BAD")) {
  508.       imap_parse_response (stream,reply->text,ERROR,NIL);
  509.       sprintf (LOCAL->tmp,"IMAP protocol error: %.80s",reply->text);
  510.     }
  511. /* bad protocol received */
  512.     else sprintf (LOCAL->tmp,"Unexpected IMAP response: %.80s %.80s",
  513.   reply->key,reply->text);
  514.     mm_log (LOCAL->tmp,ERROR); /* either way, this is not good */
  515.   }
  516.   return ret;
  517. }
  518. /* IMAP parse and act upon unsolicited reply
  519.  * Accepts: MAIL stream
  520.  *     parsed reply
  521.  */
  522. void imap_parse_unsolicited (MAILSTREAM *stream,IMAPPARSEDREPLY *reply)
  523. {
  524.   unsigned long i = 0;
  525.   unsigned long msgno;
  526.   char *s,*t;
  527.   if (isdigit (*reply->key)) { /* see if key is a number */
  528.     msgno = strtoul (reply->key,&s,10);
  529.     if (*s) { /* better be nothing after number */
  530.       sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",reply->key);
  531.       mm_log (LOCAL->tmp,WARN);
  532.       return;
  533.     }
  534.     if (!reply->text) { /* better be some data */
  535.       mm_log ("Missing message data",WARN);
  536.       return;
  537.     }
  538. /* get keyword */
  539.     s = ucase ((char *) strtok (reply->text," "));
  540. /* and locate the text after it */
  541.     t = (char *) strtok (NIL,"n");
  542. /* now take the action */
  543. /* change in size of mailbox */
  544.     if (!strcmp (s,"EXISTS")) mail_exists (stream,msgno);
  545.     else if (!strcmp (s,"RECENT")) mail_recent (stream,msgno);
  546.     else if (!strcmp (s,"EXPUNGE") && msgno && (msgno <= stream->nmsgs)) {
  547.       mailcache_t mc = (mailcache_t) mail_parameters (NIL,GET_CACHE,NIL);
  548.       MESSAGECACHE *elt = (MESSAGECACHE *) (*mc) (stream,msgno,CH_ELT);
  549.       if (elt) imap_gc_body (elt->private.msg.body);
  550. /* notify upper level */
  551.       mail_expunged (stream,msgno);
  552.     }
  553.     else if ((!strcmp (s,"FETCH") || !strcmp (s,"STORE")) &&
  554.      msgno && (msgno <= stream->nmsgs)) {
  555.       char *prop;
  556.       GETS_DATA md;
  557.       ENVELOPE **e;
  558.       MESSAGECACHE *elt = mail_elt (stream,msgno);
  559.       ENVELOPE *env = NIL;
  560.       imapenvelope_t ie =
  561. (imapenvelope_t) mail_parameters (stream,GET_IMAPENVELOPE,NIL);
  562.       ++t; /* skip past open parenthesis */
  563. /* parse Lisp-form property list */
  564.       while (prop = ((char *) strtok (t," )"))) {
  565. t = (char *) strtok (NIL,"n");
  566. INIT_GETS (md,stream,elt->msgno,NIL,0,0);
  567. e = NIL; /* not pointing at any envelope yet */
  568. /* parse the property and its value */
  569. if (!strcmp (ucase (prop),"FLAGS")) imap_parse_flags (stream,elt,&t);
  570. else if (!strcmp (prop,"INTERNALDATE") &&
  571.  (s = imap_parse_string (stream,&t,reply,NIL,NIL))) {
  572.   if (!mail_parse_date (elt,s)) {
  573.     sprintf (LOCAL->tmp,"Bogus date: %.80s",s);
  574.     mm_log (LOCAL->tmp,WARN);
  575.   }
  576.   fs_give ((void **) &s);
  577. }
  578. else if (!strcmp (prop,"UID")) /* unique identifier */
  579.   elt->private.uid = strtoul (t,&t,10);
  580. else if (!strcmp (prop,"ENVELOPE")) {
  581.   if (stream->scache) { /* short cache, flush old stuff */
  582.     mail_free_body (&stream->body);
  583.     stream->msgno = elt->msgno;
  584.     e = &stream->env; /* get pointer to envelope */
  585.   }
  586.   else e = &elt->private.msg.env;
  587.   imap_parse_envelope (stream,e,&t,reply);
  588. }
  589. else if (!strncmp (prop,"BODY",4)) {
  590.   if (!prop[4] || !strcmp (prop+4,"STRUCTURE")) {
  591.     BODY **body;
  592.     if (stream->scache){/* short cache, flush old stuff */
  593.       if (stream->msgno != msgno) {
  594. mail_free_envelope (&stream->env);
  595. sprintf (LOCAL->tmp,"Body received for %lu but current is %lu",
  596.  msgno,stream->msgno);
  597. stream->msgno = msgno;
  598.       }
  599. /* get pointer to body */
  600.       body = &stream->body;
  601.     }
  602.     else body = &elt->private.msg.body;
  603. /* flush any prior body */
  604.     mail_free_body (body);
  605. /* instantiate and parse a new body */
  606.     imap_parse_body_structure (stream,*body = mail_newbody(),&t,reply);
  607.   }
  608.   else if (prop[4] == '[') {
  609.     STRINGLIST *stl = NIL;
  610.     SIZEDTEXT text;
  611. /* will want to return envelope data */
  612.     if (!strcmp (md.what = cpystr (prop + 5),"HEADER]") ||
  613. !strcmp (md.what,"0]"))
  614.       e = stream->scache ? &stream->env : &elt->private.msg.env;
  615.     LOCAL->tmp[0] ='';/* no errors yet */
  616. /* found end of section? */
  617.     if (!(s = strchr (md.what,']'))) {
  618. /* skip leading nesting */
  619.       for (s = md.what; *s && (isdigit (*s) || (*s == '.')); s++);
  620. /* better be one of these */
  621.       if (strncmp (s,"HEADER.FIELDS",13) &&
  622.   (!s[13] || strcmp (s+13,".NOT")))
  623. sprintf (LOCAL->tmp,"Unterminated section: %.80s",md.what);
  624. /* get list of headers */
  625.       else if (!(stl = imap_parse_stringlist (stream,&t,reply)))
  626. sprintf (LOCAL->tmp,"Bogus header field list: %.80s",t);
  627.       else if (*t != ']')
  628. sprintf (LOCAL->tmp,"Unterminated header section: %.80s",t);
  629. /* point after the text */
  630.       else if (t = strchr (s = t,' ')) *t++ = '';
  631.     }
  632.     if (s && !LOCAL->tmp[0]) {
  633.       *s++ = ''; /* tie off section specifier */
  634.       if (*s == '<') { /* partial specifier? */
  635. md.first = strtoul (s+1,&s,10) + 1;
  636. if (*s++ != '>') /* make sure properly terminated */
  637.   sprintf (LOCAL->tmp,"Unterminated partial data: %.80s",s-1);
  638.       }
  639.       if (!LOCAL->tmp[0] && *s)
  640. sprintf (LOCAL->tmp,"Junk after section: %.80s",s);
  641.     }
  642.     if (LOCAL->tmp[0]) { /* got any errors? */
  643.       mm_log (LOCAL->tmp,WARN);
  644.       mail_free_stringlist (&stl);
  645.     }
  646.     else { /* parse text from server */
  647.       text.data = (unsigned char *)
  648. imap_parse_string (stream,&t,reply,
  649.    ((md.what[0] && (md.what[0] != 'H')) ||
  650.     md.first || md.last) ? &md : NIL,
  651.    &text.size);
  652. /* all done if partial */
  653.       if (md.first || md.last) mail_free_stringlist (&stl);
  654. /* otherwise register it in the cache */
  655.       else imap_cache (stream,msgno,md.what,stl,&text);
  656.     }
  657.     fs_give ((void **) &md.what);
  658.   }
  659.   else {
  660.     sprintf (LOCAL->tmp,"Unknown body message property: %.80s",prop);
  661.     mm_log (LOCAL->tmp,WARN);
  662.   }
  663. }
  664. /* one of the RFC822 props? */
  665. else if (!strncmp (prop,"RFC822",6) && (!prop[6] || (prop[6] == '.'))){
  666.   SIZEDTEXT text;
  667.   if (!prop[6]) { /* cache full message */
  668.     md.what = "";
  669.     text.data = (unsigned char *)
  670.       imap_parse_string (stream,&t,reply,&md,&text.size);
  671.     imap_cache (stream,msgno,md.what,NIL,&text);
  672.   }
  673.   else if (!strcmp (prop+7,"SIZE"))
  674.     elt->rfc822_size = strtoul (t,&t,10);
  675. /* legacy properties */
  676.   else if (!strcmp (prop+7,"HEADER")) {
  677.     text.data = (unsigned char *)
  678.       imap_parse_string (stream,&t,reply,NIL,&text.size);
  679.     imap_cache (stream,msgno,"HEADER",NIL,&text);
  680.     e = stream->scache ? &stream->env : &elt->private.msg.env;
  681.   }
  682.   else if (!strcmp (prop+7,"TEXT")) {
  683.     md.what = "TEXT";
  684.     text.data = (unsigned char *)
  685.       imap_parse_string (stream,&t,reply,&md,&text.size);
  686.     imap_cache (stream,msgno,md.what,NIL,&text);
  687.   }
  688.   else {
  689.     sprintf (LOCAL->tmp,"Unknown RFC822 message property: %.80s",prop);
  690.     mm_log (LOCAL->tmp,WARN);
  691.   }
  692. }
  693. else {
  694.   sprintf (LOCAL->tmp,"Unknown message property: %.80s",prop);
  695.   mm_log (LOCAL->tmp,WARN);
  696. }
  697. if (e && *e) env = *e; /* note envelope if we got one */
  698.       }
  699. /* do callback if requested */
  700.       if (ie && env) (*ie) (stream,msgno,env);
  701.     }
  702. /* obsolete response to COPY */
  703.     else if (strcmp (s,"COPY")) {
  704.       sprintf (LOCAL->tmp,"Unknown message data: %lu %.80s",msgno,s);
  705.       mm_log (LOCAL->tmp,WARN);
  706.     }
  707.   }
  708.   else if (!strcmp (reply->key,"FLAGS")) {
  709. /* flush old user flags if any */
  710.     while ((i < NUSERFLAGS) && stream->user_flags[i])
  711.       fs_give ((void **) &stream->user_flags[i++]);
  712.     i = 0; /* add flags */
  713.     if (reply->text && (s = (char *) strtok (reply->text+1," )"))) do
  714.       if (*s != '\') stream->user_flags[i++] = cpystr (s);
  715.     while (s = (char *) strtok (NIL," )"));
  716.   }
  717.   else if (!strcmp (reply->key,"SEARCH")) {
  718. /* only do something if have text */
  719.     if (reply->text && (t = (char *) strtok (reply->text," "))) do {
  720. /* UIDs always passed to main program */
  721.       if (LOCAL->uidsearch) mm_searched (stream,atol (t));
  722. /* should be a msgno then */
  723.       else if ((i = atol (t)) <= stream->nmsgs) {
  724. mail_elt (stream,i)->searched = T;
  725. if (!stream->silent) mm_searched (stream,i);
  726.       }
  727.     } while (t = (char *) strtok (NIL," "));
  728.   }
  729.   else if (!strcmp (reply->key,"NAMESPACE")) {
  730.     if (LOCAL->namespace) {
  731.       mail_free_namespace (&LOCAL->namespace[0]);
  732.       mail_free_namespace (&LOCAL->namespace[1]);
  733.       mail_free_namespace (&LOCAL->namespace[2]);
  734.     }
  735.     else LOCAL->namespace = (NAMESPACE **) fs_get (3 * sizeof (NAMESPACE *));
  736.     if (s = reply->text) { /* parse namespace results */
  737.       LOCAL->namespace[0] = imap_parse_namespace (stream,&s,reply);
  738.       LOCAL->namespace[1] = imap_parse_namespace (stream,&s,reply);
  739.       LOCAL->namespace[2] = imap_parse_namespace (stream,&s,reply);
  740.       if (s && *s) {
  741. sprintf (LOCAL->tmp,"Junk after namespace list: %.80s",s);
  742. mm_log (LOCAL->tmp,WARN);
  743.       }
  744.     }
  745.     else mm_log ("Missing namespace list",WARN);
  746.   }
  747.   else if (!strcmp (reply->key,"SORT")) {
  748.     sortresults_t sr = (sortresults_t)
  749.       mail_parameters (NIL,GET_SORTRESULTS,NIL);
  750.     LOCAL->sortsize = 0; /* initialize sort data */
  751.     if (LOCAL->sortdata) fs_give ((void **) &LOCAL->sortdata);
  752.     LOCAL->sortdata = (unsigned long *)
  753.       fs_get ((stream->nmsgs + 1) * sizeof (unsigned long));
  754. /* only do something if have text */
  755.     if (reply->text && (t = (char *) strtok (reply->text," "))) do
  756.       LOCAL->sortdata[LOCAL->sortsize++] = atol (t);
  757.     while ((t = (char *) strtok (NIL," ")) && (LOCAL->sortsize<stream->nmsgs));
  758.     LOCAL->sortdata[LOCAL->sortsize] = 0;
  759. /* also return via callback if requested */
  760.     if (sr) (*sr) (stream,LOCAL->sortdata,LOCAL->sortsize);
  761.   }
  762.   else if (!strcmp (reply->key,"THREAD")) {
  763.     threadresults_t tr = (threadresults_t)
  764.       mail_parameters (NIL,GET_THREADRESULTS,NIL);
  765.     if (LOCAL->threaddata) mail_free_threadnode (&LOCAL->threaddata);
  766.     if (s = reply->text) {
  767.       LOCAL->threaddata = imap_parse_thread (&s);
  768.       if (tr) (*tr) (stream,LOCAL->threaddata);
  769.       if (s && *s) {
  770. sprintf (LOCAL->tmp,"Junk at end of thread: %.80s",s);
  771. mm_log (LOCAL->tmp,WARN);
  772.       }
  773.     }
  774.   }
  775.   else if (!strcmp (reply->key,"STATUS")) {
  776.     MAILSTATUS status;
  777.     char *txt;
  778.     if (!reply->text) {
  779.       mm_log ("Missing STATUS data",WARN);
  780.       return;
  781.     }
  782.     switch (*reply->text) { /* mailbox is an astring */
  783.     case '"': /* quoted string? */
  784.     case '{': /* literal? */
  785.       txt = reply->text; /* status data is in reply */
  786.       t = imap_parse_string (stream,&txt,reply,NIL,NIL);
  787. /* must be followed by space */
  788.       if (!(txt && (*txt++ == ' '))) txt = NIL;
  789.       break;
  790.     default: /* must be atom */
  791.       t = cpystr (reply->text);
  792.       if (txt = strchr (t,' ')) *txt++ = '';
  793.       break;
  794.     }
  795.     if (t && txt && (*txt++ == '(') && (s = strchr (txt,')')) && (s - txt) &&
  796. !s[1]) {
  797.       *s = ''; /* tie off status data */
  798. /* initialize data block */
  799.       status.flags = status.messages = status.recent = status.unseen =
  800. status.uidnext = status.uidvalidity = 0;
  801.       ucase (txt); /* do case-independent match */
  802.       while (*txt && (s = strchr (txt,' '))) {
  803. *s++ = ''; /* tie off status attribute name */
  804. i = strtoul (s,&s,10);/* get attribute value */
  805. if (!strcmp (txt,"MESSAGES")) {
  806.   status.flags |= SA_MESSAGES;
  807.   status.messages = i;
  808. }
  809. else if (!strcmp (txt,"RECENT")) {
  810.   status.flags |= SA_RECENT;
  811.   status.recent = i;
  812. }
  813. else if (!strcmp (txt,"UNSEEN")) {
  814.   status.flags |= SA_UNSEEN;
  815.   status.unseen = i;
  816. }
  817. else if (!strcmp (txt,"UIDNEXT") || !strcmp (txt,"UID-NEXT")) {
  818.   status.flags |= SA_UIDNEXT;
  819.   status.uidnext = i;
  820. }
  821. else if (!strcmp (txt,"UIDVALIDITY")|| !strcmp (txt,"UID-VALIDITY")){
  822.   status.flags |= SA_UIDVALIDITY;
  823.   status.uidvalidity = i;
  824. }
  825. /* next attribute */
  826. txt = (*s == ' ') ? s + 1 : s;
  827.       }
  828.       strcpy (strchr (strcpy (LOCAL->tmp,stream->mailbox),'}') + 1,t);
  829. /* pass status to main program */
  830.       mm_status (stream,LOCAL->tmp,&status);
  831.     }
  832.     fs_give ((void **) &t);
  833.   }
  834.   else if ((!strcmp (reply->key,"LIST") || !strcmp (reply->key,"LSUB")) &&
  835.    (*reply->text == '(') && (s = strchr (reply->text,')')) &&
  836.    (s[1] == ' ')) {
  837.     char delimiter = '';
  838.     if (!reply->text) {
  839.       mm_log ("Missing LIST/LSUB data",WARN);
  840.       return;
  841.     }
  842.     *s++ = ''; /* tie off attribute list */
  843. /* parse attribute list */
  844.     if (t = (char *) strtok (reply->text+1," ")) do {
  845.       if (!strcmp (ucase (t),"\NOINFERIORS")) i |= LATT_NOINFERIORS;
  846.       else if (!strcmp (t,"\NOSELECT")) i |= LATT_NOSELECT;
  847.       else if (!strcmp (t,"\MARKED")) i |= LATT_MARKED;
  848.       else if (!strcmp (t,"\UNMARKED")) i |= LATT_UNMARKED;
  849. /* ignore extension flags */
  850.     }
  851.     while (t = (char *) strtok (NIL," "));
  852.     switch (*++s) { /* process delimiter */
  853.     case 'N': /* NIL */
  854.     case 'n':
  855.       s += 4; /* skip over NIL<space> */
  856.       break;
  857.     case '"': /* have a delimiter */
  858.       delimiter = (*++s == '\') ? *++s : *s;
  859.       s += 3; /* skip over <delimiter><quote><space> */
  860.     }
  861. /* need to prepend a prefix? */
  862.     if (LOCAL->prefix) strcpy (LOCAL->tmp,LOCAL->prefix);
  863.     else LOCAL->tmp[0] = ''; /* no prefix needed */
  864. /* need to do string parse? */
  865.     if ((*s == '"') || (*s == '{')) {
  866.       strcat (LOCAL->tmp,t = imap_parse_string (stream,&s,reply,NIL,NIL));
  867.       fs_give ((void **) &t);
  868.     }
  869.     else strcat(LOCAL->tmp,s);/* atom is easy */
  870.     if (reply->key[1] == 'S') mm_lsub (stream,delimiter,LOCAL->tmp,i);
  871.     else mm_list (stream,delimiter,LOCAL->tmp,i);
  872.   }
  873.   else if (!strcmp (reply->key,"MAILBOX")) {
  874.     if (!reply->text) {
  875.       mm_log ("Missing MAILBOX data",WARN);
  876.       return;
  877.     }
  878.     if (LOCAL->prefix)
  879.       sprintf (t = LOCAL->tmp,"%s%s",LOCAL->prefix,reply->text);
  880.     else t = reply->text;
  881.     mm_list (stream,NIL,t,NIL);
  882.   }
  883.   else if (!strcmp (reply->key,"OK") || !strcmp (reply->key,"PREAUTH"))
  884.     imap_parse_response (stream,reply->text,NIL,T);
  885.   else if (!strcmp (reply->key,"NO"))
  886.     imap_parse_response (stream,reply->text,WARN,T);
  887.   else if (!strcmp (reply->key,"BAD"))
  888.     imap_parse_response (stream,reply->text,ERROR,T);
  889.   else if (!strcmp (reply->key,"BYE")) {
  890.     LOCAL->byeseen = T; /* note that a BYE seen */
  891.     imap_parse_response (stream,reply->text,BYE,T);
  892.   }
  893.   else if (!strcmp (reply->key,"CAPABILITY")) {
  894. /* only do something if have text */
  895.     if (reply->text && (t = (char *) strtok (ucase (reply->text)," "))) do {
  896.       if (!strcmp (t,"IMAP4")) LOCAL->imap4 = T;
  897.       else if (!strcmp (t,"IMAP4REV1")) LOCAL->imap4rev1 = T;
  898.       else if (!strcmp (t,"NAMESPACE")) LOCAL->use_namespace = T;
  899.       else if (!strcmp (t,"MAILBOX-REFERRALS")) LOCAL->use_mbx_ref = T;
  900.       else if (!strcmp (t,"LOGIN-REFERRALS")) LOCAL->use_log_ref = T;
  901.       else if (!strcmp (t,"SCAN")) LOCAL->use_scan = T;
  902.       else if (!strncmp (t,"SORT",4)) LOCAL->use_sort = T;
  903.       else if (!strncmp (t,"THREAD=",7)) {
  904. THREADER *thread = (THREADER *) fs_get (sizeof (THREADER));
  905. thread->name = cpystr (t+7);
  906. thread->dispatch = NIL;
  907. thread->next = LOCAL->threader;
  908. LOCAL->threader = thread;
  909.       }
  910.       else if (!strncmp (t,"AUTH",4) && ((t[4] == '=') || (t[4] == '-'))) {
  911. if ((i = mail_lookup_auth_name (t+5,stream->secure)) &&
  912.     (--i < MAXAUTHENTICATORS)) LOCAL->use_auth |= (1 << i);
  913. else if (!strcmp (t+5,"ANONYMOUS")) LOCAL->use_authanon = T;
  914.       }
  915. /* unsupported IMAP4 extension */
  916.       else if (!strcmp (t,"STATUS")) LOCAL->use_status = T;
  917. /* ignore other capabilities */
  918.     }
  919.     while (t = (char *) strtok (NIL," "));
  920.   }
  921.   else {
  922.     sprintf (LOCAL->tmp,"Unexpected untagged message: %.80s",reply->key);
  923.     mm_log (LOCAL->tmp,WARN);
  924.   }
  925. }
  926. /* Parse human-readable response text
  927.  * Accepts: mail stream
  928.  *     text
  929.  *     error level for mm_notify()
  930.  *     non-NIL if want mm_notify() event even if no response code
  931.  */
  932. void imap_parse_response (MAILSTREAM *stream,char *text,long errflg,long ntfy)
  933. {
  934.   char *s,*t;
  935.   size_t i;
  936.   if (text && (*text == '[') && (t = strchr (s = text + 1,']')) &&
  937.       ((i = t - s) < IMAPTMPLEN)) {
  938.     LOCAL->tmp[i] = ''; /* make mungable copy of text code */
  939.     if (s = strchr (strncpy (LOCAL->tmp,s,i),' ')) *s++ = '';
  940.     ucase (LOCAL->tmp); /* make code uppercase */
  941.     if (s) { /* have argument? */
  942.       ntfy = NIL; /* suppress mm_notify if normal SELECT data */
  943.       if (!strcmp (LOCAL->tmp,"UIDVALIDITY"))
  944. stream->uid_validity = strtoul (s,NIL,10);
  945.       else if (!strcmp (LOCAL->tmp,"UIDNEXT"))
  946. stream->uid_last = strtoul (s,NIL,10) - 1;
  947.       else if (!strcmp (LOCAL->tmp,"PERMANENTFLAGS") && (*s == '(') &&
  948.        (LOCAL->tmp[i-1] == ')')) {
  949. LOCAL->tmp[i-1] = ''; /* tie off flags */
  950. stream->perm_seen = stream->perm_deleted = stream->perm_answered =
  951.   stream->perm_draft = stream->kwd_create = NIL;
  952. stream->perm_user_flags = NIL;
  953. if (s = strtok (s+1," ")) do {
  954.   if (*ucase (s) == '\') { /* system flags */
  955.     if (!strcmp (s,"\SEEN")) stream->perm_seen = T;
  956.     else if (!strcmp (s,"\DELETED")) stream->perm_deleted = T;
  957.     else if (!strcmp (s,"\FLAGGED")) stream->perm_flagged = T;
  958.     else if (!strcmp (s,"\ANSWERED")) stream->perm_answered = T;
  959.     else if (!strcmp (s,"\DRAFT")) stream->perm_draft = T;
  960.     else if (!strcmp (s,"\*")) stream->kwd_create = T;
  961.   }
  962.   else stream->perm_user_flags |= imap_parse_user_flag (stream,s);
  963. }
  964. while (s = strtok (NIL," "));
  965.       }
  966.       else { /* all other response code events */
  967. ntfy = T; /* must mm_notify() */
  968. if (!strcmp (LOCAL->tmp,"REFERRAL"))
  969.   LOCAL->referral = cpystr (LOCAL->tmp + 9);
  970.       }
  971.     }
  972.     else { /* no arguments */
  973.       if (!strcmp (LOCAL->tmp,"UIDNOTSTICKY")) {
  974. ntfy = NIL;
  975. stream->uid_nosticky = T;
  976.       }
  977.       else if (!strcmp (LOCAL->tmp,"READ-ONLY")) stream->rdonly = T;
  978.       else if (!strcmp (LOCAL->tmp,"READ-WRITE")) stream->rdonly = NIL;
  979.     }
  980.   }
  981. /* give event to main program */
  982.   if (ntfy && !stream->silent) mm_notify (stream,text ? text : "",errflg);
  983. }
  984. /* Parse a namespace
  985.  * Accepts: mail stream
  986.  *     current text pointer
  987.  *     parsed reply
  988.  * Returns: namespace list, text pointer updated
  989.  */
  990. NAMESPACE *imap_parse_namespace (MAILSTREAM *stream,char **txtptr,
  991.  IMAPPARSEDREPLY *reply)
  992. {
  993.   NAMESPACE *ret = NIL;
  994.   NAMESPACE *nam = NIL;
  995.   NAMESPACE *prev = NIL;
  996.   PARAMETER *par = NIL;
  997.   if (*txtptr) { /* only if argument given */
  998. /* ignore leading space */
  999.     while (**txtptr == ' ') ++*txtptr;
  1000.     switch (**txtptr) {
  1001.     case 'N': /* if NIL */
  1002.     case 'n':
  1003.       ++*txtptr; /* bump past "N" */
  1004.       ++*txtptr; /* bump past "I" */
  1005.       ++*txtptr; /* bump past "L" */
  1006.       break;
  1007.     case '(':
  1008.       ++*txtptr; /* skip past open paren */
  1009.       while (**txtptr == '(') {
  1010. ++*txtptr; /* skip past open paren */
  1011. prev = nam; /* note previous if any */
  1012. nam = (NAMESPACE *) memset (fs_get (sizeof (NAMESPACE)),0,
  1013.   sizeof (NAMESPACE));
  1014. if (!ret) ret = nam; /* if first time note first namespace */
  1015. /* if previous link new block to it */
  1016. if (prev) prev->next = nam;
  1017. nam->name = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1018. /* ignore whitespace */
  1019. while (**txtptr == ' ') ++*txtptr;
  1020. switch (**txtptr) { /* parse delimiter */
  1021. case 'N':
  1022. case 'n':
  1023.   *txtptr += 3; /* bump past "NIL" */
  1024.   break;
  1025. case '"':
  1026.   if (*++*txtptr == '\') nam->delimiter = *++*txtptr;
  1027.   else nam->delimiter = **txtptr;
  1028.   *txtptr += 2; /* bump past character and closing quote */
  1029.   break;
  1030. default:
  1031.   sprintf (LOCAL->tmp,"Missing delimiter in namespace: %.80s",*txtptr);
  1032.   mm_log (LOCAL->tmp,WARN);
  1033.   *txtptr = NIL; /* stop parse */
  1034.   return ret;
  1035. }
  1036. while (**txtptr == ' '){/* append new parameter to tail */
  1037.   if (nam->param) par = par->next = mail_newbody_parameter ();
  1038.   else nam->param = par = mail_newbody_parameter ();
  1039.   if (!(par->attribute = imap_parse_string (stream,txtptr,reply,NIL,
  1040.     NIL))) {
  1041.     mm_log ("Missing namespace extension attribute",WARN);
  1042.     par->attribute = cpystr ("UNKNOWN");
  1043.   }
  1044.   if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,NIL))){
  1045.     sprintf (LOCAL->tmp,"Missing value for namespace attribute %.80s",
  1046.      par->attribute);
  1047.     mm_log (LOCAL->tmp,WARN);
  1048.     par->value = cpystr ("UNKNOWN");
  1049.   }
  1050. }
  1051. if (**txtptr == ')') ++*txtptr;
  1052. else { /* missing trailing paren */
  1053.   sprintf (LOCAL->tmp,"Junk at end of namespace: %.80s",*txtptr);
  1054.   mm_log (LOCAL->tmp,WARN);
  1055.   return ret;
  1056. }
  1057.       }
  1058.       if (**txtptr == ')') { /* expected trailing paren? */
  1059. ++*txtptr; /* got it! */
  1060. break;
  1061.       }
  1062.     default:
  1063.       sprintf (LOCAL->tmp,"Not a namespace: %.80s",*txtptr);
  1064.       mm_log (LOCAL->tmp,WARN);
  1065.       *txtptr = NIL; /* stop parse now */
  1066.       break;
  1067.     }
  1068.   }
  1069.   return ret;
  1070. }
  1071. /* Parse a thread node list
  1072.  * Accepts: current text pointer
  1073.  * Returns: thread node list, text pointer updated
  1074.  */
  1075. THREADNODE *imap_parse_thread (char **txtptr)
  1076. {
  1077.   THREADNODE *ret = NIL;
  1078.   THREADNODE *last = NIL;
  1079.   THREADNODE *cur = NIL;
  1080.   THREADNODE *n;
  1081.   while (**txtptr == '(') { /* see a thread? */
  1082.     ++*txtptr; /* skip past open paren */
  1083.     while (**txtptr && (isdigit (**txtptr) || (**txtptr == '('))) {
  1084. /* thread branch */
  1085.       if (**txtptr == '(') n = imap_parse_thread (txtptr);
  1086. /* threaded message number */
  1087.       else (n = mail_newthreadnode (NIL))->num = strtoul (*txtptr,txtptr,10);
  1088. /* new current node */
  1089.       if (cur) cur = cur->next = n;
  1090.       else { /* entirely new thread */
  1091. /* plop at end of tree */
  1092. if (ret) last = last->branch = cur = n;
  1093. /* entirely new tree */
  1094. else ret = last = cur = n;
  1095.       }
  1096. /* skip past any space */
  1097.       if (**txtptr == ' ') ++*txtptr;
  1098.     }
  1099.     if (**txtptr == ')') { /* end of thread? */
  1100.       ++*txtptr; /* skip past end of thread */
  1101.       cur = NIL; /* close current thread */
  1102.     }
  1103.     else break;
  1104.   }
  1105.   return ret;
  1106. }
  1107. /* Parse RFC822 message header
  1108.  * Accepts: MAIL stream
  1109.  *     envelope to parse into
  1110.  *     header as sized text
  1111.  *     stringlist if partial header
  1112.  */
  1113. void imap_parse_header (MAILSTREAM *stream,ENVELOPE **env,SIZEDTEXT *hdr,
  1114. STRINGLIST *stl)
  1115. {
  1116.   ENVELOPE *nenv;
  1117. /* parse what we can from this header */
  1118.   rfc822_parse_msg (&nenv,NIL,(char *) hdr->data,hdr->size,NIL,
  1119.     imap_host (stream),stream->dtb->flags);
  1120.   if (*env) { /* need to merge this header into envelope? */
  1121.     if (!(*env)->newsgroups) { /* need Newsgroups? */
  1122.       (*env)->newsgroups = nenv->newsgroups;
  1123.       (*env)->ngbogus = nenv->ngbogus;
  1124.       nenv->newsgroups = NIL;
  1125.     }
  1126.     if (!(*env)->followup_to) { /* need Followup-To? */
  1127.       (*env)->followup_to = nenv->followup_to;
  1128.       nenv->followup_to = NIL;
  1129.     }
  1130.     if (!(*env)->references) { /* need References? */
  1131.       (*env)->references = nenv->references;
  1132.       nenv->references = NIL;
  1133.     }
  1134.     mail_free_envelope (&nenv);
  1135.   }
  1136. /* otherwise set it to this envelope */
  1137.   else (*env = nenv)->incomplete = stl ? T : NIL;
  1138. }
  1139. /* IMAP parse envelope
  1140.  * Accepts: MAIL stream
  1141.  *     pointer to envelope pointer
  1142.  *     current text pointer
  1143.  *     parsed reply
  1144.  *
  1145.  * Updates text pointer
  1146.  */
  1147. void imap_parse_envelope (MAILSTREAM *stream,ENVELOPE **env,char **txtptr,
  1148.   IMAPPARSEDREPLY *reply)
  1149. {
  1150.   ENVELOPE *oenv = *env;
  1151.   char c = *((*txtptr)++); /* grab first character */
  1152. /* ignore leading spaces */
  1153.   while (c == ' ') c = *((*txtptr)++);
  1154.   switch (c) { /* dispatch on first character */
  1155.   case '(': /* if envelope S-expression */
  1156.     *env = mail_newenvelope (); /* parse the new envelope */
  1157.     (*env)->date = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1158.     (*env)->subject = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1159.     (*env)->from = imap_parse_adrlist (stream,txtptr,reply);
  1160.     (*env)->sender = imap_parse_adrlist (stream,txtptr,reply);
  1161.     (*env)->reply_to = imap_parse_adrlist (stream,txtptr,reply);
  1162.     (*env)->to = imap_parse_adrlist (stream,txtptr,reply);
  1163.     (*env)->cc = imap_parse_adrlist (stream,txtptr,reply);
  1164.     (*env)->bcc = imap_parse_adrlist (stream,txtptr,reply);
  1165.     (*env)->in_reply_to = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1166.     (*env)->message_id = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1167.     if (oenv) { /* need to merge old envelope? */
  1168.       (*env)->newsgroups = oenv->newsgroups;
  1169.       oenv->newsgroups = NIL;
  1170.       (*env)->ngbogus = oenv->ngbogus;
  1171.       (*env)->followup_to = oenv->followup_to;
  1172.       oenv->followup_to = NIL;
  1173.       (*env)->references = oenv->references;
  1174.       oenv->references = NIL;
  1175.       mail_free_envelope(&oenv);/* free old envelope */
  1176.     }
  1177.     if (**txtptr != ')') {
  1178.       sprintf (LOCAL->tmp,"Junk at end of envelope: %.80s",*txtptr);
  1179.       mm_log (LOCAL->tmp,WARN);
  1180.     }
  1181.     else ++*txtptr; /* skip past delimiter */
  1182.     break;
  1183.   case 'N': /* if NIL */
  1184.   case 'n':
  1185.     ++*txtptr; /* bump past "I" */
  1186.     ++*txtptr; /* bump past "L" */
  1187.     break;
  1188.   default:
  1189.     sprintf (LOCAL->tmp,"Not an envelope: %.80s",*txtptr);
  1190.     mm_log (LOCAL->tmp,WARN);
  1191.     break;
  1192.   }
  1193. }
  1194. /* IMAP parse address list
  1195.  * Accepts: MAIL stream
  1196.  *     current text pointer
  1197.  *     parsed reply
  1198.  * Returns: address list, NIL on failure
  1199.  *
  1200.  * Updates text pointer
  1201.  */
  1202. ADDRESS *imap_parse_adrlist (MAILSTREAM *stream,char **txtptr,
  1203.      IMAPPARSEDREPLY *reply)
  1204. {
  1205.   ADDRESS *adr = NIL;
  1206.   char c = **txtptr; /* sniff at first character */
  1207. /* ignore leading spaces */
  1208.   while (c == ' ') c = *++*txtptr;
  1209.   ++*txtptr; /* skip past open paren */
  1210.   switch (c) {
  1211.   case '(': /* if envelope S-expression */
  1212.     adr = imap_parse_address (stream,txtptr,reply);
  1213.     if (**txtptr != ')') {
  1214.       sprintf (LOCAL->tmp,"Junk at end of address list: %.80s",*txtptr);
  1215.       mm_log (LOCAL->tmp,WARN);
  1216.     }
  1217.     else ++*txtptr; /* skip past delimiter */
  1218.     break;
  1219.   case 'N': /* if NIL */
  1220.   case 'n':
  1221.     ++*txtptr; /* bump past "I" */
  1222.     ++*txtptr; /* bump past "L" */
  1223.     break;
  1224.   default:
  1225.     sprintf (LOCAL->tmp,"Not an address: %.80s",*txtptr);
  1226.     mm_log (LOCAL->tmp,WARN);
  1227.     break;
  1228.   }
  1229.   return adr;
  1230. }
  1231. /* IMAP parse address
  1232.  * Accepts: MAIL stream
  1233.  *     current text pointer
  1234.  *     parsed reply
  1235.  * Returns: address, NIL on failure
  1236.  *
  1237.  * Updates text pointer
  1238.  */
  1239. ADDRESS *imap_parse_address (MAILSTREAM *stream,char **txtptr,
  1240.      IMAPPARSEDREPLY *reply)
  1241. {
  1242.   ADDRESS *adr = NIL;
  1243.   ADDRESS *ret = NIL;
  1244.   ADDRESS *prev = NIL;
  1245.   char c = **txtptr; /* sniff at first address character */
  1246.   switch (c) {
  1247.   case '(': /* if envelope S-expression */
  1248.     while (c == '(') { /* recursion dies on small stack machines */
  1249.       ++*txtptr; /* skip past open paren */
  1250.       if (adr) prev = adr; /* note previous if any */
  1251.       adr = mail_newaddr (); /* instantiate address and parse its fields */
  1252.       adr->personal = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1253.       adr->adl = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1254.       adr->mailbox = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1255.       adr->host = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1256.       if (**txtptr != ')') { /* handle trailing paren */
  1257. sprintf (LOCAL->tmp,"Junk at end of address: %.80s",*txtptr);
  1258. mm_log (LOCAL->tmp,WARN);
  1259.       }
  1260.       else ++*txtptr; /* skip past close paren */
  1261.       c = **txtptr; /* set up for while test */
  1262. /* ignore leading spaces in front of next */
  1263.       while (c == ' ') c = *++*txtptr;
  1264.       if (!ret) ret = adr; /* if first time note first adr */
  1265. /* if previous link new block to it */
  1266.       if (prev) prev->next = adr;
  1267.     }
  1268.     break;
  1269.   case 'N': /* if NIL */
  1270.   case 'n':
  1271.     *txtptr += 3; /* bump past NIL */
  1272.     break;
  1273.   default:
  1274.     sprintf (LOCAL->tmp,"Not an address: %.80s",*txtptr);
  1275.     mm_log (LOCAL->tmp,WARN);
  1276.     break;
  1277.   }
  1278.   return ret;
  1279. }
  1280. /* IMAP parse flags
  1281.  * Accepts: current message cache
  1282.  *     current text pointer
  1283.  *
  1284.  * Updates text pointer
  1285.  */
  1286. void imap_parse_flags (MAILSTREAM *stream,MESSAGECACHE *elt,char **txtptr)
  1287. {
  1288.   char *flag;
  1289.   char c = '';
  1290.   struct { /* old flags */
  1291.     unsigned int valid : 1;
  1292.     unsigned int seen : 1;
  1293.     unsigned int deleted : 1;
  1294.     unsigned int flagged : 1;
  1295.     unsigned int answered : 1;
  1296.     unsigned int draft : 1;
  1297.     unsigned long user_flags;
  1298.   } old;
  1299.   old.valid = elt->valid; old.seen = elt->seen; old.deleted = elt->deleted;
  1300.   old.flagged = elt->flagged; old.answered = elt->answered;
  1301.   old.draft = elt->draft; old.user_flags = elt->user_flags;
  1302.   elt->valid = T; /* mark have valid flags now */
  1303.   elt->user_flags = NIL; /* zap old flag values */
  1304.   elt->seen = elt->deleted = elt->flagged = elt->answered = elt->draft =
  1305.     elt->recent = NIL;
  1306.   while (c != ')') { /* parse list of flags */
  1307. /* point at a flag */
  1308.     while (*(flag = ++*txtptr) == ' ');
  1309. /* scan for end of flag */
  1310.     while (**txtptr != ' ' && **txtptr != ')') ++*txtptr;
  1311.     c = **txtptr; /* save delimiter */
  1312.     **txtptr = ''; /* tie off flag */
  1313.     if (!*flag) break; /* null flag */
  1314. /* if starts with  must be sys flag */
  1315.     else if (*ucase (flag) == '\') {
  1316.       if (!strcmp (flag,"\SEEN")) elt->seen = T;
  1317.       else if (!strcmp (flag,"\DELETED")) elt->deleted = T;
  1318.       else if (!strcmp (flag,"\FLAGGED")) elt->flagged = T;
  1319.       else if (!strcmp (flag,"\ANSWERED")) elt->answered = T;
  1320.       else if (!strcmp (flag,"\RECENT")) elt->recent = T;
  1321.       else if (!strcmp (flag,"\DRAFT")) elt->draft = T;
  1322.     }
  1323. /* otherwise user flag */
  1324.     else elt->user_flags |= imap_parse_user_flag (stream,flag);
  1325.   }
  1326.   ++*txtptr; /* bump past delimiter */
  1327.   if (!old.valid || (old.seen != elt->seen) ||
  1328.       (old.deleted != elt->deleted) || (old.flagged != elt->flagged) ||
  1329.       (old.answered != elt->answered) || (old.draft != elt->draft) ||
  1330.       (old.user_flags != elt->user_flags)) mm_flags (stream,elt->msgno);
  1331. }
  1332. /* IMAP parse user flag
  1333.  * Accepts: MAIL stream
  1334.  *     flag name
  1335.  * Returns: flag bit position
  1336.  */
  1337. unsigned long imap_parse_user_flag (MAILSTREAM *stream,char *flag)
  1338. {
  1339.   char tmp[MAILTMPLEN];
  1340.   long i;
  1341. /* sniff through all user flags */
  1342.   for (i = 0; i < NUSERFLAGS; ++i) if (stream->user_flags[i]) {
  1343.     sprintf (tmp,"%.1000s",stream->user_flags[i]);
  1344.     if (!strcmp (flag,ucase (tmp))) return (1 << i);
  1345.   }
  1346.   return (unsigned long) 0; /* not found */
  1347. }
  1348. /* IMAP parse string
  1349.  * Accepts: MAIL stream
  1350.  *     current text pointer
  1351.  *     parsed reply
  1352.  *     mailgets data
  1353.  *     returned string length
  1354.  * Returns: string
  1355.  *
  1356.  * Updates text pointer
  1357.  */
  1358. char *imap_parse_string (MAILSTREAM *stream,char **txtptr,
  1359.  IMAPPARSEDREPLY *reply,GETS_DATA *md,
  1360.  unsigned long *len)
  1361. {
  1362.   char *st;
  1363.   char *string = NIL;
  1364.   unsigned long i,j,k;
  1365.   char c = **txtptr; /* sniff at first character */
  1366.   mailgets_t mg = (mailgets_t) mail_parameters (NIL,GET_GETS,NIL);
  1367.   readprogress_t rp =
  1368.     (readprogress_t) mail_parameters (NIL,GET_READPROGRESS,NIL);
  1369. /* ignore leading spaces */
  1370.   while (c == ' ') c = *++*txtptr;
  1371.   st = ++*txtptr; /* remember start of string */
  1372.   switch (c) {
  1373.   case '"': /* if quoted string */
  1374.     i = 0; /* initial byte count */
  1375.     while (**txtptr != '"') { /* search for end of string */
  1376.       if (**txtptr == '\') ++*txtptr;
  1377.       ++i; /* bump count */
  1378.       ++*txtptr; /* bump pointer */
  1379.     }
  1380.     ++*txtptr; /* bump past delimiter */
  1381.     string = (char *) fs_get ((size_t) i + 1);
  1382.     for (j = 0; j < i; j++) { /* copy the string */
  1383.       if (*st == '\') ++st; /* quoted character */
  1384.       string[j] = *st++;
  1385.     }
  1386.     string[j] = ''; /* tie off string */
  1387.     if (len) *len = i; /* set return value too */
  1388.     if (md && mg) { /* have special routine to slurp string? */
  1389.       STRING bs;
  1390.       if (md->first) { /* partial fetch? */
  1391. md->first--; /* restore origin octet */
  1392. md->last = i; /* number of octets that we got */
  1393.       }
  1394.       INIT (&bs,mail_string,string,i);
  1395.       (*mg) (mail_read,&bs,i,md);
  1396.     }
  1397.     break;
  1398.   case 'N': /* if NIL */
  1399.   case 'n':
  1400.     ++*txtptr; /* bump past "I" */
  1401.     ++*txtptr; /* bump past "L" */
  1402.     if (len) *len = 0;
  1403.     break;
  1404.   case '{': /* if literal string */
  1405. /* get size of string */ 
  1406.     i = strtoul (*txtptr,txtptr,10);
  1407.     if (len) *len = i; /* set return value */
  1408.     if (md && mg) { /* have special routine to slurp string? */
  1409.       if (md->first) { /* partial fetch? */
  1410. md->first--; /* restore origin octet */
  1411. md->last = i; /* number of octets that we got */
  1412.       }
  1413.       else md->flags |= MG_COPY;/* otherwise flag need to copy */
  1414.       string = (*mg) (net_getbuffer,LOCAL->netstream,i,md);
  1415.     }
  1416.     else { /* must slurp into free storage */
  1417.       string = (char *) fs_get ((size_t) i + 1);
  1418.       *string = ''; /* init in case getbuffer fails */
  1419. /* get the literal */
  1420.       if (rp) for (k = 0; j = min ((long) MAILTMPLEN,(long) i); i -= j) {
  1421. net_getbuffer (LOCAL->netstream,j,string + k);
  1422. (*rp) (md,k += j);
  1423.       }
  1424.       else net_getbuffer (LOCAL->netstream,i,string);
  1425.     }
  1426.     fs_give ((void **) &reply->line);
  1427. /* get new reply text line */
  1428.     reply->line = net_getline (LOCAL->netstream);
  1429.     if (stream->debug) mm_dlog (reply->line);
  1430.     *txtptr = reply->line; /* set text pointer to point at it */
  1431.     break;
  1432.   default:
  1433.     sprintf (LOCAL->tmp,"Not a string: %c%.80s",c,*txtptr);
  1434.     mm_log (LOCAL->tmp,WARN);
  1435.     if (len) *len = 0;
  1436.     break;
  1437.   }
  1438.   return string;
  1439. }
  1440. /* Register text in IMAP cache
  1441.  * Accepts: MAIL stream
  1442.  *     message number
  1443.  *     IMAP segment specifier
  1444.  *     header string list (if a HEADER section specifier)
  1445.  *     sized text to register
  1446.  * Returns: non-zero if cache non-empty
  1447.  */
  1448. long imap_cache (MAILSTREAM *stream,unsigned long msgno,char *seg,
  1449.  STRINGLIST *stl,SIZEDTEXT *text)
  1450. {
  1451.   char *t,tmp[MAILTMPLEN];
  1452.   unsigned long i;
  1453.   BODY *b;
  1454.   SIZEDTEXT *ret;
  1455.   STRINGLIST *stc;
  1456.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1457. /* top-level header never does mailgets */
  1458.   if (!strcmp (seg,"HEADER") || !strcmp (seg,"0") ||
  1459.       !strcmp (seg,"HEADER.FIELDS") || !strcmp (seg,"HEADER.FIELDS.NOT")) {
  1460.     ret = &elt->private.msg.header.text;
  1461.     if (text) { /* don't do this if no text */
  1462.       if (ret->data) fs_give ((void **) &ret->data);
  1463.       mail_free_stringlist (&elt->private.msg.lines);
  1464.       elt->private.msg.lines = stl;
  1465. /* prevent cache reuse of .NOT */
  1466.       if ((seg[0] == 'H') && (seg[6] == '.') && (seg[13] == '.'))
  1467. for (stc = stl; stc; stc = stc->next) stc->text.size = 0;
  1468.       if (stream->scache) { /* short caching puts it in the stream */
  1469. if (stream->msgno != msgno) {
  1470. /* flush old stuff */
  1471.   mail_free_envelope (&stream->env);
  1472.   mail_free_body (&stream->body);
  1473.   stream->msgno = msgno;
  1474. }
  1475. imap_parse_header (stream,&stream->env,text,stl);
  1476.       }
  1477. /* regular caching */
  1478.       else imap_parse_header (stream,&elt->private.msg.env,text,stl);
  1479.     }
  1480.   }
  1481. /* top level text */
  1482.   else if (!strcmp (seg,"TEXT")) {
  1483.     ret = &elt->private.msg.text.text;
  1484.     if (text && ret->data) fs_give ((void **) &ret->data);
  1485.   }
  1486.   else if (!*seg) { /* full message */
  1487.     ret = &elt->private.msg.full.text;
  1488.     if (text && ret->data) fs_give ((void **) &ret->data);
  1489.   }
  1490.   else { /* nested, find non-contents specifier */
  1491.     for (t = seg; *t && !((*t == '.') && (isalpha(t[1]) || !atol (t+1))); t++);
  1492.     if (*t) *t++ = ''; /* tie off section from data specifier */
  1493.     if (!(b = mail_body (stream,msgno,seg))) {
  1494.       sprintf (tmp,"Unknown section number: %.80s",seg);
  1495.       mm_log (tmp,WARN);
  1496.       return NIL;
  1497.     }
  1498.     if (*t) { /* if a non-numberic subpart */
  1499.       if ((i = (b->type == TYPEMESSAGE) && (!strcmp (b->subtype,"RFC822"))) &&
  1500.   (!strcmp (t,"HEADER") || !strcmp (t,"0") ||
  1501.    !strcmp (t,"HEADER.FIELDS") || !strcmp (t,"HEADER.FIELDS.NOT"))) {
  1502. ret = &b->nested.msg->header.text;
  1503. if (text) {
  1504.   if (ret->data) fs_give ((void **) &ret->data);
  1505.   mail_free_stringlist (&b->nested.msg->lines);
  1506.   b->nested.msg->lines = stl;
  1507. /* prevent cache reuse of .NOT */
  1508.   if ((t[0] == 'H') && (t[6] == '.') && (t[13] == '.'))
  1509.     for (stc = stl; stc; stc = stc->next) stc->text.size = 0;
  1510.   imap_parse_header (stream,&b->nested.msg->env,text,stl);
  1511. }
  1512.       }
  1513.       else if (i && !strcmp (t,"TEXT")) {
  1514. ret = &b->nested.msg->text.text;
  1515. if (text && ret->data) fs_give ((void **) &ret->data);
  1516.       }
  1517. /* otherwise it must be MIME */
  1518.       else if (!strcmp (t,"MIME")) {
  1519. ret = &b->mime.text;
  1520. if (text && ret->data) fs_give ((void **) &ret->data);
  1521.       }
  1522.       else {
  1523. sprintf (tmp,"Unknown section specifier: %.80s.%.80s",seg,t);
  1524. mm_log (tmp,WARN);
  1525. return NIL;
  1526.       }
  1527.     }
  1528.     else { /* ordinary contents */
  1529.       ret = &b->contents.text;
  1530.       if (text && ret->data) fs_give ((void **) &ret->data);
  1531.     }
  1532.   }
  1533.   if (text) { /* update cache if requested */
  1534.     ret->data = text->data;
  1535.     ret->size = text->size;
  1536.   }
  1537.   return ret->data ? LONGT : NIL;
  1538. }
  1539. /* IMAP parse body structure
  1540.  * Accepts: MAIL stream
  1541.  *     body structure to write into
  1542.  *     current text pointer
  1543.  *     parsed reply
  1544.  *
  1545.  * Updates text pointer
  1546.  */
  1547. void imap_parse_body_structure (MAILSTREAM *stream,BODY *body,char **txtptr,
  1548. IMAPPARSEDREPLY *reply)
  1549. {
  1550.   int i;
  1551.   char *s;
  1552.   PART *part = NIL;
  1553.   char c = *((*txtptr)++); /* grab first character */
  1554. /* ignore leading spaces */
  1555.   while (c == ' ') c = *((*txtptr)++);
  1556.   switch (c) { /* dispatch on first character */
  1557.   case '(': /* body structure list */
  1558.     if (**txtptr == '(') { /* multipart body? */
  1559.       body->type= TYPEMULTIPART;/* yes, set its type */
  1560.       do { /* instantiate new body part */
  1561. if (part) part = part->next = mail_newbody_part ();
  1562. else body->nested.part = part = mail_newbody_part ();
  1563. /* parse it */
  1564. imap_parse_body_structure (stream,&part->body,txtptr,reply);
  1565.       } while (**txtptr == '(');/* for each body part */
  1566.       if (body->subtype = imap_parse_string (stream,txtptr,reply,NIL,NIL))
  1567. ucase (body->subtype);
  1568.       else {
  1569. mm_log ("Missing multipart subtype",WARN);
  1570. body->subtype = cpystr (rfc822_default_subtype (body->type));
  1571.       }
  1572.       if (**txtptr == ' ') /* multipart parameters */
  1573. body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
  1574.       if (**txtptr == ' ') /* disposition */
  1575. imap_parse_disposition (stream,body,txtptr,reply);
  1576.       if (**txtptr == ' ') /* language */
  1577. body->language = imap_parse_language (stream,txtptr,reply);
  1578.       while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
  1579.       if (**txtptr != ')') { /* validate ending */
  1580. sprintf (LOCAL->tmp,"Junk at end of multipart body: %.80s",*txtptr);
  1581. mm_log (LOCAL->tmp,WARN);
  1582.       }
  1583.       else ++*txtptr; /* skip past delimiter */
  1584.     }
  1585.     else { /* not multipart, parse type name */
  1586.       if (**txtptr == ')') { /* empty body? */
  1587. ++*txtptr; /* bump past it */
  1588. break; /* and punt */
  1589.       }
  1590.       body->type = TYPEOTHER; /* assume unknown type */
  1591.       body->encoding = ENCOTHER;/* and unknown encoding */
  1592. /* parse type */
  1593.       if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL)) {
  1594. ucase (s);
  1595. for (i=0;(i<=TYPEMAX) && body_types[i] && strcmp(s,body_types[i]);i++);
  1596. if (i <= TYPEMAX) { /* only if found a slot */
  1597.   body->type = i; /* set body type */
  1598.   if (body_types[i]) fs_give ((void **) &s);
  1599.   else body_types[i]=s; /* assign empty slot */
  1600. }
  1601.       }
  1602. /* parse subtype */
  1603.       if (body->subtype = imap_parse_string (stream,txtptr,reply,NIL,NIL))
  1604. ucase (body->subtype);
  1605.       else {
  1606. mm_log ("Missing body subtype",WARN);
  1607. body->subtype = cpystr (rfc822_default_subtype (body->type));
  1608.       }
  1609.       body->parameter = imap_parse_body_parameter (stream,txtptr,reply);
  1610.       body->id = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1611.       body->description = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1612.       if (s = imap_parse_string (stream,txtptr,reply,NIL,NIL)) {
  1613. ucase (s); /* search for body encoding */
  1614. for (i = 0; (i <= ENCMAX) && body_encodings[i] &&
  1615.      strcmp (s,body_encodings[i]); i++);
  1616. if (i > ENCMAX) body->type = ENCOTHER;
  1617. else { /* only if found a slot */
  1618.   body->encoding = i; /* set body encoding */
  1619.   if (body_encodings[i]) fs_give ((void **) &s);
  1620. /* assign empty slot */
  1621.   else body_encodings[i] = s;
  1622. }
  1623.       }
  1624. /* parse size of contents in bytes */
  1625.       body->size.bytes = strtoul (*txtptr,txtptr,10);
  1626.       switch (body->type) { /* possible extra stuff */
  1627.       case TYPEMESSAGE: /* message envelope and body */
  1628. if (strcmp (body->subtype,"RFC822")) break;
  1629. body->nested.msg = mail_newmsg ();
  1630. imap_parse_envelope (stream,&body->nested.msg->env,txtptr,reply);
  1631. body->nested.msg->body = mail_newbody ();
  1632. imap_parse_body_structure (stream,body->nested.msg->body,txtptr,reply);
  1633. /* drop into text case */
  1634.       case TYPETEXT: /* size in lines */
  1635. body->size.lines = strtoul (*txtptr,txtptr,10);
  1636. break;
  1637.       default: /* otherwise nothing special */
  1638. break;
  1639.       }
  1640.       if (**txtptr == ' ') /* if extension data */
  1641. body->md5 = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1642.       if (**txtptr == ' ') /* disposition */
  1643. imap_parse_disposition (stream,body,txtptr,reply);
  1644.       if (**txtptr == ' ') /* language */
  1645. body->language = imap_parse_language (stream,txtptr,reply);
  1646.       while (**txtptr == ' ') imap_parse_extension (stream,txtptr,reply);
  1647.       if (**txtptr != ')') { /* validate ending */
  1648. sprintf (LOCAL->tmp,"Junk at end of body part: %.80s",*txtptr);
  1649. mm_log (LOCAL->tmp,WARN);
  1650.       }
  1651.       else ++*txtptr; /* skip past delimiter */
  1652.     }
  1653.     break;
  1654.   case 'N': /* if NIL */
  1655.   case 'n':
  1656.     ++*txtptr; /* bump past "I" */
  1657.     ++*txtptr; /* bump past "L" */
  1658.     break;
  1659.   default: /* otherwise quite bogus */
  1660.     sprintf (LOCAL->tmp,"Bogus body structure: %.80s",*txtptr);
  1661.     mm_log (LOCAL->tmp,WARN);
  1662.     break;
  1663.   }
  1664. }
  1665. /* IMAP parse body parameter
  1666.  * Accepts: MAIL stream
  1667.  *     current text pointer
  1668.  *     parsed reply
  1669.  * Returns: body parameter
  1670.  * Updates text pointer
  1671.  */
  1672. PARAMETER *imap_parse_body_parameter (MAILSTREAM *stream,char **txtptr,
  1673.       IMAPPARSEDREPLY *reply)
  1674. {
  1675.   PARAMETER *ret = NIL;
  1676.   PARAMETER *par = NIL;
  1677.   char c,*s;
  1678. /* ignore leading spaces */
  1679.   while ((c = *(*txtptr)++) == ' ');
  1680. /* parse parameter list */
  1681.   if (c == '(') while (c != ')') {
  1682. /* append new parameter to tail */
  1683.     if (ret) par = par->next = mail_newbody_parameter ();
  1684.     else ret = par = mail_newbody_parameter ();
  1685.     if(!(par->attribute=imap_parse_string (stream,txtptr,reply,NIL,NIL))) {
  1686.       mm_log ("Missing parameter attribute",WARN);
  1687.       par->attribute = cpystr ("UNKNOWN");
  1688.     }
  1689.     if (!(par->value = imap_parse_string (stream,txtptr,reply,NIL,NIL))) {
  1690.       sprintf (LOCAL->tmp,"Missing value for parameter %.80s",par->attribute);
  1691.       mm_log (LOCAL->tmp,WARN);
  1692.       par->value = cpystr ("UNKNOWN");
  1693.     }
  1694.     switch (c = **txtptr) { /* see what comes after */
  1695.     case ' ': /* flush whitespace */
  1696.       while ((c = *++*txtptr) == ' ');
  1697.       break;
  1698.     case ')': /* end of attribute/value pairs */
  1699.       ++*txtptr; /* skip past closing paren */
  1700.       break;
  1701.     default:
  1702.       sprintf (LOCAL->tmp,"Junk at end of parameter: %.80s",*txtptr);
  1703.       mm_log (LOCAL->tmp,WARN);
  1704.       break;
  1705.     }
  1706.   }
  1707. /* empty parameter, must be NIL */
  1708.   else if (((c == 'N') || (c == 'n')) &&
  1709.    ((*(s = *txtptr) == 'I') || (*s == 'i')) &&
  1710.    ((s[1] == 'L') || (s[1] == 'l'))) *txtptr += 2;
  1711.   else {
  1712.     sprintf (LOCAL->tmp,"Bogus body parameter: %c%.80s",c,(*txtptr) - 1);
  1713.     mm_log (LOCAL->tmp,WARN);
  1714.   }
  1715.   return ret;
  1716. }
  1717. /* IMAP parse body disposition
  1718.  * Accepts: MAIL stream
  1719.  *     body structure to write into
  1720.  *     current text pointer
  1721.  *     parsed reply
  1722.  */
  1723. void imap_parse_disposition (MAILSTREAM *stream,BODY *body,char **txtptr,
  1724.      IMAPPARSEDREPLY *reply)
  1725. {
  1726.   switch (*++*txtptr) {
  1727.   case '(':
  1728.     ++*txtptr; /* skip open paren */
  1729.     body->disposition.type = imap_parse_string (stream,txtptr,reply,NIL,NIL);
  1730.     body->disposition.parameter =
  1731.       imap_parse_body_parameter (stream,txtptr,reply);
  1732.     if (**txtptr != ')') { /* validate ending */
  1733.       sprintf (LOCAL->tmp,"Junk at end of disposition: %.80s",*txtptr);
  1734.       mm_log (LOCAL->tmp,WARN);
  1735.     }
  1736.     else ++*txtptr; /* skip past delimiter */
  1737.     break;
  1738.   case 'N': /* if NIL */
  1739.   case 'n':
  1740.     ++*txtptr; /* bump past "N" */
  1741.     ++*txtptr; /* bump past "I" */
  1742.     ++*txtptr; /* bump past "L" */
  1743.     break;
  1744.   default:
  1745.     sprintf (LOCAL->tmp,"Unknown body disposition: %.80s",*txtptr);
  1746.     mm_log (LOCAL->tmp,WARN);
  1747. /* try to skip to next space */
  1748.     while ((*++*txtptr != ' ') && (**txtptr != ')') && **txtptr);
  1749.     break;
  1750.   }
  1751. }
  1752. /* IMAP parse body language
  1753.  * Accepts: MAIL stream
  1754.  *     current text pointer
  1755.  *     parsed reply
  1756.  * Returns: string list or NIL if empty or error
  1757.  */
  1758. STRINGLIST *imap_parse_language (MAILSTREAM *stream,char **txtptr,
  1759.  IMAPPARSEDREPLY *reply)
  1760. {
  1761.   unsigned long i;
  1762.   char *s;
  1763.   STRINGLIST *ret = NIL;
  1764. /* language is a list */
  1765.   if (*++*txtptr == '(') ret = imap_parse_stringlist (stream,txtptr,reply);
  1766.   else if (s = imap_parse_string (stream,txtptr,reply,NIL,&i)) {
  1767.     (ret = mail_newstringlist ())->text.data = (unsigned char *) s;
  1768.     ret->text.size = i;
  1769.   }
  1770.   return ret;
  1771. }
  1772. /* IMAP parse string list
  1773.  * Accepts: MAIL stream
  1774.  *     current text pointer
  1775.  *     parsed reply
  1776.  * Returns: string list or NIL if empty or error
  1777.  */
  1778. STRINGLIST *imap_parse_stringlist (MAILSTREAM *stream,char **txtptr,
  1779.    IMAPPARSEDREPLY *reply)
  1780. {
  1781.   STRINGLIST *stl = NIL;
  1782.   STRINGLIST *stc = NIL;
  1783.   char c,*s;
  1784.   char *t = *txtptr;
  1785. /* parse the list */
  1786.   if (*t++ == '(') while (*t != ')') {
  1787.     if (stl) stc = stc->next = mail_newstringlist ();
  1788.     else stc = stl = mail_newstringlist ();
  1789. /* atom */
  1790.     if ((*t != '{') && (*t != '"') && (s = strpbrk (t," )"))) {
  1791.       c = *s; /* note delimiter */
  1792.       *s = ''; /* tie off atom and copy it*/
  1793.       stc->text.size = strlen ((char *) (stc->text.data =
  1794.  (unsigned char *) cpystr (t)));
  1795.       if (c == ' ') t = ++s; /* another token follows */
  1796.       else *(t = s) = c; /* restore delimiter */
  1797.     }
  1798. /* string */
  1799.     else if (!(stc->text.data = (unsigned char *)
  1800.        imap_parse_string (stream,&t,reply,NIL,&stc->text.size))) {
  1801.       sprintf (LOCAL->tmp,"Bogus string list member: %.80s",t);
  1802.       mm_log (LOCAL->tmp,WARN);
  1803.       mail_free_stringlist (&stl);
  1804.       break;
  1805.     }
  1806.     else if (*t == ' ') ++t; /* another token follows */
  1807.   }
  1808.   if (stl) *txtptr = ++t; /* update return string */
  1809.   return stl;
  1810. }
  1811. /* IMAP parse unknown body extension data
  1812.  * Accepts: MAIL stream
  1813.  *     current text pointer
  1814.  *     parsed reply
  1815.  *
  1816.  * Updates text pointer
  1817.  */
  1818. void imap_parse_extension (MAILSTREAM *stream,char **txtptr,
  1819.    IMAPPARSEDREPLY *reply)
  1820. {
  1821.   unsigned long i,j;
  1822.   switch (*++*txtptr) { /* action depends upon first character */
  1823.   case '(':
  1824.     while (**txtptr != ')') imap_parse_extension (stream,txtptr,reply);
  1825.     ++*txtptr; /* bump past closing parenthesis */
  1826.     break;
  1827.   case '"': /* if quoted string */
  1828.     while (*++*txtptr != '"') if (**txtptr == '\') ++*txtptr;
  1829.     ++*txtptr; /* bump past closing quote */
  1830.     break;
  1831.   case 'N': /* if NIL */
  1832.   case 'n':
  1833.     ++*txtptr; /* bump past "N" */
  1834.     ++*txtptr; /* bump past "I" */
  1835.     ++*txtptr; /* bump past "L" */
  1836.     break;
  1837.   case '{': /* get size of literal */
  1838.     ++*txtptr; /* bump past open squiggle */
  1839.     if (i = strtoul (*txtptr,txtptr,10)) do
  1840.       net_getbuffer (LOCAL->netstream,j = max (i,(long)IMAPTMPLEN),LOCAL->tmp);
  1841.     while (i -= j);
  1842. /* get new reply text line */
  1843.     reply->line = net_getline (LOCAL->netstream);
  1844.     if (stream->debug) mm_dlog (reply->line);
  1845.     *txtptr = reply->line; /* set text pointer to point at it */
  1846.     break;
  1847.   case '0': case '1': case '2': case '3': case '4':
  1848.   case '5': case '6': case '7': case '8': case '9':
  1849.     strtoul (*txtptr,txtptr,10);
  1850.     break;
  1851.   default:
  1852.     sprintf (LOCAL->tmp,"Unknown extension token: %.80s",*txtptr);
  1853.     mm_log (LOCAL->tmp,WARN);
  1854. /* try to skip to next space */
  1855.     while ((*++*txtptr != ' ') && (**txtptr != ')') && **txtptr);
  1856.     break;
  1857.   }
  1858. }
  1859. /* IMAP return host name
  1860.  * Accepts: MAIL stream
  1861.  * Returns: host name
  1862.  */
  1863. char *imap_host (MAILSTREAM *stream)
  1864. {
  1865. /* return host name on stream if open */
  1866.   return (LOCAL && LOCAL->netstream) ? net_host (LOCAL->netstream) :
  1867.     ".NO-IMAP-CONNECTION.";
  1868. }